Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC3593: Safety Controls through a generic Administration API #3593

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 310 additions & 0 deletions proposals/3593-admin-safety-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
# MSC3593: Safety Controls through a generic Administration API

## Abstract

This MSC tries to do two things;
- Define a foundation on how to expose a generic administration API to matrix clients
- Define 4 core APIs on that interface with regard to server oversight and moderation functionality.

## Background & Rationale

Historically, matrix administration tasks have been done through a non-specced API on synapse, which
today lives under the `_synapse/` prefix.

This API handles everything from background updates to registration tokens, documentation on this is
available on it's own
[documentation page](https://matrix-org.github.io/synapse/latest/usage/administration/admin_api/index.html).

However, this API is specific to Synapse, and unspecced. Other servers, such as Dendrite and
Conduit, would have to implement their own ways to expose admin interfaces.

Conduit already does so by exposing an internal room with bot commands.

Meanwhile, clients could like to integrate administration tasks into their own interfaces, making
server moderation easy and seamless. Today this cannot be executed, as it would mean a reliance on
unspecced interfaces or integrations such as Synapse's custom API, or Conduit's Bot commands.

## Proposal

This is what this proposal wishes to fix; a generic interface through which users could request
administrative data and functionality from its own server.

A non-goal of this proposal, and this interface, is to exhaustively replicate or spec the entirety
of Synapse's admin API. Instead, it wishes to give a foundation of properties to easily have future
MSCs integrate and work off of.

To give this base some tests, base functionality, and recognition, it'll (with it) bring 4 endpoints
that'll render core administration functionality that is common to all matrix servers today.

### Historic note

Currently in the matrix spec, a "module" called ["server
administration"](https://spec.matrix.org/v1.1/client-server-api/#server-administration) exists, this
module only exposes a `/whois` endpoint.

This proposal wishes to "adopt" this endpoint into the proposed foundation, and use the
`/_matrix/client/vX/admin/` prefix as its base.

### Capability API

This interface will not (as of yet) provide an in-band way to promote users to be able to do
administration tasks, this will stay out-of-band for now.

However, a client would wish to know which APIs it is *able* to access at any time, and for this, a
"capability" will be exposed.

This proposal will call the user for which this capability is exposed a "Capable User", this is to
have an as-ambiguous naming as possible for such an endpoint, as then homeserver implementations can
focus on sub-dividing this API according to its own philosophy of administration (i.e. let it by
itself define ideas such as "admin", "mod", or even "janitor", all according to its own rules.)

As such, the Capability API will look like so;

```
GET /_matrix/client/v1/admin/capabilities
Authentication: yes

Response:
200: Json array of Capability
```

A `Capability` is a matrix identifier (java-notated domain) which positively signals that a client
can access a particular endpoint.

A capability could look like so; `m.user.whois`.

A user which would have absolutely no capability to act in any administrative format would receive a
`200` with an empty array: `[]`.

The next section will define some APIs, and will list their corresponding Capability string.

### Safety Controls

This proposal will also add some endpoints pertaining to the most basic form of safety controls.

These endpoints would give some semblance of moderation on a server-scale basis. And with this
addition to the generic interface

#### Listing all active rooms

`GET /_matrix/client/v1/admin/rooms/active`
`Authentication: yes`
`Capability: m.rooms.list.active`

This API lists all rooms on a server which at least one local user has joined.

On `200`, this API returns;
```json5
{
// Positive integer of total results
"count": 1234,
"rooms":[
// Array of room IDs
]
}
```

XXX: Only room IDs are exposed here because we assume the client to fetch+cache a lot of room
information, so that it could more easily be re-arranged. Bots or automated functionality would
likely not have to know those details either, just information about active rooms.

FIXME: How to expose further room details? Capability to request preview for any room?

A few parameters exist;

`?sort`;
- `id`, Lexicographic sort by room ID, "pseudo-random" (default)
- `name`, Lexicographic sort by room Name
- `users`, Descending sort by how many local users have joined this room

Filters;
- `?user=`, Filter on which rooms the exact MXID is currently in
- `?name_s=`, Substring search on room displayname
- `?domain=`, Shorthand `:{}^` regex search on room ID (`example.com` becomes `:example\.com^`)

Pagination;
- `?amount`, Positive integer, the amount of results to return, default 100
- `?offset`, Positive integer, the offset of the returned results, default 0
- `?rev`, bool, Whether to reverse the sort, default `false`

#### Listing all users

`GET /_matrix/client/v1/admin/users/list`
`Authentication: yes`
`Capability: m.users.list`

Lists all users on the local server, this includes appservice and deactivated users.

On `200`, this API returns;
```json5
{
// Positive integer of total results
"count": 1234,
"rooms":[
// Array of user IDs
]
}
```

XXX: Same problem as rooms, we dont expose a lot of info here because we assume the client to
fetch+cache the rest.

FIXME: How to expose further user details? Capability to request profile for any user? How to expose
deactivated and appservice status? Expose corresponding appservice?

A few parameters exist;

`?sort`;
- `id`, Lexicographic sort by user ID (default)
- `displayname`, Lexicographic sort by user profile displayname
- `avatar_url`, Lexicographic sort by user profile avatar MXC URL

Filter;
- `?deactivated`, bool, if Whether to include deactivated users, defaults to `false`,
- `?appservice`, bool, if Whether to include appservice users, defaults to `true`,

Pagination;
- `?amount`, Positive integer, the amount of results to return, default 100
- `?offset`, Positive integer, the offset of the returned results, default 0
- `?rev`, bool, Whether to reverse the sort, default `false`

#### Banning a room ID

`POST /_matrix/client/v1/admin/room/{room_id}/ban`
`Authentication: yes`
`Capability: m.room.ban`

"Bans" a room from a local server, prohibiting all local users from joining or interacting with it.

XXX: Should we also immidiately define an "unban" API in this proposal?

It accepts a request body with the following JSON;
```json5
{
// Have all local users leave the room immediately, defaults to true
"leave": true,
}
```

This API returns `204`.

After a room has been banned, a user will get `403` errors on any attempt to "read" or "write" to a
room, e.g. sending a message, a typing indicator, read indicator, or fetching an event.

If `leave: false`, a server can opt-in emulating/injecting a `leave`-like event in a sync stream if
they wish so to minimize client breakage, but minimizing client synchronization breakage is a
secondary concern to this API.

Note that this API does not ban a room *alias*, but a room *id*, this would still enable third-party
actors to upgrade or switch room IDs sequentially for their activities. A conservative approach such
as this has been taken to ensure at least the basic functionality for prohibiting rooms on a server
can be ensured via a generic interface.

#### Deactivating a user

`POST /_matrix/client/v1/admin/user/{user_id}/deactivate`
`Authentication: yes`
`Capability: m.user.deactivate`

XXX: This API is pretty severe, I'd like to also put a "put your password here" UIA-like interaction
on it, or some way that this cannot be abused once an admin token has been acquired.

Deactivates the user, accepts a JSON body with the following;

```json5
{
// Whether to delete additional profile information of this user, bool, mandatory
"erase": true,
}
```

This does the following; (Partially copied from the synapse API)
- Delete all devices and E2EE keys
- Delete all access tokens
- Delete all pushers
- Delete login information
- Force-leaves the user from all rooms
- Rejects all pending invites

And with `erase: true`;
- Remove the user's profile information;
- Display Name
- Avatar URL

Implementations may also have further actions taken when deactivating a user.

A user deactivation is considered **non-reversible**.

#### XXX: API Design

*Note: These are WIP concerns, please comment on these*

Synapse's admin API has a bit of an inconsistent theme, but one theme is that a GET-POST relation is
kept in some of them.

Blocking rooms (similar to banning rooms here), uses `GET` to retrieve a `{block: true}` body, and
`POST` to set the status. In `GET`, it can also get a user_id of whom has banned this room.

Maybe we should change the ban-room API to that?
`GET /room/{id}/ban`
```json5
{
"banned": true,
"user": "@it_was_me_all_along:example.com"
}
```
`POST /room/{id}/ban` with `banned: true`

This'd possibly also coalesce the `m.room.ban` capability into both reading and writing to these
endpoints.

### Implementation Requirements

While this proposal adds 4 endpoints it strongly requires every server to implement, this "strength"
may not apply to every endpoint.

Some use-cases may exist which do not make sense for some homeservers; a requirement to implement
"freezing" or "quarantining" users, disallowing them any "write" access to matrix, may be costly to
implement with non-critical benefits.

As such, the design of the Capability API deliberately blinds clients to probing if some APIs are
not implemented on a homeserver, this proposal wishes for any future MSCs to add a degree of
"requirement" (such as RFC2119's "MUST", "SHOULD", "COULD") for which these servers are required to
implement these interfaces.

For the sake of brevity, the 4 APIs listed in this proposal are on a "MUST-implement" basis, as they
are core to matrix's safety controls.

*Rationale: This places an admin API to be definition-first, standardizing it. This makes more sense
when looked upon in the context of the matrix ecosystem, if 90% of all homeserver implementations
are able to implement an administration API, and while the remaining 10% wouldn't, would they be
absolutely compelled to implement it, regardless of the complexity cost? This proposal wishes to
standardize common administration functionality, not compel. In the case of safety controls, the
"compelling" displayed in this proposal comes more from the value and need of safety controls
themselves, not from its implementation as a generic API.*

### Possible Future Proposals

This proposal wishes to spawn more proposals, a short list of possible immediate future expansions
on this API could be;
- Reset Passwords
- Freeze/Quarantine User
- List and Manipulate User devices
- List and Manipulate User access tokens
- List and Manipulate Media
- List and Manipulate Registration Tokens
- Peek local rooms
- Show Event Reports

Furthermore, future MSCs could define capabilities which alter the way conventional APIs such as
event fetching or context requests can have their "normal restrictions" be bypassed on admin
override.

## Security Considerations

All of these APIs work off of a user's normal access token, any compromise would enable the attacker
access to this API surface. (Note: This was already the case with synapse's Admin API)
Comment on lines +305 to +306
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the reason that it is recommended to not expose this to the Internet via a reverse proxy (see documentation), and related to why these endpoints are on /_synapse/admin -- this allows easily configuring access controls for any endpoints under that.

I wonder if it would make more sense to place all of these under /_matrix/admin instead? That seems to have obvious downsides though (e.g. if you wanted admin endpoints for a push server or something).

Anyway, this probably needs some words about whether this is meant to be exposed to clients or if it is meant for use in other ways and is just about standardization across homeserver ecosystems.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is primarily meant to be exposed to clients, to make matrix administration simple and straight-forward. I think that security by obscurity (by exposing it on a different domain), or not exposing this at all (which defeats the purpose of these endpoints in the first place), is not an option.

However, I think that a large part of these problems could potentially be mitigated by introducing a mechanism which forces the admin API to be secure even if an attacker has an access token, and for this, I was indeed thinking that the Capable User would have to re-assess their password, just as they would to change their password in the normal endpoints.

For this, maybe an additional endpoint could be introduced which enables "sudo mode", just as github would request when you change or look at sensitive settings, this'd either enable the access token that "sudo mode", or returns an additional token which is valid for such a period. I don't know if this additional complexity in this way is reasonable for the spec, i'm open to finding more ergonomic ways of doing it.

One problem i already see, for example, is that some accounts might not have passwords at all, and attest their identity via third-party SSO, so such a "sudo mode" endpoint would have to be generic across multiple solutions.


The main takeaway is that i'd like for matrix administration to be simple, yet secure, as simple as it can be to be implementable in all clients, accessible via the same CSAPI domain it is pointed to. I get that for larger servers, of course, security has to be more professional and more strict, but that is what this API would allow; modular access which is then set up in a server-specific way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is good to standardise the administration and to organise and build up the URLs conceptually.
IMO the API to not expose is not "security by obscurity". Security by obscurity is when you don't document the API or the urls are random long strings similar to the media files in Matrix.
In this case it is an out of band management.
There is no such thing as 100% security. Not even with a web application firewall. Therefore, the consistent separation of admin and user traffic greatly increases security. In this way, each administrator can decide for themselves whether users should administer via app or not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also just like the APIs to be under /_matrix/admin for visibility. Those APIs need a separate set of permissions and shouldn't be required for normal client applications, but are rather used to manage a server. As such all such endpoints should be under /admin to separate them conceptually. Not from a security or anything stand point, just because I think it belongs there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would make more sense to place all of these under /_matrix/admin instead? That seems to have obvious downsides though (e.g. if you wanted admin endpoints for a push server or something).

Apologies, i didn't realise what you said here earlier.

So, essentially, you're proposing that this is a whole new branch of API, on-par with CS and SS APIs? That could make sense imo, but it would also raise the bar of scrutiny on this proposal.

I'll think about it, the idea is sound, but i also don't know if this specifically resolves the concern of user compromise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a few months now, and I've had time to think about this.

I think that adding these endpoints under a separate API prefix can help with some security aspects.

Additionally, I think that I will add a section to this proposal outlining a compromise between ease and security; Allowing the admin API bearer token to be and-or the user's access token, or a specific token retrieved via another out-of-band means.

This would allow server administration to become easy for simple servers, such as conduit servers, where the server would listen on the same hostname for authentication through bearer tokens.

However, on a large server (such as matrix.org), the endpoints could be bundled together under a different domain, by a different token process, not bundled to someone's access token.

It'd probably also be possible to "bless" a specific (refresh/access) token - also via out-of-band. These are all possible implementation details, however it should be clear that a simple discovery mechanism is to call /capabilities and observe a return. Then within clients there could be a menu section to input this admin API, both hostname and token, for the user's client.

I hope this could be an acceptable resolution, at the cost of making this proposal a bit more complex. I'll submit these changes and ask y'all to make another round of comments on this new approach afterwards.


## Unstable prefix

This MSC has an unstable prefix of `org.matrix.msc3593` for any `vX` or `m.` instance.