mirror of
https://github.com/hoernschen/dendrite.git
synced 2024-12-26 15:08:28 +00:00
Allow configuring old verify keys (#1443)
* Allow configuring old verify keys * Update sample config * Update sample config * Fix config population * Key ID formatting validity of old_verify_keys * Update comment
This commit is contained in:
parent
6fbf89a166
commit
145db37d89
5 changed files with 62 additions and 6 deletions
|
@ -38,6 +38,14 @@ global:
|
||||||
# The path to the signing private key file, used to sign requests and events.
|
# The path to the signing private key file, used to sign requests and events.
|
||||||
private_key: matrix_key.pem
|
private_key: matrix_key.pem
|
||||||
|
|
||||||
|
# The paths and expiry timestamps (as a UNIX timestamp in millisecond precision)
|
||||||
|
# to old signing private keys that were formerly in use on this domain. These
|
||||||
|
# keys will not be used for federation request or event signing, but will be
|
||||||
|
# provided to any other homeserver that asks when trying to verify old events.
|
||||||
|
# old_private_keys:
|
||||||
|
# - private_key: old_matrix_key.pem
|
||||||
|
# expired_at: 1601024554498
|
||||||
|
|
||||||
# How long a remote server can cache our server signing key before requesting it
|
# How long a remote server can cache our server signing key before requesting it
|
||||||
# again. Increasing this number will reduce the number of requests made by other
|
# again. Increasing this number will reduce the number of requests made by other
|
||||||
# servers for our key but increases the period that a compromised key will be
|
# servers for our key but increases the period that a compromised key will be
|
||||||
|
|
|
@ -136,6 +136,8 @@ func localKeys(cfg *config.FederationAPI, validUntil time.Time) (*gomatrixserver
|
||||||
var keys gomatrixserverlib.ServerKeys
|
var keys gomatrixserverlib.ServerKeys
|
||||||
|
|
||||||
keys.ServerName = cfg.Matrix.ServerName
|
keys.ServerName = cfg.Matrix.ServerName
|
||||||
|
keys.TLSFingerprints = cfg.TLSFingerPrints
|
||||||
|
keys.ValidUntilTS = gomatrixserverlib.AsTimestamp(validUntil)
|
||||||
|
|
||||||
publicKey := cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey)
|
publicKey := cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey)
|
||||||
|
|
||||||
|
@ -145,9 +147,15 @@ func localKeys(cfg *config.FederationAPI, validUntil time.Time) (*gomatrixserver
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
keys.TLSFingerprints = cfg.TLSFingerPrints
|
|
||||||
keys.OldVerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.OldVerifyKey{}
|
keys.OldVerifyKeys = map[gomatrixserverlib.KeyID]gomatrixserverlib.OldVerifyKey{}
|
||||||
keys.ValidUntilTS = gomatrixserverlib.AsTimestamp(validUntil)
|
for _, oldVerifyKey := range cfg.Matrix.OldVerifyKeys {
|
||||||
|
keys.OldVerifyKeys[oldVerifyKey.KeyID] = gomatrixserverlib.OldVerifyKey{
|
||||||
|
VerifyKey: gomatrixserverlib.VerifyKey{
|
||||||
|
Key: gomatrixserverlib.Base64Bytes(oldVerifyKey.PrivateKey),
|
||||||
|
},
|
||||||
|
ExpiredTS: oldVerifyKey.ExpiredAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toSign, err := json.Marshal(keys.ServerKeyFields)
|
toSign, err := json.Marshal(keys.ServerKeyFields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -228,10 +228,30 @@ func loadConfig(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Global.KeyID, c.Global.PrivateKey, err = readKeyPEM(privateKeyPath, privateKeyData); err != nil {
|
if c.Global.KeyID, c.Global.PrivateKey, err = readKeyPEM(privateKeyPath, privateKeyData, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i, oldPrivateKey := range c.Global.OldVerifyKeys {
|
||||||
|
var oldPrivateKeyData []byte
|
||||||
|
|
||||||
|
oldPrivateKeyPath := absPath(basePath, oldPrivateKey.PrivateKeyPath)
|
||||||
|
oldPrivateKeyData, err = readFile(oldPrivateKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTSPEC: Ordinarily we should enforce key ID formatting, but since there are
|
||||||
|
// a number of private keys out there with non-compatible symbols in them due
|
||||||
|
// to lack of validation in Synapse, we won't enforce that for old verify keys.
|
||||||
|
keyID, privateKey, perr := readKeyPEM(oldPrivateKeyPath, oldPrivateKeyData, false)
|
||||||
|
if perr != nil {
|
||||||
|
return nil, perr
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Global.OldVerifyKeys[i].KeyID, c.Global.OldVerifyKeys[i].PrivateKey = keyID, privateKey
|
||||||
|
}
|
||||||
|
|
||||||
for _, certPath := range c.FederationAPI.FederationCertificatePaths {
|
for _, certPath := range c.FederationAPI.FederationCertificatePaths {
|
||||||
absCertPath := absPath(basePath, certPath)
|
absCertPath := absPath(basePath, certPath)
|
||||||
var pemData []byte
|
var pemData []byte
|
||||||
|
@ -444,7 +464,7 @@ func absPath(dir string, path Path) string {
|
||||||
return filepath.Join(dir, string(path))
|
return filepath.Join(dir, string(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
func readKeyPEM(path string, data []byte) (gomatrixserverlib.KeyID, ed25519.PrivateKey, error) {
|
func readKeyPEM(path string, data []byte, enforceKeyIDFormat bool) (gomatrixserverlib.KeyID, ed25519.PrivateKey, error) {
|
||||||
for {
|
for {
|
||||||
var keyBlock *pem.Block
|
var keyBlock *pem.Block
|
||||||
keyBlock, data = pem.Decode(data)
|
keyBlock, data = pem.Decode(data)
|
||||||
|
@ -462,7 +482,7 @@ func readKeyPEM(path string, data []byte) (gomatrixserverlib.KeyID, ed25519.Priv
|
||||||
if !strings.HasPrefix(keyID, "ed25519:") {
|
if !strings.HasPrefix(keyID, "ed25519:") {
|
||||||
return "", nil, fmt.Errorf("key ID %q doesn't start with \"ed25519:\" in %q", keyID, path)
|
return "", nil, fmt.Errorf("key ID %q doesn't start with \"ed25519:\" in %q", keyID, path)
|
||||||
}
|
}
|
||||||
if !keyIDRegexp.MatchString(keyID) {
|
if enforceKeyIDFormat && !keyIDRegexp.MatchString(keyID) {
|
||||||
return "", nil, fmt.Errorf("key ID %q in %q contains illegal characters (use a-z, A-Z, 0-9 and _ only)", keyID, path)
|
return "", nil, fmt.Errorf("key ID %q in %q contains illegal characters (use a-z, A-Z, 0-9 and _ only)", keyID, path)
|
||||||
}
|
}
|
||||||
_, privKey, err := ed25519.GenerateKey(bytes.NewReader(keyBlock.Bytes))
|
_, privKey, err := ed25519.GenerateKey(bytes.NewReader(keyBlock.Bytes))
|
||||||
|
|
|
@ -22,6 +22,11 @@ type Global struct {
|
||||||
// prefix "ed25519:".
|
// prefix "ed25519:".
|
||||||
KeyID gomatrixserverlib.KeyID `yaml:"-"`
|
KeyID gomatrixserverlib.KeyID `yaml:"-"`
|
||||||
|
|
||||||
|
// Information about old private keys that used to be used to sign requests and
|
||||||
|
// events on this domain. They will not be used but will be advertised to other
|
||||||
|
// servers that ask for them to help verify old events.
|
||||||
|
OldVerifyKeys []OldVerifyKeys `yaml:"old_private_keys"`
|
||||||
|
|
||||||
// How long a remote server can cache our server key for before requesting it again.
|
// 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
|
// 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
|
// for our key, but increases the period a compromised key will be considered valid
|
||||||
|
@ -60,6 +65,21 @@ func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) {
|
||||||
c.Metrics.Verify(configErrs, isMonolith)
|
c.Metrics.Verify(configErrs, isMonolith)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OldVerifyKeys struct {
|
||||||
|
// Path to the private key.
|
||||||
|
PrivateKeyPath Path `yaml:"private_key"`
|
||||||
|
|
||||||
|
// The private key itself.
|
||||||
|
PrivateKey ed25519.PrivateKey `yaml:"-"`
|
||||||
|
|
||||||
|
// The key ID of the private key.
|
||||||
|
KeyID gomatrixserverlib.KeyID `yaml:"-"`
|
||||||
|
|
||||||
|
// When the private key was designed as "expired", as a UNIX timestamp
|
||||||
|
// in millisecond precision.
|
||||||
|
ExpiredAt gomatrixserverlib.Timestamp `yaml:"expired_at"`
|
||||||
|
}
|
||||||
|
|
||||||
// The configuration to use for Prometheus metrics
|
// The configuration to use for Prometheus metrics
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
// Whether or not the metrics are enabled
|
// Whether or not the metrics are enabled
|
||||||
|
|
|
@ -234,7 +234,7 @@ func (m mockReadFile) readFile(path string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadKey(t *testing.T) {
|
func TestReadKey(t *testing.T) {
|
||||||
keyID, _, err := readKeyPEM("path/to/key", []byte(testKey))
|
keyID, _, err := readKeyPEM("path/to/key", []byte(testKey), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("failed to load private key:", err)
|
t.Error("failed to load private key:", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue