diff --git a/charts/gardener/operator/files/crd-gardens.yaml b/charts/gardener/operator/files/crd-gardens.yaml index c4f4fc15bedf..9b418ff7ffc3 100644 --- a/charts/gardener/operator/files/crd-gardens.yaml +++ b/charts/gardener/operator/files/crd-gardens.yaml @@ -1881,6 +1881,11 @@ spec: - configMapName - kubeconfigs type: object + tlsMinVersion: + description: |- + TLSMinVersion is the minimum TLS version accepted by the kube-apiserver. + Supported values: VersionTLS12, VersionTLS13. + type: string watchCacheSizes: description: |- WatchCacheSizes contains configuration of the API server's watch cache sizes. diff --git a/docs/api-reference/core.md b/docs/api-reference/core.md index a81ba38c0c75..21ee31770968 100644 --- a/docs/api-reference/core.md +++ b/docs/api-reference/core.md @@ -5572,6 +5572,18 @@ integer

Autoscaling contains auto-scaling configuration options for the kube-apiserver.

+ + +tlsMinVersion
+ +string + + + +(Optional) +

TLSMinVersion is the minimum TLS version accepted by the kube-apiserver.
Supported values: VersionTLS12, VersionTLS13.

+ + diff --git a/example/90-shoot.yaml b/example/90-shoot.yaml index b9b871b0a1d5..0f6f22a56bdf 100644 --- a/example/90-shoot.yaml +++ b/example/90-shoot.yaml @@ -249,6 +249,7 @@ spec: # minAllowed: # cpu: "1" # memory: 3Gi + # tlsMinVersion: VersionTLS12 # Minimum TLS version accepted by the kube-apiserver. Supported values: VersionTLS12, VersionTLS13. # kubeControllerManager: # nodeCIDRMaskSize: 24 # nodeCIDRMaskSizeIPv6: 80 diff --git a/example/operator/10-crd-operator.gardener.cloud_gardens.yaml b/example/operator/10-crd-operator.gardener.cloud_gardens.yaml index c4f4fc15bedf..9b418ff7ffc3 100644 --- a/example/operator/10-crd-operator.gardener.cloud_gardens.yaml +++ b/example/operator/10-crd-operator.gardener.cloud_gardens.yaml @@ -1881,6 +1881,11 @@ spec: - configMapName - kubeconfigs type: object + tlsMinVersion: + description: |- + TLSMinVersion is the minimum TLS version accepted by the kube-apiserver. + Supported values: VersionTLS12, VersionTLS13. + type: string watchCacheSizes: description: |- WatchCacheSizes contains configuration of the API server's watch cache sizes. diff --git a/go.mod b/go.mod index 9b5f0a351cdc..7161748a7f92 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/coreos/go-systemd/v22 v22.7.0 github.com/distribution/distribution/v3 v3.1.1 github.com/distribution/reference v0.6.0 - github.com/docker/cli v29.7.1+incompatible + github.com/docker/cli v29.7.2+incompatible github.com/docker/docker v28.5.2+incompatible github.com/docker/go-connections v0.8.0 github.com/elliotchance/orderedmap/v3 v3.1.1 diff --git a/go.sum b/go.sum index 7727f0856485..5bbe343b7361 100644 --- a/go.sum +++ b/go.sum @@ -208,8 +208,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v29.7.1+incompatible h1:ILZpP6B7fedIr6ANy824QkDp1WMJuouIq0O2SrBkB2w= -github.com/docker/cli v29.7.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.7.2+incompatible h1:dlkwallR8XqfeVnA2ELEhdwvb4lsSwuB4IgsG8Q9cLY= +github.com/docker/cli v29.7.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= diff --git a/pkg/api/core/validation/shoot.go b/pkg/api/core/validation/shoot.go index 17582f5f0367..3b13a913c8d2 100644 --- a/pkg/api/core/validation/shoot.go +++ b/pkg/api/core/validation/shoot.go @@ -1888,6 +1888,13 @@ func ValidateKubeAPIServer(kubeAPIServer *core.KubeAPIServerConfig, kubernetesVe } } + if kubeAPIServer.TLSMinVersion != nil { + validTLSVersions := sets.New("VersionTLS12", "VersionTLS13") + if !validTLSVersions.Has(*kubeAPIServer.TLSMinVersion) { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("tlsMinVersion"), *kubeAPIServer.TLSMinVersion, sets.List(validTLSVersions))) + } + } + allErrs = append(allErrs, ValidateControlPlaneAutoscaling( kubeAPIServer.Autoscaling, corev1.ResourceList{ diff --git a/pkg/api/core/validation/shoot_test.go b/pkg/api/core/validation/shoot_test.go index f7f4d02b3d34..020c2def6f20 100644 --- a/pkg/api/core/validation/shoot_test.go +++ b/pkg/api/core/validation/shoot_test.go @@ -3729,6 +3729,34 @@ var _ = Describe("Shoot Validation Tests", func() { })))) }) + DescribeTable("should not allow unsupported or invalid TLS min versions", + func(version string) { + shoot.Spec.Kubernetes.KubeAPIServer.TLSMinVersion = &version + + errorList := ValidateShoot(shoot) + + Expect(errorList).To(ConsistOf(PointTo(MatchFields(IgnoreExtras, Fields{ + "Type": Equal(field.ErrorTypeNotSupported), + "Field": Equal("spec.kubernetes.kubeAPIServer.tlsMinVersion"), + })))) + }, + Entry("VersionTLS10", "VersionTLS10"), + Entry("VersionTLS11", "VersionTLS11"), + Entry("VersionTLS00", "VersionTLS00"), + ) + + DescribeTable("should allow all valid TLS min versions", + func(version string) { + shoot.Spec.Kubernetes.KubeAPIServer.TLSMinVersion = &version + + errorList := ValidateShoot(shoot) + + Expect(errorList).To(BeEmpty()) + }, + Entry("VersionTLS12", "VersionTLS12"), + Entry("VersionTLS13", "VersionTLS13"), + ) + It("should not allow to specify a negative defaultNotReadyTolerationSeconds", func() { shoot.Spec.Kubernetes.KubeAPIServer.DefaultNotReadyTolerationSeconds = new(int64(-1)) diff --git a/pkg/apis/core/types_shoot.go b/pkg/apis/core/types_shoot.go index 8b0b72c0d453..f56c3c248625 100644 --- a/pkg/apis/core/types_shoot.go +++ b/pkg/apis/core/types_shoot.go @@ -784,6 +784,9 @@ type KubeAPIServerConfig struct { StructuredAuthorization *StructuredAuthorization // Autoscaling contains auto-scaling configuration options for the kube-apiserver. Autoscaling *ControlPlaneAutoscaling + // TLSMinVersion is the minimum TLS version accepted by the kube-apiserver. + // Supported values: VersionTLS12, VersionTLS13. + TLSMinVersion *string } // ControlPlaneAutoscaling contains auto-scaling configuration options for control-plane components. diff --git a/pkg/apis/core/v1beta1/generated.pb.go b/pkg/apis/core/v1beta1/generated.pb.go index 817ba35e4ba8..eb016ccc51d3 100644 --- a/pkg/apis/core/v1beta1/generated.pb.go +++ b/pkg/apis/core/v1beta1/generated.pb.go @@ -5303,6 +5303,15 @@ func (m *KubeAPIServerConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.TLSMinVersion != nil { + i -= len(*m.TLSMinVersion) + copy(dAtA[i:], *m.TLSMinVersion) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.TLSMinVersion))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa2 + } if m.Autoscaling != nil { { size, err := m.Autoscaling.MarshalToSizedBuffer(dAtA[:i]) @@ -15481,6 +15490,10 @@ func (m *KubeAPIServerConfig) Size() (n int) { l = m.Autoscaling.Size() n += 2 + l + sovGenerated(uint64(l)) } + if m.TLSMinVersion != nil { + l = len(*m.TLSMinVersion) + n += 2 + l + sovGenerated(uint64(l)) + } return n } @@ -19792,6 +19805,7 @@ func (this *KubeAPIServerConfig) String() string { `StructuredAuthentication:` + strings.Replace(this.StructuredAuthentication.String(), "StructuredAuthentication", "StructuredAuthentication", 1) + `,`, `StructuredAuthorization:` + strings.Replace(this.StructuredAuthorization.String(), "StructuredAuthorization", "StructuredAuthorization", 1) + `,`, `Autoscaling:` + strings.Replace(this.Autoscaling.String(), "ControlPlaneAutoscaling", "ControlPlaneAutoscaling", 1) + `,`, + `TLSMinVersion:` + valueToStringGenerated(this.TLSMinVersion) + `,`, `}`, }, "") return s @@ -36398,6 +36412,39 @@ func (m *KubeAPIServerConfig) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 20: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TLSMinVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.TLSMinVersion = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/core/v1beta1/generated.proto b/pkg/apis/core/v1beta1/generated.proto index 4592842e93dc..6cb2a81ff5e8 100644 --- a/pkg/apis/core/v1beta1/generated.proto +++ b/pkg/apis/core/v1beta1/generated.proto @@ -1553,6 +1553,11 @@ message KubeAPIServerConfig { // Autoscaling contains auto-scaling configuration options for the kube-apiserver. // +optional optional ControlPlaneAutoscaling autoscaling = 19; + + // TLSMinVersion is the minimum TLS version accepted by the kube-apiserver. + // Supported values: VersionTLS12, VersionTLS13. + // +optional + optional string tlsMinVersion = 20; } // KubeControllerManagerConfig contains configuration settings for the kube-controller-manager. diff --git a/pkg/apis/core/v1beta1/types_shoot.go b/pkg/apis/core/v1beta1/types_shoot.go index 1a98eb1defcd..ff83ad057c39 100644 --- a/pkg/apis/core/v1beta1/types_shoot.go +++ b/pkg/apis/core/v1beta1/types_shoot.go @@ -1049,6 +1049,10 @@ type KubeAPIServerConfig struct { // Autoscaling contains auto-scaling configuration options for the kube-apiserver. // +optional Autoscaling *ControlPlaneAutoscaling `json:"autoscaling,omitempty" protobuf:"bytes,19,opt,name=autoscaling"` + // TLSMinVersion is the minimum TLS version accepted by the kube-apiserver. + // Supported values: VersionTLS12, VersionTLS13. + // +optional + TLSMinVersion *string `json:"tlsMinVersion,omitempty" protobuf:"bytes,20,opt,name=tlsMinVersion"` } // ControlPlaneAutoscaling contains auto-scaling configuration options for control-plane components. diff --git a/pkg/apis/core/v1beta1/zz_generated.conversion.go b/pkg/apis/core/v1beta1/zz_generated.conversion.go index a36f0151cb41..aaa6b8144cdb 100644 --- a/pkg/apis/core/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/core/v1beta1/zz_generated.conversion.go @@ -4525,6 +4525,7 @@ func autoConvert_v1beta1_KubeAPIServerConfig_To_core_KubeAPIServerConfig(in *Kub out.StructuredAuthentication = (*core.StructuredAuthentication)(unsafe.Pointer(in.StructuredAuthentication)) out.StructuredAuthorization = (*core.StructuredAuthorization)(unsafe.Pointer(in.StructuredAuthorization)) out.Autoscaling = (*core.ControlPlaneAutoscaling)(unsafe.Pointer(in.Autoscaling)) + out.TLSMinVersion = (*string)(unsafe.Pointer(in.TLSMinVersion)) return nil } @@ -4563,6 +4564,7 @@ func autoConvert_core_KubeAPIServerConfig_To_v1beta1_KubeAPIServerConfig(in *cor out.StructuredAuthentication = (*StructuredAuthentication)(unsafe.Pointer(in.StructuredAuthentication)) out.StructuredAuthorization = (*StructuredAuthorization)(unsafe.Pointer(in.StructuredAuthorization)) out.Autoscaling = (*ControlPlaneAutoscaling)(unsafe.Pointer(in.Autoscaling)) + out.TLSMinVersion = (*string)(unsafe.Pointer(in.TLSMinVersion)) return nil } diff --git a/pkg/apis/core/v1beta1/zz_generated.deepcopy.go b/pkg/apis/core/v1beta1/zz_generated.deepcopy.go index 1cf37a35b46e..b28e0d139d4c 100644 --- a/pkg/apis/core/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/core/v1beta1/zz_generated.deepcopy.go @@ -2768,6 +2768,11 @@ func (in *KubeAPIServerConfig) DeepCopyInto(out *KubeAPIServerConfig) { *out = new(ControlPlaneAutoscaling) (*in).DeepCopyInto(*out) } + if in.TLSMinVersion != nil { + in, out := &in.TLSMinVersion, &out.TLSMinVersion + *out = new(string) + **out = **in + } return } diff --git a/pkg/apis/core/zz_generated.deepcopy.go b/pkg/apis/core/zz_generated.deepcopy.go index 9c65f92174c7..2511274b2e79 100644 --- a/pkg/apis/core/zz_generated.deepcopy.go +++ b/pkg/apis/core/zz_generated.deepcopy.go @@ -2773,6 +2773,11 @@ func (in *KubeAPIServerConfig) DeepCopyInto(out *KubeAPIServerConfig) { *out = new(ControlPlaneAutoscaling) (*in).DeepCopyInto(*out) } + if in.TLSMinVersion != nil { + in, out := &in.TLSMinVersion, &out.TLSMinVersion + *out = new(string) + **out = **in + } return } diff --git a/pkg/apiserver/openapi/openapi_generated.go b/pkg/apiserver/openapi/openapi_generated.go index c0eb0a9577c1..bde94b484ad8 100644 --- a/pkg/apiserver/openapi/openapi_generated.go +++ b/pkg/apiserver/openapi/openapi_generated.go @@ -5064,6 +5064,13 @@ func schema_pkg_apis_core_v1beta1_KubeAPIServerConfig(ref common.ReferenceCallba Ref: ref(v1beta1.ControlPlaneAutoscaling{}.OpenAPIModelName()), }, }, + "tlsMinVersion": { + SchemaProps: spec.SchemaProps{ + Description: "TLSMinVersion is the minimum TLS version accepted by the kube-apiserver. Supported values: VersionTLS12, VersionTLS13.", + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, diff --git a/pkg/component/kubernetes/apiserver/apiserver.go b/pkg/component/kubernetes/apiserver/apiserver.go index 4b2976324a4d..152fe065e593 100644 --- a/pkg/component/kubernetes/apiserver/apiserver.go +++ b/pkg/component/kubernetes/apiserver/apiserver.go @@ -108,6 +108,8 @@ type Values struct { DefaultUnreachableTolerationSeconds *int64 // EventTTL is the amount of time to retain events. EventTTL *metav1.Duration + // TLSMinVersion is the minimum TLS version accepted by the kube-apiserver. + TLSMinVersion *string // ExternalHostname is the external hostname which should be exposed by the kube-apiserver. ExternalHostname string // Images is a set of container images used for the containers of the kube-apiserver pods. diff --git a/pkg/component/kubernetes/apiserver/apiserver_test.go b/pkg/component/kubernetes/apiserver/apiserver_test.go index a7ad9064bcac..e8b58020877d 100644 --- a/pkg/component/kubernetes/apiserver/apiserver_test.go +++ b/pkg/component/kubernetes/apiserver/apiserver_test.go @@ -3124,6 +3124,7 @@ kind: AuthorizationConfiguration }, } eventTTL = 2 * time.Hour + tlsMinVersion = "VersionTLS13" externalHostname = "api.foo.bar.com" images = Images{KubeAPIServer: "some-kapi-image:latest"} serviceAccountIssuer = "issuer" @@ -3144,6 +3145,7 @@ kind: AuthorizationConfiguration }, Autoscaling: AutoscalingConfig{APIServerResources: apiServerResources}, EventTTL: &metav1.Duration{Duration: eventTTL}, + TLSMinVersion: &tlsMinVersion, ExternalHostname: externalHostname, Images: images, IsWorkerless: true, @@ -3193,6 +3195,7 @@ kind: AuthorizationConfiguration "--etcd-servers-overrides=/events#https://etcd-events-client:2379", "--encryption-provider-config=/etc/kubernetes/etcd-encryption-secret/encryption-configuration.yaml", "--event-ttl="+eventTTL.String(), + "--tls-min-version="+tlsMinVersion, "--external-hostname="+externalHostname, "--livez-grace-period=1m", "--shutdown-delay-duration=15s", diff --git a/pkg/component/kubernetes/apiserver/deployment.go b/pkg/component/kubernetes/apiserver/deployment.go index 58cb348260f6..733351a03a9a 100644 --- a/pkg/component/kubernetes/apiserver/deployment.go +++ b/pkg/component/kubernetes/apiserver/deployment.go @@ -425,6 +425,10 @@ func (k *kubeAPIServer) computeKubeAPIServerArgs() []string { out = append(out, fmt.Sprintf("--event-ttl=%s", k.values.EventTTL.Duration)) } + if k.values.TLSMinVersion != nil { + out = append(out, fmt.Sprintf("--tls-min-version=%s", *k.values.TLSMinVersion)) + } + out = append(out, fmt.Sprintf("--proxy-client-cert-file=%s/%s", volumeMountPathKubeAggregator, secrets.DataKeyCertificate)) out = append(out, fmt.Sprintf("--proxy-client-key-file=%s/%s", volumeMountPathKubeAggregator, secrets.DataKeyPrivateKey)) out = append(out, fmt.Sprintf("--requestheader-client-ca-file=%s/%s", volumeMountPathCAFrontProxy, secrets.DataKeyCertificateBundle)) diff --git a/pkg/component/kubernetes/apiserverexposure/sni.go b/pkg/component/kubernetes/apiserverexposure/sni.go index f941743049f2..2c818e35b676 100644 --- a/pkg/component/kubernetes/apiserverexposure/sni.go +++ b/pkg/component/kubernetes/apiserverexposure/sni.go @@ -109,6 +109,7 @@ type SNIValues struct { IstioIngressGateway IstioIngressGateway IstioTLSTermination bool WildcardConfiguration *WildcardConfiguration + TLSMinVersion *string } // APIServerProxy contains values for the APIServer proxy protocol configuration. @@ -360,12 +361,23 @@ func (s *sni) Deploy(ctx context.Context) error { gatewayMutateFn := istio.GatewayWithTLSPassthrough(configuration.gateway, getLabels(), configuration.istioIngressGateway.Labels, allHosts) if values.IstioTLSTermination { + minProtocolVersion := tlsVersionToIstioProtocolVersion(values.TLSMinVersion) var serverConfigs []istio.ServerConfig if len(configuration.hosts) > 0 { - serverConfigs = append(serverConfigs, istio.ServerConfig{Hosts: configuration.hosts, PortName: portNameTLS, TLSSecret: s.namespace + istioTLSSecretSuffix}) + serverConfigs = append(serverConfigs, istio.ServerConfig{ + Hosts: configuration.hosts, + PortName: portNameTLS, + TLSSecret: s.namespace + istioTLSSecretSuffix, + MinProtocolVersion: minProtocolVersion, + }) } if configuration.wildcardConfiguration != nil { - serverConfigs = append(serverConfigs, istio.ServerConfig{Hosts: configuration.wildcardConfiguration.Hosts, PortName: portNameWildcardTLS, TLSSecret: s.emptyIstioWildcardTLSSecret().Name}) + serverConfigs = append(serverConfigs, istio.ServerConfig{ + Hosts: configuration.wildcardConfiguration.Hosts, + PortName: portNameWildcardTLS, + TLSSecret: s.emptyIstioWildcardTLSSecret().Name, + MinProtocolVersion: minProtocolVersion, + }) } gatewayMutateFn = istio.GatewayWithMutualTLS(configuration.gateway, getLabels(), configuration.istioIngressGateway.Labels, serverConfigs) } @@ -668,3 +680,18 @@ func getExportTo(istioGatewayConfigurations []istioGatewayConfiguration) []strin return namespaces } + +func tlsVersionToIstioProtocolVersion(v *string) istioapinetworkingv1beta1.ServerTLSSettings_TLSProtocol { + if v == nil { + return istioapinetworkingv1beta1.ServerTLSSettings_TLS_AUTO + } + + switch *v { + case "VersionTLS13": + return istioapinetworkingv1beta1.ServerTLSSettings_TLSV1_3 + case "VersionTLS12": + return istioapinetworkingv1beta1.ServerTLSSettings_TLSV1_2 + default: + return istioapinetworkingv1beta1.ServerTLSSettings_TLS_AUTO + } +} diff --git a/pkg/component/kubernetes/apiserverexposure/sni_test.go b/pkg/component/kubernetes/apiserverexposure/sni_test.go index 5266f884573e..3b54d688abbc 100644 --- a/pkg/component/kubernetes/apiserverexposure/sni_test.go +++ b/pkg/component/kubernetes/apiserverexposure/sni_test.go @@ -53,6 +53,7 @@ var _ = Describe("#SNI", func() { istioNamespace string istioWildcardNamespace string istioTLSTermination bool + tlsMinVersion *string hosts []string hostName string connectionUpgradeHostName string @@ -95,6 +96,7 @@ var _ = Describe("#SNI", func() { istioWildcardLabels = map[string]string{"bar": "foo"} istioWildcardNamespace = "istio-bar" istioTLSTermination = false + tlsMinVersion = nil hosts = []string{"foo.bar"} hostName = "kube-apiserver." + namespace + ".svc.cluster.local" connectionUpgradeHostName = "kube-apiserver-connection-upgrade." + namespace + ".svc.cluster.local" @@ -341,6 +343,7 @@ var _ = Describe("#SNI", func() { }, IstioTLSTermination: istioTLSTermination, WildcardConfiguration: wildcardConfiguration, + TLSMinVersion: tlsMinVersion, } return val }) @@ -518,8 +521,141 @@ var _ = Describe("#SNI", func() { expectedGateway.Spec.Servers[0].Port.Protocol = "HTTPS" expectedGateway.Spec.Servers[0].Tls = &istioapinetworkingv1beta1.ServerTLSSettings{ - Mode: istioapinetworkingv1beta1.ServerTLSSettings_OPTIONAL_MUTUAL, - CredentialName: namespace + "-kube-apiserver-tls", + Mode: istioapinetworkingv1beta1.ServerTLSSettings_OPTIONAL_MUTUAL, + CredentialName: namespace + "-kube-apiserver-tls", + MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLS_AUTO, + } + + expectedVirtualService.Spec.Tls = nil + expectedVirtualService.Spec.Http = []*istioapinetworkingv1beta1.HTTPRoute{ + { + Name: "connection-upgrade", + Match: []*istioapinetworkingv1beta1.HTTPMatchRequest{ + { + Headers: map[string]*istioapinetworkingv1beta1.StringMatch{ + "Connection": {MatchType: &istioapinetworkingv1beta1.StringMatch_Exact{Exact: "Upgrade"}}, + "Upgrade": {}, + }, + }, + }, + Route: []*istioapinetworkingv1beta1.HTTPRouteDestination{ + { + Destination: &istioapinetworkingv1beta1.Destination{ + Host: connectionUpgradeHostName, + Port: &istioapinetworkingv1beta1.PortSelector{Number: 443}, + }, + }, + }, + }, + { + Route: []*istioapinetworkingv1beta1.HTTPRouteDestination{ + { + Destination: &istioapinetworkingv1beta1.Destination{ + Host: hostName, + Port: &istioapinetworkingv1beta1.PortSelector{Number: 443}, + }, + }, + }, + }, + } + }) + + It("should succeed deploying", func() { + testFunc() + }) + }) + + Context("when IstioTLSTermination feature gate is true and TLSMinVersion is VersionTLS12", func() { + BeforeEach(func() { + istioTLSTermination = true + tlsMinVersion = new("VersionTLS12") + + expectedDestinationRule.Spec.TrafficPolicy.ConnectionPool.Http = &istioapinetworkingv1beta1.ConnectionPoolSettings_HTTPSettings{ + UseClientProtocol: true, + } + expectedDestinationRule.Spec.TrafficPolicy.LoadBalancer = &istioapinetworkingv1beta1.LoadBalancerSettings{ + LbPolicy: &istioapinetworkingv1beta1.LoadBalancerSettings_Simple{ + Simple: istioapinetworkingv1beta1.LoadBalancerSettings_LEAST_REQUEST, + }, + } + expectedDestinationRule.Spec.TrafficPolicy.OutlierDetection = nil + expectedDestinationRule.Spec.TrafficPolicy.Tls = &istioapinetworkingv1beta1.ClientTLSSettings{ + Mode: istioapinetworkingv1beta1.ClientTLSSettings_SIMPLE, + CredentialName: namespace + "-kube-apiserver-istio-mtls", + Sni: "kubernetes.default.svc.cluster.local", + } + + expectedGateway.Spec.Servers[0].Port.Protocol = "HTTPS" + expectedGateway.Spec.Servers[0].Tls = &istioapinetworkingv1beta1.ServerTLSSettings{ + Mode: istioapinetworkingv1beta1.ServerTLSSettings_OPTIONAL_MUTUAL, + CredentialName: namespace + "-kube-apiserver-tls", + MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLSV1_2, + } + + expectedVirtualService.Spec.Tls = nil + expectedVirtualService.Spec.Http = []*istioapinetworkingv1beta1.HTTPRoute{ + { + Name: "connection-upgrade", + Match: []*istioapinetworkingv1beta1.HTTPMatchRequest{ + { + Headers: map[string]*istioapinetworkingv1beta1.StringMatch{ + "Connection": {MatchType: &istioapinetworkingv1beta1.StringMatch_Exact{Exact: "Upgrade"}}, + "Upgrade": {}, + }, + }, + }, + Route: []*istioapinetworkingv1beta1.HTTPRouteDestination{ + { + Destination: &istioapinetworkingv1beta1.Destination{ + Host: connectionUpgradeHostName, + Port: &istioapinetworkingv1beta1.PortSelector{Number: 443}, + }, + }, + }, + }, + { + Route: []*istioapinetworkingv1beta1.HTTPRouteDestination{ + { + Destination: &istioapinetworkingv1beta1.Destination{ + Host: hostName, + Port: &istioapinetworkingv1beta1.PortSelector{Number: 443}, + }, + }, + }, + }, + } + }) + + It("should succeed deploying", func() { + testFunc() + }) + }) + + Context("when IstioTLSTermination feature gate is true and TLSMinVersion is VersionTLS13", func() { + BeforeEach(func() { + istioTLSTermination = true + tlsMinVersion = new("VersionTLS13") + + expectedDestinationRule.Spec.TrafficPolicy.ConnectionPool.Http = &istioapinetworkingv1beta1.ConnectionPoolSettings_HTTPSettings{ + UseClientProtocol: true, + } + expectedDestinationRule.Spec.TrafficPolicy.LoadBalancer = &istioapinetworkingv1beta1.LoadBalancerSettings{ + LbPolicy: &istioapinetworkingv1beta1.LoadBalancerSettings_Simple{ + Simple: istioapinetworkingv1beta1.LoadBalancerSettings_LEAST_REQUEST, + }, + } + expectedDestinationRule.Spec.TrafficPolicy.OutlierDetection = nil + expectedDestinationRule.Spec.TrafficPolicy.Tls = &istioapinetworkingv1beta1.ClientTLSSettings{ + Mode: istioapinetworkingv1beta1.ClientTLSSettings_SIMPLE, + CredentialName: namespace + "-kube-apiserver-istio-mtls", + Sni: "kubernetes.default.svc.cluster.local", + } + + expectedGateway.Spec.Servers[0].Port.Protocol = "HTTPS" + expectedGateway.Spec.Servers[0].Tls = &istioapinetworkingv1beta1.ServerTLSSettings{ + Mode: istioapinetworkingv1beta1.ServerTLSSettings_OPTIONAL_MUTUAL, + CredentialName: namespace + "-kube-apiserver-tls", + MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLSV1_3, } expectedVirtualService.Spec.Tls = nil diff --git a/pkg/component/observability/logging/victorialogs/victorialogs.go b/pkg/component/observability/logging/victorialogs/victorialogs.go index e11d2472be3b..fe59aaa211a8 100644 --- a/pkg/component/observability/logging/victorialogs/victorialogs.go +++ b/pkg/component/observability/logging/victorialogs/victorialogs.go @@ -302,12 +302,18 @@ func (v *victoriaLogs) getServiceMonitor() *monitoringv1.ServiceMonitor { return &monitoringv1.ServiceMonitor{ ObjectMeta: monitoringutils.ConfigObjectMeta("victoria-logs", v.namespace, v.getPrometheusLabel()), Spec: monitoringv1.ServiceMonitorSpec{ - Selector: metav1.LabelSelector{MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vlsingle", - "app.kubernetes.io/instance": constants.VLSingleResourceName, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }}, + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": "vlsingle", + "app.kubernetes.io/instance": constants.VLSingleResourceName, + "app.kubernetes.io/component": "monitoring", + "managed-by": "vm-operator", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "operator.victoriametrics.com/additional-service", + Operator: metav1.LabelSelectorOpDoesNotExist, + }}, + }, Endpoints: []monitoringv1.Endpoint{{ Port: "http", RelabelConfigs: []monitoringv1.RelabelConfig{ diff --git a/pkg/component/observability/logging/victorialogs/victorialogs_test.go b/pkg/component/observability/logging/victorialogs/victorialogs_test.go index c3e08b46740a..9bf57254cdf9 100644 --- a/pkg/component/observability/logging/victorialogs/victorialogs_test.go +++ b/pkg/component/observability/logging/victorialogs/victorialogs_test.go @@ -172,12 +172,18 @@ var _ = Describe("VictoriaLogs", func() { serviceMonitor = &monitoringv1.ServiceMonitor{ ObjectMeta: monitoringutils.ConfigObjectMeta("victoria-logs", namespace, shoot.Label), Spec: monitoringv1.ServiceMonitorSpec{ - Selector: metav1.LabelSelector{MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vlsingle", - "app.kubernetes.io/instance": victorialogsconstants.VLSingleResourceName, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }}, + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": "vlsingle", + "app.kubernetes.io/instance": victorialogsconstants.VLSingleResourceName, + "app.kubernetes.io/component": "monitoring", + "managed-by": "vm-operator", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "operator.victoriametrics.com/additional-service", + Operator: metav1.LabelSelectorOpDoesNotExist, + }}, + }, Endpoints: []monitoringv1.Endpoint{{ Port: "http", RelabelConfigs: []monitoringv1.RelabelConfig{ diff --git a/pkg/component/observability/opentelemetry/collector/collector.go b/pkg/component/observability/opentelemetry/collector/collector.go index 899be301736c..b74003bd3b2a 100644 --- a/pkg/component/observability/opentelemetry/collector/collector.go +++ b/pkg/component/observability/opentelemetry/collector/collector.go @@ -15,10 +15,12 @@ import ( istioapiannotation "istio.io/api/annotation" istioapinetworkingv1beta1 "istio.io/api/networking/v1beta1" istionetworkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" + autoscalingv1 "k8s.io/api/autoscaling/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + vpaautoscalingv1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" "sigs.k8s.io/controller-runtime/pkg/client" v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" @@ -45,6 +47,7 @@ const ( managedResourceNameTarget = "logging-target" managedResourceName = "opentelemetry-collector" serviceMonitorName = "opentelemetry-collector" + vpaName = "opentelemetry-collector" openTelemetryCollectorName = "gardener-opentelemetry-collector" kubeRBACProxyName = "rbac-proxy" @@ -201,6 +204,7 @@ func (o *otelCollector) Deploy(ctx context.Context) error { seedObjects = append(seedObjects, o.openTelemetryCollector(o.namespace, o.values.LokiEndpoint, genericTokenKubeconfigSecretName)) seedObjects = append(seedObjects, o.serviceMonitor()) seedObjects = append(seedObjects, o.serviceAccount()) + seedObjects = append(seedObjects, o.vpa()) seedRegistry := managedresources.NewRegistry(kubernetes.SeedScheme, kubernetes.SeedCodec, kubernetes.SeedSerializer) serializedResources, err := seedRegistry.AddAllAndSerialize(seedObjects...) @@ -259,6 +263,41 @@ func (o *otelCollector) WaitCleanup(ctx context.Context) error { return managedresources.WaitUntilDeleted(timeoutCtx, o.client, o.namespace, managedResourceName) } +func (o *otelCollector) vpa() *vpaautoscalingv1.VerticalPodAutoscaler { + return &vpaautoscalingv1.VerticalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: vpaName, + Namespace: o.namespace, + Labels: getLabels(), + }, + Spec: vpaautoscalingv1.VerticalPodAutoscalerSpec{ + TargetRef: &autoscalingv1.CrossVersionObjectReference{ + APIVersion: otelv1beta1.GroupVersion.String(), + Kind: "OpenTelemetryCollector", + Name: collectorconstants.OpenTelemetryCollectorResourceName, + }, + UpdatePolicy: &vpaautoscalingv1.PodUpdatePolicy{ + UpdateMode: new(vpaautoscalingv1.UpdateModeInPlaceOrRecreate), + }, + ResourcePolicy: &vpaautoscalingv1.PodResourcePolicy{ + ContainerPolicies: []vpaautoscalingv1.ContainerResourcePolicy{ + { + ContainerName: collectorconstants.ContainerName, + MinAllowed: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("64Mi"), + }, + ControlledValues: new(vpaautoscalingv1.ContainerControlledValuesRequestsOnly), + }, + { + ContainerName: vpaautoscalingv1.DefaultContainerResourcePolicy, + Mode: new(vpaautoscalingv1.ContainerScalingModeOff), + }, + }, + }, + }, + } +} + func (o *otelCollector) serviceAccount() *corev1.ServiceAccount { return &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ @@ -358,7 +397,7 @@ func (o *otelCollector) openTelemetryCollector(namespace, lokiEndpoint, genericT Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), + corev1.ResourceMemory: resource.MustParse("64Mi"), }, }, SecurityContext: &corev1.SecurityContext{ diff --git a/pkg/component/observability/opentelemetry/collector/collector_test.go b/pkg/component/observability/opentelemetry/collector/collector_test.go index b27b299abb28..4746edac7227 100644 --- a/pkg/component/observability/opentelemetry/collector/collector_test.go +++ b/pkg/component/observability/opentelemetry/collector/collector_test.go @@ -17,11 +17,13 @@ import ( "google.golang.org/protobuf/types/known/wrapperspb" istioapinetworkingv1beta1 "istio.io/api/networking/v1beta1" istionetworkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" + autoscalingv1 "k8s.io/api/autoscaling/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + vpaautoscalingv1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -77,6 +79,7 @@ var _ = Describe("OpenTelemetry Collector", func() { volume corev1.Volume volumeMount corev1.VolumeMount + vpa *vpaautoscalingv1.VerticalPodAutoscaler managedResourceTarget *resourcesv1alpha1.ManagedResource openTelemetryCollector *otelv1beta1.OpenTelemetryCollector serviceMonitor *monitoringv1.ServiceMonitor @@ -353,7 +356,7 @@ var _ = Describe("OpenTelemetry Collector", func() { Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("10m"), - corev1.ResourceMemory: resource.MustParse("50Mi"), + corev1.ResourceMemory: resource.MustParse("64Mi"), }, }, ServiceAccount: "opentelemetry-collector", @@ -525,6 +528,39 @@ var _ = Describe("OpenTelemetry Collector", func() { }, } + vpa = &vpaautoscalingv1.VerticalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "opentelemetry-collector", + Namespace: namespace, + Labels: getLabels(), + }, + Spec: vpaautoscalingv1.VerticalPodAutoscalerSpec{ + TargetRef: &autoscalingv1.CrossVersionObjectReference{ + APIVersion: otelv1beta1.GroupVersion.String(), + Kind: "OpenTelemetryCollector", + Name: "opentelemetry-collector", + }, + UpdatePolicy: &vpaautoscalingv1.PodUpdatePolicy{ + UpdateMode: new(vpaautoscalingv1.UpdateModeInPlaceOrRecreate), + }, + ResourcePolicy: &vpaautoscalingv1.PodResourcePolicy{ + ContainerPolicies: []vpaautoscalingv1.ContainerResourcePolicy{ + { + ContainerName: "otc-container", + MinAllowed: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("64Mi"), + }, + ControlledValues: new(vpaautoscalingv1.ContainerControlledValuesRequestsOnly), + }, + { + ContainerName: vpaautoscalingv1.DefaultContainerResourcePolicy, + Mode: new(vpaautoscalingv1.ContainerScalingModeOff), + }, + }, + }, + }, + } + openTelemetryCollector.Spec.AdditionalContainers = []corev1.Container{kubeRBACProxyValiContainer, kubeRBACProxyOTLPContainer} openTelemetryCollector.Spec.Volumes = []corev1.Volume{volume} openTelemetryCollector.Spec.Ports = append(openTelemetryCollector.Spec.Ports, otelv1beta1.PortsSpec{ @@ -587,6 +623,7 @@ var _ = Describe("OpenTelemetry Collector", func() { getVirtualService(), getDestinationRule(), getTLSSecret(tlsSecret), + vpa, serviceMonitor, serviceAccount, )) @@ -644,6 +681,7 @@ var _ = Describe("OpenTelemetry Collector", func() { getVirtualService(), getDestinationRule(), getTLSSecret(tlsSecret), + vpa, serviceMonitor, serviceAccount, )) @@ -733,6 +771,7 @@ var _ = Describe("OpenTelemetry Collector", func() { getVirtualService(), getDestinationRule(), getTLSSecret(tlsSecret), + vpa, serviceMonitor, serviceAccount, )) @@ -768,6 +807,7 @@ var _ = Describe("OpenTelemetry Collector", func() { getVirtualService(), getDestinationRule(), getTLSSecret(tlsSecret), + vpa, serviceMonitor, serviceAccount, )) diff --git a/pkg/component/observability/opentelemetry/collector/constants/constants.go b/pkg/component/observability/opentelemetry/collector/constants/constants.go index 18fc8829a7b0..ed2dda4e253e 100644 --- a/pkg/component/observability/opentelemetry/collector/constants/constants.go +++ b/pkg/component/observability/opentelemetry/collector/constants/constants.go @@ -10,6 +10,9 @@ const ( // DeploymentName is the name that the OpenTelemetry Operator will for the Collector deployment. // Note: Currently, the OpenTelemetry Operator hardcodes the deployment name to be the same as the resource name with a '-collector' suffix. DeploymentName = OpenTelemetryCollectorResourceName + "-collector" + // ContainerName is the name of the main container in the OpenTelemetry Collector deployment. + // Note: Currently, the OpenTelemetry Operator hardcodes the container name to 'otc-container'. + ContainerName = "otc-container" // ServiceName is the name the OpenTelemetry Operator will use for the Collector service. // Note: Currently, the OpenTelemetry Operator hardcodes the service name to be the same as the resource name with a '-collector' suffix. ServiceName = OpenTelemetryCollectorResourceName + "-collector" diff --git a/pkg/component/shared/kubeapiserver.go b/pkg/component/shared/kubeapiserver.go index 7bd5de9101b0..ae2665718043 100644 --- a/pkg/component/shared/kubeapiserver.go +++ b/pkg/component/shared/kubeapiserver.go @@ -109,6 +109,7 @@ func NewKubeAPIServer( defaultNotReadyTolerationSeconds *int64 defaultUnreachableTolerationSeconds *int64 eventTTL *metav1.Duration + tlsMinVersion *string featureGates map[string]bool requests *gardencorev1beta1.APIServerRequests runtimeConfig map[string]bool @@ -151,6 +152,7 @@ func NewKubeAPIServer( defaultNotReadyTolerationSeconds = apiServerConfig.DefaultNotReadyTolerationSeconds defaultUnreachableTolerationSeconds = apiServerConfig.DefaultUnreachableTolerationSeconds eventTTL = apiServerConfig.EventTTL + tlsMinVersion = apiServerConfig.TLSMinVersion featureGates = apiServerConfig.FeatureGates logging = apiServerConfig.Logging requests = apiServerConfig.Requests @@ -202,6 +204,7 @@ func NewKubeAPIServer( DefaultNotReadyTolerationSeconds: defaultNotReadyTolerationSeconds, DefaultUnreachableTolerationSeconds: defaultUnreachableTolerationSeconds, EventTTL: eventTTL, + TLSMinVersion: tlsMinVersion, Images: images, IsWorkerless: isWorkerless, NamePrefix: namePrefix, diff --git a/pkg/component/shared/kubeapiserver_test.go b/pkg/component/shared/kubeapiserver_test.go index 42e559b509ea..2206f2851728 100644 --- a/pkg/component/shared/kubeapiserver_test.go +++ b/pkg/component/shared/kubeapiserver_test.go @@ -1004,6 +1004,26 @@ authorizers: }) }) + Describe("TLSMinVersion", func() { + It("should not set the tls min version field", func() { + kubeAPIServer, err := NewKubeAPIServer(ctx, runtimeClientSet, resourceConfigClient, namespace, objectMeta, runtimeVersion, targetVersion, sm, namePrefix, apiServerConfig, autoscalingConfig, vpnConfig, priorityClassName, isWorkerless, runsAsStaticPod, istioTLSTerminationEnabled, auditWebhookConfig, authenticationWebhookConfig, authorizationWebhookConfigs, resourcesToStoreInETCDEvents) + Expect(err).NotTo(HaveOccurred()) + Expect(kubeAPIServer.GetValues().TLSMinVersion).To(BeNil()) + }) + + It("should set the field to the configured value", func() { + tlsMinVersion := new("VersionTLS13") + + apiServerConfig = &gardencorev1beta1.KubeAPIServerConfig{ + TLSMinVersion: tlsMinVersion, + } + + kubeAPIServer, err := NewKubeAPIServer(ctx, runtimeClientSet, resourceConfigClient, namespace, objectMeta, runtimeVersion, targetVersion, sm, namePrefix, apiServerConfig, autoscalingConfig, vpnConfig, priorityClassName, isWorkerless, runsAsStaticPod, istioTLSTerminationEnabled, auditWebhookConfig, authenticationWebhookConfig, authorizationWebhookConfigs, resourcesToStoreInETCDEvents) + Expect(err).NotTo(HaveOccurred()) + Expect(kubeAPIServer.GetValues().TLSMinVersion).To(Equal(tlsMinVersion)) + }) + }) + Describe("FeatureGates", func() { It("should set the field to nil by default", func() { kubeAPIServer, err := NewKubeAPIServer(ctx, runtimeClientSet, resourceConfigClient, namespace, objectMeta, runtimeVersion, targetVersion, sm, namePrefix, apiServerConfig, autoscalingConfig, vpnConfig, priorityClassName, isWorkerless, runsAsStaticPod, istioTLSTerminationEnabled, auditWebhookConfig, authenticationWebhookConfig, authorizationWebhookConfigs, resourcesToStoreInETCDEvents) diff --git a/pkg/gardenlet/operation/botanist/kubeapiserverexposure.go b/pkg/gardenlet/operation/botanist/kubeapiserverexposure.go index fe87e3ed65a5..f05d8bf1890b 100644 --- a/pkg/gardenlet/operation/botanist/kubeapiserverexposure.go +++ b/pkg/gardenlet/operation/botanist/kubeapiserverexposure.go @@ -120,6 +120,7 @@ func (b *Botanist) DefaultKubeAPIServerSNI() component.DeployWaiter { }, IstioTLSTermination: b.ShootUsesIstioTLSTermination(), WildcardConfiguration: wildcardConfiguration, + TLSMinVersion: b.shootKubeAPIServerTLSMinVersion(), } }, )) @@ -186,6 +187,7 @@ func (b *Botanist) setAPIServerServiceClusterIPs(clusterIPs []string) { }, IstioTLSTermination: b.ShootUsesIstioTLSTermination(), WildcardConfiguration: wildcardConfiguration, + TLSMinVersion: b.shootKubeAPIServerTLSMinVersion(), } if b.Shoot.ExternalClusterDomain != nil { @@ -197,6 +199,14 @@ func (b *Botanist) setAPIServerServiceClusterIPs(clusterIPs []string) { ) } +func (b *Botanist) shootKubeAPIServerTLSMinVersion() *string { + if cfg := b.Shoot.GetInfo().Spec.Kubernetes.KubeAPIServer; cfg != nil { + return cfg.TLSMinVersion + } + + return nil +} + func mapToReservedKubeApiServerRange(ip net.IP) string { // prevent leakage of real cluster ip to shoot. we use the reserved range 240.0.0.0/8 as prefix instead. // e.g. cluster ip in seed: 192.168.102.23 => ip in shoot: 240.168.102.23 diff --git a/pkg/utils/istio/gateway.go b/pkg/utils/istio/gateway.go index 77683189bc67..99991920adaf 100644 --- a/pkg/utils/istio/gateway.go +++ b/pkg/utils/istio/gateway.go @@ -13,9 +13,10 @@ const httpsPort = 443 // ServerConfig is a configuration for a server in an Istio Gateway. type ServerConfig struct { - Hosts []string - PortName string - TLSSecret string + Hosts []string + PortName string + TLSSecret string + MinProtocolVersion istioapinetworkingv1beta1.ServerTLSSettings_TLSProtocol } // GatewayWithTLSPassthrough returns a function setting the given attributes to a gateway object. @@ -81,8 +82,9 @@ func GatewayWithMutualTLS(gateway *istionetworkingv1beta1.Gateway, labels map[st Protocol: "HTTPS", }, Tls: &istioapinetworkingv1beta1.ServerTLSSettings{ - Mode: istioapinetworkingv1beta1.ServerTLSSettings_OPTIONAL_MUTUAL, - CredentialName: serverConfig.TLSSecret, + Mode: istioapinetworkingv1beta1.ServerTLSSettings_OPTIONAL_MUTUAL, + CredentialName: serverConfig.TLSSecret, + MinProtocolVersion: serverConfig.MinProtocolVersion, }, }) } diff --git a/pkg/utils/istio/gateway_test.go b/pkg/utils/istio/gateway_test.go index 17f37fb1ff42..8b5e39b73182 100644 --- a/pkg/utils/istio/gateway_test.go +++ b/pkg/utils/istio/gateway_test.go @@ -77,11 +77,14 @@ var _ = Describe("Gateway", func() { Expect(gateway.Spec.Servers[i].Port.Protocol).To(Equal("HTTPS")) Expect(gateway.Spec.Servers[i].Tls.CredentialName).To(Equal(serverConfig.TLSSecret)) Expect(gateway.Spec.Servers[i].Tls.Mode).To(Equal(istioapinetworkingv1beta1.ServerTLSSettings_OPTIONAL_MUTUAL)) + Expect(gateway.Spec.Servers[i].Tls.MinProtocolVersion).To(Equal(serverConfig.MinProtocolVersion)) } }, - Entry("Nil values", nil, nil, []ServerConfig{{Hosts: nil, PortName: "", TLSSecret: ""}}), - Entry("Some values", map[string]string{"foo": "bar", "key": "value"}, map[string]string{"app": "istio", "istio": "gateway"}, []ServerConfig{{Hosts: []string{"host-1", "host-2"}, PortName: "foo", TLSSecret: "my-secret"}}), - Entry("Multiple servers", map[string]string{"foo": "bar", "key": "value"}, map[string]string{"app": "istio", "istio": "gateway"}, []ServerConfig{{Hosts: []string{"host-1", "host-2"}, PortName: "foo", TLSSecret: "my-secret"}, {Hosts: []string{"host-3", "host-4"}, PortName: "bar", TLSSecret: "my-other-secret"}}), + Entry("Nil values", nil, nil, []ServerConfig{{Hosts: nil, PortName: "", TLSSecret: "", MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLS_AUTO}}), + Entry("Some values", map[string]string{"foo": "bar", "key": "value"}, map[string]string{"app": "istio", "istio": "gateway"}, []ServerConfig{{Hosts: []string{"host-1", "host-2"}, PortName: "foo", TLSSecret: "my-secret", MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLS_AUTO}}), + Entry("Multiple servers", map[string]string{"foo": "bar", "key": "value"}, map[string]string{"app": "istio", "istio": "gateway"}, []ServerConfig{{Hosts: []string{"host-1", "host-2"}, PortName: "foo", TLSSecret: "my-secret", MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLS_AUTO}, {Hosts: []string{"host-3", "host-4"}, PortName: "bar", TLSSecret: "my-other-secret", MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLS_AUTO}}), + Entry("TLSV1_2", map[string]string{"foo": "bar"}, map[string]string{"app": "istio"}, []ServerConfig{{Hosts: []string{"host-1"}, PortName: "tls", TLSSecret: "secret", MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLSV1_2}}), + Entry("TLSV1_3", map[string]string{"foo": "bar"}, map[string]string{"app": "istio"}, []ServerConfig{{Hosts: []string{"host-1"}, PortName: "tls", TLSSecret: "secret", MinProtocolVersion: istioapinetworkingv1beta1.ServerTLSSettings_TLSV1_3}}), ) })