diff --git a/.changelog/24442.txt b/.changelog/24442.txt new file mode 100644 index 00000000000..324bab1a400 --- /dev/null +++ b/.changelog/24442.txt @@ -0,0 +1,3 @@ +```release-note:bug +keyring: Fixed a bug when decrypting aead with an empty RSA block on state upserts +``` diff --git a/nomad/encrypter.go b/nomad/encrypter.go index 64bd4c047a1..0409f5c30f6 100644 --- a/nomad/encrypter.go +++ b/nomad/encrypter.go @@ -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) diff --git a/nomad/encrypter_test.go b/nomad/encrypter_test.go index e539ac1a5b5..6d866e84294 100644 --- a/nomad/encrypter_test.go +++ b/nomad/encrypter_test.go @@ -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) +}