keyring: Fix a panic when decrypting aead with empty RSA block. (#24442)

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:
James Rasell
2024-11-12 14:26:35 +00:00
committed by GitHub
parent 7a9e3d2487
commit 4e7496d246
3 changed files with 44 additions and 1 deletions

3
.changelog/24442.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:bug
keyring: Fixed a bug when decrypting aead with an empty RSA block on state upserts
```

View File

@@ -477,7 +477,7 @@ func (e *Encrypter) decryptWrappedKeyTask(ctx context.Context, cancel context.Ca
// Decrypt RSAKey for Workload Identity JWT signing if one exists. Prior to
// 1.7 an ed25519 key derived from the root key was used instead of an RSA
// key.
if wrappedKey.WrappedRSAKey != nil {
if wrappedKey.WrappedRSAKey != nil && len(wrappedKey.WrappedRSAKey.Ciphertext) > 0 {
rsaKey, err = wrapper.Decrypt(e.srv.shutdownCtx, wrappedKey.WrappedRSAKey)
if err != nil {
err := fmt.Errorf("%w (rsa key): %w", ErrDecryptFailed, err)

View File

@@ -17,6 +17,7 @@ import (
"time"
"github.com/go-jose/go-jose/v3/jwt"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc/v2"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/helper/pointer"
@@ -834,3 +835,42 @@ func TestEncrypter_TransitConfigFallback(t *testing.T) {
fallbackVaultConfig(providers[2], &config.VaultConfig{})
must.Eq(t, expect, providers[2].Config, must.Sprint("expected fallback to env"))
}
func TestEncrypter_decryptWrappedKeyTask(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)
// Purposely empty the RSA key, but do not nil it, so we can test for a
// panic where the key doesn't contain the ciphertext.
wrappedKey.WrappedRSAKey = &wrapping.BlobInfo{}
provider, ok := encrypter.providerConfigs[string(structs.KEKProviderAEAD)]
must.True(t, ok)
must.NotNil(t, provider)
KMSWrapper, err := encrypter.newKMSWrapper(provider, key.Meta.KeyID, wrappedKey.KeyEncryptionKey)
must.NoError(t, err)
must.NotNil(t, KMSWrapper)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err = encrypter.decryptWrappedKeyTask(ctx, cancel, KMSWrapper, provider, key.Meta, wrappedKey)
must.NoError(t, err)
}