diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index bbbdd82a..96a36171 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,6 +3,6 @@ * [ ] I have added Go unit tests or [Complement integration tests](https://github.com/matrix-org/complement) for this PR _or_ I have justified why this PR doesn't need tests -* [ ] Pull request includes a [sign off below using a legally identifiable name](https://matrix-org.github.io/dendrite/development/contributing#sign-off) _or_ I have already signed off privately +* [ ] Pull request includes a [sign off below](https://element-hq.github.io/dendrite/development/contributing#sign-off) _or_ I have already signed off privately Signed-off-by: `Your Name ` diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d3969475..fdf77640 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -98,7 +98,7 @@ jobs: output: "trivy-results.sarif" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: "trivy-results.sarif" diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml index 10eb7c02..d1964069 100644 --- a/.github/workflows/helm.yml +++ b/.github/workflows/helm.yml @@ -27,7 +27,7 @@ jobs: git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - name: Install Helm - uses: azure/setup-helm@v3 + uses: azure/setup-helm@v4 with: version: v3.10.0 diff --git a/.github/workflows/k8s.yml b/.github/workflows/k8s.yml index fd97bf7e..41c671e1 100644 --- a/.github/workflows/k8s.yml +++ b/.github/workflows/k8s.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: azure/setup-helm@v3 + - uses: azure/setup-helm@v4 with: version: v3.10.0 - uses: actions/setup-python@v5 diff --git a/CHANGES.md b/CHANGES.md index 7d69459e..b5947c44 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,23 @@ # Changelog +## Dendrite 0.14.1 (2025-01-16) + +### ⚠ Important + +This is a security release, [gomatrixserverlib](https://github.com/matrix-org/gomatrixserverlib) was vulnerable to +server-side request forgery, serving content from a private network it can access, under certain conditions. + +Upgrading to this version is **highly** recommended. + +### Security + +- Support for blocking access to certain networks, fixing [CVE-2024-52594](https://www.cve.org/CVERecord?id=CVE-2024-52594) and + [GHSA-4ff6-858j-r822](https://github.com/matrix-org/gomatrixserverlib/security/advisories/GHSA-4ff6-858j-r822) + +### Fixes + +- Speed-up loading server ACLs on startup, this is mostly noticeable on larger instances with many rooms. + ## Dendrite 0.14.0 (2024-12-18) This is the first release after forking matrix-org/dendrite, this repository is now licensed under AGPLv3.0. diff --git a/clientapi/routing/key_crosssigning.go b/clientapi/routing/key_crosssigning.go index 94e66040..2a1321f6 100644 --- a/clientapi/routing/key_crosssigning.go +++ b/clientapi/routing/key_crosssigning.go @@ -7,11 +7,12 @@ package routing import ( + "context" "net/http" - "slices" - "strings" "time" + "github.com/sirupsen/logrus" + "github.com/element-hq/dendrite/clientapi/auth" "github.com/element-hq/dendrite/clientapi/auth/authtypes" "github.com/element-hq/dendrite/clientapi/httputil" @@ -29,10 +30,15 @@ type crossSigningRequest struct { Auth newPasswordAuth `json:"auth"` } +type UploadKeysAPI interface { + QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) + api.UploadDeviceKeysAPI +} + func UploadCrossSigningDeviceKeys( - req *http.Request, userInteractiveAuth *auth.UserInteractive, - keyserverAPI api.ClientKeyAPI, device *api.Device, - accountAPI api.ClientUserAPI, cfg *config.ClientAPI, + req *http.Request, + keyserverAPI UploadKeysAPI, device *api.Device, + accountAPI auth.GetAccountByPassword, cfg *config.ClientAPI, ) util.JSONResponse { uploadReq := &crossSigningRequest{} uploadRes := &api.PerformUploadDeviceKeysResponse{} @@ -41,121 +47,58 @@ func UploadCrossSigningDeviceKeys( if resErr != nil { return *resErr } - sessionID := uploadReq.Auth.Session - if sessionID == "" { - sessionID = util.RandomString(sessionIDLength) - } - isCrossSigningSetup := false - masterKeyUpdatableWithoutUIA := false - { - var keysResp api.QueryMasterKeysResponse - keyserverAPI.QueryMasterKeys(req.Context(), &api.QueryMasterKeysRequest{UserID: device.UserID}, &keysResp) - if err := keysResp.Error; err != nil { - return convertKeyError(err) - } - if k := keysResp.Key; k != nil { - isCrossSigningSetup = true - if k.UpdatableWithoutUIABeforeMs != nil { - masterKeyUpdatableWithoutUIA = time.Now().UnixMilli() < *k.UpdatableWithoutUIABeforeMs - } + // Query existing keys to determine if UIA is required + keyResp := api.QueryKeysResponse{} + keyserverAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{ + UserID: device.UserID, + UserToDevices: map[string][]string{device.UserID: {device.ID}}, + Timeout: time.Second * 10, + }, &keyResp) + + if keyResp.Error != nil { + logrus.WithError(keyResp.Error).Error("Failed to query keys") + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.Unknown(keyResp.Error.Error()), } } - { - var keysResp api.QueryKeysResponse - keyserverAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{UserID: device.UserID, UserToDevices: map[string][]string{device.UserID: []string{}}}, &keysResp) - if err := keysResp.Error; err != nil { - return convertKeyError(err) - } - hasDifferentKeys := func(userID string, uploadReqCSKey *fclient.CrossSigningKey, dbCSKeys map[string]fclient.CrossSigningKey) bool { - dbCSKey, ok := dbCSKeys[userID] - if !ok { - return true - } - dbKeysExist := len(dbCSKey.Keys) > 0 - for keyID, key := range uploadReqCSKey.Keys { - // If dbKeysExist is false and we enter the loop, it means we have received at least one key that is not in the DB, and we want to persist it. - if !dbKeysExist { - return true - } - dbKey, ok := dbCSKey.Keys[keyID] - if !ok || !slices.Equal(dbKey, key) { - return true - } - } - return false - } + existingMasterKey, hasMasterKey := keyResp.MasterKeys[device.UserID] + requireUIA := false + if hasMasterKey { + // If we have a master key, check if any of the existing keys differ. If they do, + // we need to re-authenticate the user. + requireUIA = keysDiffer(existingMasterKey, keyResp, uploadReq, device.UserID) + } - if !hasDifferentKeys(device.UserID, &uploadReq.MasterKey, keysResp.MasterKeys) && - !hasDifferentKeys(device.UserID, &uploadReq.SelfSigningKey, keysResp.SelfSigningKeys) && - !hasDifferentKeys(device.UserID, &uploadReq.UserSigningKey, keysResp.UserSigningKeys) { + if requireUIA { + sessionID := uploadReq.Auth.Session + if sessionID == "" { + sessionID = util.RandomString(sessionIDLength) + } + if uploadReq.Auth.Type != authtypes.LoginTypePassword { return util.JSONResponse{ - Code: http.StatusOK, - JSON: map[int]interface{}{}, + Code: http.StatusUnauthorized, + JSON: newUserInteractiveResponse( + sessionID, + []authtypes.Flow{ + { + Stages: []authtypes.LoginType{authtypes.LoginTypePassword}, + }, + }, + nil, + ), } } - } - - if isCrossSigningSetup { - // With MSC3861, UIA is not possible. Instead, the auth service has to explicitly mark the master key as replaceable. - if cfg.MSCs.MSC3861Enabled() { - if !masterKeyUpdatableWithoutUIA { - url := "" - if m := cfg.MSCs.MSC3861; m.AccountManagementURL != "" { - url = strings.Join([]string{m.AccountManagementURL, "?action=", CrossSigningResetStage}, "") - } else { - url = m.Issuer - } - return util.JSONResponse{ - Code: http.StatusUnauthorized, - JSON: newUserInteractiveResponse( - "dummy", - []authtypes.Flow{ - { - Stages: []authtypes.LoginType{CrossSigningResetStage}, - }, - }, - map[string]interface{}{ - CrossSigningResetStage: map[string]string{ - "url": url, - }, - }, - strings.Join([]string{ - "To reset your end-to-end encryption cross-signing identity, you first need to approve it at", - url, - "and then try again.", - }, " "), - ), - } - } - // XXX: is it necessary? - sessions.addCompletedSessionStage(sessionID, CrossSigningResetStage) - } else { - if uploadReq.Auth.Type != authtypes.LoginTypePassword { - return util.JSONResponse{ - Code: http.StatusUnauthorized, - JSON: newUserInteractiveResponse( - sessionID, - []authtypes.Flow{ - { - Stages: []authtypes.LoginType{authtypes.LoginTypePassword}, - }, - }, - nil, - "", - ), - } - } - typePassword := auth.LoginTypePassword{ - GetAccountByPassword: accountAPI.QueryAccountByPassword, - Config: cfg, - } - if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil { - return *authErr - } - sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword) + typePassword := auth.LoginTypePassword{ + GetAccountByPassword: accountAPI, + Config: cfg, } + if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil { + return *authErr + } + sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword) } uploadReq.UserID = device.UserID @@ -192,6 +135,21 @@ func UploadCrossSigningDeviceKeys( } } +func keysDiffer(existingMasterKey fclient.CrossSigningKey, keyResp api.QueryKeysResponse, uploadReq *crossSigningRequest, userID string) bool { + masterKeyEqual := existingMasterKey.Equal(&uploadReq.MasterKey) + if !masterKeyEqual { + return true + } + existingSelfSigningKey := keyResp.SelfSigningKeys[userID] + selfSigningEqual := existingSelfSigningKey.Equal(&uploadReq.SelfSigningKey) + if !selfSigningEqual { + return true + } + existingUserSigningKey := keyResp.UserSigningKeys[userID] + userSigningEqual := existingUserSigningKey.Equal(&uploadReq.UserSigningKey) + return !userSigningEqual +} + func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.ClientKeyAPI, device *api.Device) util.JSONResponse { uploadReq := &api.PerformUploadDeviceSignaturesRequest{} uploadRes := &api.PerformUploadDeviceSignaturesResponse{} diff --git a/clientapi/routing/key_crosssigning_test.go b/clientapi/routing/key_crosssigning_test.go new file mode 100644 index 00000000..0ebb91e0 --- /dev/null +++ b/clientapi/routing/key_crosssigning_test.go @@ -0,0 +1,316 @@ +package routing + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/element-hq/dendrite/setup/config" + "github.com/element-hq/dendrite/test" + "github.com/element-hq/dendrite/test/testrig" + "github.com/element-hq/dendrite/userapi/api" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/gomatrixserverlib/fclient" + "github.com/matrix-org/gomatrixserverlib/spec" +) + +type mockKeyAPI struct { + t *testing.T + userResponses map[string]api.QueryKeysResponse +} + +func (m mockKeyAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) { + res.MasterKeys = m.userResponses[req.UserID].MasterKeys + res.SelfSigningKeys = m.userResponses[req.UserID].SelfSigningKeys + res.UserSigningKeys = m.userResponses[req.UserID].UserSigningKeys + if m.t != nil { + m.t.Logf("QueryKeys: %+v => %+v", req, res) + } +} + +func (m mockKeyAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) { + // Just a dummy upload which always succeeds +} + +func getAccountByPassword(ctx context.Context, req *api.QueryAccountByPasswordRequest, res *api.QueryAccountByPasswordResponse) error { + res.Exists = true + res.Account = &api.Account{UserID: fmt.Sprintf("@%s:%s", req.Localpart, req.ServerName)} + return nil +} + +// Tests that if there is no existing master key for the user, the request is allowed +func Test_UploadCrossSigningDeviceKeys_ValidRequest(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{ + "master_key": {"user_id": "@user:example.com", "usage": ["master"], "keys": {"ed25519:1": "key1"}}, + "self_signing_key": {"user_id": "@user:example.com", "usage": ["self_signing"], "keys": {"ed25519:2": "key2"}}, + "user_signing_key": {"user_id": "@user:example.com", "usage": ["user_signing"], "keys": {"ed25519:3": "key3"}} + }`)) + req.Header.Set("Content-Type", "application/json") + + keyserverAPI := &mockKeyAPI{ + userResponses: map[string]api.QueryKeysResponse{ + "@user:example.com": {}, + }, + } + device := &api.Device{UserID: "@user:example.com", ID: "device"} + cfg := &config.ClientAPI{} + + res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, cfg) + if res.Code != http.StatusOK { + t.Fatalf("expected status %d, got %d", http.StatusOK, res.Code) + } +} + +// Require UIA if there is an existing master key and there is no auth provided. +func Test_UploadCrossSigningDeviceKeys_Unauthorised(t *testing.T) { + userID := "@user:example.com" + + // Note that there is no auth field. + request := fclient.CrossSigningKeys{ + MasterKey: fclient.CrossSigningKey{ + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key1")}, + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster}, + UserID: userID, + }, + SelfSigningKey: fclient.CrossSigningKey{ + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key2")}, + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning}, + UserID: userID, + }, + UserSigningKey: fclient.CrossSigningKey{ + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key3")}, + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning}, + UserID: userID, + }, + } + + b := bytes.Buffer{} + m := json.NewEncoder(&b) + err := m.Encode(request) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest(http.MethodPost, "/", &b) + req.Header.Set("Content-Type", "application/json") + + keyserverAPI := &mockKeyAPI{ + t: t, + userResponses: map[string]api.QueryKeysResponse{ + "@user:example.com": { + MasterKeys: map[string]fclient.CrossSigningKey{ + "@user:example.com": {UserID: "@user:example.com", Usage: []fclient.CrossSigningKeyPurpose{"master"}, Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key1")}}, + }, + SelfSigningKeys: nil, + UserSigningKeys: nil, + }, + }, + } + device := &api.Device{UserID: "@user:example.com", ID: "device"} + cfg := &config.ClientAPI{} + + res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, cfg) + if res.Code != http.StatusUnauthorized { + t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, res.Code) + } +} + +// Invalid JSON is rejected +func Test_UploadCrossSigningDeviceKeys_InvalidJSON(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{ + "auth": {"type": "m.login.password", "session": "session", "user": "user", "password": "password"}, + "master_key": {"user_id": "@user:example.com", "usage": ["master"], "keys": {"ed25519:1": "key1"}}, + "self_signing_key": {"user_id": "@user:example.com", "usage": ["self_signing"], "keys": {"ed25519:2": "key2"}}, + "user_signing_key": {"user_id": "@user:example.com", "usage": ["user_signing"], "keys": {"ed25519:3": "key3"} + }`)) // Missing closing brace + req.Header.Set("Content-Type", "application/json") + + keyserverAPI := &mockKeyAPI{} + device := &api.Device{UserID: "@user:example.com", ID: "device"} + cfg := &config.ClientAPI{} + + res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, cfg) + if res.Code != http.StatusBadRequest { + t.Fatalf("expected status %d, got %d", http.StatusBadRequest, res.Code) + } +} + +// Require UIA if an existing master key is present and the keys differ. +func Test_UploadCrossSigningDeviceKeys_ExistingKeysMismatch(t *testing.T) { + // Again, no auth provided + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{ + "master_key": {"user_id": "@user:example.com", "usage": ["master"], "keys": {"ed25519:1": "key1"}}, + "self_signing_key": {"user_id": "@user:example.com", "usage": ["self_signing"], "keys": {"ed25519:2": "key2"}}, + "user_signing_key": {"user_id": "@user:example.com", "usage": ["user_signing"], "keys": {"ed25519:3": "key3"}} + }`)) + req.Header.Set("Content-Type", "application/json") + + keyserverAPI := &mockKeyAPI{ + userResponses: map[string]api.QueryKeysResponse{ + "@user:example.com": { + MasterKeys: map[string]fclient.CrossSigningKey{ + "@user:example.com": {UserID: "@user:example.com", Usage: []fclient.CrossSigningKeyPurpose{"master"}, Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("different_key")}}, + }, + }, + }, + } + device := &api.Device{UserID: "@user:example.com", ID: "device"} + + cfg, _, _ := testrig.CreateConfig(t, test.DBTypeSQLite) + cfg.Global.ServerName = "example.com" + + res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, &cfg.ClientAPI) + if res.Code != http.StatusUnauthorized { + t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, res.Code) + } +} + +func Test_KeysDiffer_MasterKeyMismatch(t *testing.T) { + existingMasterKey := fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("existing_key")}, + } + keyResp := api.QueryKeysResponse{} + uploadReq := &crossSigningRequest{ + PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{ + CrossSigningKeys: fclient.CrossSigningKeys{ + MasterKey: fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("new_key")}, + }, + }, + }, + } + userID := "@user:example.com" + + result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID) + if !result { + t.Fatalf("expected keys to differ, but they did not") + } +} + +func Test_KeysDiffer_SelfSigningKeyMismatch(t *testing.T) { + existingMasterKey := fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")}, + } + keyResp := api.QueryKeysResponse{ + SelfSigningKeys: map[string]fclient.CrossSigningKey{ + "@user:example.com": { + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("existing_key")}, + }, + }, + } + uploadReq := &crossSigningRequest{ + PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{ + CrossSigningKeys: fclient.CrossSigningKeys{ + SelfSigningKey: fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("new_key")}, + }, + }, + }, + } + userID := "@user:example.com" + + result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID) + if !result { + t.Fatalf("expected keys to differ, but they did not") + } +} + +func Test_KeysDiffer_UserSigningKeyMismatch(t *testing.T) { + existingMasterKey := fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")}, + } + keyResp := api.QueryKeysResponse{ + UserSigningKeys: map[string]fclient.CrossSigningKey{ + "@user:example.com": { + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("existing_key")}, + }, + }, + } + uploadReq := &crossSigningRequest{ + PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{ + CrossSigningKeys: fclient.CrossSigningKeys{ + UserSigningKey: fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("new_key")}, + }, + }, + }, + } + userID := "@user:example.com" + + result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID) + if !result { + t.Fatalf("expected keys to differ, but they did not") + } +} + +func Test_KeysDiffer_AllKeysMatch(t *testing.T) { + existingMasterKey := fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")}, + } + keyResp := api.QueryKeysResponse{ + SelfSigningKeys: map[string]fclient.CrossSigningKey{ + "@user:example.com": { + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("key")}, + }, + }, + UserSigningKeys: map[string]fclient.CrossSigningKey{ + "@user:example.com": { + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("key")}, + }, + }, + } + uploadReq := &crossSigningRequest{ + PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{ + CrossSigningKeys: fclient.CrossSigningKeys{ + MasterKey: fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")}, + }, + SelfSigningKey: fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("key")}, + }, + UserSigningKey: fclient.CrossSigningKey{ + UserID: "@user:example.com", + Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning}, + Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("key")}, + }, + }, + }, + } + userID := "@user:example.com" + + result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID) + if result { + t.Fatalf("expected keys to match, but they did not") + } +} diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index bea7332f..2a8e8427 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -1492,7 +1492,7 @@ func Setup( // Cross-signing device keys postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse { - return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, userAPI, device, userAPI, cfg) + return UploadCrossSigningDeviceKeys(req, userAPI, device, userAPI.QueryAccountByPassword, cfg) }) postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userVerifier, func(req *http.Request, device *userapi.Device) util.JSONResponse { diff --git a/cmd/dendrite/main.go b/cmd/dendrite/main.go index da43432f..5badbda2 100644 --- a/cmd/dendrite/main.go +++ b/cmd/dendrite/main.go @@ -94,6 +94,8 @@ func main() { dnsCache = fclient.NewDNSCache( cfg.Global.DNSCache.CacheSize, cfg.Global.DNSCache.CacheLifetime, + cfg.FederationAPI.AllowNetworkCIDRs, + cfg.FederationAPI.DenyNetworkCIDRs, ) logrus.Infof( "DNS cache enabled (size %d, lifetime %s)", diff --git a/cmd/generate-config/main.go b/cmd/generate-config/main.go index c6399ec5..63e1dde7 100644 --- a/cmd/generate-config/main.go +++ b/cmd/generate-config/main.go @@ -71,6 +71,10 @@ func main() { cfg.ClientAPI.RateLimiting.Enabled = false cfg.FederationAPI.DisableTLSValidation = false cfg.FederationAPI.DisableHTTPKeepalives = true + // Allow allow networks when running in CI, as otherwise connections + // to other servers might be blocked when running Complement/Sytest. + cfg.FederationAPI.DenyNetworkCIDRs = []string{} + cfg.FederationAPI.AllowNetworkCIDRs = []string{} // don't hit matrix.org when running tests!!! cfg.FederationAPI.KeyPerspectives = config.KeyPerspectives{} cfg.MediaAPI.BasePath = config.Path(filepath.Join(*dirPath, "media")) diff --git a/contrib/dendrite-demo-i2p/main.go b/contrib/dendrite-demo-i2p/main.go index 27f69acb..139edacc 100644 --- a/contrib/dendrite-demo-i2p/main.go +++ b/contrib/dendrite-demo-i2p/main.go @@ -70,6 +70,8 @@ func main() { dnsCache = fclient.NewDNSCache( cfg.Global.DNSCache.CacheSize, cfg.Global.DNSCache.CacheLifetime, + cfg.FederationAPI.AllowNetworkCIDRs, + cfg.FederationAPI.DenyNetworkCIDRs, ) logrus.Infof( "DNS cache enabled (size %d, lifetime %s)", diff --git a/contrib/dendrite-demo-tor/main.go b/contrib/dendrite-demo-tor/main.go index 132b557f..ab32e1db 100644 --- a/contrib/dendrite-demo-tor/main.go +++ b/contrib/dendrite-demo-tor/main.go @@ -65,6 +65,8 @@ func main() { dnsCache = fclient.NewDNSCache( cfg.Global.DNSCache.CacheSize, cfg.Global.DNSCache.CacheLifetime, + cfg.FederationAPI.AllowNetworkCIDRs, + cfg.FederationAPI.DenyNetworkCIDRs, ) logrus.Infof( "DNS cache enabled (size %d, lifetime %s)", diff --git a/dendrite-sample.yaml b/dendrite-sample.yaml index 279d3581..bfa17051 100644 --- a/dendrite-sample.yaml +++ b/dendrite-sample.yaml @@ -254,6 +254,24 @@ federation_api: # last resort. prefer_direct_fetch: false + # deny_networks and allow_networks are the CIDR ranges used to prevent requests + # from accessing private IPs. If your system has specific IPs it should never + # contact, add them here with CIDR notation. + # + # The deny list is checked before the allow list. + deny_networks: + - "127.0.0.1/8" + - "10.0.0.0/8" + - "172.16.0.0/12" + - "192.168.0.0/16" + - "100.64.0.0/10" + - "169.254.0.0/16" + - "::1/128" + - "fe80::/64" + - "fc00::/7" + allow_networks: + - "0.0.0.0/0" # "Everything". The deny list will help limit this. + # Configuration for the Media API. media_api: # Storage path for uploaded media. May be relative or absolute. diff --git a/docs/development/CONTRIBUTING.md b/docs/development/CONTRIBUTING.md index 0d6f533c..d12e151d 100644 --- a/docs/development/CONTRIBUTING.md +++ b/docs/development/CONTRIBUTING.md @@ -34,27 +34,49 @@ The following items are unlikely to be accepted into a main Dendrite release for ## Sign off -We require that everyone who contributes to the project signs off their contributions -in accordance with the [Developer Certificate of Origin](https://github.com/matrix-org/matrix-spec/blob/main/CONTRIBUTING.rst#sign-off). -In effect, this means adding a statement to your pull requests or commit messages -along the lines of: +We ask that everybody who contributes to this project signs off their contributions, as explained below. + +We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is Apache Software License v2 (see [LICENSE](../..//LICENSE)). + +In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach used by the [Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other projects: the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to Matrix: ``` -Signed-off-by: Full Name +Developer Certificate of Origin +Version 1.1 +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. +Developer's Certificate of Origin 1.1 +By making a contribution to this project, I certify that: +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. ``` -Unfortunately we can't accept contributions without a sign-off. +If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment: -Please note that we can only accept contributions under a legally identifiable name, -such as your name as it appears on government-issued documentation or common-law names -(claimed by legitimate usage or repute). We cannot accept sign-offs from a pseudonym or -alias and cannot accept anonymous contributions. +``` +Signed-off-by: Your Name +``` -If you would prefer to sign off privately instead (so as to not reveal your full -name on a public pull request), you can do so by emailing a sign-off declaration -and a link to your pull request directly to the [Matrix.org Foundation](https://matrix.org/foundation/) -at `dco@matrix.org`. Once a private sign-off has been made, you will not be required -to do so for future contributions. +Git allows you to add this signoff automatically when using the `-s` flag to `git commit`, which uses the name and email set in your `user.name` and `user.email` git configs. ## Getting up and running diff --git a/go.mod b/go.mod index 7e06503b..4c15f3bb 100644 --- a/go.mod +++ b/go.mod @@ -25,12 +25,12 @@ require ( github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 - github.com/matrix-org/gomatrixserverlib v0.0.0-20241215094829-e86ab16eabe8 + github.com/matrix-org/gomatrixserverlib v0.0.0-20250116181547-c4f1e01eab0d github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 github.com/mattn/go-sqlite3 v1.14.24 github.com/nats-io/nats-server/v2 v2.10.23 - github.com/nats-io/nats.go v1.37.0 + github.com/nats-io/nats.go v1.38.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible @@ -55,7 +55,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.4.0 maunium.net/go/mautrix v0.15.1 - modernc.org/sqlite v1.34.2 + modernc.org/sqlite v1.34.5 ) require ( @@ -101,7 +101,6 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect github.com/h2non/filetype v1.1.3 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hjson/hjson-go/v4 v4.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juju/errors v1.0.0 // indirect @@ -116,7 +115,7 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/nats-io/jwt/v2 v2.5.8 // indirect - github.com/nats-io/nkeys v0.4.8 // indirect + github.com/nats-io/nkeys v0.4.9 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/onsi/ginkgo/v2 v2.11.0 // indirect @@ -142,7 +141,7 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.32.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect @@ -151,12 +150,9 @@ require ( gopkg.in/macaroon.v2 v2.1.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect maunium.net/go/maulogger/v2 v2.4.1 // indirect - modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect nhooyr.io/websocket v1.8.7 // indirect ) diff --git a/go.sum b/go.sum index ff0bbeb3..dedb8053 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,6 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298= github.com/hjson/hjson-go/v4 v4.4.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -232,8 +230,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20241215094829-e86ab16eabe8 h1:nC998SaawQwbZ16/V70Pil3pY3rSQwTaeLOpHWp7ZTo= -github.com/matrix-org/gomatrixserverlib v0.0.0-20241215094829-e86ab16eabe8/go.mod h1:qil34SWn6VB6gO5312rzziCUcZtgROPjrLE+4ly/0os= +github.com/matrix-org/gomatrixserverlib v0.0.0-20250116181547-c4f1e01eab0d h1:c3Dkci0GDH/6cGGt8zGIiJMP+UOdtX0DPY6dxiJvtZM= +github.com/matrix-org/gomatrixserverlib v0.0.0-20250116181547-c4f1e01eab0d/go.mod h1:qil34SWn6VB6gO5312rzziCUcZtgROPjrLE+4ly/0os= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 h1:6t8kJr8i1/1I5nNttw6nn1ryQJgzVlBmSGgPiiaTdw4= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7/go.mod h1:ReWMS/LoVnOiRAdq9sNUC2NZnd1mZkMNB52QhpTRWjg= github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y= @@ -270,10 +268,10 @@ github.com/nats-io/jwt/v2 v2.5.8 h1:uvdSzwWiEGWGXf+0Q+70qv6AQdvcvxrv9hPM0RiPamE= github.com/nats-io/jwt/v2 v2.5.8/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= github.com/nats-io/nats-server/v2 v2.10.23 h1:jvfb9cEi5h8UG6HkZgJGdn9f1UPaX3Dohk0PohEekJI= github.com/nats-io/nats-server/v2 v2.10.23/go.mod h1:hMFnpDT2XUXsvHglABlFl/uroQCCOcW6X/0esW6GpBk= -github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= -github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= -github.com/nats-io/nkeys v0.4.8 h1:+wee30071y3vCZAYRsnrmIPaOe47A/SkK/UBDPdIV70= -github.com/nats-io/nkeys v0.4.8/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= +github.com/nats-io/nats.go v1.38.0 h1:A7P+g7Wjp4/NWqDOOP/K6hfhr54DvdDQUznt5JFg9XA= +github.com/nats-io/nats.go v1.38.0/go.mod h1:IGUM++TwokGnXPs82/wCuiHS02/aKrdYUQkU8If6yjw= +github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= +github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= @@ -418,8 +416,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -515,8 +513,6 @@ modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= @@ -527,8 +523,8 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/sqlite v1.34.2 h1:J9n76TPsfYYkFkZ9Uy1QphILYifiVEwwOT7yP5b++2Y= -modernc.org/sqlite v1.34.2/go.mod h1:dnR723UrTtjKpoHCAMN0Q/gZ9MT4r+iRvIBb9umWFkU= +modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= +modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/helm/dendrite/Chart.yaml b/helm/dendrite/Chart.yaml index 9d932462..b9ea7bf3 100644 --- a/helm/dendrite/Chart.yaml +++ b/helm/dendrite/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: dendrite -version: "0.15.0" -appVersion: "0.14.0" +version: "0.15.1" +appVersion: "0.14.1" description: Dendrite Matrix Homeserver type: application icon: https://avatars.githubusercontent.com/u/8418310?s=48&v=4 diff --git a/helm/dendrite/README.md b/helm/dendrite/README.md index fd90869b..5cafac9d 100644 --- a/helm/dendrite/README.md +++ b/helm/dendrite/README.md @@ -1,7 +1,7 @@ # dendrite -![Version: 0.15.0](https://img.shields.io/badge/Version-0.15.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.14.0](https://img.shields.io/badge/AppVersion-0.14.0-informational?style=flat-square) +![Version: 0.15.1](https://img.shields.io/badge/Version-0.15.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.14.1](https://img.shields.io/badge/AppVersion-0.14.1-informational?style=flat-square) Dendrite Matrix Homeserver Status: **NOT PRODUCTION READY** diff --git a/internal/transactionrequest.go b/internal/transactionrequest.go index 474195f6..bd6e70ce 100644 --- a/internal/transactionrequest.go +++ b/internal/transactionrequest.go @@ -216,13 +216,17 @@ func (t *TxnReq) processEDUs(ctx context.Context) { util.GetLogger(ctx).WithError(err).Debug("Failed to unmarshal typing event") continue } - if _, serverName, err := gomatrixserverlib.SplitID('@', typingPayload.UserID); err != nil { + _, serverName, err := gomatrixserverlib.SplitID('@', typingPayload.UserID) + if err != nil { continue } else if serverName == t.ourServerName { continue } else if serverName != t.Origin { continue } + if api.IsServerBannedFromRoom(ctx, t.rsAPI, typingPayload.RoomID, serverName) { + continue + } if err := t.producer.SendTyping(ctx, typingPayload.UserID, typingPayload.RoomID, typingPayload.Typing, 30*1000); err != nil { util.GetLogger(ctx).WithError(err).Error("Failed to send typing event to JetStream") } @@ -278,6 +282,9 @@ func (t *TxnReq) processEDUs(ctx context.Context) { util.GetLogger(ctx).Debugf("Dropping receipt event where sender domain (%q) doesn't match origin (%q)", domain, t.Origin) continue } + if api.IsServerBannedFromRoom(ctx, t.rsAPI, roomID, domain) { + continue + } if err := t.processReceiptEvent(ctx, userID, roomID, "m.read", mread.Data.TS, mread.EventIDs); err != nil { util.GetLogger(ctx).WithError(err).WithFields(logrus.Fields{ "sender": t.Origin, diff --git a/internal/version.go b/internal/version.go index d262146b..5df808d7 100644 --- a/internal/version.go +++ b/internal/version.go @@ -18,7 +18,7 @@ var build string const ( VersionMajor = 0 VersionMinor = 14 - VersionPatch = 0 + VersionPatch = 1 VersionTag = "" // example: "rc1" gitRevLen = 7 // 7 matches the displayed characters on github.com diff --git a/setup/base/base.go b/setup/base/base.go index 359a6816..ffc2be37 100644 --- a/setup/base/base.go +++ b/setup/base/base.go @@ -82,6 +82,7 @@ func CreateFederationClient(cfg *config.Dendrite, dnsCache *fclient.DNSCache) fc fclient.WithSkipVerify(cfg.FederationAPI.DisableTLSValidation), fclient.WithKeepAlives(!cfg.FederationAPI.DisableHTTPKeepalives), fclient.WithUserAgent(fmt.Sprintf("Dendrite/%s", internal.VersionString())), + fclient.WithAllowDenyNetworks(cfg.FederationAPI.AllowNetworkCIDRs, cfg.FederationAPI.DenyNetworkCIDRs), } if cfg.Global.DNSCache.Enabled { opts = append(opts, fclient.WithDNSCache(dnsCache)) diff --git a/setup/config/config_federationapi.go b/setup/config/config_federationapi.go index 073c46e0..ed417a74 100644 --- a/setup/config/config_federationapi.go +++ b/setup/config/config_federationapi.go @@ -46,6 +46,10 @@ type FederationAPI struct { // Should we prefer direct key fetches over perspective ones? PreferDirectFetch bool `yaml:"prefer_direct_fetch"` + + // Deny/Allow lists used for restricting request scopes. + DenyNetworkCIDRs []string `yaml:"deny_networks"` + AllowNetworkCIDRs []string `yaml:"allow_networks"` } func (c *FederationAPI) Defaults(opts DefaultOpts) { @@ -53,6 +57,20 @@ func (c *FederationAPI) Defaults(opts DefaultOpts) { c.P2PFederationRetriesUntilAssumedOffline = 1 c.DisableTLSValidation = false c.DisableHTTPKeepalives = false + c.DenyNetworkCIDRs = []string{ + "127.0.0.1/8", + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "100.64.0.0/10", + "169.254.0.0/16", + "::1/128", + "fe80::/64", + "fc00::/7", + } + c.AllowNetworkCIDRs = []string{ + "0.0.0.0/0", + } if opts.Generate { c.KeyPerspectives = KeyPerspectives{ {