diff --git a/cmd/dendrite-federation-api-server/main.go b/cmd/dendrite-federation-api-server/main.go index 49c2fe0da..b8db7927a 100644 --- a/cmd/dendrite-federation-api-server/main.go +++ b/cmd/dendrite-federation-api-server/main.go @@ -27,14 +27,15 @@ func main() { accountDB := base.CreateAccountsDB() deviceDB := base.CreateDeviceDB() federation := base.CreateFederationClient() + serverKeyAPI := base.ServerKeyAPIClient() + keyRing := serverKeyAPI.KeyRing() fsAPI := base.FederationSenderHTTPClient() rsAPI := base.RoomserverHTTPClient() asAPI := base.AppserviceHTTPClient() - skAPI := base.ServerKeyAPIClient() federationapi.AddPublicRoutes( - base.PublicAPIMux, base.Cfg, accountDB, deviceDB, federation, - skAPI, rsAPI, asAPI, fsAPI, base.EDUServerClient(), + base.PublicAPIMux, base.Cfg, accountDB, deviceDB, federation, keyRing, + rsAPI, asAPI, fsAPI, base.EDUServerClient(), ) base.SetupAndServeHTTP(string(base.Cfg.Bind.FederationAPI), string(base.Cfg.Listen.FederationAPI)) diff --git a/cmd/roomserver-integration-tests/main.go b/cmd/roomserver-integration-tests/main.go index 3860ca1f7..43aca0789 100644 --- a/cmd/roomserver-integration-tests/main.go +++ b/cmd/roomserver-integration-tests/main.go @@ -255,7 +255,7 @@ func testRoomserver(input []string, wantOutput []string, checkQueries func(api.R panic(err) } - cache, err := caching.NewInMemoryLRUCache(false) + cache, err := caching.NewInMemoryLRUCache() if err != nil { panic(err) } diff --git a/federationapi/federationapi.go b/federationapi/federationapi.go index 7eddbf124..9299b5016 100644 --- a/federationapi/federationapi.go +++ b/federationapi/federationapi.go @@ -23,7 +23,6 @@ import ( federationSenderAPI "github.com/matrix-org/dendrite/federationsender/api" "github.com/matrix-org/dendrite/internal/config" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" - serverkeyAPI "github.com/matrix-org/dendrite/serverkeyapi/api" "github.com/matrix-org/dendrite/federationapi/routing" "github.com/matrix-org/gomatrixserverlib" @@ -36,15 +35,16 @@ func AddPublicRoutes( accountsDB accounts.Database, deviceDB devices.Database, federation *gomatrixserverlib.FederationClient, - skAPI serverkeyAPI.ServerKeyInternalAPI, + keyRing *gomatrixserverlib.KeyRing, rsAPI roomserverAPI.RoomserverInternalAPI, asAPI appserviceAPI.AppServiceQueryAPI, federationSenderAPI federationSenderAPI.FederationSenderInternalAPI, eduAPI eduserverAPI.EDUServerInputAPI, ) { + routing.Setup( router, cfg, rsAPI, asAPI, - eduAPI, federationSenderAPI, skAPI, + eduAPI, federationSenderAPI, *keyRing, federation, accountsDB, deviceDB, ) } diff --git a/federationapi/routing/keys.go b/federationapi/routing/keys.go new file mode 100644 index 000000000..a1dd0fd09 --- /dev/null +++ b/federationapi/routing/keys.go @@ -0,0 +1,68 @@ +// Copyright 2017 Vector Creations Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package routing + +import ( + "encoding/json" + "net/http" + "time" + + "github.com/matrix-org/dendrite/internal/config" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" + "golang.org/x/crypto/ed25519" +) + +// LocalKeys returns the local keys for the server. +// See https://matrix.org/docs/spec/server_server/unstable.html#publishing-keys +func LocalKeys(cfg *config.Dendrite) util.JSONResponse { + keys, err := localKeys(cfg, time.Now().Add(cfg.Matrix.KeyValidityPeriod)) + if err != nil { + return util.ErrorResponse(err) + } + return util.JSONResponse{Code: http.StatusOK, JSON: keys} +} + +func localKeys(cfg *config.Dendrite, validUntil time.Time) (*gomatrixserverlib.ServerKeys, error) { + var keys gomatrixserverlib.ServerKeys + + keys.ServerName = cfg.Matrix.ServerName + + publicKey := cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey) + + keys.VerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.VerifyKey{ + cfg.Matrix.KeyID: { + Key: gomatrixserverlib.Base64Bytes(publicKey), + }, + } + + keys.TLSFingerprints = cfg.Matrix.TLSFingerPrints + keys.OldVerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.OldVerifyKey{} + keys.ValidUntilTS = gomatrixserverlib.AsTimestamp(validUntil) + + toSign, err := json.Marshal(keys.ServerKeyFields) + if err != nil { + return nil, err + } + + keys.Raw, err = gomatrixserverlib.SignJSON( + string(cfg.Matrix.ServerName), cfg.Matrix.KeyID, cfg.Matrix.PrivateKey, toSign, + ) + if err != nil { + return nil, err + } + + return &keys, nil +} diff --git a/federationapi/routing/routing.go b/federationapi/routing/routing.go index cceb74ad9..70b77a4c3 100644 --- a/federationapi/routing/routing.go +++ b/federationapi/routing/routing.go @@ -26,7 +26,6 @@ import ( "github.com/matrix-org/dendrite/internal/config" "github.com/matrix-org/dendrite/internal/httputil" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" - serverkeyAPI "github.com/matrix-org/dendrite/serverkeyapi/api" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) @@ -52,12 +51,11 @@ func Setup( asAPI appserviceAPI.AppServiceQueryAPI, eduAPI eduserverAPI.EDUServerInputAPI, fsAPI federationSenderAPI.FederationSenderInternalAPI, - skAPI serverkeyAPI.ServerKeyInternalAPI, + keys gomatrixserverlib.KeyRing, federation *gomatrixserverlib.FederationClient, accountDB accounts.Database, deviceDB devices.Database, ) { - keys := *skAPI.KeyRing() v2keysmux := publicAPIMux.PathPrefix(pathPrefixV2Keys).Subrouter() v1fedmux := publicAPIMux.PathPrefix(pathPrefixV1Federation).Subrouter() v2fedmux := publicAPIMux.PathPrefix(pathPrefixV2Federation).Subrouter() @@ -67,15 +65,7 @@ func Setup( } localKeys := httputil.MakeExternalAPI("localkeys", func(req *http.Request) util.JSONResponse { - request := &serverkeyAPI.QueryLocalKeysRequest{} - response := &serverkeyAPI.QueryLocalKeysResponse{} - if err := skAPI.QueryLocalKeys(req.Context(), request, response); err != nil { - return util.ErrorResponse(err) - } - return util.JSONResponse{ - Code: http.StatusOK, - JSON: response.ServerKeys, - } + return LocalKeys(cfg) }) // Ignore the {keyID} argument as we only have a single server key so we always diff --git a/internal/caching/impl_inmemorylru.go b/internal/caching/impl_inmemorylru.go index 7bb791dd8..158deca49 100644 --- a/internal/caching/impl_inmemorylru.go +++ b/internal/caching/impl_inmemorylru.go @@ -8,12 +8,11 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" ) -func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) { +func NewInMemoryLRUCache() (*Caches, error) { roomVersions, err := NewInMemoryLRUCachePartition( RoomVersionCacheName, RoomVersionCacheMutable, RoomVersionCacheMaxEntries, - enablePrometheus, ) if err != nil { return nil, err @@ -22,7 +21,6 @@ func NewInMemoryLRUCache(enablePrometheus bool) (*Caches, error) { ServerKeyCacheName, ServerKeyCacheMutable, ServerKeyCacheMaxEntries, - enablePrometheus, ) if err != nil { return nil, err @@ -40,7 +38,7 @@ type InMemoryLRUCachePartition struct { lru *lru.Cache } -func NewInMemoryLRUCachePartition(name string, mutable bool, maxEntries int, enablePrometheus bool) (*InMemoryLRUCachePartition, error) { +func NewInMemoryLRUCachePartition(name string, mutable bool, maxEntries int) (*InMemoryLRUCachePartition, error) { var err error cache := InMemoryLRUCachePartition{ name: name, @@ -51,15 +49,13 @@ func NewInMemoryLRUCachePartition(name string, mutable bool, maxEntries int, ena if err != nil { return nil, err } - if enablePrometheus { - promauto.NewGaugeFunc(prometheus.GaugeOpts{ - Namespace: "dendrite", - Subsystem: "caching_in_memory_lru", - Name: name, - }, func() float64 { - return float64(cache.lru.Len()) - }) - } + promauto.NewGaugeFunc(prometheus.GaugeOpts{ + Namespace: "dendrite", + Subsystem: "caching_in_memory_lru", + Name: name, + }, func() float64 { + return float64(cache.lru.Len()) + }) return &cache, nil } diff --git a/internal/setup/base.go b/internal/setup/base.go index 59bdfd2e4..414b7964a 100644 --- a/internal/setup/base.go +++ b/internal/setup/base.go @@ -96,7 +96,7 @@ func NewBaseDendrite(cfg *config.Dendrite, componentName string, useHTTPAPIs boo kafkaConsumer, kafkaProducer = setupKafka(cfg) } - cache, err := caching.NewInMemoryLRUCache(true) + cache, err := caching.NewInMemoryLRUCache() if err != nil { logrus.WithError(err).Warnf("Failed to create cache") } diff --git a/internal/setup/monolith.go b/internal/setup/monolith.go index 1bf97a15f..4dfbf7115 100644 --- a/internal/setup/monolith.go +++ b/internal/setup/monolith.go @@ -75,7 +75,7 @@ func (m *Monolith) AddAllPublicRoutes(publicMux *mux.Router) { keyserver.AddPublicRoutes(publicMux, m.Config, m.DeviceDB, m.AccountDB) federationapi.AddPublicRoutes( publicMux, m.Config, m.AccountDB, m.DeviceDB, m.FedClient, - m.ServerKeyAPI, m.RoomserverAPI, m.AppserviceAPI, m.FederationSenderAPI, + m.KeyRing, m.RoomserverAPI, m.AppserviceAPI, m.FederationSenderAPI, m.EDUInternalAPI, ) mediaapi.AddPublicRoutes(publicMux, m.Config, m.DeviceDB) diff --git a/serverkeyapi/api/api.go b/serverkeyapi/api/api.go index 624cf0fca..7af626345 100644 --- a/serverkeyapi/api/api.go +++ b/serverkeyapi/api/api.go @@ -22,12 +22,6 @@ type ServerKeyInternalAPI interface { request *QueryPublicKeysRequest, response *QueryPublicKeysResponse, ) error - - QueryLocalKeys( - ctx context.Context, - request *QueryLocalKeysRequest, - response *QueryLocalKeysResponse, - ) error } type QueryPublicKeysRequest struct { @@ -44,10 +38,3 @@ type InputPublicKeysRequest struct { type InputPublicKeysResponse struct { } - -type QueryLocalKeysRequest struct { -} - -type QueryLocalKeysResponse struct { - ServerKeys gomatrixserverlib.ServerKeys `json:"server_keys"` -} diff --git a/serverkeyapi/internal/api.go b/serverkeyapi/internal/api.go index 2b42d2081..b2348e1ed 100644 --- a/serverkeyapi/internal/api.go +++ b/serverkeyapi/internal/api.go @@ -3,11 +3,9 @@ package internal import ( "context" "crypto/ed25519" - "encoding/json" "fmt" "time" - "github.com/matrix-org/dendrite/internal/config" "github.com/matrix-org/dendrite/serverkeyapi/api" "github.com/matrix-org/gomatrixserverlib" "github.com/sirupsen/logrus" @@ -16,38 +14,15 @@ import ( type ServerKeyAPI struct { api.ServerKeyInternalAPI - Cfg *config.Dendrite + ServerName gomatrixserverlib.ServerName + ServerPublicKey ed25519.PublicKey + ServerKeyID gomatrixserverlib.KeyID + ServerKeyValidity time.Duration + OurKeyRing gomatrixserverlib.KeyRing FedClient *gomatrixserverlib.FederationClient } -func (s *ServerKeyAPI) QueryLocalKeys(ctx context.Context, request *api.QueryLocalKeysRequest, response *api.QueryLocalKeysResponse) error { - publicKey := s.Cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey) - - response.ServerKeys.ServerName = s.Cfg.Matrix.ServerName - response.ServerKeys.VerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.VerifyKey{ - s.Cfg.Matrix.KeyID: { - Key: gomatrixserverlib.Base64Bytes(publicKey), - }, - } - response.ServerKeys.TLSFingerprints = s.Cfg.Matrix.TLSFingerPrints - // TODO: Handle old expired keys. We should probably have a configuration section - // for these, as it's really counter-intuitive for people to have to rake through - // the database to find their own past keys. - response.ServerKeys.OldVerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.OldVerifyKey{} - response.ServerKeys.ValidUntilTS = gomatrixserverlib.AsTimestamp(time.Now().Add(s.Cfg.Matrix.KeyValidityPeriod)) - - toSign, err := json.Marshal(response.ServerKeys.ServerKeyFields) - if err != nil { - return err - } - - response.ServerKeys.Raw, err = gomatrixserverlib.SignJSON( - string(s.Cfg.Matrix.ServerName), s.Cfg.Matrix.KeyID, s.Cfg.Matrix.PrivateKey, toSign, - ) - return err -} - func (s *ServerKeyAPI) KeyRing() *gomatrixserverlib.KeyRing { // Return a keyring that forces requests to be proxied through the // below functions. That way we can enforce things like validity @@ -146,35 +121,19 @@ func (s *ServerKeyAPI) handleLocalKeys( results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, ) error { for req := range requests { - if req.ServerName == s.Cfg.Matrix.ServerName { + if req.ServerName == s.ServerName { // We found a key request that is supposed to be for our own // keys. Remove it from the request list so we don't hit the // database or the fetchers for it. delete(requests, req) - // Look up our own keys. - request := &api.QueryLocalKeysRequest{} - response := &api.QueryLocalKeysResponse{} - if err := s.QueryLocalKeys(ctx, request, response); err != nil { - return err - } - - // Depending on whether the key is expired or not, we'll need - // to write slightly different - if verifyKeys, ok := response.ServerKeys.VerifyKeys[req.KeyID]; ok { - // The key is current. - results[req] = gomatrixserverlib.PublicKeyLookupResult{ - VerifyKey: verifyKeys, - ExpiredTS: gomatrixserverlib.PublicKeyNotExpired, - ValidUntilTS: response.ServerKeys.ValidUntilTS, - } - } else if verifyKeys, ok := response.ServerKeys.OldVerifyKeys[req.KeyID]; ok { - // The key is expired. - results[req] = gomatrixserverlib.PublicKeyLookupResult{ - VerifyKey: verifyKeys.VerifyKey, - ExpiredTS: verifyKeys.ExpiredTS, - ValidUntilTS: gomatrixserverlib.PublicKeyNotValid, - } + // Insert our own key into the response. + results[req] = gomatrixserverlib.PublicKeyLookupResult{ + VerifyKey: gomatrixserverlib.VerifyKey{ + Key: gomatrixserverlib.Base64Bytes(s.ServerPublicKey), + }, + ExpiredTS: gomatrixserverlib.PublicKeyNotExpired, + ValidUntilTS: gomatrixserverlib.AsTimestamp(time.Now().Add(s.ServerKeyValidity)), } } } @@ -247,14 +206,14 @@ func (s *ServerKeyAPI) handleFetcherKeys( if res.ValidUntilTS > prev.ValidUntilTS { // This key is newer than the one we had so let's store // it in the database. - if req.ServerName != s.Cfg.Matrix.ServerName { + if req.ServerName != s.ServerName { storeResults[req] = res } } } else { // We didn't already have a previous entry for this request // so store it in the database anyway for now. - if req.ServerName != s.Cfg.Matrix.ServerName { + if req.ServerName != s.ServerName { storeResults[req] = res } } diff --git a/serverkeyapi/inthttp/client.go b/serverkeyapi/inthttp/client.go index 8575c5592..39ab8c6c5 100644 --- a/serverkeyapi/inthttp/client.go +++ b/serverkeyapi/inthttp/client.go @@ -16,7 +16,6 @@ import ( const ( ServerKeyInputPublicKeyPath = "/serverkeyapi/inputPublicKey" ServerKeyQueryPublicKeyPath = "/serverkeyapi/queryPublicKey" - ServerKeyQueryLocalKeysPath = "/serverkeyapi/queryLocalKeys" ) // NewServerKeyClient creates a ServerKeyInternalAPI implemented by talking to a HTTP POST API. @@ -131,15 +130,3 @@ func (h *httpServerKeyInternalAPI) QueryPublicKeys( apiURL := h.serverKeyAPIURL + ServerKeyQueryPublicKeyPath return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) } - -func (h *httpServerKeyInternalAPI) QueryLocalKeys( - ctx context.Context, - request *api.QueryLocalKeysRequest, - response *api.QueryLocalKeysResponse, -) error { - span, ctx := opentracing.StartSpanFromContext(ctx, "QueryLocalKeys") - defer span.Finish() - - apiURL := h.serverKeyAPIURL + ServerKeyQueryLocalKeysPath - return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) -} diff --git a/serverkeyapi/inthttp/server.go b/serverkeyapi/inthttp/server.go index a5b26f1aa..cd4748392 100644 --- a/serverkeyapi/inthttp/server.go +++ b/serverkeyapi/inthttp/server.go @@ -12,19 +12,6 @@ import ( ) func AddRoutes(s api.ServerKeyInternalAPI, internalAPIMux *mux.Router, cache caching.ServerKeyCache) { - internalAPIMux.Handle(ServerKeyQueryLocalKeysPath, - httputil.MakeInternalAPI("queryLocalKeys", func(req *http.Request) util.JSONResponse { - request := api.QueryLocalKeysRequest{} - response := api.QueryLocalKeysResponse{} - if err := json.NewDecoder(req.Body).Decode(&request); err != nil { - return util.MessageResponse(http.StatusBadRequest, err.Error()) - } - if err := s.QueryLocalKeys(req.Context(), &request, &response); err != nil { - return util.ErrorResponse(err) - } - return util.JSONResponse{Code: http.StatusOK, JSON: &response} - }), - ) internalAPIMux.Handle(ServerKeyQueryPublicKeyPath, httputil.MakeInternalAPI("queryPublicKeys", func(req *http.Request) util.JSONResponse { request := api.QueryPublicKeysRequest{} diff --git a/serverkeyapi/serverkeyapi.go b/serverkeyapi/serverkeyapi.go index ac644e589..cddd392ed 100644 --- a/serverkeyapi/serverkeyapi.go +++ b/serverkeyapi/serverkeyapi.go @@ -46,8 +46,11 @@ func NewInternalAPI( } internalAPI := internal.ServerKeyAPI{ - Cfg: cfg, - FedClient: fedClient, + ServerName: cfg.Matrix.ServerName, + ServerPublicKey: cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey), + ServerKeyID: cfg.Matrix.KeyID, + ServerKeyValidity: cfg.Matrix.KeyValidityPeriod, + FedClient: fedClient, OurKeyRing: gomatrixserverlib.KeyRing{ KeyFetchers: []gomatrixserverlib.KeyFetcher{ &gomatrixserverlib.DirectKeyFetcher{ diff --git a/serverkeyapi/serverkeyapi_test.go b/serverkeyapi/serverkeyapi_test.go index 4d8ce016e..353ac6a78 100644 --- a/serverkeyapi/serverkeyapi_test.go +++ b/serverkeyapi/serverkeyapi_test.go @@ -21,7 +21,6 @@ import ( type server struct { name gomatrixserverlib.ServerName - validity time.Duration config *config.Dendrite fedclient *gomatrixserverlib.FederationClient cache *caching.Caches @@ -70,10 +69,9 @@ func TestMain(m *testing.M) { // API to work. s.config = &config.Dendrite{} s.config.SetDefaults() - s.config.Matrix.KeyValidityPeriod = s.validity s.config.Matrix.ServerName = gomatrixserverlib.ServerName(s.name) s.config.Matrix.PrivateKey = testPriv - s.config.Matrix.KeyID = serverKeyID + s.config.Matrix.KeyID = "ed25519:test" s.config.Database.ServerKey = config.DataSource("file::memory:") // Create a transport which redirects federation requests to @@ -84,7 +82,7 @@ func TestMain(m *testing.M) { // Create the federation client. s.fedclient = gomatrixserverlib.NewFederationClientWithTransport( - s.config.Matrix.ServerName, serverKeyID, testPriv, transport, + s.config.Matrix.ServerName, "ed25519:test", testPriv, transport, ) // Finally, build the server key APIs. @@ -316,4 +314,5 @@ func TestRenewalBehaviour(t *testing.T) { if oldcached.ValidUntilTS >= newcached.ValidUntilTS { t.Fatalf("the server B key should have been renewed but wasn't") } + t.Log(res) }