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.
|
||||
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
|
||||
# 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
|
||||
|
|
|
@ -136,6 +136,8 @@ func localKeys(cfg *config.FederationAPI, validUntil time.Time) (*gomatrixserver
|
|||
var keys gomatrixserverlib.ServerKeys
|
||||
|
||||
keys.ServerName = cfg.Matrix.ServerName
|
||||
keys.TLSFingerprints = cfg.TLSFingerPrints
|
||||
keys.ValidUntilTS = gomatrixserverlib.AsTimestamp(validUntil)
|
||||
|
||||
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.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)
|
||||
if err != nil {
|
||||
|
|
|
@ -228,10 +228,30 @@ func loadConfig(
|
|||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
absCertPath := absPath(basePath, certPath)
|
||||
var pemData []byte
|
||||
|
@ -444,7 +464,7 @@ func absPath(dir string, path Path) string {
|
|||
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 {
|
||||
var keyBlock *pem.Block
|
||||
keyBlock, data = pem.Decode(data)
|
||||
|
@ -462,7 +482,7 @@ func readKeyPEM(path string, data []byte) (gomatrixserverlib.KeyID, ed25519.Priv
|
|||
if !strings.HasPrefix(keyID, "ed25519:") {
|
||||
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)
|
||||
}
|
||||
_, privKey, err := ed25519.GenerateKey(bytes.NewReader(keyBlock.Bytes))
|
||||
|
|
|
@ -22,6 +22,11 @@ type Global struct {
|
|||
// prefix "ed25519:".
|
||||
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.
|
||||
// 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
|
||||
|
@ -60,6 +65,21 @@ func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) {
|
|||
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
|
||||
type Metrics struct {
|
||||
// Whether or not the metrics are enabled
|
||||
|
|
|
@ -234,7 +234,7 @@ func (m mockReadFile) readFile(path string) ([]byte, error) {
|
|||
}
|
||||
|
||||
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 {
|
||||
t.Error("failed to load private key:", err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue