diff options
author | Neil Alexander <neilalexander@users.noreply.github.com> | 2020-08-11 18:19:11 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-11 18:19:11 +0100 |
commit | bcdf9577a3db0727b4966cfdd3c4471ca6d3f1f6 (patch) | |
tree | bc9af1f38e6113e56d597c68e708bea587c49dc0 /federationapi | |
parent | 8b6ab272fb06e59d7747f3f52e1bdd462a52a463 (diff) |
Support for server ACLs (#1261)
* First pass at server ACLs (not efficient)
* Use transaction origin, update whitelist
* Fix federation API test
It's sufficient for us to return nothing in response to current state, so that the server ACL check returns no ACLs.
* More efficient server ACLs - hopefully
* Fix queries
* Fix queries
* Avoid panics by nil pointers
* Bug fixes
* Fix state event type
* Fix mutex
* Update logging
* Ignore port when matching servername
* Use read mutex
* Fix bugs
* Fix sync API test
* Comments
* Add tests, tweaks to behaviour
* Fix test output
Diffstat (limited to 'federationapi')
-rw-r--r-- | federationapi/federationapi.go | 1 | ||||
-rw-r--r-- | federationapi/routing/routing.go | 106 | ||||
-rw-r--r-- | federationapi/routing/send.go | 10 | ||||
-rw-r--r-- | federationapi/routing/send_test.go | 45 |
4 files changed, 155 insertions, 7 deletions
diff --git a/federationapi/federationapi.go b/federationapi/federationapi.go index e9a8e40a..e838e8e3 100644 --- a/federationapi/federationapi.go +++ b/federationapi/federationapi.go @@ -41,7 +41,6 @@ func AddPublicRoutes( stateAPI currentstateAPI.CurrentStateInternalAPI, keyAPI keyserverAPI.KeyInternalAPI, ) { - routing.Setup( router, cfg, rsAPI, eduAPI, federationSenderAPI, keyRing, diff --git a/federationapi/routing/routing.go b/federationapi/routing/routing.go index 88fd8757..027827fa 100644 --- a/federationapi/routing/routing.go +++ b/federationapi/routing/routing.go @@ -82,7 +82,7 @@ func Setup( func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return Send( httpReq, request, gomatrixserverlib.TransactionID(vars["txnID"]), - cfg, rsAPI, eduAPI, keyAPI, keys, federation, + cfg, rsAPI, eduAPI, keyAPI, stateAPI, keys, federation, ) }, )).Methods(http.MethodPut, http.MethodOptions) @@ -90,6 +90,12 @@ func Setup( v1fedmux.Handle("/invite/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_invite", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } res := InviteV1( httpReq, request, vars["roomID"], vars["eventID"], cfg, rsAPI, keys, @@ -106,6 +112,12 @@ func Setup( v2fedmux.Handle("/invite/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_invite", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } return InviteV2( httpReq, request, vars["roomID"], vars["eventID"], cfg, rsAPI, keys, @@ -140,6 +152,12 @@ func Setup( v1fedmux.Handle("/state/{roomID}", httputil.MakeFedAPI( "federation_get_state", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } return GetState( httpReq.Context(), request, rsAPI, vars["roomID"], ) @@ -149,6 +167,12 @@ func Setup( v1fedmux.Handle("/state_ids/{roomID}", httputil.MakeFedAPI( "federation_get_state_ids", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } return GetStateIDs( httpReq.Context(), request, rsAPI, vars["roomID"], ) @@ -158,6 +182,12 @@ func Setup( v1fedmux.Handle("/event_auth/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_get_event_auth", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } return GetEventAuth( httpReq.Context(), request, rsAPI, vars["roomID"], vars["eventID"], ) @@ -194,6 +224,12 @@ func Setup( v1fedmux.Handle("/make_join/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_make_join", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } roomID := vars["roomID"] eventID := vars["eventID"] queryVars := httpReq.URL.Query() @@ -219,6 +255,12 @@ func Setup( v1fedmux.Handle("/send_join/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_send_join", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } roomID := vars["roomID"] eventID := vars["eventID"] res := SendJoin( @@ -245,6 +287,12 @@ func Setup( v2fedmux.Handle("/send_join/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_send_join", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } roomID := vars["roomID"] eventID := vars["eventID"] return SendJoin( @@ -256,6 +304,12 @@ func Setup( v1fedmux.Handle("/make_leave/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_make_leave", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } roomID := vars["roomID"] eventID := vars["eventID"] return MakeLeave( @@ -264,9 +318,47 @@ func Setup( }, )).Methods(http.MethodGet) + v1fedmux.Handle("/send_leave/{roomID}/{eventID}", httputil.MakeFedAPI( + "federation_send_leave", cfg.Matrix.ServerName, keys, wakeup, + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } + roomID := vars["roomID"] + eventID := vars["eventID"] + res := SendLeave( + httpReq, request, cfg, rsAPI, keys, roomID, eventID, + ) + // not all responses get wrapped in [code, body] + var body interface{} + body = []interface{}{ + res.Code, res.JSON, + } + jerr, ok := res.JSON.(*jsonerror.MatrixError) + if ok { + body = jerr + } + + return util.JSONResponse{ + Headers: res.Headers, + Code: res.Code, + JSON: body, + } + }, + )).Methods(http.MethodPut) + v2fedmux.Handle("/send_leave/{roomID}/{eventID}", httputil.MakeFedAPI( "federation_send_leave", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } roomID := vars["roomID"] eventID := vars["eventID"] return SendLeave( @@ -285,6 +377,12 @@ func Setup( v1fedmux.Handle("/get_missing_events/{roomID}", httputil.MakeFedAPI( "federation_get_missing_events", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } return GetMissingEvents(httpReq, request, rsAPI, vars["roomID"]) }, )).Methods(http.MethodPost) @@ -292,6 +390,12 @@ func Setup( v1fedmux.Handle("/backfill/{roomID}", httputil.MakeFedAPI( "federation_backfill", cfg.Matrix.ServerName, keys, wakeup, func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { + if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("Forbidden by server ACLs"), + } + } return Backfill(httpReq, request, rsAPI, vars["roomID"], cfg) }, )).Methods(http.MethodGet) diff --git a/federationapi/routing/send.go b/federationapi/routing/send.go index d1aa728c..cad77921 100644 --- a/federationapi/routing/send.go +++ b/federationapi/routing/send.go @@ -21,6 +21,7 @@ import ( "net/http" "github.com/matrix-org/dendrite/clientapi/jsonerror" + currentstateAPI "github.com/matrix-org/dendrite/currentstateserver/api" eduserverAPI "github.com/matrix-org/dendrite/eduserver/api" "github.com/matrix-org/dendrite/internal/config" keyapi "github.com/matrix-org/dendrite/keyserver/api" @@ -39,6 +40,7 @@ func Send( rsAPI api.RoomserverInternalAPI, eduAPI eduserverAPI.EDUServerInputAPI, keyAPI keyapi.KeyInternalAPI, + stateAPI currentstateAPI.CurrentStateInternalAPI, keys gomatrixserverlib.JSONVerifier, federation *gomatrixserverlib.FederationClient, ) util.JSONResponse { @@ -46,6 +48,7 @@ func Send( context: httpReq.Context(), rsAPI: rsAPI, eduAPI: eduAPI, + stateAPI: stateAPI, keys: keys, federation: federation, haveEvents: make(map[string]*gomatrixserverlib.HeaderedEvent), @@ -104,6 +107,7 @@ type txnReq struct { rsAPI api.RoomserverInternalAPI eduAPI eduserverAPI.EDUServerInputAPI keyAPI keyapi.KeyInternalAPI + stateAPI currentstateAPI.CurrentStateInternalAPI keys gomatrixserverlib.JSONVerifier federation txnFederationClient // local cache of events for auth checks, etc - this may include events @@ -164,6 +168,12 @@ func (t *txnReq) processTransaction() (*gomatrixserverlib.RespSend, *util.JSONRe util.GetLogger(t.context).WithError(err).Warnf("Transaction: Failed to parse event JSON of event %s", string(pdu)) continue } + if currentstateAPI.IsServerBannedFromRoom(t.context, t.stateAPI, event.RoomID(), t.Origin) { + results[event.EventID()] = gomatrixserverlib.PDUResult{ + Error: "Forbidden by server ACLs", + } + continue + } if err = gomatrixserverlib.VerifyAllEventSignatures(t.context, []gomatrixserverlib.Event{event}, t.keys); err != nil { util.GetLogger(t.context).WithError(err).Warnf("Transaction: Couldn't validate signature of event %q", event.EventID()) results[event.EventID()] = gomatrixserverlib.PDUResult{ diff --git a/federationapi/routing/send_test.go b/federationapi/routing/send_test.go index bfbdaa5f..5d3ed230 100644 --- a/federationapi/routing/send_test.go +++ b/federationapi/routing/send_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + currentstateAPI "github.com/matrix-org/dendrite/currentstateserver/api" eduAPI "github.com/matrix-org/dendrite/eduserver/api" fsAPI "github.com/matrix-org/dendrite/federationsender/api" "github.com/matrix-org/dendrite/internal/test" @@ -294,6 +295,33 @@ func (t *testRoomserverAPI) RemoveRoomAlias( return fmt.Errorf("not implemented") } +type testStateAPI struct { +} + +func (t *testStateAPI) QueryCurrentState(ctx context.Context, req *currentstateAPI.QueryCurrentStateRequest, res *currentstateAPI.QueryCurrentStateResponse) error { + return nil +} + +func (t *testStateAPI) QueryRoomsForUser(ctx context.Context, req *currentstateAPI.QueryRoomsForUserRequest, res *currentstateAPI.QueryRoomsForUserResponse) error { + return fmt.Errorf("not implemented") +} + +func (t *testStateAPI) QueryBulkStateContent(ctx context.Context, req *currentstateAPI.QueryBulkStateContentRequest, res *currentstateAPI.QueryBulkStateContentResponse) error { + return fmt.Errorf("not implemented") +} + +func (t *testStateAPI) QuerySharedUsers(ctx context.Context, req *currentstateAPI.QuerySharedUsersRequest, res *currentstateAPI.QuerySharedUsersResponse) error { + return fmt.Errorf("not implemented") +} + +func (t *testStateAPI) QueryKnownUsers(ctx context.Context, req *currentstateAPI.QueryKnownUsersRequest, res *currentstateAPI.QueryKnownUsersResponse) error { + return fmt.Errorf("not implemented") +} + +func (t *testStateAPI) QueryServerBannedFromRoom(ctx context.Context, req *currentstateAPI.QueryServerBannedFromRoomRequest, res *currentstateAPI.QueryServerBannedFromRoomResponse) error { + return nil +} + type txnFedClient struct { state map[string]gomatrixserverlib.RespState // event_id to response stateIDs map[string]gomatrixserverlib.RespStateIDs // event_id to response @@ -338,11 +366,12 @@ func (c *txnFedClient) LookupMissingEvents(ctx context.Context, s gomatrixserver return c.getMissingEvents(missing) } -func mustCreateTransaction(rsAPI api.RoomserverInternalAPI, fedClient txnFederationClient, pdus []json.RawMessage) *txnReq { +func mustCreateTransaction(rsAPI api.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI, fedClient txnFederationClient, pdus []json.RawMessage) *txnReq { t := &txnReq{ context: context.Background(), rsAPI: rsAPI, eduAPI: &testEDUProducer{}, + stateAPI: stateAPI, keys: &test.NopJSONVerifier{}, federation: fedClient, haveEvents: make(map[string]*gomatrixserverlib.HeaderedEvent), @@ -422,10 +451,11 @@ func TestBasicTransaction(t *testing.T) { } }, } + stateAPI := &testStateAPI{} pdus := []json.RawMessage{ testData[len(testData)-1], // a message event } - txn := mustCreateTransaction(rsAPI, &txnFedClient{}, pdus) + txn := mustCreateTransaction(rsAPI, stateAPI, &txnFedClient{}, pdus) mustProcessTransaction(t, txn, nil) assertInputRoomEvents(t, rsAPI.inputRoomEvents, []gomatrixserverlib.HeaderedEvent{testEvents[len(testEvents)-1]}) } @@ -444,10 +474,11 @@ func TestTransactionFailAuthChecks(t *testing.T) { } }, } + stateAPI := &testStateAPI{} pdus := []json.RawMessage{ testData[len(testData)-1], // a message event } - txn := mustCreateTransaction(rsAPI, &txnFedClient{}, pdus) + txn := mustCreateTransaction(rsAPI, stateAPI, &txnFedClient{}, pdus) mustProcessTransaction(t, txn, []string{ // expect the event to have an error testEvents[len(testEvents)-1].EventID(), @@ -502,6 +533,8 @@ func TestTransactionFetchMissingPrevEvents(t *testing.T) { }, } + stateAPI := &testStateAPI{} + cli := &txnFedClient{ getMissingEvents: func(missing gomatrixserverlib.MissingEvents) (res gomatrixserverlib.RespMissingEvents, err error) { if !reflect.DeepEqual(missing.EarliestEvents, []string{haveEvent.EventID()}) { @@ -521,7 +554,7 @@ func TestTransactionFetchMissingPrevEvents(t *testing.T) { pdus := []json.RawMessage{ inputEvent.JSON(), } - txn := mustCreateTransaction(rsAPI, cli, pdus) + txn := mustCreateTransaction(rsAPI, stateAPI, cli, pdus) mustProcessTransaction(t, txn, nil) assertInputRoomEvents(t, rsAPI.inputRoomEvents, []gomatrixserverlib.HeaderedEvent{prevEvent, inputEvent}) } @@ -671,10 +704,12 @@ func TestTransactionFetchMissingStateByStateIDs(t *testing.T) { }, } + stateAPI := &testStateAPI{} + pdus := []json.RawMessage{ eventD.JSON(), } - txn := mustCreateTransaction(rsAPI, cli, pdus) + txn := mustCreateTransaction(rsAPI, stateAPI, cli, pdus) mustProcessTransaction(t, txn, nil) assertInputRoomEvents(t, rsAPI.inputRoomEvents, []gomatrixserverlib.HeaderedEvent{eventB, eventC, eventD}) } |