Gnuxie 2 months ago committed by GitHub
commit fe2c9fce51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,173 @@
# MSC4099: Participation based authorization for servers in the Matrix DAG
This is a proposal for the representation of servers and their basic responsibilities in the Matrix
DAG. This MSC does not define or amend a state resolution algorithm, since there are several possible
routes that can be explored with other MSCs. We make considerations to allow this proposal to be
implemented on top of the existing `m.room.member`/`m.room.power_levels` centric authorization and
state resolution algorithms.
The key merits of this proposal are:
- Authorization rules are changed so that there is no way for a server to append an event to the DAG
until they have been explicitly named in a new authorization event, `m.room.server.participation`.
- All events have to be authorized with a corresponding `m.room.server.participation` event for their
origin server.
- Room admins and their tools now have the ability to examine joining servers before making a decision
to permit them to participate in the room. This can be thought of as the equivalent of "knocking for servers"[^knocking].
- We envisage that for most rooms, permitting servers to participate will happen quickly and automatically,
probably before the server even attempts to join the room if they already well known and trusted within
the community[^tooling-for-accepting].
Additional merits that can be explored as an indirect result of this proposal:
- A way for servers to preemptively load and cache rooms that their users are likely to join.
- A way for servers to advertise to other servers about rooms that their users are likely to join,
so that these rooms can be optionally pre-loaded and cached.
This is a more specific component and redesign of the general idea of [MSC3953: Server capability DAG](https://github.com/Gnuxie/matrix-doc/blob/gnuxie/capability-dag/proposals/3953-capability-dag.md).
## Context
### The role of the existing `m.room.member` for a user
In order to develop ideas about how to represent server membership in the room DAG,
which is NOT what this proposal does, we need to understand the responsibilities that `m.room.member`
already has:
- A representation of the desire for the user's server to be informed of new events.
- The capability for the server to participate in the room on behalf of the user,
being used to authorize the user's events.
- The capability for the user to backfill in relation to visibility.
+ it is unclear to me whether `m.room.history_visibility` restricts a server's ability to backfill or not.
- A representation of the user's profile and participation information, who they are, why they are in the room, avatar, and displayname.
## Proposal
### Considerations for amending the `make_join` handshake
When a joining server is instructed by a client to join a room, the joining server sends an
EDU, `m.server.knock`, to any available resident server that the joining server is aware of.
The server then waits until it receives an `m.server.participation` event, which will contain the
joining server's name within the `state_key`.
The `m.server.participation` event can be received from any resident server that is participating
in the room. However, the `m.server.participation` event should only be sent by the room admins.
When `m.server.participation`'s `participation` field has the value `permitted`, then
the joining server can begin to use `make_join` and `send_join`. However, `send_join` could be amended
in another MSC so that a server is able to produce an `m.server.subscription` configuration event,
rather than an `m.room.member` event for a specific user. This is so that a server can begin the
process of joining the room in advance of a user accepting or joining the room via a client,
in order to improve the response time.
### The: `m.server.knock` EDU
`m.server.knock` is an EDU to make a client in a resident server aware of the joining server's intent
to join the room. This client will usually be a room admin. A client can then arbitrarily research
the reputation of the joining server before deciding whether resident servers of the room should
accept any PDU whatsoever from the joining server. Currently in room V11 and below, it is not
possible for room operators to stop a new server from sending multiple PDUs to a room without first
knowing of, and anticipating a malicious server's existence. This is a fact which has already
presented major problems in Matrix's history.
This proposal does not just aim to remove the risk of spam joins for members from the same server,
but also spam joins from many servers at the same time. While it is seen as technically difficult
to acquire user accounts from a large number of Matrix homeservers, it is still possible and
has happened before. For example, servers could become compromised with a common exploit in a server
implementation. Existing servers that have weak registration requirements could also be exploited,
and this has happened already in Matrix's past.
Having an EDU allows us to accept a knock arbitrarily with clients, and more accurately automated bots
like Draupnir. We can then arbitrarily research the reputation of the server before deciding
to accept. This also conveniently keeps auth_rules around restricted join rules clean and simple,
because all logic can be deferred to clients.
The `m.server.knock` EDU can be treated as idempotent by the receiver, although the effect should
expire after a duration that is subjective to the receiver.
```
{
"content": {
"room_id": "!example:example.com",
},
"edu_type": "m.server.knock"
}
```
### The `m.server.participation` event, `state_key: ${serverName}`
This is a capability that allows the server named in the `state_key` to send `m.server.subscription`,
it is sent to accept the `m.server.knock` EDU. The event can also be used to make a server aware of
a room's existence, so that it can be optionally preload and cache a room before the server's users
discover it.
`participation` can be one of `permitted` or `deny`. When `participation` is `permitted`, the server
is able to join the room. When `participation` is `denied`, then the server is not allowed to send
any PDU's into the room. The denied server must not be sent a `m.server.participation` event unless
the targeted is already present within the room, or it has attempted to knock.
This is to prevent malicious servers being made aware of rooms that they have not yet discovered.
A `reason` field can be present alongside `participation` in order to explain the reason why
a server has been `denied`. This reason is to be shown to the knocking, or previously present
server, so that they can understand what has happened.
### The `m.server.subscription` event, `state_key: ${serverName}`
This is a configuration event that uses the `m.server.participation` capability to manage
the server's subscription to the event stream. This is NOT an authorization event.
This is distinct from `m.server.participation` because this event is exclusively controlled
by the participating server, and other servers cannot modify this event[^spec-discussion].
This allows the server to have exclusive control over whether it is to be sent events (where
its participation is still `permitted`). We specifically do not want to merge this with
`participation` to avoid having to specialise state resolution for write conflicts,
or "force joining" servers back into rooms. This allows a server to remain permitted to participate,
but opt out of receiving further events from this room, and can then optionally stop replicating the
room and delete all persistent data relating to it (should all clients have also forgotten the room).
### Considerations for event authorization
All events that a server can send need to be authorized by an `m.server.participation` event
with the field `participation` with a value of `permitted`.
## Potential issues
### Permitting, then denying a malicious server.
The feature in principle that a malicious server can never send a PDU into the room can be worked
around if the server manages to have their `participation` `permitted` at some point in the room's
history. Since now they can create PDU's that reference this stale state, and all the other
participating servers have no option but to soft fail these events
(ignoring that we don't block them at the network level).
While this is still a huge improvement over the existing situation, we need suggestions for how
to stop this at the event authorization level. I'm begging for advice.
### Unclear if a joining server can receive a PDU from a room that it is not joined to
The amendments to the join handshake described in this MSC mean that a server has to wait
for a PDU, `m.server.participation` before it has attempted to join the room beyond sending an EDU.
It's not clear to me whether this is currently possible or changes are required to federation send.
### Surely the joining server needs to send the EDU via resident servers, so `make_join` has to be modified
The EDU `m.server.knock` surely has to be sent via a resident server so that it can be received
by all servers within the room.
## Alternatives
## Security considerations
## Unstable prefix
## Dependencies
None.
[^spec-discussion]: This was derived from the following spec discussion: https://matrix.to/#/!NasysSDfxKxZBzJJoE:matrix.org/$0pv9JVVKzuRE6mVBUGQMq44vNTZ1-l19yFcKgqt8Zl8?via=matrix.org&via=envs.net&via=element.io
[^knocking]: Although, knocking is implemented with the auth event `m.room.member` we don't want joining
servers to be able to send any event to the room at all (other than the `m.server.knock` EDU).
[^tooling-for-accepting]: Though now I say this, we probably need to be able to demonstrate that
this will be the case. A lot of this is now looking obvious, why weren't we thinking about this
years ago? Well, there's a lot of context. There always is buddy, you've got the easy view of hindsight.
Someone had to both conceive and write this and get us out of the dark ages. Ths MSC looks poorer
in my eyes by the minute.
Loading…
Cancel
Save