From 65c38821f382cc708cb5a672d895e2420525e091 Mon Sep 17 00:00:00 2001 From: Swarup Ghosh Date: Thu, 21 Nov 2024 14:29:29 +0530 Subject: [PATCH] [FeatureGate/KMSEncryptionProvider] APIServer config for AWS KMS Signed-off-by: Swarup Ghosh --- .../KMSEncryptionProvider.yaml | 88 +++++++++++++++++++ config/v1/types_apiserver.go | 27 +++++- config/v1/types_kmsencryption.go | 52 +++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 config/v1/tests/apiservers.config.openshift.io/KMSEncryptionProvider.yaml create mode 100644 config/v1/types_kmsencryption.go diff --git a/config/v1/tests/apiservers.config.openshift.io/KMSEncryptionProvider.yaml b/config/v1/tests/apiservers.config.openshift.io/KMSEncryptionProvider.yaml new file mode 100644 index 00000000000..b01234ddaad --- /dev/null +++ b/config/v1/tests/apiservers.config.openshift.io/KMSEncryptionProvider.yaml @@ -0,0 +1,88 @@ +apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this +name: "APIServer" +crdName: apiservers.config.openshift.io +featureGate: KMSEncryptionProvider +tests: + onCreate: + - name: Should be able to create encrypt with KMS for AWS with valid values + initial: | + apiVersion: config.openshift.io/v1 + kind: APIServer + spec: + encryption: + type: KMS + kms: + type: AWS + aws: + keyARN: arn:aws:kms:us-east-1:101010101010:key/9a512e29-0d9c-4cf5-8174-fc1a5b22cd6a + region: us-east-1 + expected: | + apiVersion: config.openshift.io/v1 + kind: APIServer + spec: + audit: + profile: Default + encryption: + type: KMS + kms: + type: AWS + aws: + keyARN: arn:aws:kms:us-east-1:101010101010:key/9a512e29-0d9c-4cf5-8174-fc1a5b22cd6a + region: us-east-1 + - name: Should fail to create with an empty KMS config + initial: | + apiVersion: config.openshift.io/v1 + kind: APIServer + spec: + encryption: + type: KMS + kms: {} + expectedError: "Invalid value: \"null\": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation" + - name: Should fail to create with kms type AWS but without aws config + initial: | + apiVersion: config.openshift.io/v1 + kind: APIServer + spec: + encryption: + type: KMS + kms: + type: AWS + expectedError: "Invalid value: \"object\": aws config is required when kms provider type is AWS, and forbidden otherwise" + - name: Should fail to create AWS KMS without a keyARN + initial: | + apiVersion: config.openshift.io/v1 + kind: APIServer + spec: + encryption: + type: KMS + kms: + type: AWS + aws: + region: us-east-1 + expectedError: "Invalid value: \"null\": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation" + - name: Should fail to create AWS KMS with invalid keyARN format + initial: | + apiVersion: config.openshift.io/v1 + kind: APIServer + spec: + encryption: + type: KMS + kms: + type: AWS + aws: + keyARN: not-a-kms-arn + region: us-east-1 + expectedError: "Invalid value: \"string\": keyARN must follow the format `arn:aws:kms:::key/`. The account ID must be a 12 digit number and the region and key ID should consist only of lowercase hexadecimal characters and hyphens (-)." + - name: Should fail to create AWS KMS with empty region + initial: | + apiVersion: config.openshift.io/v1 + kind: APIServer + spec: + encryption: + type: KMS + kms: + type: AWS + aws: + keyARN: arn:aws:kms:us-east-1:101010101010:key/9a512e29-0d9c-4cf5-8174-fc1a5b22cd6a + region: "" + expectedError: "Invalid value: \"string\": AWS region cannot be empty" \ No newline at end of file diff --git a/config/v1/types_apiserver.go b/config/v1/types_apiserver.go index d815556d28c..2e946d6b69f 100644 --- a/config/v1/types_apiserver.go +++ b/config/v1/types_apiserver.go @@ -52,6 +52,7 @@ type APIServerSpec struct { // server from JavaScript applications. // The values are regular expressions that correspond to the Golang regular expression language. // +optional + // +listType=atomic AdditionalCORSAllowedOrigins []string `json:"additionalCORSAllowedOrigins,omitempty"` // encryption allows the configuration of encryption of resources at the datastore layer. // +optional @@ -156,6 +157,7 @@ type APIServerServingCerts struct { // If no named certificates are provided, or no named certificates match the server name as understood by a client, // the defaultServingCertificate will be used. // +optional + // +listType=atomic NamedCertificates []APIServerNamedServingCert `json:"namedCertificates,omitempty"` } @@ -165,6 +167,7 @@ type APIServerNamedServingCert struct { // serve secure traffic. If no names are provided, the implicit names will be extracted from the certificates. // Exact names trump over wildcard names. Explicit names defined here trump over extracted implicit names. // +optional + // +listType=atomic Names []string `json:"names,omitempty"` // servingCertificate references a kubernetes.io/tls type secret containing the TLS cert info for serving secure traffic. // The secret must exist in the openshift-config namespace and contain the following required fields: @@ -173,6 +176,9 @@ type APIServerNamedServingCert struct { ServingCertificate SecretNameReference `json:"servingCertificate"` } +// APIServerEncryption is used to encrypt sensitive resources on the cluster. +// +openshift:validation:FeatureGateAwareXValidation:featureGate=KMSEncryptionProvider,rule="has(self.type) && self.type == 'KMS' ? has(self.kms) : !has(self.kms)",message="kms config is required when encryption type is KMS, and forbidden otherwise" +// +union type APIServerEncryption struct { // type defines what encryption type should be used to encrypt resources at the datastore layer. // When this field is unset (i.e. when it is set to the empty string), identity is implied. @@ -191,9 +197,23 @@ type APIServerEncryption struct { // +unionDiscriminator // +optional Type EncryptionType `json:"type,omitempty"` + + // kms defines the configuration for the external KMS instance that manages the encryption keys, + // when KMS encryption is enabled sensitive resources will be encrypted using keys managed by an + // externally configured KMS instance. + // + // The Key Management Service (KMS) instance provides symmetric encryption and is responsible for + // managing the lifecyle of the encryption keys outside of the control plane. + // This allows integration with an external provider to manage the data encryption keys securely. + // + // +openshift:enable:FeatureGate=KMSEncryptionProvider + // +unionMember + // +optional + KMS *KMSConfig `json:"kms,omitempty"` } -// +kubebuilder:validation:Enum="";identity;aescbc;aesgcm +// +openshift:validation:FeatureGateAwareEnum:featureGate="",enum="";identity;aescbc;aesgcm +// +openshift:validation:FeatureGateAwareEnum:featureGate=KMSEncryptionProvider,enum="";identity;aescbc;aesgcm;KMS type EncryptionType string const ( @@ -208,6 +228,11 @@ const ( // aesgcm refers to a type where AES-GCM with random nonce and a 32-byte key // is used to perform encryption at the datastore layer. EncryptionTypeAESGCM EncryptionType = "aesgcm" + + // kms refers to a type of encryption where the encryption keys are managed + // outside the control plane in a Key Management Service instance, + // encryption is still performed at the datastore layer. + EncryptionTypeKMS EncryptionType = "KMS" ) type APIServerStatus struct { diff --git a/config/v1/types_kmsencryption.go b/config/v1/types_kmsencryption.go new file mode 100644 index 00000000000..d679f094301 --- /dev/null +++ b/config/v1/types_kmsencryption.go @@ -0,0 +1,52 @@ +package v1 + +// KMSConfig defines the configuration for the KMS instance +// that will be used with KMSEncryptionProvider encryption +// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'AWS' ? has(self.aws) : !has(self.aws)",message="aws config is required when kms provider type is AWS, and forbidden otherwise" +// +union +type KMSConfig struct { + // type defines the kind of platform for the KMS provider. + // Available provider types are AWS only. + // + // +unionDiscriminator + // +kubebuilder:validation:Required + Type KMSProviderType `json:"type"` + + // aws defines the key config for using an AWS KMS instance + // for the encryption. The AWS KMS instance is managed + // by the user outside the purview of the control plane. + // + // +unionMember + // +optional + AWS *AWSKMSConfig `json:"aws,omitempty"` +} + +// AWSKMSConfig defines the KMS config specific to AWS KMS provider +type AWSKMSConfig struct { + // keyARN specifies the Amazon Resource Name (ARN) of the AWS KMS key used for encryption. + // The value must adhere to the format `arn:aws:kms:::key/`, where: + // - `` is the AWS region consisting of lowercase letters and hyphens followed by a number. + // - `` is a 12-digit numeric identifier for the AWS account. + // - `` is a unique identifier for the KMS key, consisting of lowercase hexadecimal characters and hyphens. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:MaxLength=128 + // +kubebuilder:validation:XValidation:rule="self.matches('^arn:aws:kms:[a-z0-9-]+:[0-9]{12}:key/[a-f0-9-]+$')",message="keyARN must follow the format `arn:aws:kms:::key/`. The account ID must be a 12 digit number and the region and key ID should consist only of lowercase hexadecimal characters and hyphens (-)." + KeyARN string `json:"keyARN"` + // region specifies the AWS region where the KMS instance exists, and follows the format + // `--`, e.g.: `us-east-1`. + // Only lowercase letters and hyphens followed by numbers are allowed. + // + // +kubebuilder:validation:MaxLength=64 + // +kubebuilder:validation:XValidation:rule="self.size() > 0",message="AWS region cannot be empty" + Region string `json:"region"` +} + +// KMSProviderType is a specific supported KMS provider +// +kubebuilder:validation:Enum=AWS +type KMSProviderType string + +const ( + // AWSKMSProvider represents a supported KMS provider for use with AWS KMS + AWSKMSProvider KMSProviderType = "AWS" +)