diff --git a/charts/gardener-extension-provider-stackit/templates/deployment.yaml b/charts/gardener-extension-provider-stackit/templates/deployment.yaml
index 73882df6..3f13b58e 100644
--- a/charts/gardener-extension-provider-stackit/templates/deployment.yaml
+++ b/charts/gardener-extension-provider-stackit/templates/deployment.yaml
@@ -130,10 +130,6 @@ spec:
volumeMounts:
- name: config
mountPath: /etc/{{ include "name" . }}/config
- - name: custom-certs
- mountPath: /etc/ssl/certs/customca.crt
- readOnly: true
- subPath: customca.crt
{{- if .Values.imageVectorOverwrite }}
- name: imagevector-overwrite
mountPath: /charts_overwrite/
@@ -144,10 +140,6 @@ spec:
configMap:
name: {{ include "name" . }}-configmap
defaultMode: 420
- - name: custom-certs
- secret:
- secretName: {{ include "name" . }}-ca
- optional: true
{{- if .Values.imageVectorOverwrite }}
- name: imagevector-overwrite
configMap:
diff --git a/charts/gardener-extension-provider-stackit/templates/secret.yaml b/charts/gardener-extension-provider-stackit/templates/secret.yaml
deleted file mode 100644
index 1f159aad..00000000
--- a/charts/gardener-extension-provider-stackit/templates/secret.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-{{- if .Values.config.stackitAPIEndpoints.caBundle }}
-apiVersion: v1
-kind: Secret
-metadata:
- name: {{ include "name" . }}-ca
- namespace: {{ .Release.Namespace }}
-data:
- customca.crt: {{ .Values.config.stackitAPIEndpoints.caBundle }}
-{{- end }}
diff --git a/charts/gardener-extension-provider-stackit/values.yaml b/charts/gardener-extension-provider-stackit/values.yaml
index f72ae03f..fb60b55d 100644
--- a/charts/gardener-extension-provider-stackit/values.yaml
+++ b/charts/gardener-extension-provider-stackit/values.yaml
@@ -75,9 +75,6 @@ config:
capacity: 25Gi
provisioner: block-storage.csi.stackit.cloud
volumeBindingMode: WaitForFirstConsumer
- stackitAPIEndpoints:
- # Must be base64 encoded
- caBundle: ""
registryCaches:
# - server: reg.example.com
# cache: reg-cache.example.com
diff --git a/charts/internal/cloud-provider-config/templates/stackit-ca-bundle.yaml b/charts/internal/cloud-provider-config/templates/stackit-ca-bundle.yaml
new file mode 100644
index 00000000..14185525
--- /dev/null
+++ b/charts/internal/cloud-provider-config/templates/stackit-ca-bundle.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.CABundle }}
+apiVersion: v1
+kind: Secret
+metadata:
+ name: cloudprofile-ca-bundle
+ namespace: {{ .Release.Namespace }}
+type: Opaque
+data:
+ {{- if .Values.CABundle }}
+ stackit-ca.crt: {{ .Values.CABundle | b64enc }}
+ {{- end }}
+{{- end }}
diff --git a/charts/internal/cloud-provider-config/values.yaml b/charts/internal/cloud-provider-config/values.yaml
index 2f537842..fd0fbbd7 100644
--- a/charts/internal/cloud-provider-config/values.yaml
+++ b/charts/internal/cloud-provider-config/values.yaml
@@ -1,4 +1,5 @@
stackitOnly: false
+CABundle: ""
# [Global]
authUrl: fooURL
domainName: fooDomain
diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml
index df9581f8..ebab6de2 100644
--- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml
+++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml
@@ -89,13 +89,9 @@ spec:
mountPath: {{ .Values.socketPath }}
- name: cloud-provider-config
mountPath: /etc/config
- # Host certificates are mounted to accommodate OpenStack endpoints that might be served with a certificate
- # signed by a CA that is not globally trusted.
- - mountPath: /usr/share/ca-certificates
- name: usr-share-ca-certificates
- readOnly: true
- - mountPath: /etc/ssl
- name: etc-ssl
+ - mountPath: /etc/ssl/certs/stackit-ca.crt
+ name: stackit-ca
+ subPath: stackit-ca.crt
readOnly: true
securityContext:
allowPrivilegeEscalation: false
@@ -344,15 +340,11 @@ spec:
path: token
name: shoot-access-csi-resizer
optional: false
- - name: usr-share-ca-certificates
- hostPath:
- path: /usr/share/ca-certificates
- name: cloud-provider-config
configMap:
defaultMode: 420
name: {{ .Values.prefix }}-cloud-provider-config
- # Host certificates are mounted to accommodate OpenStack endpoints that might be served with a certificate
- # signed by a CA that is not globally trusted.
- - name: etc-ssl
- hostPath:
- path: /etc/ssl
+ - name: stackit-ca
+ secret:
+ optional: true
+ secretName: cloudprofile-ca-bundle
diff --git a/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/templates/stackit-cloud-controller-manager.yaml b/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/templates/stackit-cloud-controller-manager.yaml
index 5099c90e..44168188 100644
--- a/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/templates/stackit-cloud-controller-manager.yaml
+++ b/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/templates/stackit-cloud-controller-manager.yaml
@@ -89,9 +89,9 @@ spec:
name: config-volume
- mountPath: /etc/serviceaccount
name: sa-secret
- - mountPath: /etc/ssl/certs/keystone-ca.crt
- name: cloud-config-ca
- subPath: keystone-ca.crt
+ - mountPath: /etc/ssl/certs/stackit-ca.crt
+ name: stackit-ca
+ subPath: stackit-ca.crt
readOnly: true
securityContext:
allowPrivilegeEscalation: false
@@ -111,9 +111,9 @@ spec:
items:
- key: "serviceaccount.json"
path: "serviceaccount.json"
- - name: cloud-config-ca
+ - name: stackit-ca
secret:
- secretName: cloud-provider-config
+ secretName: cloudprofile-ca-bundle
optional: true
- name: kubeconfig
projected:
diff --git a/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/values.yaml b/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/values.yaml
index b4ed7ae0..4f60b830 100644
--- a/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/values.yaml
+++ b/charts/internal/seed-controlplane/charts/stackit-cloud-controller-manager/values.yaml
@@ -5,9 +5,10 @@ config:
stackitProjectID: foo
stackitNetworkID: foo
stackitRegion: foo
- #loadBalancerApiUrl: foo.bar.example.com/v1
- #iaasApiUrl: foo.bar.example.com/v1
- #tokenUrl: foo.bar.example.com/v1
+ loadBalancerApiUrl: ""
+ iaasApiUrl: ""
+ tokenUrl: ""
+ loadBalancerEmergencyToken: ""
port: 10258
metricsPort: 9090
podAnnotations: {}
diff --git a/hack/api-reference/api.md b/hack/api-reference/api.md
index 77bdac1f..8981aef5 100644
--- a/hack/api-reference/api.md
+++ b/hack/api-reference/api.md
@@ -430,7 +430,7 @@ string
(Optional)
- CABundle is the CA certificate bundle for API endpoints. This field is currently ignored and reserved for future use.
+CABundle is the CA certificate bundle for API endpoints. This field is currently ignored and reserved for future use. Deprecated: Will be removed soon. This field is not used anymore
|
diff --git a/pkg/admission/mutator/namespacedcloudprofile.go b/pkg/admission/mutator/namespacedcloudprofile.go
index d66b6fa6..4c944a2e 100644
--- a/pkg/admission/mutator/namespacedcloudprofile.go
+++ b/pkg/admission/mutator/namespacedcloudprofile.go
@@ -64,11 +64,6 @@ func (p *namespacedCloudProfile) Mutate(_ context.Context, newObj, _ client.Obje
statusConfig.APIEndpoints = specConfig.APIEndpoints
}
- // Overwrite CA bundle from spec
- if specConfig.CABundle != nil {
- statusConfig.CABundle = specConfig.CABundle
- }
-
modifiedStatusConfig, err := json.Marshal(statusConfig)
if err != nil {
return err
diff --git a/pkg/admission/mutator/namespacedcloudprofile_test.go b/pkg/admission/mutator/namespacedcloudprofile_test.go
index 069efc06..04133e56 100644
--- a/pkg/admission/mutator/namespacedcloudprofile_test.go
+++ b/pkg/admission/mutator/namespacedcloudprofile_test.go
@@ -96,25 +96,6 @@ var _ = Describe("NamespacedCloudProfile Mutator", func() {
Expect(mergedConfig.APIEndpoints.LoadBalancer).To(PointTo(Equal("https://custom-lb.example.com")))
})
- It("should correctly merge caBundle from spec", func() {
- namespacedCloudProfile.Status.CloudProfileSpec.ProviderConfig = &runtime.RawExtension{Raw: []byte(`{
-"apiVersion":"stackit.provider.extensions.gardener.cloud/v1alpha1",
-"kind":"CloudProfileConfig",
-"machineImages":[
- {"name":"image-1","versions":[{"version":"1.0","image":"image-name-1"}]}
-]}`)}
- namespacedCloudProfile.Spec.ProviderConfig = &runtime.RawExtension{Raw: []byte(`{
-"apiVersion":"stackit.provider.extensions.gardener.cloud/v1alpha1",
-"kind":"CloudProfileConfig",
-"caBundle":"-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----"}`)}
-
- Expect(namespacedCloudProfileMutator.Mutate(ctx, namespacedCloudProfile, nil)).To(Succeed())
-
- mergedConfig, err := helper.CloudProfileConfigFromRawExtension(namespacedCloudProfile.Status.CloudProfileSpec.ProviderConfig)
- Expect(err).ToNot(HaveOccurred())
- Expect(mergedConfig.CABundle).To(PointTo(Equal("-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----")))
- })
-
It("should correctly merge extended machineImages", func() {
namespacedCloudProfile.Status.CloudProfileSpec.ProviderConfig = &runtime.RawExtension{Raw: []byte(`{
"apiVersion":"stackit.provider.extensions.gardener.cloud/v1alpha1",
diff --git a/pkg/admission/validator/namespacedcloudprofile.go b/pkg/admission/validator/namespacedcloudprofile.go
index e09b13c5..da3e2641 100644
--- a/pkg/admission/validator/namespacedcloudprofile.go
+++ b/pkg/admission/validator/namespacedcloudprofile.go
@@ -76,12 +76,11 @@ func (p *namespacedCloudProfile) validateNamespacedCloudProfileProviderConfig(pr
},
MachineImages: providerConfig.MachineImages,
APIEndpoints: providerConfig.APIEndpoints,
- CABundle: providerConfig.CABundle,
}
if !equality.Semantic.DeepEqual(validationProviderConfig, providerConfig) {
allErrs = append(allErrs, field.Forbidden(
field.NewPath("spec.providerConfig"),
- "must only set machineImages, stackitAPIEndpoints, and stackitCABundle",
+ "must only set machineImages and stackitAPIEndpoints",
))
}
diff --git a/pkg/admission/validator/namespacedcloudprofile_test.go b/pkg/admission/validator/namespacedcloudprofile_test.go
index 2c63f349..b717fdcb 100644
--- a/pkg/admission/validator/namespacedcloudprofile_test.go
+++ b/pkg/admission/validator/namespacedcloudprofile_test.go
@@ -281,7 +281,7 @@ var _ = Describe("NamespacedCloudProfile Validator", func() {
Expect(err).To(ConsistOf(PointTo(MatchFields(IgnoreExtras, Fields{
"Type": Equal(field.ErrorTypeForbidden),
"Field": Equal("spec.providerConfig"),
- "Detail": Equal("must only set machineImages, stackitAPIEndpoints, and stackitCABundle"),
+ "Detail": Equal("must only set machineImages and stackitAPIEndpoints"),
}))))
})
@@ -297,24 +297,11 @@ var _ = Describe("NamespacedCloudProfile Validator", func() {
Expect(namespacedCloudProfileValidator.Validate(ctx, namespacedCloudProfile, nil)).To(Succeed())
})
- It("should succeed for NamespacedCloudProfile specifying only caBundle", func() {
- namespacedCloudProfile.Spec.ProviderConfig = &runtime.RawExtension{Raw: []byte(`{
-"apiVersion":"stackit.provider.extensions.gardener.cloud/v1alpha1",
-"kind":"CloudProfileConfig",
-"caBundle":"-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----"
-}`)}
-
- Expect(fakeClient.Create(ctx, cloudProfile)).To(Succeed())
-
- Expect(namespacedCloudProfileValidator.Validate(ctx, namespacedCloudProfile, nil)).To(Succeed())
- })
-
- It("should succeed for NamespacedCloudProfile specifying apiEndpoints, caBundle, and machineImages together", func() {
+ It("should succeed for NamespacedCloudProfile specifying apiEndpoints, and machineImages together", func() {
namespacedCloudProfile.Spec.ProviderConfig = &runtime.RawExtension{Raw: []byte(`{
"apiVersion":"stackit.provider.extensions.gardener.cloud/v1alpha1",
"kind":"CloudProfileConfig",
"apiEndpoints":{"iaas":"https://custom-iaas.example.com"},
-"caBundle":"-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----",
"machineImages":[
{"name":"image-1","versions":[{"version":"1.0","image":"image-name-1","regions":[{"name":"image-region-1","id":"id-img-reg-1"}]}]}
]
diff --git a/pkg/apis/stackit/v1alpha1/types_cloudprofile.go b/pkg/apis/stackit/v1alpha1/types_cloudprofile.go
index 38a3ad36..bc565426 100644
--- a/pkg/apis/stackit/v1alpha1/types_cloudprofile.go
+++ b/pkg/apis/stackit/v1alpha1/types_cloudprofile.go
@@ -33,6 +33,8 @@ type CloudProfileConfig struct {
APIEndpoints *APIEndpoints `json:"apiEndpoints,omitempty"`
// CABundle is the CA certificate bundle for API endpoints.
// This field is currently ignored and reserved for future use.
+ //
+ // Deprecated: Will be removed soon. This field is not used anymore
// +optional
CABundle *string `json:"caBundle,omitempty"`
// Bastion are the bastion options for configuring the bastion server
diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go
index 180ac234..d6a9565b 100644
--- a/pkg/controller/controlplane/valuesprovider.go
+++ b/pkg/controller/controlplane/valuesprovider.go
@@ -135,6 +135,7 @@ var (
Objects: []*chart.Object{
{Type: &corev1.Secret{}, Name: openstack.CloudProviderConfigName},
{Type: &corev1.Secret{}, Name: openstack.CloudProviderDiskConfigName},
+ {Type: &corev1.Secret{}, Name: openstack.CloudProfileCASecretName},
},
}
@@ -681,6 +682,16 @@ func getConfigChartValues(
}
}
+ // Deploy the cloudprofile-ca-bundle when at least one of stackit-mcm, stackit-csi or stackit-ccm is deployed
+ if feature.UseStackitMachineControllerManager(cluster) ||
+ getCSIDriver(controlPlaneConfig) == stackitv1alpha1.STACKIT ||
+ getCCMController(controlPlaneConfig) == stackitv1alpha1.STACKIT {
+ if cluster.CloudProfile != nil && cluster.CloudProfile.Spec.CABundle != nil {
+ caBundle := ptr.Deref(cluster.CloudProfile.Spec.CABundle, "")
+ values["CABundle"] = caBundle
+ }
+ }
+
return values, nil
}
diff --git a/pkg/openstack/types.go b/pkg/openstack/types.go
index fc3a090c..6b38156b 100644
--- a/pkg/openstack/types.go
+++ b/pkg/openstack/types.go
@@ -57,6 +57,8 @@ const (
// DNSCABundle is a constant for the key in a DNS secret that holds the Openstack CA Bundle for the KeyStone server.
DNSCABundle = "OS_CACERT"
+ // CloudProfileCASecretName is the name of the secret containing the CloudPorifle caBundle
+ CloudProfileCASecretName = "cloudprofile-ca-bundle"
// CloudProviderConfigName is the name of the secret containing the cloud provider config.
CloudProviderConfigName = "cloud-provider-config"
// CloudProviderDiskConfigName is the name of the secret containing the cloud provider config for disk/volume handling. It is used by kube-controller-manager.
diff --git a/pkg/stackit/client/dns.go b/pkg/stackit/client/dns.go
index e28689de..ea786e98 100644
--- a/pkg/stackit/client/dns.go
+++ b/pkg/stackit/client/dns.go
@@ -13,8 +13,11 @@ import (
"github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/stackit"
)
-func NewDNSClient(_ context.Context, endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials) (DNSClient, error) {
- options := clientOptions(endpoints, credentials)
+func NewDNSClient(_ context.Context, endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials, caBundle string) (DNSClient, error) {
+ options, err := clientOptions(endpoints, credentials, caBundle)
+ if err != nil {
+ return nil, err
+ }
if endpoints.DNS != nil {
options = append(options, sdkconfig.WithEndpoint(*endpoints.DNS))
@@ -24,6 +27,7 @@ func NewDNSClient(_ context.Context, endpoints stackitv1alpha1.APIEndpoints, cre
if err != nil {
return nil, err
}
+
return &dnsClient{
api: apiClient.DefaultAPI,
projectID: credentials.ProjectID,
diff --git a/pkg/stackit/client/factory.go b/pkg/stackit/client/factory.go
index 543123a9..cbece73e 100644
--- a/pkg/stackit/client/factory.go
+++ b/pkg/stackit/client/factory.go
@@ -2,6 +2,10 @@ package client
import (
"context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net/http"
extensionscontroller "github.com/gardener/gardener/extensions/pkg/controller"
sdkconfig "github.com/stackitcloud/stackit-sdk-go/core/config"
@@ -33,18 +37,25 @@ type Factory interface {
type factory struct {
StackitRegion string
StackitAPIEndpoints stackitv1alpha1.APIEndpoints
+ CABundleB64 string
}
func New(region string, cluster *extensionscontroller.Cluster) Factory {
var apiEndpoints stackitv1alpha1.APIEndpoints
+ var caBundle string
if cloudProfileConfig, err := helper.CloudProfileConfigFromCluster(cluster); err == nil {
apiEndpoints = ptr.Deref(cloudProfileConfig.APIEndpoints, stackitv1alpha1.APIEndpoints{})
}
+ if cluster.CloudProfile != nil && cluster.CloudProfile.Spec.CABundle != nil {
+ caBundle = ptr.Deref(cluster.CloudProfile.Spec.CABundle, "")
+ }
+
return &factory{
StackitRegion: region,
StackitAPIEndpoints: apiEndpoints,
+ CABundleB64: caBundle,
}
}
@@ -54,7 +65,7 @@ func (f factory) LoadBalancing(ctx context.Context, c client.Client, secretRef c
return nil, err
}
- return NewLoadBalancingClient(ctx, f.StackitRegion, f.StackitAPIEndpoints, credentials)
+ return NewLoadBalancingClient(ctx, f.StackitRegion, f.StackitAPIEndpoints, credentials, f.CABundleB64)
}
func (f factory) IaaS(ctx context.Context, c client.Client, secretRef corev1.SecretReference) (IaaSClient, error) {
@@ -63,7 +74,7 @@ func (f factory) IaaS(ctx context.Context, c client.Client, secretRef corev1.Sec
return nil, err
}
- return NewIaaSClient(f.StackitRegion, f.StackitAPIEndpoints, credentials)
+ return NewIaaSClient(f.StackitRegion, f.StackitAPIEndpoints, credentials, f.CABundleB64)
}
func (f factory) DNS(ctx context.Context, c client.Client, secretRef corev1.SecretReference) (DNSClient, error) {
@@ -72,10 +83,28 @@ func (f factory) DNS(ctx context.Context, c client.Client, secretRef corev1.Secr
return nil, err
}
- return NewDNSClient(ctx, f.StackitAPIEndpoints, credentials)
+ return NewDNSClient(ctx, f.StackitAPIEndpoints, credentials, f.CABundleB64)
+}
+
+// newHTTPClientWithCustomCA creates an http.Client with a custom CA
+func newHTTPClientWithCustomCA(caBundle []byte) (*http.Client, error) {
+ caCertPool, err := x509.SystemCertPool()
+ if err != nil {
+ // we could also fall back here and use an empty pool via x509.NewCertPool()
+ return nil, fmt.Errorf("failed to load system cert pool: %w", err)
+ }
+ if ok := caCertPool.AppendCertsFromPEM(caBundle); !ok {
+ return nil, fmt.Errorf("failed to append CA bundle to cert pool")
+ }
+
+ return &http.Client{Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ RootCAs: caCertPool,
+ },
+ }}, nil
}
-func clientOptions(endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials) []sdkconfig.ConfigurationOption {
+func clientOptions(endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials, caBundle string) ([]sdkconfig.ConfigurationOption, error) {
result := []sdkconfig.ConfigurationOption{
sdkconfig.WithUserAgent(UserAgent),
sdkconfig.WithServiceAccountKey(credentials.SaKeyJSON),
@@ -85,5 +114,13 @@ func clientOptions(endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.
result = append(result, sdkconfig.WithTokenEndpoint(*endpoints.TokenEndpoint))
}
- return result
+ if caBundle != "" {
+ customHttpClient, err := newHTTPClientWithCustomCA([]byte(caBundle))
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, sdkconfig.WithHTTPClient(customHttpClient))
+ }
+
+ return result, nil
}
diff --git a/pkg/stackit/client/iaas.go b/pkg/stackit/client/iaas.go
index f941ac5c..628f1a96 100644
--- a/pkg/stackit/client/iaas.go
+++ b/pkg/stackit/client/iaas.go
@@ -126,9 +126,11 @@ func (c iaasClient) GetNetworkByName(ctx context.Context, name string) ([]iaas.N
return filteredNetworks, nil
}
-func NewIaaSClient(region string, endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials) (IaaSClient, error) {
- options := clientOptions(endpoints, credentials)
-
+func NewIaaSClient(region string, endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials, caBundle string) (IaaSClient, error) {
+ options, err := clientOptions(endpoints, credentials, caBundle)
+ if err != nil {
+ return nil, err
+ }
if endpoints.IaaS != nil {
options = append(options, sdkconfig.WithEndpoint(*endpoints.IaaS))
}
@@ -141,6 +143,7 @@ func NewIaaSClient(region string, endpoints stackitv1alpha1.APIEndpoints, creden
if err != nil {
return nil, err
}
+
return &iaasClient{
Client: apiClient.DefaultAPI,
projectID: credentials.ProjectID,
diff --git a/pkg/stackit/client/loadbalancing.go b/pkg/stackit/client/loadbalancing.go
index 66bb7e68..c25f7477 100644
--- a/pkg/stackit/client/loadbalancing.go
+++ b/pkg/stackit/client/loadbalancing.go
@@ -26,9 +26,11 @@ type loadBalancingClient struct {
region string
}
-func NewLoadBalancingClient(_ context.Context, region string, endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials) (LoadBalancingClient, error) {
- options := clientOptions(endpoints, credentials)
-
+func NewLoadBalancingClient(_ context.Context, region string, endpoints stackitv1alpha1.APIEndpoints, credentials *stackit.Credentials, caBundle string) (LoadBalancingClient, error) {
+ options, err := clientOptions(endpoints, credentials, caBundle)
+ if err != nil {
+ return nil, err
+ }
if endpoints.LoadBalancer != nil {
options = append(options, sdkconfig.WithEndpoint(*endpoints.LoadBalancer))
}
diff --git a/pkg/webhook/controlplane/ensurer.go b/pkg/webhook/controlplane/ensurer.go
index 2a1a2adc..4e329a70 100644
--- a/pkg/webhook/controlplane/ensurer.go
+++ b/pkg/webhook/controlplane/ensurer.go
@@ -34,6 +34,11 @@ import (
"github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/webhook/controlplane/registrycache"
)
+const (
+ CASecretName = "cloudprofile-ca-bundle"
+ CAVolumeName = "stackit-ca"
+)
+
// NewEnsurer creates a new controlplane ensurer.
func NewEnsurer(regCaches []config.RegistryCacheConfiguration, logger logr.Logger) genericmutator.Ensurer {
return &ensurer{
@@ -83,15 +88,32 @@ func (e *ensurer) EnsureMachineControllerManagerDeployment(ctx context.Context,
apiEndpoints := ptr.Deref(cloudProfileConfig.APIEndpoints, stackitv1alpha1.APIEndpoints{})
+ if cluster.CloudProfile != nil && cluster.CloudProfile.Spec.CABundle != nil {
+ newObj.Spec.Template.Spec.Volumes = extensionswebhook.EnsureVolumeWithName(newObj.Spec.Template.Spec.Volumes, corev1.Volume{
+ Name: CAVolumeName,
+ VolumeSource: corev1.VolumeSource{
+ Secret: &corev1.SecretVolumeSource{
+ SecretName: CASecretName,
+ },
+ },
+ })
+ sidecarContainer.VolumeMounts = extensionswebhook.EnsureVolumeMountWithName(sidecarContainer.VolumeMounts, corev1.VolumeMount{
+ Name: CAVolumeName,
+ MountPath: "/etc/ssl/certs/stackit-ca.crt",
+ SubPath: "stackit-ca.crt",
+ ReadOnly: true,
+ })
+ }
+
sidecarContainer.Env = []corev1.EnvVar{}
if apiEndpoints.IaaS != nil {
- sidecarContainer.Env = append(sidecarContainer.Env, corev1.EnvVar{
+ sidecarContainer.Env = extensionswebhook.EnsureEnvVarWithName(sidecarContainer.Env, corev1.EnvVar{
Name: "STACKIT_IAAS_ENDPOINT",
Value: *apiEndpoints.IaaS,
})
}
if apiEndpoints.TokenEndpoint != nil {
- sidecarContainer.Env = append(sidecarContainer.Env, corev1.EnvVar{
+ sidecarContainer.Env = extensionswebhook.EnsureEnvVarWithName(sidecarContainer.Env, corev1.EnvVar{
Name: "STACKIT_TOKEN_BASEURL",
Value: *apiEndpoints.TokenEndpoint,
})
diff --git a/pkg/webhook/controlplane/ensurer_test.go b/pkg/webhook/controlplane/ensurer_test.go
index fd55dbc9..f1975176 100644
--- a/pkg/webhook/controlplane/ensurer_test.go
+++ b/pkg/webhook/controlplane/ensurer_test.go
@@ -6,9 +6,16 @@ package controlplane
import (
"context"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
"encoding/json"
+ "encoding/pem"
+ "math/big"
"strings"
"testing"
+ "time"
"github.com/Masterminds/semver/v3"
"github.com/coreos/go-systemd/v22/unit"
@@ -114,6 +121,27 @@ var _ = Describe("Ensurer", func() {
},
},
)
+ eContextK8s127WithSTACKITMCMANDCUSTOMCA = gcontext.NewInternalGardenContext(
+ &extensionscontroller.Cluster{
+ CloudProfile: &gardencorev1beta1.CloudProfile{
+ Spec: gardencorev1beta1.CloudProfileSpec{
+ CABundle: new(generateRootCA()),
+ },
+ },
+ Shoot: &gardencorev1beta1.Shoot{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ feature.ShootUseSTACKITMachineControllerManager: "true",
+ },
+ },
+ Spec: gardencorev1beta1.ShootSpec{
+ Kubernetes: gardencorev1beta1.Kubernetes{
+ Version: "1.27.1",
+ },
+ },
+ },
+ },
+ )
eContextK8s127WithSTACKITMCMAndAPIEndpoints = gcontext.NewInternalGardenContext(
&extensionscontroller.Cluster{
CloudProfile: &gardencorev1beta1.CloudProfile{
@@ -576,6 +604,28 @@ WantedBy=multi-user.target
expectedContainer.Env = []corev1.EnvVar{}
Expect(deployment.Spec.Template.Spec.Containers).To(ConsistOf(expectedContainer))
})
+
+ It("should add cloudprofile-ca-bundle to Deployment and sidecar when CloudProfile CA Bundle is set", func() {
+ Expect(deployment.Spec.Template.Spec.Containers).To(BeEmpty())
+ Expect(ensurer.EnsureMachineControllerManagerDeployment(context.TODO(), eContextK8s127WithSTACKITMCMANDCUSTOMCA, deployment, nil)).To(Succeed())
+ expectedContainer := machinecontrollermanager.ProviderSidecarContainer(shoot, deployment.Namespace, "provider-stackit", "foo:bar")
+ expectedContainer.Env = []corev1.EnvVar{}
+ expectedContainer.VolumeMounts = append(expectedContainer.VolumeMounts, corev1.VolumeMount{
+ Name: CAVolumeName,
+ MountPath: "/etc/ssl/certs/stackit-ca.crt",
+ SubPath: "stackit-ca.crt",
+ ReadOnly: true,
+ })
+ Expect(deployment.Spec.Template.Spec.Containers).To(ConsistOf(expectedContainer))
+ Expect(deployment.Spec.Template.Spec.Volumes).To(ConsistOf(corev1.Volume{
+ Name: CAVolumeName,
+ VolumeSource: corev1.VolumeSource{
+ Secret: &corev1.SecretVolumeSource{
+ SecretName: CASecretName,
+ },
+ },
+ }))
+ })
})
Describe("#EnsureMachineControllerManagerDeployment with stackit and custom env vars", func() {
@@ -688,3 +738,34 @@ func encode(obj runtime.Object) []byte {
data, _ := json.Marshal(obj)
return data
}
+
+func generateRootCA() string {
+ priv, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ return ""
+ }
+
+ template := &x509.Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: "Minimalist Root CA",
+ },
+ NotBefore: time.Now(),
+ NotAfter: time.Now().AddDate(10, 0, 0),
+ IsCA: true,
+ KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
+ BasicConstraintsValid: true,
+ }
+
+ certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
+ if err != nil {
+ return ""
+ }
+
+ pemBlock := pem.EncodeToMemory(&pem.Block{
+ Type: "CERTIFICATE",
+ Bytes: certBytes,
+ })
+
+ return string(pemBlock)
+}
diff --git a/test/integration/infrastructure/stackit/infrastructure_test.go b/test/integration/infrastructure/stackit/infrastructure_test.go
index c861c827..569db7db 100644
--- a/test/integration/infrastructure/stackit/infrastructure_test.go
+++ b/test/integration/infrastructure/stackit/infrastructure_test.go
@@ -132,7 +132,7 @@ var _ = BeforeSuite(func() {
// TODO: Consider creating manual STACKIT NLB to ensure stackit NLB deletion works
DeferCleanup(testutils.WithFeatureGate(feature.MutableGate, feature.EnsureSTACKITLBDeletion, false))
- iaasClient, err = stackitclient.NewIaaSClient(*region, endpoints, credentials)
+ iaasClient, err = stackitclient.NewIaaSClient(*region, endpoints, credentials, "")
Expect(err).NotTo(HaveOccurred())
repoRoot := filepath.Join("..", "..", "..", "..")
diff --git a/test/integration/selfhostedshootexposure/selfhostedshootexposure_test.go b/test/integration/selfhostedshootexposure/selfhostedshootexposure_test.go
index d40e7082..2eafc29f 100644
--- a/test/integration/selfhostedshootexposure/selfhostedshootexposure_test.go
+++ b/test/integration/selfhostedshootexposure/selfhostedshootexposure_test.go
@@ -110,10 +110,10 @@ var _ = BeforeSuite(func() {
Expect(*region).NotTo(BeEmpty())
Expect(validateEnvs()).To(Succeed())
- iaasClient, err = stackitclient.NewIaaSClient(*region, endpoints, credentials)
+ iaasClient, err = stackitclient.NewIaaSClient(*region, endpoints, credentials, "")
Expect(err).NotTo(HaveOccurred())
- lbClient, err = stackitclient.NewLoadBalancingClient(ctx, *region, endpoints, credentials)
+ lbClient, err = stackitclient.NewLoadBalancingClient(ctx, *region, endpoints, credentials, "")
Expect(err).NotTo(HaveOccurred())
repoRoot := filepath.Join("..", "..", "..")