Skip to content

Commit 5d7c5d4

Browse files
committed
✨ Add support for specifying TLS config including custom CA certificates
1 parent 293b35e commit 5d7c5d4

12 files changed

+316
-50
lines changed

api/v1alpha1/condition_consts.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,7 @@ const (
8181

8282
// GetCredentialsFailedReason indicates that the HelmReleaseProxy failed to get the credentials for the Helm registry.
8383
GetCredentialsFailedReason = "GetCredentialsFailed"
84+
85+
// GetCACertificateFailedReason indicates that the HelmReleaseProxy failed to get the CA certiicate for the Helm registry.
86+
GetCACertificateFailedReason = "GetCACertificateFailed"
8487
)

api/v1alpha1/helmchartproxy_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ type HelmChartProxySpec struct {
7272
// Credentials is a reference to an object containing the OCI credentials. If it is not specified, no credentials will be used.
7373
// +optional
7474
Credentials *Credentials `json:"credentials,omitempty"`
75+
76+
// TLSConfig contains the TLS configuration for the HelmChartProxy.
77+
TLSConfig *TLSConfig `json:"tlsConfig,omitempty"`
7578
}
7679

7780
type HelmOptions struct {
@@ -199,6 +202,16 @@ type Credentials struct {
199202
Key string `json:"key"`
200203
}
201204

205+
type TLSConfig struct {
206+
// Secret is a reference to a Secret containing the TLS CA certificate at the key ca.crt.
207+
// +optional
208+
CASecretRef *corev1.SecretReference `json:"caSecret,omitempty"`
209+
210+
// InsecureSkipTLSverify controls whether the Helm client should verify the server's certificate.
211+
// +optional
212+
InsecureSkipTLSverify bool `json:"insecureSkipTLSVerify,omitempty"`
213+
}
214+
202215
// HelmChartProxyStatus defines the observed state of HelmChartProxy.
203216
type HelmChartProxyStatus struct {
204217
// Conditions defines current state of the HelmChartProxy.

api/v1alpha1/helmreleaseproxy_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ type HelmReleaseProxySpec struct {
7474
// Credentials is a reference to an object containing the OCI credentials. If it is not specified, no credentials will be used.
7575
// +optional
7676
Credentials *Credentials `json:"credentials,omitempty"`
77+
78+
// TLSConfig contains the TLS configuration for the HelmReleaseProxy.
79+
TLSConfig *TLSConfig `json:"tlsConfig,omitempty"`
7780
}
7881

7982
// HelmReleaseProxyStatus defines the observed state of HelmReleaseProxy.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/addons.cluster.x-k8s.io_helmchartproxies.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,28 @@ spec:
261261
RepoURL is the URL of the Helm chart repository.
262262
e.g. chart-path oci://repo-url/chart-name as repoURL: oci://repo-url and https://repo-url/chart-name as repoURL: https://repo-url
263263
type: string
264+
tlsConfig:
265+
description: TLSConfig contains the TLS configuration for the HelmChartProxy.
266+
properties:
267+
caSecret:
268+
description: Secret is a reference to a Secret containing the
269+
TLS CA certificate at the key ca.crt.
270+
properties:
271+
name:
272+
description: name is unique within a namespace to reference
273+
a secret resource.
274+
type: string
275+
namespace:
276+
description: namespace defines the space within which the
277+
secret name must be unique.
278+
type: string
279+
type: object
280+
x-kubernetes-map-type: atomic
281+
insecureSkipTLSVerify:
282+
description: InsecureSkipTLSverify controls whether the Helm client
283+
should verify the server's certificate.
284+
type: boolean
285+
type: object
264286
valuesTemplate:
265287
description: |-
266288
ValuesTemplate is an inline YAML representing the values for the Helm chart. This YAML supports Go templating to reference

config/crd/bases/addons.cluster.x-k8s.io_helmreleaseproxies.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,28 @@ spec:
270270
RepoURL is the URL of the Helm chart repository.
271271
e.g. chart-path oci://repo-url/chart-name as repoURL: oci://repo-url and https://repo-url/chart-name as repoURL: https://repo-url
272272
type: string
273+
tlsConfig:
274+
description: TLSConfig contains the TLS configuration for the HelmReleaseProxy.
275+
properties:
276+
caSecret:
277+
description: Secret is a reference to a Secret containing the
278+
TLS CA certificate at the key ca.crt.
279+
properties:
280+
name:
281+
description: name is unique within a namespace to reference
282+
a secret resource.
283+
type: string
284+
namespace:
285+
description: namespace defines the space within which the
286+
secret name must be unique.
287+
type: string
288+
type: object
289+
x-kubernetes-map-type: atomic
290+
insecureSkipTLSVerify:
291+
description: InsecureSkipTLSverify controls whether the Helm client
292+
should verify the server's certificate.
293+
type: boolean
294+
type: object
273295
values:
274296
description: |-
275297
Values is an inline YAML representing the values for the Helm chart. This YAML is the result of the rendered

controllers/helmchartproxy/helmchartproxy_controller_phases.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,15 @@ func constructHelmReleaseProxy(existing *addonsv1alpha1.HelmReleaseProxy, helmCh
235235
}
236236
}
237237

238+
helmReleaseProxy.Spec.TLSConfig = helmChartProxy.Spec.TLSConfig
239+
240+
if helmReleaseProxy.Spec.TLSConfig != nil {
241+
// If the namespace is not set, set it to the namespace of the HelmChartProxy
242+
if helmReleaseProxy.Spec.TLSConfig.CASecretRef.Namespace == "" {
243+
helmReleaseProxy.Spec.TLSConfig.CASecretRef.Namespace = helmChartProxy.Namespace
244+
}
245+
}
246+
238247
return helmReleaseProxy
239248
}
240249

