From 74e07b79fc9f21fed820a711d2e4bb45d4ca3b37 Mon Sep 17 00:00:00 2001 From: Ricardo Maraschini Date: Wed, 10 Jun 2026 11:05:46 +0200 Subject: [PATCH] feat: propagate tls profile to aws-pod-identity-webhook aws-pod-identity-webhook needs to receive two flags: - --tls-min-version - needs to comply with the hcp apiserver min tls version. - --tls-cipher-suites - needs to comply with the hcp apiserver cipher suites config. this commit propagates both configuration into the flags. --- ...eComponents_kube_apiserver_deployment.yaml | 2 + ...eComponents_kube_apiserver_deployment.yaml | 2 + .../hostedcontrolplane/v2/kas/deployment.go | 50 +++- .../v2/kas/deployment_test.go | 238 ++++++++++++++++++ 4 files changed, 280 insertions(+), 12 deletions(-) diff --git a/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml b/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml index aaf09d80886..4a59bc3e763 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml +++ b/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml @@ -315,6 +315,8 @@ spec: - --tls-cert=/var/run/app/certs/tls.crt - --tls-key=/var/run/app/certs/tls.key - --token-audience=openshift + - --tls-min-version=1.2 + - --tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 image: aws-pod-identity-webhook imagePullPolicy: IfNotPresent name: aws-pod-identity-webhook diff --git a/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml b/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml index 0b4e199b96e..4b8c524eafc 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml +++ b/control-plane-operator/controllers/hostedcontrolplane/testdata/kube-apiserver/zz_fixture_TestControlPlaneComponents_kube_apiserver_deployment.yaml @@ -315,6 +315,8 @@ spec: - --tls-cert=/var/run/app/certs/tls.crt - --tls-key=/var/run/app/certs/tls.key - --token-audience=openshift + - --tls-min-version=1.2 + - --tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 image: aws-pod-identity-webhook imagePullPolicy: IfNotPresent name: aws-pod-identity-webhook diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go b/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go index a8f0aa04000..017fc907712 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go @@ -299,22 +299,33 @@ func updateBootstrapInitContainer(deployment *appsv1.Deployment, hcp *hyperv1.Ho } func applyAWSPodIdentityWebhookContainer(podSpec *corev1.PodSpec, hcp *hyperv1.HostedControlPlane) { + command := []string{ + "/usr/bin/aws-pod-identity-webhook", + "--annotation-prefix=eks.amazonaws.com", + "--in-cluster=false", + "--kubeconfig=/var/run/app/kubeconfig/kubeconfig", + "--logtostderr", + "--port=4443", + fmt.Sprintf("--aws-default-region=%s", hcp.Spec.Platform.AWS.Region), + "--tls-cert=/var/run/app/certs/tls.crt", + "--tls-key=/var/run/app/certs/tls.key", + "--token-audience=openshift", + } + + if tlsMinVersion := config.MinTLSVersion(hcp.Spec.Configuration.GetTLSSecurityProfile()); tlsMinVersion != "" { + if version := convertTLSVersion(tlsMinVersion); version != "" { + command = append(command, fmt.Sprintf("--tls-min-version=%s", version)) + } + } + if cipherSuites := config.CipherSuites(hcp.Spec.Configuration.GetTLSSecurityProfile()); len(cipherSuites) != 0 { + command = append(command, fmt.Sprintf("--tls-cipher-suites=%s", strings.Join(cipherSuites, ","))) + } + podSpec.Containers = append(podSpec.Containers, corev1.Container{ Name: "aws-pod-identity-webhook", Image: "aws-pod-identity-webhook", ImagePullPolicy: corev1.PullIfNotPresent, - Command: []string{ - "/usr/bin/aws-pod-identity-webhook", - "--annotation-prefix=eks.amazonaws.com", - "--in-cluster=false", - "--kubeconfig=/var/run/app/kubeconfig/kubeconfig", - "--logtostderr", - "--port=4443", - fmt.Sprintf("--aws-default-region=%s", hcp.Spec.Platform.AWS.Region), - "--tls-cert=/var/run/app/certs/tls.crt", - "--tls-key=/var/run/app/certs/tls.key", - "--token-audience=openshift", - }, + Command: command, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("10m"), @@ -343,6 +354,21 @@ func applyAWSPodIdentityWebhookContainer(podSpec *corev1.PodSpec, hcp *hyperv1.H ) } +func convertTLSVersion(version string) string { + switch version { + case "VersionTLS10": + return "1.0" + case "VersionTLS11": + return "1.1" + case "VersionTLS12": + return "1.2" + case "VersionTLS13": + return "1.3" + default: + return "" + } +} + func applyAzureWorkloadIdentityWebhookContainer(podSpec *corev1.PodSpec, hcp *hyperv1.HostedControlPlane) { waitForKASScript := fmt.Sprintf(azureWorkloadIdentityWebhookWaitForKASVersionTemplate, netutil.KASPodPort(hcp)) diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment_test.go b/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment_test.go index a00ee0b3dfd..bfc83b24164 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment_test.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment_test.go @@ -1,11 +1,15 @@ package kas import ( + "slices" "strings" "testing" . "github.com/onsi/gomega" + configv1 "github.com/openshift/api/config/v1" + hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" + corev1 "k8s.io/api/core/v1" ) @@ -85,3 +89,237 @@ func TestAddImagePrePullInitContainers(t *testing.T) { }) } } + +func TestConvertTLSVersion(t *testing.T) { + testCases := []struct { + name string + input string + expected string + }{ + { + name: "When input is VersionTLS10 it should return 1.0", + input: "VersionTLS10", + expected: "1.0", + }, + { + name: "When input is VersionTLS11 it should return 1.1", + input: "VersionTLS11", + expected: "1.1", + }, + { + name: "When input is VersionTLS12 it should return 1.2", + input: "VersionTLS12", + expected: "1.2", + }, + { + name: "When input is VersionTLS13 it should return 1.3", + input: "VersionTLS13", + expected: "1.3", + }, + { + name: "When input is unknown it should return empty string", + input: "UnknownVersion", + expected: "", + }, + { + name: "When input is empty it should return empty string", + input: "", + expected: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + result := convertTLSVersion(tc.input) + g.Expect(result).To(Equal(tc.expected)) + }) + } +} + +func TestApplyAWSPodIdentityWebhookContainer(t *testing.T) { + testCases := []struct { + name string + hcp *hyperv1.HostedControlPlane + validatePod func(*GomegaWithT, *corev1.PodSpec) + }{ + { + name: "When TLS security profile is nil it should use default Intermediate profile", + hcp: &hyperv1.HostedControlPlane{ + Spec: hyperv1.HostedControlPlaneSpec{ + Platform: hyperv1.PlatformSpec{ + Type: hyperv1.AWSPlatform, + AWS: &hyperv1.AWSPlatformSpec{ + Region: "us-east-1", + }, + }, + }, + }, + validatePod: func(g *GomegaWithT, podSpec *corev1.PodSpec) { + var webhookContainer *corev1.Container + for i := range podSpec.Containers { + if podSpec.Containers[i].Name == "aws-pod-identity-webhook" { + webhookContainer = &podSpec.Containers[i] + break + } + } + g.Expect(webhookContainer).NotTo(BeNil()) + g.Expect(webhookContainer.Command).To(ContainElement("--tls-min-version=1.2")) + g.Expect(slices.ContainsFunc(webhookContainer.Command, func(arg string) bool { + return strings.HasPrefix(arg, "--tls-cipher-suites=") + })).To(BeTrue()) + }, + }, + { + name: "When TLS security profile is Intermediate it should add TLS configuration", + hcp: &hyperv1.HostedControlPlane{ + Spec: hyperv1.HostedControlPlaneSpec{ + Platform: hyperv1.PlatformSpec{ + Type: hyperv1.AWSPlatform, + AWS: &hyperv1.AWSPlatformSpec{ + Region: "us-east-1", + }, + }, + Configuration: &hyperv1.ClusterConfiguration{ + APIServer: &configv1.APIServerSpec{ + TLSSecurityProfile: &configv1.TLSSecurityProfile{ + Type: configv1.TLSProfileIntermediateType, + }, + }, + }, + }, + }, + validatePod: func(g *GomegaWithT, podSpec *corev1.PodSpec) { + var webhookContainer *corev1.Container + for i := range podSpec.Containers { + if podSpec.Containers[i].Name == "aws-pod-identity-webhook" { + webhookContainer = &podSpec.Containers[i] + break + } + } + g.Expect(webhookContainer).NotTo(BeNil()) + g.Expect(webhookContainer.Command).To(ContainElement("--tls-min-version=1.2")) + g.Expect(slices.ContainsFunc(webhookContainer.Command, func(arg string) bool { + return strings.HasPrefix(arg, "--tls-cipher-suites=") + })).To(BeTrue()) + }, + }, + { + name: "When TLS security profile is Modern it should add TLS 1.3 configuration", + hcp: &hyperv1.HostedControlPlane{ + Spec: hyperv1.HostedControlPlaneSpec{ + Platform: hyperv1.PlatformSpec{ + Type: hyperv1.AWSPlatform, + AWS: &hyperv1.AWSPlatformSpec{ + Region: "us-east-1", + }, + }, + Configuration: &hyperv1.ClusterConfiguration{ + APIServer: &configv1.APIServerSpec{ + TLSSecurityProfile: &configv1.TLSSecurityProfile{ + Type: configv1.TLSProfileModernType, + }, + }, + }, + }, + }, + validatePod: func(g *GomegaWithT, podSpec *corev1.PodSpec) { + var webhookContainer *corev1.Container + for i := range podSpec.Containers { + if podSpec.Containers[i].Name == "aws-pod-identity-webhook" { + webhookContainer = &podSpec.Containers[i] + break + } + } + g.Expect(webhookContainer).NotTo(BeNil()) + g.Expect(webhookContainer.Command).To(ContainElement("--tls-min-version=1.3")) + }, + }, + { + name: "When TLS security profile is Custom with TLS 1.2 it should add tls-min-version flag", + hcp: &hyperv1.HostedControlPlane{ + Spec: hyperv1.HostedControlPlaneSpec{ + Platform: hyperv1.PlatformSpec{ + Type: hyperv1.AWSPlatform, + AWS: &hyperv1.AWSPlatformSpec{ + Region: "us-east-1", + }, + }, + Configuration: &hyperv1.ClusterConfiguration{ + APIServer: &configv1.APIServerSpec{ + TLSSecurityProfile: &configv1.TLSSecurityProfile{ + Type: configv1.TLSProfileCustomType, + Custom: &configv1.CustomTLSProfile{ + TLSProfileSpec: configv1.TLSProfileSpec{ + MinTLSVersion: configv1.VersionTLS12, + Ciphers: []string{ + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES128-GCM-SHA256", + }, + }, + }, + }, + }, + }, + }, + }, + validatePod: func(g *GomegaWithT, podSpec *corev1.PodSpec) { + var webhookContainer *corev1.Container + for i := range podSpec.Containers { + if podSpec.Containers[i].Name == "aws-pod-identity-webhook" { + webhookContainer = &podSpec.Containers[i] + break + } + } + g.Expect(webhookContainer).NotTo(BeNil()) + g.Expect(webhookContainer.Command).To(ContainElement("--tls-min-version=1.2")) + g.Expect(webhookContainer.Command).To(ContainElement("--tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")) + }, + }, + { + name: "When TLS security profile is Custom with TLS 1.3 it should add tls-min-version flag", + hcp: &hyperv1.HostedControlPlane{ + Spec: hyperv1.HostedControlPlaneSpec{ + Platform: hyperv1.PlatformSpec{ + Type: hyperv1.AWSPlatform, + AWS: &hyperv1.AWSPlatformSpec{ + Region: "us-east-1", + }, + }, + Configuration: &hyperv1.ClusterConfiguration{ + APIServer: &configv1.APIServerSpec{ + TLSSecurityProfile: &configv1.TLSSecurityProfile{ + Type: configv1.TLSProfileCustomType, + Custom: &configv1.CustomTLSProfile{ + TLSProfileSpec: configv1.TLSProfileSpec{ + MinTLSVersion: configv1.VersionTLS13, + }, + }, + }, + }, + }, + }, + }, + validatePod: func(g *GomegaWithT, podSpec *corev1.PodSpec) { + var webhookContainer *corev1.Container + for i := range podSpec.Containers { + if podSpec.Containers[i].Name == "aws-pod-identity-webhook" { + webhookContainer = &podSpec.Containers[i] + break + } + } + g.Expect(webhookContainer).NotTo(BeNil()) + g.Expect(webhookContainer.Command).To(ContainElement("--tls-min-version=1.3")) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + podSpec := &corev1.PodSpec{} + applyAWSPodIdentityWebhookContainer(podSpec, tc.hcp) + tc.validatePod(g, podSpec) + }) + } +}