mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
keyring: Fix a panic when decrypting aead with empty RSA block. (#24383)
Clusters that have gone through several upgrades have be found to include keyring material which has an empty RSA block. In more recent versions of Nomad, an empty RSA block is omitted from being written to disk. This results in the panic not being present. Older versions, however, did not have this struct tag meaning we wrote an empty JSON block which is not accounted for in the current version.
This commit is contained in:
3
.changelog/24383.txt
Normal file
3
.changelog/24383.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:bug
|
||||
keyring: Fixed a panic on server startup when decrypting AEAD key data with empty RSA block
|
||||
```
|
||||
@@ -805,7 +805,7 @@ func (e *Encrypter) loadKeyFromStore(path string) (*structs.UnwrappedRootKey, er
|
||||
// 1.7 an ed25519 key derived from the root key was used instead of an RSA
|
||||
// key.
|
||||
var rsaKey []byte
|
||||
if kekWrapper.WrappedRSAKey != nil {
|
||||
if kekWrapper.WrappedRSAKey != nil && len(kekWrapper.WrappedRSAKey.Ciphertext) > 0 {
|
||||
rsaKey, err = wrapper.Decrypt(e.srv.shutdownCtx, kekWrapper.WrappedRSAKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w (rsa key): %w", ErrDecryptFailed, err)
|
||||
|
||||
@@ -120,6 +120,67 @@ func TestEncrypter_LoadSave(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
// TestEncrypter_loadKeyFromStore_emptyRSA tests a panic seen by some
|
||||
// operators where the aead key disk file content had an empty RSA block.
|
||||
func TestEncrypter_loadKeyFromStore_emptyRSA(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
|
||||
srv := &Server{
|
||||
logger: testlog.HCLogger(t),
|
||||
config: &Config{},
|
||||
}
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
key, err := structs.NewUnwrappedRootKey(structs.EncryptionAlgorithmAES256GCM)
|
||||
must.NoError(t, err)
|
||||
|
||||
encrypter, err := NewEncrypter(srv, tmpDir)
|
||||
must.NoError(t, err)
|
||||
|
||||
wrappedKey, err := encrypter.encryptDEK(key, &structs.KEKProviderConfig{})
|
||||
must.NotNil(t, wrappedKey)
|
||||
must.NoError(t, err)
|
||||
|
||||
// Use an artisanally crafted key file.
|
||||
kek, err := json.Marshal(wrappedKey.KeyEncryptionKey)
|
||||
must.NoError(t, err)
|
||||
|
||||
wrappedDEKCipher, err := json.Marshal(wrappedKey.WrappedDataEncryptionKey.Ciphertext)
|
||||
must.NoError(t, err)
|
||||
|
||||
testData := fmt.Sprintf(`
|
||||
{
|
||||
"Meta": {
|
||||
"KeyID": %q,
|
||||
"Algorithm": "aes256-gcm",
|
||||
"CreateTime": 1730000000000000000,
|
||||
"CreateIndex": 1555555,
|
||||
"ModifyIndex": 1555555,
|
||||
"State": "active",
|
||||
"PublishTime": 0
|
||||
},
|
||||
"ProviderID": "aead",
|
||||
"WrappedDEK": {
|
||||
"ciphertext": %s,
|
||||
"key_info": {
|
||||
"key_id": %q
|
||||
}
|
||||
},
|
||||
"WrappedRSAKey": {},
|
||||
"KEK": %s
|
||||
}
|
||||
`, key.Meta.KeyID, wrappedDEKCipher, key.Meta.KeyID, kek)
|
||||
|
||||
path := filepath.Join(tmpDir, key.Meta.KeyID+".nks.json")
|
||||
err = os.WriteFile(path, []byte(testData), 0o600)
|
||||
must.NoError(t, err)
|
||||
|
||||
unwrappedKey, err := encrypter.loadKeyFromStore(path)
|
||||
must.NoError(t, err)
|
||||
must.NotNil(t, unwrappedKey)
|
||||
}
|
||||
|
||||
// TestEncrypter_Restore exercises the entire reload of a keystore,
|
||||
// including pairing metadata with key material
|
||||
func TestEncrypter_Restore(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user