controllers/helmreleaseproxy/helmreleaseproxy_controller.go

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,21 +224,39 @@ func (r *HelmReleaseProxyReconciler) Reconcile(ctx context.Context, req ctrl.Req
224224
return ctrl.Result{}, wrappedErr
225225
}
226226

227-
defer func() {
228-
if err := os.Remove(credentialsPath); err != nil {
229-
log.Error(err, "failed to remove credentials file in path", "credentialsPath", credentialsPath)
230-
}
231-
}()
227+
if credentialsPath != "" {
228+
defer func() {
229+
if err := os.Remove(credentialsPath); err != nil {
230+
log.Error(err, "failed to remove credentials file in path", "credentialsPath", credentialsPath)
231+
}
232+
}()
233+
}
234+
235+
caFilePath, err := r.getCAFile(ctx, helmReleaseProxy)
236+
if err != nil {
237+
wrappedErr := errors.Wrapf(err, "failed to get CA file for cluster")
238+
conditions.MarkFalse(helmReleaseProxy, addonsv1alpha1.ClusterAvailableCondition, addonsv1alpha1.GetCACertificateFailedReason, clusterv1.ConditionSeverityError, wrappedErr.Error())
239+
240+
return ctrl.Result{}, wrappedErr
241+
}
242+
243+
if caFilePath != "" {
244+
defer func() {
245+
if err := os.Remove(caFilePath); err != nil {
246+
log.Error(err, "failed to remove CA certificate file in path", "credentialsPath", caFilePath)
247+
}
248+
}()
249+
}
232250

233251
log.V(2).Info("Reconciling HelmReleaseProxy", "releaseProxyName", helmReleaseProxy.Name)
234-
err = r.reconcileNormal(ctx, helmReleaseProxy, r.HelmClient, credentialsPath, kubeconfig)
252+
err = r.reconcileNormal(ctx, helmReleaseProxy, r.HelmClient, credentialsPath, caFilePath, kubeconfig)
235253

236254
return ctrl.Result{}, err
237255
}
238256

239257
// reconcileNormal handles HelmReleaseProxy reconciliation when it is not being deleted. This will install or upgrade the HelmReleaseProxy on the Cluster.
240258
// It will set the ReleaseName on the HelmReleaseProxy if the name is generated and also set the release status and release revision.
241-
func (r *HelmReleaseProxyReconciler) reconcileNormal(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy, client internal.Client, credentialsPath string, kubeconfig string) error {
259+
func (r *HelmReleaseProxyReconciler) reconcileNormal(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy, client internal.Client, credentialsPath, caFilePath string, kubeconfig string) error {
242260
log := ctrl.LoggerFrom(ctx)
243261

244262
log.V(2).Info("Reconciling HelmReleaseProxy on cluster", "HelmReleaseProxy", helmReleaseProxy.Name, "cluster", helmReleaseProxy.Spec.ClusterRef.Name)
@@ -250,7 +268,7 @@ func (r *HelmReleaseProxyReconciler) reconcileNormal(ctx context.Context, helmRe
250268
})
251269
}
252270

