From 912893b70cf12590e355c86aa65e284bfbd39dd0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Nov 2022 16:04:54 +0000 Subject: [PATCH] Update config shape --- federationapi/federationapi.go | 12 +----- federationapi/queue/destinationqueue.go | 2 +- federationapi/queue/queue.go | 18 +++------ federationapi/queue/queue_test.go | 4 +- federationapi/routing/keys.go | 54 ++++++++++++++++--------- setup/base/base.go | 14 +------ setup/config/config.go | 15 +++++++ setup/config/config_global.go | 47 ++++++++++++++++----- 8 files changed, 99 insertions(+), 67 deletions(-) diff --git a/federationapi/federationapi.go b/federationapi/federationapi.go index 4578e33aa..854251220 100644 --- a/federationapi/federationapi.go +++ b/federationapi/federationapi.go @@ -120,17 +120,7 @@ func NewInternalAPI( js, nats := base.NATS.Prepare(base.ProcessContext, &cfg.Matrix.JetStream) - signingInfo := map[gomatrixserverlib.ServerName]*queue.SigningInfo{} - for _, serverName := range append( - []gomatrixserverlib.ServerName{base.Cfg.Global.ServerName}, - base.Cfg.Global.SecondaryServerNames..., - ) { - signingInfo[serverName] = &queue.SigningInfo{ - KeyID: cfg.Matrix.KeyID, - PrivateKey: cfg.Matrix.PrivateKey, - ServerName: serverName, - } - } + signingInfo := base.Cfg.Global.SigningIdentities() queues := queue.NewOutgoingQueues( federationDB, base.ProcessContext, diff --git a/federationapi/queue/destinationqueue.go b/federationapi/queue/destinationqueue.go index bf04ee99a..63fc59c89 100644 --- a/federationapi/queue/destinationqueue.go +++ b/federationapi/queue/destinationqueue.go @@ -50,7 +50,7 @@ type destinationQueue struct { queues *OutgoingQueues db storage.Database process *process.ProcessContext - signing map[gomatrixserverlib.ServerName]*SigningInfo + signing map[gomatrixserverlib.ServerName]*gomatrixserverlib.SigningIdentity rsAPI api.FederationRoomserverAPI client fedapi.FederationClient // federation client origin gomatrixserverlib.ServerName // origin of requests diff --git a/federationapi/queue/queue.go b/federationapi/queue/queue.go index 68f354993..31124e0c1 100644 --- a/federationapi/queue/queue.go +++ b/federationapi/queue/queue.go @@ -15,7 +15,6 @@ package queue import ( - "crypto/ed25519" "encoding/json" "fmt" "sync" @@ -46,7 +45,7 @@ type OutgoingQueues struct { origin gomatrixserverlib.ServerName client fedapi.FederationClient statistics *statistics.Statistics - signing map[gomatrixserverlib.ServerName]*SigningInfo + signing map[gomatrixserverlib.ServerName]*gomatrixserverlib.SigningIdentity queuesMutex sync.Mutex // protects the below queues map[gomatrixserverlib.ServerName]*destinationQueue } @@ -91,7 +90,7 @@ func NewOutgoingQueues( client fedapi.FederationClient, rsAPI api.FederationRoomserverAPI, statistics *statistics.Statistics, - signing map[gomatrixserverlib.ServerName]*SigningInfo, + signing []*gomatrixserverlib.SigningIdentity, ) *OutgoingQueues { queues := &OutgoingQueues{ disabled: disabled, @@ -101,9 +100,12 @@ func NewOutgoingQueues( origin: origin, client: client, statistics: statistics, - signing: signing, + signing: map[gomatrixserverlib.ServerName]*gomatrixserverlib.SigningIdentity{}, queues: map[gomatrixserverlib.ServerName]*destinationQueue{}, } + for _, identity := range signing { + queues.signing[identity.ServerName] = identity + } // Look up which servers we have pending items for and then rehydrate those queues. if !disabled { serverNames := map[gomatrixserverlib.ServerName]struct{}{} @@ -135,14 +137,6 @@ func NewOutgoingQueues( return queues } -// TODO: Move this somewhere useful for other components as we often need to ferry these 3 variables -// around together -type SigningInfo struct { - ServerName gomatrixserverlib.ServerName - KeyID gomatrixserverlib.KeyID - PrivateKey ed25519.PrivateKey -} - type queuedPDU struct { receipt *shared.Receipt pdu *gomatrixserverlib.HeaderedEvent diff --git a/federationapi/queue/queue_test.go b/federationapi/queue/queue_test.go index 58745c607..b2ec4b836 100644 --- a/federationapi/queue/queue_test.go +++ b/federationapi/queue/queue_test.go @@ -350,8 +350,8 @@ func testSetup(failuresUntilBlacklist uint32, shouldTxSucceed bool, t *testing.T } rs := &stubFederationRoomServerAPI{} stats := statistics.NewStatistics(db, failuresUntilBlacklist) - signingInfo := map[gomatrixserverlib.ServerName]*SigningInfo{ - "localhost": { + signingInfo := []*gomatrixserverlib.SigningIdentity{ + { KeyID: "ed21019:auto", PrivateKey: test.PrivateKeyA, ServerName: "localhost", diff --git a/federationapi/routing/keys.go b/federationapi/routing/keys.go index 5650e3d53..98b4f3bf6 100644 --- a/federationapi/routing/keys.go +++ b/federationapi/routing/keys.go @@ -136,38 +136,56 @@ func ClaimOneTimeKeys( // LocalKeys returns the local keys for the server. // See https://matrix.org/docs/spec/server_server/unstable.html#publishing-keys func LocalKeys(cfg *config.FederationAPI, serverName gomatrixserverlib.ServerName) util.JSONResponse { - keys, err := localKeys(cfg, serverName, time.Now().Add(cfg.Matrix.KeyValidityPeriod)) + keys, err := localKeys(cfg, serverName) if err != nil { return util.ErrorResponse(err) } return util.JSONResponse{Code: http.StatusOK, JSON: keys} } -func localKeys(cfg *config.FederationAPI, serverName gomatrixserverlib.ServerName, validUntil time.Time) (*gomatrixserverlib.ServerKeys, error) { +func localKeys(cfg *config.FederationAPI, serverName gomatrixserverlib.ServerName) (*gomatrixserverlib.ServerKeys, error) { var keys gomatrixserverlib.ServerKeys if !cfg.Matrix.IsLocalServerName(serverName) { return nil, fmt.Errorf("server name not known") } - keys.ServerName = serverName - keys.ValidUntilTS = gomatrixserverlib.AsTimestamp(validUntil) - - publicKey := cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey) - - keys.VerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.VerifyKey{ - cfg.Matrix.KeyID: { - Key: gomatrixserverlib.Base64Bytes(publicKey), - }, + var virtualHost *config.VirtualHost + for _, v := range cfg.Matrix.VirtualHosts { + if v.ServerName != serverName { + continue + } + virtualHost = v } - keys.OldVerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.OldVerifyKey{} - for _, oldVerifyKey := range cfg.Matrix.OldVerifyKeys { - keys.OldVerifyKeys[oldVerifyKey.KeyID] = gomatrixserverlib.OldVerifyKey{ - VerifyKey: gomatrixserverlib.VerifyKey{ - Key: oldVerifyKey.PublicKey, + if virtualHost == nil { + publicKey := cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey) + keys.ServerName = cfg.Matrix.ServerName + keys.ValidUntilTS = gomatrixserverlib.AsTimestamp(time.Now().Add(cfg.Matrix.KeyValidityPeriod)) + keys.VerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.VerifyKey{ + cfg.Matrix.KeyID: { + Key: gomatrixserverlib.Base64Bytes(publicKey), }, - ExpiredTS: oldVerifyKey.ExpiredAt, } + keys.OldVerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.OldVerifyKey{} + for _, oldVerifyKey := range cfg.Matrix.OldVerifyKeys { + keys.OldVerifyKeys[oldVerifyKey.KeyID] = gomatrixserverlib.OldVerifyKey{ + VerifyKey: gomatrixserverlib.VerifyKey{ + Key: oldVerifyKey.PublicKey, + }, + ExpiredTS: oldVerifyKey.ExpiredAt, + } + } + } else { + publicKey := virtualHost.PrivateKey.Public().(ed25519.PublicKey) + keys.ServerName = virtualHost.ServerName + keys.ValidUntilTS = gomatrixserverlib.AsTimestamp(time.Now().Add(virtualHost.KeyValidityPeriod)) + keys.VerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.VerifyKey{ + virtualHost.KeyID: { + Key: gomatrixserverlib.Base64Bytes(publicKey), + }, + } + // TODO: Virtual hosts probably want to be able to specify old signing + // keys too, just in case } toSign, err := json.Marshal(keys.ServerKeyFields) @@ -213,7 +231,7 @@ func NotaryKeys( for serverName, kidToCriteria := range req.ServerKeys { var keyList []gomatrixserverlib.ServerKeys if serverName == cfg.Matrix.ServerName { - if k, err := localKeys(cfg, serverName, time.Now().Add(cfg.Matrix.KeyValidityPeriod)); err == nil { + if k, err := localKeys(cfg, serverName); err == nil { keyList = append(keyList, *k) } else { return util.ErrorResponse(err) diff --git a/setup/base/base.go b/setup/base/base.go index 2d6548483..14edadd96 100644 --- a/setup/base/base.go +++ b/setup/base/base.go @@ -364,19 +364,7 @@ func (b *BaseDendrite) CreateClient() *gomatrixserverlib.Client { // CreateFederationClient creates a new federation client. Should only be called // once per component. func (b *BaseDendrite) CreateFederationClient() *gomatrixserverlib.FederationClient { - identities := make([]*gomatrixserverlib.SigningIdentity, 0, 1+len(b.Cfg.Global.SecondaryServerNames)) - identities = append(identities, &gomatrixserverlib.SigningIdentity{ - ServerName: b.Cfg.Global.ServerName, - KeyID: b.Cfg.Global.KeyID, - PrivateKey: b.Cfg.Global.PrivateKey, - }) - for _, serverName := range b.Cfg.Global.SecondaryServerNames { - identities = append(identities, &gomatrixserverlib.SigningIdentity{ - ServerName: serverName, - KeyID: b.Cfg.Global.KeyID, // TODO: Per-virtual host key - PrivateKey: b.Cfg.Global.PrivateKey, // TODO: Per-virtual host key - }) - } + identities := b.Cfg.Global.SigningIdentities() if b.Cfg.Global.DisableFederation { return gomatrixserverlib.NewFederationClient( identities, gomatrixserverlib.WithTransport(noOpHTTPTransport), diff --git a/setup/config/config.go b/setup/config/config.go index e99852ec9..918bcbe3b 100644 --- a/setup/config/config.go +++ b/setup/config/config.go @@ -231,6 +231,21 @@ func loadConfig( return nil, err } + for _, v := range c.Global.VirtualHosts { + if v.KeyValidityPeriod == 0 { + v.KeyValidityPeriod = c.Global.KeyValidityPeriod + } + if v.PrivateKeyPath == "" { + v.KeyID = c.Global.KeyID + v.PrivateKey = c.Global.PrivateKey + continue + } + privateKeyPath := absPath(basePath, v.PrivateKeyPath) + if v.KeyID, v.PrivateKey, err = LoadMatrixKey(privateKeyPath, readFile); err != nil { + return nil, err + } + } + for _, key := range c.Global.OldVerifyKeys { switch { case key.PrivateKeyPath != "": diff --git a/setup/config/config_global.go b/setup/config/config_global.go index ff8352485..6548fc5c5 100644 --- a/setup/config/config_global.go +++ b/setup/config/config_global.go @@ -16,7 +16,7 @@ type Global struct { ServerName gomatrixserverlib.ServerName `yaml:"server_name"` // The secondary server names, used for virtual hosting. - SecondaryServerNames []gomatrixserverlib.ServerName `yaml:"secondary_server_names"` + VirtualHosts []*VirtualHost `yaml:"virtual_hosts"` // Path to the private key which will be used to sign requests and events. PrivateKeyPath Path `yaml:"private_key"` @@ -128,8 +128,8 @@ func (c *Global) IsLocalServerName(serverName gomatrixserverlib.ServerName) bool if c.ServerName == serverName { return true } - for _, secondaryName := range c.SecondaryServerNames { - if secondaryName == serverName { + for _, v := range c.VirtualHosts { + if v.ServerName == serverName { return true } } @@ -148,22 +148,49 @@ func (c *Global) SplitLocalID(sigil byte, id string) (string, gomatrixserverlib. } func (c *Global) SigningIdentities() []*gomatrixserverlib.SigningIdentity { - identities := make([]*gomatrixserverlib.SigningIdentity, 0, len(c.SecondaryServerNames)+1) + identities := make([]*gomatrixserverlib.SigningIdentity, 0, len(c.VirtualHosts)+1) identities = append(identities, &gomatrixserverlib.SigningIdentity{ ServerName: c.ServerName, KeyID: c.KeyID, PrivateKey: c.PrivateKey, }) - for _, serverName := range c.SecondaryServerNames { - identities = append(identities, &gomatrixserverlib.SigningIdentity{ - ServerName: serverName, - KeyID: c.KeyID, - PrivateKey: c.PrivateKey, - }) + for _, v := range c.VirtualHosts { + identities = append(identities, v.SigningIdentity()) } return identities } +type VirtualHost struct { + // The server name of the virtual host. + ServerName gomatrixserverlib.ServerName `yaml:"server_name"` + + // The key ID of the private key. If not specified, the default global key ID + // will be used instead. + KeyID gomatrixserverlib.KeyID `yaml:"key_id"` + + // Path to the private key. If not specified, the default global private key + // will be used instead. + PrivateKeyPath Path `yaml:"private_key"` + + // The private key itself. + PrivateKey ed25519.PrivateKey `yaml:"-"` + + // How long a remote server can cache our server key for before requesting it again. + // Increasing this number will reduce the number of requests made by remote servers + // for our key, but increases the period a compromised key will be considered valid + // by remote servers. + // Defaults to 24 hours. + KeyValidityPeriod time.Duration `yaml:"key_validity_period"` +} + +func (v *VirtualHost) SigningIdentity() *gomatrixserverlib.SigningIdentity { + return &gomatrixserverlib.SigningIdentity{ + ServerName: v.ServerName, + KeyID: v.KeyID, + PrivateKey: v.PrivateKey, + } +} + type OldVerifyKeys struct { // Path to the private key. PrivateKeyPath Path `yaml:"private_key"`