Skip to content

feat: generate a self-signed cert for registry addon #1127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
replicaCount: 2
replicaCount: {{ .Replicas }}
persistence:
enabled: true
size: 50Gi
service:
type: ClusterIP
clusterIP: {{ .ServiceIP }}
port: 80
port: 443
statefulSet:
enabled: true
syncer:
interval: 2m
tlsSecretName: {{ .TLSSecretName }}
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,20 @@ spec:
kind: {{ .Values.certificates.issuer.kind }}
name: {{ template "chart.issuerName" . }}
secretName: {{ template "chart.name" . }}-admission-tls
---
# CA used to sign certificates for the clusters' registry addons
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: registry-addon-root-ca
namespace: {{ .Release.Namespace }}
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
isCA: true
commonName: registry-addon
secretName: registry-addon-root-ca
issuerRef:
kind: {{ .Values.certificates.issuer.kind }}
name: {{ template "chart.issuerName" . }}
duration: 87600h # 10 years
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ data:
RepositoryURL: '{{ if .Values.helmRepository.enabled }}oci://helm-repository.{{ .Release.Namespace }}.svc/charts{{ else }}https://kubernetes.github.io/autoscaler{{ end }}'
cncf-distribution-registry: |
ChartName: docker-registry
ChartVersion: 2.3.1
ChartVersion: 2.3.2
RepositoryURL: '{{ if .Values.helmRepository.enabled }}oci://helm-repository.{{ .Release.Namespace }}.svc/charts{{ else }}https://mesosphere.github.io/charts/staging/{{ end }}'
cosi-controller: |
ChartName: cosi
Expand Down
2 changes: 1 addition & 1 deletion hack/addons/helm-chart-bundler/repos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ repositories:
repoURL: https://mesosphere.github.io/charts/staging/
charts:
docker-registry:
- 2.3.1
- 2.3.2
local-path-provisioner:
repoURL: https://charts.containeroo.ch
charts:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ helmCharts:
- name: docker-registry
repo: https://mesosphere.github.io/charts/staging/
releaseName: cncf-distribution-registry
version: 2.3.1
version: 2.3.2
valuesFile: helm-values.yaml
includeCRDs: true
skipTests: true
Expand Down
103 changes: 100 additions & 3 deletions pkg/handlers/generic/lifecycle/registry/cncfdistribution/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import (
const (
DefaultHelmReleaseName = "cncf-distribution-registry"
DefaultHelmReleaseNamespace = "registry-system"

stsName = "cncf-distribution-registry-docker-registry"
stsHeadlessServiceName = "cncf-distribution-registry-docker-registry-headless"
stsReplicas = 2
tlsSecretName = "registry-tls"
)

type Config struct {
Expand Down Expand Up @@ -59,14 +64,71 @@ func New(
}
}

func (n *CNCFDistribution) Setup(
ctx context.Context,
_ v1alpha1.RegistryAddon,
cluster *clusterv1.Cluster,
log logr.Logger,
) error {
log.Info("Setting up CA for CNCF Distribution registry")
err := utils.EnsureCASecretForCluster(
ctx,
n.client,
cluster,
)
if err != nil {
return fmt.Errorf("failed to ensure CA secret for CNCF Distribution registry addon: %w", err)
}
return nil
}

func (n *CNCFDistribution) Apply(
ctx context.Context,
_ v1alpha1.RegistryAddon,
cluster *clusterv1.Cluster,
log logr.Logger,
) error {
log.Info("Applying CNCF Distribution registry installation")
log.Info("Copying TLS Certificate for CNCF Distribution registry to remote cluster")
// Ensure the CA secret exists and is up to date in the cluster's namespace before trying to use it.
err := utils.EnsureCASecretForCluster(
ctx,
n.client,
cluster,
)
if err != nil {
return fmt.Errorf("failed to ensure CA secret for CNCF Distribution registry addon: %w", err)
}

// Copy the TLS secret to the remote cluster.
serviceIP, err := utils.ServiceIPForCluster(cluster)
if err != nil {
return fmt.Errorf("error getting service IP for the CNCF distribution registry: %w", err)
}
opts := &utils.EnsureCertificateOpts{
RemoteSecretKey: ctrlclient.ObjectKey{
Name: tlsSecretName,
Namespace: DefaultHelmReleaseNamespace,
},
Spec: utils.CertificateSpec{
CommonName: stsName,
DNSNames: certificateDNSNames(),
IPAddresses: certificateIPAddresses(serviceIP),
},
}
err = utils.EnsureTLSCertificateSecretOnRemoteCluster(
ctx,
n.client,
cluster,
opts,
)
if err != nil {
return fmt.Errorf(
"failed to copy certificate secret for CNCF Distribution registry addon to remote cluster: %w",
err,
)
}

log.Info("Applying CNCF Distribution registry installation")
helmChartInfo, err := n.helmChartInfoGetter.For(ctx, log, config.CNCFDistributionRegistry)
if err != nil {
return fmt.Errorf("failed to get CNCF Distribution registry helm chart: %w", err)
Expand Down Expand Up @@ -101,11 +163,15 @@ func templateValues(cluster *clusterv1.Cluster, text string) (string, error) {
}

type input struct {
ServiceIP string
ServiceIP string
Replicas int32
TLSSecretName string
}

templateInput := input{
ServiceIP: serviceIP,
Replicas: stsReplicas,
ServiceIP: serviceIP,
TLSSecretName: tlsSecretName,
}

var b bytes.Buffer
Expand All @@ -119,3 +185,34 @@ func templateValues(cluster *clusterv1.Cluster, text string) (string, error) {

return b.String(), nil
}

func certificateDNSNames() []string {
names := []string{
stsName,
fmt.Sprintf("%s.%s", stsName, DefaultHelmReleaseNamespace),
fmt.Sprintf("%s.%s.svc", stsName, DefaultHelmReleaseNamespace),
fmt.Sprintf("%s.%s.svc.cluster.local", stsName, DefaultHelmReleaseNamespace),
}
for i := 0; i < stsReplicas; i++ {
names = append(names,
[]string{
fmt.Sprintf("%s-%d", stsName, i),
fmt.Sprintf("%s-%d.%s.%s", stsName, i, stsHeadlessServiceName, DefaultHelmReleaseNamespace),
fmt.Sprintf("%s-%d.%s.%s.svc", stsName, i, stsHeadlessServiceName, DefaultHelmReleaseNamespace),
fmt.Sprintf(
"%s-%d.%s.%s.svc.cluster.local",
stsName, i, stsHeadlessServiceName, DefaultHelmReleaseNamespace,
),
}...,
)
}

return names
}

func certificateIPAddresses(serviceIP string) []string {
return []string{
serviceIP,
"127.0.0.1",
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2025 Nutanix. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package cncfdistribution

import (
"testing"

"github.com/stretchr/testify/assert"
)

func Test_certificateDNSNames(t *testing.T) {
//nolint:lll // Keep long lines for readability.
expected := []string{
"cncf-distribution-registry-docker-registry",
"cncf-distribution-registry-docker-registry.registry-system",
"cncf-distribution-registry-docker-registry.registry-system.svc",
"cncf-distribution-registry-docker-registry.registry-system.svc.cluster.local",
"cncf-distribution-registry-docker-registry-0",
"cncf-distribution-registry-docker-registry-0.cncf-distribution-registry-docker-registry-headless.registry-system",
"cncf-distribution-registry-docker-registry-0.cncf-distribution-registry-docker-registry-headless.registry-system.svc",
"cncf-distribution-registry-docker-registry-0.cncf-distribution-registry-docker-registry-headless.registry-system.svc.cluster.local",
"cncf-distribution-registry-docker-registry-1",
"cncf-distribution-registry-docker-registry-1.cncf-distribution-registry-docker-registry-headless.registry-system",
"cncf-distribution-registry-docker-registry-1.cncf-distribution-registry-docker-registry-headless.registry-system.svc",
"cncf-distribution-registry-docker-registry-1.cncf-distribution-registry-docker-registry-headless.registry-system.svc.cluster.local",
}
assert.Equal(t, expected, certificateDNSNames())
}
104 changes: 104 additions & 0 deletions pkg/handlers/generic/lifecycle/registry/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ import (
)

type RegistryProvider interface {
Setup(
ctx context.Context,
registryVar v1alpha1.RegistryAddon,
cluster *clusterv1.Cluster,
log logr.Logger,
) error
Apply(
ctx context.Context,
registryVar v1alpha1.RegistryAddon,
Expand All @@ -37,6 +43,7 @@ type RegistryHandler struct {

var (
_ commonhandlers.Named = &RegistryHandler{}
_ lifecycle.BeforeClusterCreate = &RegistryHandler{}
_ lifecycle.AfterControlPlaneInitialized = &RegistryHandler{}
_ lifecycle.BeforeClusterUpgrade = &RegistryHandler{}
)
Expand All @@ -57,6 +64,17 @@ func (r *RegistryHandler) Name() string {
return "RegistryHandler"
}

func (r *RegistryHandler) BeforeClusterCreate(
ctx context.Context,
req *runtimehooksv1.BeforeClusterCreateRequest,
resp *runtimehooksv1.BeforeClusterCreateResponse,
) {
commonResponse := &runtimehooksv1.CommonResponse{}
r.setup(ctx, &req.Cluster, commonResponse)
resp.Status = commonResponse.GetStatus()
resp.Message = commonResponse.GetMessage()
}

func (r *RegistryHandler) AfterControlPlaneInitialized(
ctx context.Context,
req *runtimehooksv1.AfterControlPlaneInitializedRequest,
Expand All @@ -74,11 +92,97 @@ func (r *RegistryHandler) BeforeClusterUpgrade(
resp *runtimehooksv1.BeforeClusterUpgradeResponse,
) {
commonResponse := &runtimehooksv1.CommonResponse{}
r.setup(ctx, &req.Cluster, commonResponse)
r.apply(ctx, &req.Cluster, commonResponse)
resp.Status = commonResponse.GetStatus()
resp.Message = commonResponse.GetMessage()
}

func (r *RegistryHandler) setup(
ctx context.Context,
cluster *clusterv1.Cluster,
resp *runtimehooksv1.CommonResponse,
) {
clusterKey := ctrlclient.ObjectKeyFromObject(cluster)

log := ctrl.LoggerFrom(ctx).WithValues(
"cluster",
clusterKey,
)

varMap := variables.ClusterVariablesToVariablesMap(cluster.Spec.Topology.Variables)
registryVar, err := variables.Get[v1alpha1.RegistryAddon](
varMap,
r.variableName,
r.variablePath...)
if err != nil {
if variables.IsNotFoundError(err) {
log.V(5).
Info(
"Skipping RegistryAddon, field is not specified",
"error",
err,
)
return
}
log.Error(
err,
"failed to read RegistryAddon provider from cluster definition",
)
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(
fmt.Sprintf("failed to read RegistryAddon provider from cluster definition: %v",
err,
),
)
return
}

handler, ok := r.ProviderHandler[registryVar.Provider]
if !ok {
err = fmt.Errorf("unknown RegistryAddon Provider")
log.Error(err, "provider", registryVar.Provider)
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(
fmt.Sprintf("%s %s", err, registryVar.Provider),
)
return
}

log.Info(fmt.Sprintf("Setting up RegistryAddon provider prerequisites %s", registryVar.Provider))
err = handler.Setup(
ctx,
registryVar,
cluster,
log,
)
if err != nil {
log.Error(
err,
fmt.Sprintf(
"failed to set up RegistryAddon provider prerequisites %s",
registryVar.Provider,
),
)
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
resp.SetMessage(
fmt.Sprintf(
"failed to set up RegistryAddon provider prerequisites: %v",
err,
),
)
return
}

resp.SetStatus(runtimehooksv1.ResponseStatusSuccess)
resp.SetMessage(
fmt.Sprintf(
"Set up RegistryAddon provider prerequisites %s",
registryVar.Provider,
),
)
}

func (r *RegistryHandler) apply(
ctx context.Context,
cluster *clusterv1.Cluster,
Expand Down
Loading
Loading