253-
release, err := client.InstallOrUpgradeHelmRelease(ctx, kubeconfig, credentialsPath, helmReleaseProxy.Spec)
271+
release, err := client.InstallOrUpgradeHelmRelease(ctx, kubeconfig, credentialsPath, caFilePath, helmReleaseProxy.Spec)
254272
if err != nil {
255273
log.Error(err, fmt.Sprintf("Failed to install or upgrade release '%s' on cluster %s", helmReleaseProxy.Spec.ReleaseName, helmReleaseProxy.Spec.ClusterRef.Name))
256274
conditions.MarkFalse(helmReleaseProxy, addonsv1alpha1.HelmReleaseReadyCondition, addonsv1alpha1.HelmInstallOrUpgradeFailedReason, clusterv1.ConditionSeverityError, err.Error())
@@ -377,6 +395,31 @@ func (r *HelmReleaseProxyReconciler) getCredentials(ctx context.Context, helmRel
377395
return credentialsPath, nil
378396
}
379397

398+
// getCAFile fetches the CA certificate from a Secret and writes them to a temporary file it returns the path to the temporary file.
399+
func (r *HelmReleaseProxyReconciler) getCAFile(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy) (string, error) {
400+
caFilePath := ""
401+
if helmReleaseProxy.Spec.TLSConfig != nil && helmReleaseProxy.Spec.TLSConfig.CASecretRef.Name != "" {
402+
// By default, the secret is in the same namespace as the HelmReleaseProxy
403+
if helmReleaseProxy.Spec.TLSConfig.CASecretRef.Namespace == "" {
404+
helmReleaseProxy.Spec.TLSConfig.CASecretRef.Namespace = helmReleaseProxy.Namespace
405+
}
406+
caSecretValues, err := r.getCACertificateFromSecret(ctx, helmReleaseProxy.Spec.TLSConfig.CASecretRef.Name, helmReleaseProxy.Spec.TLSConfig.CASecretRef.Namespace)
407+
if err != nil {
408+
return "", err
409+
}
410+
411+
// Write to a file
412+
filename, err := writeCACertificateToFile(ctx, caSecretValues)
413+
if err != nil {
414+
return "", err
415+
}
416+
417+
caFilePath = filename
418+
}
419+
420+
return caFilePath, nil
421+
}
422+
380423
// getCredentialsFromSecret returns the OCI credentials from a Secret.
381424
func (r *HelmReleaseProxyReconciler) getCredentialsFromSecret(ctx context.Context, name, namespace, key string) ([]byte, error) {
382425
secret := &corev1.Secret{}
@@ -407,3 +450,35 @@ func writeCredentialsToFile(ctx context.Context, credentials []byte) (string, er
407450

408451
return credentialsFile.Name(), nil
409452
}
453+
454+
// getCredentialsFromSecret returns the OCI credentials from a Secret.
455+
func (r *HelmReleaseProxyReconciler) getCACertificateFromSecret(ctx context.Context, name, namespace string) ([]byte, error) {
456+
secret := &corev1.Secret{}
457+
if err := r.Client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, secret); err != nil {
458+
return nil, err
459+
}
460+
461+
const key = "ca.crt"
462+
credentials, ok := secret.Data[key]
463+
if !ok {
464+
return nil, errors.New(fmt.Sprintf("key %s not found in secret %s/%s", key, namespace, name))
465+
}
466+
467+
return credentials, nil
468+
}
469+
470+
// writeCACertificateToFile writes the CA certificate to a temporary file.
471+
func writeCACertificateToFile(ctx context.Context, caCertificate []byte) (string, error) {
472+
log := ctrl.LoggerFrom(ctx)
473+
log.V(2).Info("Writing CA certficate to file")
474+
caCertFile, err := os.CreateTemp("", "ca-*.crt")
475+
if err != nil {
476+
return "", err
477+
}
478+
479+
if _, err := caCertFile.Write(caCertificate); err != nil {
480+
return "", err
481+
}
482+
483+
return caCertFile.Name(), nil
484+
}

0 commit comments

Comments
 (0)