From eee831991bf32c9c7026f9670682152df141c1fe Mon Sep 17 00:00:00 2001 From: Ilya Dvorkin Date: Mon, 5 Jan 2026 15:01:11 +0200 Subject: [PATCH] feature/add-public-key-status-field: add public key field for asymmetric KMS keys --- apis/v1alpha1/key.go | 5 +++ .../crd/bases/kms.services.k8s.aws_keys.yaml | 6 +++ helm/crds/kms.services.k8s.aws_keys.yaml | 6 +++ pkg/resource/key/hooks.go | 41 +++++++++++++++++++ pkg/resource/key/sdk.go | 10 +++++ .../key/sdk_create_post_set_output.go.tpl | 7 +++- .../key/sdk_read_one_post_set_output.go.tpl | 7 +++- 7 files changed, 80 insertions(+), 2 deletions(-) diff --git a/apis/v1alpha1/key.go b/apis/v1alpha1/key.go index 17ac7b9..da9013a 100644 --- a/apis/v1alpha1/key.go +++ b/apis/v1alpha1/key.go @@ -345,6 +345,11 @@ type KeyStatus struct { // is KEY_MATERIAL_EXPIRES, otherwise this value is omitted. // +kubebuilder:validation:Optional ValidTo *metav1.Time `json:"validTo,omitempty"` + // The public key material for asymmetric KMS keys, base64-encoded DER format. + // This field is only populated for asymmetric keys (e.g., ECC_SECG_P256K1, RSA_2048). + // For symmetric keys, this field is nil. + // +kubebuilder:validation:Optional + PublicKey *string `json:"publicKey,omitempty"` } // Key is the Schema for the Keys API diff --git a/config/crd/bases/kms.services.k8s.aws_keys.yaml b/config/crd/bases/kms.services.k8s.aws_keys.yaml index cf1e678..abae0b3 100644 --- a/config/crd/bases/kms.services.k8s.aws_keys.yaml +++ b/config/crd/bases/kms.services.k8s.aws_keys.yaml @@ -489,6 +489,12 @@ spec: to PendingDeletion and the deletion date appears in the DeletionDate field. format: int64 type: integer + publicKey: + description: |- + The public key material for asymmetric KMS keys, base64-encoded DER format. + This field is only populated for asymmetric keys (e.g., ECC_SECG_P256K1, RSA_2048). + For symmetric keys, this field is nil. + type: string signingAlgorithms: description: |- The signing algorithms that the KMS key supports. You cannot use the KMS diff --git a/helm/crds/kms.services.k8s.aws_keys.yaml b/helm/crds/kms.services.k8s.aws_keys.yaml index 9cb9faa..821b9c8 100644 --- a/helm/crds/kms.services.k8s.aws_keys.yaml +++ b/helm/crds/kms.services.k8s.aws_keys.yaml @@ -489,6 +489,12 @@ spec: to PendingDeletion and the deletion date appears in the DeletionDate field. format: int64 type: integer + publicKey: + description: |- + The public key material for asymmetric KMS keys, base64-encoded DER format. + This field is only populated for asymmetric keys (e.g., ECC_SECG_P256K1, RSA_2048). + For symmetric keys, this field is nil. + type: string signingAlgorithms: description: |- The signing algorithms that the KMS key supports. You cannot use the KMS diff --git a/pkg/resource/key/hooks.go b/pkg/resource/key/hooks.go index 1a2d498..6e7e1a4 100644 --- a/pkg/resource/key/hooks.go +++ b/pkg/resource/key/hooks.go @@ -15,11 +15,14 @@ package key import ( "context" + "encoding/base64" + "errors" "strconv" ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" svcsdk "github.com/aws/aws-sdk-go-v2/service/kms" + smithy "github.com/aws/smithy-go" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" svcapitypes "github.com/aws-controllers-k8s/kms-controller/apis/v1alpha1" @@ -167,3 +170,41 @@ func (rm *resourceManager) disableKeyRotation(ctx context.Context, keyId *string } return nil } + +// getPublicKey retrieves the public key for asymmetric KMS keys. +// Returns nil for symmetric keys or keys that don't support GetPublicKey. +func (rm *resourceManager) getPublicKey(ctx context.Context, r *resource) (*string, error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.getPublicKey") + defer func() { exit(nil) }() + + if r.ko.Status.KeyID == nil { + return nil, nil + } + + input := &svcsdk.GetPublicKeyInput{ + KeyId: r.ko.Status.KeyID, + } + resp, err := rm.sdkapi.GetPublicKey(ctx, input) + rm.metrics.RecordAPICall("GET", "GetPublicKey", err) + if err != nil { + // Symmetric keys and some key types don't support GetPublicKey + // Return nil gracefully instead of error + var apiErr smithy.APIError + if errors.As(err, &apiErr) { + if apiErr.ErrorCode() == "UnsupportedOperationException" || + apiErr.ErrorCode() == "DisabledException" { + return nil, nil + } + } + return nil, err + } + + if resp.PublicKey == nil { + return nil, nil + } + + // Return base64-encoded DER format + encoded := base64.StdEncoding.EncodeToString(resp.PublicKey) + return &encoded, nil +} diff --git a/pkg/resource/key/sdk.go b/pkg/resource/key/sdk.go index ee95d90..8c7df36 100644 --- a/pkg/resource/key/sdk.go +++ b/pkg/resource/key/sdk.go @@ -263,6 +263,11 @@ func (rm *resourceManager) sdkFind( } enabled := keyRotationStatus.KeyRotationEnabled ko.Spec.EnableKeyRotation = &enabled + publicKey, err := rm.getPublicKey(ctx, &resource{ko}) + if err != nil { + return &resource{ko}, err + } + ko.Status.PublicKey = publicKey return &resource{ko}, nil } @@ -484,6 +489,11 @@ func (rm *resourceManager) sdkCreate( if err != nil { return &resource{ko}, err } + publicKey, err := rm.getPublicKey(ctx, &resource{ko}) + if err != nil { + return &resource{ko}, err + } + ko.Status.PublicKey = publicKey return &resource{ko}, nil } diff --git a/templates/hooks/key/sdk_create_post_set_output.go.tpl b/templates/hooks/key/sdk_create_post_set_output.go.tpl index 248d984..71d1b38 100644 --- a/templates/hooks/key/sdk_create_post_set_output.go.tpl +++ b/templates/hooks/key/sdk_create_post_set_output.go.tpl @@ -6,4 +6,9 @@ err = rm.updateKeyRotation(ctx, &resource{ko}) if err != nil { return &resource{ko}, err - } \ No newline at end of file + } + publicKey, err := rm.getPublicKey(ctx, &resource{ko}) + if err != nil { + return &resource{ko}, err + } + ko.Status.PublicKey = publicKey \ No newline at end of file diff --git a/templates/hooks/key/sdk_read_one_post_set_output.go.tpl b/templates/hooks/key/sdk_read_one_post_set_output.go.tpl index 296ede8..c9a05bf 100644 --- a/templates/hooks/key/sdk_read_one_post_set_output.go.tpl +++ b/templates/hooks/key/sdk_read_one_post_set_output.go.tpl @@ -13,4 +13,9 @@ return &resource{ko}, err } enabled := keyRotationStatus.KeyRotationEnabled - ko.Spec.EnableKeyRotation = &enabled \ No newline at end of file + ko.Spec.EnableKeyRotation = &enabled + publicKey, err := rm.getPublicKey(ctx, &resource{ko}) + if err != nil { + return &resource{ko}, err + } + ko.Status.PublicKey = publicKey \ No newline at end of file