aboutsummaryrefslogtreecommitdiff
path: root/federationapi
diff options
context:
space:
mode:
authorNeil Alexander <neilalexander@users.noreply.github.com>2020-08-11 18:19:11 +0100
committerGitHub <noreply@github.com>2020-08-11 18:19:11 +0100
commitbcdf9577a3db0727b4966cfdd3c4471ca6d3f1f6 (patch)
treebc9af1f38e6113e56d597c68e708bea587c49dc0 /federationapi
parent8b6ab272fb06e59d7747f3f52e1bdd462a52a463 (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.go1
-rw-r--r--federationapi/routing/routing.go106
-rw-r--r--federationapi/routing/send.go10
-rw-r--r--federationapi/routing/send_test.go45
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})
}