Files
mariadb-operator/internal/controller/maxscale_controller_tls.go
Artyom Babiy 2485c599d5 Fix v25
2025-07-28 15:33:30 +02:00

260 lines
8.3 KiB
Go

package controller
import (
"context"
"errors"
"fmt"
mariadbv1alpha1 "github.com/mariadb-operator/mariadb-operator/v25/api/v1alpha1"
certctrl "github.com/mariadb-operator/mariadb-operator/v25/pkg/controller/certificate"
"github.com/mariadb-operator/mariadb-operator/v25/pkg/controller/secret"
"github.com/mariadb-operator/mariadb-operator/v25/pkg/hash"
"github.com/mariadb-operator/mariadb-operator/v25/pkg/metadata"
"github.com/mariadb-operator/mariadb-operator/v25/pkg/pki"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log"
)
func (r *MaxScaleReconciler) reconcileTLS(ctx context.Context, req *requestMaxScale) (ctrl.Result, error) {
if !req.mxs.IsTLSEnabled() {
return ctrl.Result{}, nil
}
if err := r.reconcileTLSCerts(ctx, req.mxs); err != nil {
return ctrl.Result{}, err
}
if err := r.reconcileTLSCABundle(ctx, req.mxs); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *MaxScaleReconciler) reconcileTLSCerts(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) error {
tls := ptr.Deref(mxs.Spec.TLS, mariadbv1alpha1.MaxScaleTLS{})
// MaxScale TLS can't communicate with a non-TLS MariaDB server
if tls.ServerCASecretRef == nil || tls.ServerCertSecretRef == nil {
return errors.New("'spec.tls.serverCASecretRef' and 'spec.tls.ServerCertSecretRef' fields" +
"must be set in order to communicate with MariaDB server")
}
adminCertOpts := []certctrl.CertReconcilerOpt{
certctrl.WithCABundle(mxs.TLSCABundleSecretKeyRef(), mxs.Namespace),
certctrl.WithCA(
tls.AdminCASecretRef == nil,
mxs.TLSAdminCASecretKey(),
),
certctrl.WithCert(
tls.AdminCertSecretRef == nil,
mxs.TLSAdminCertSecretKey(),
mxs.TLSAdminDNSNames(),
),
certctrl.WithServerCertKeyUsage(),
certctrl.WithCertIssuerRef(tls.AdminCertIssuerRef),
certctrl.WithRelatedObject(mxs),
}
if _, err := r.CertReconciler.Reconcile(ctx, adminCertOpts...); err != nil {
return fmt.Errorf("error reconciling admin cert: %v", err)
}
listenerCertOpts := []certctrl.CertReconcilerOpt{
certctrl.WithCABundle(mxs.TLSCABundleSecretKeyRef(), mxs.Namespace),
certctrl.WithCA(
tls.ListenerCASecretRef == nil,
mxs.TLSAdminCASecretKey(),
),
certctrl.WithCert(
tls.ListenerCertSecretRef == nil,
mxs.TLSListenerCertSecretKey(),
mxs.TLSListenerDNSNames(),
),
certctrl.WithServerCertKeyUsage(),
certctrl.WithCertIssuerRef(tls.ListenerCertIssuerRef),
certctrl.WithRelatedObject(mxs),
}
if _, err := r.CertReconciler.Reconcile(ctx, listenerCertOpts...); err != nil {
return fmt.Errorf("error reconciling listener cert: %v", err)
}
return nil
}
func (r *MaxScaleReconciler) reconcileTLSCABundle(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) error {
logger := log.FromContext(ctx).WithName("ca-bundle")
caBundleKeySelector := mxs.TLSCABundleSecretKeyRef()
adminCAKeySelector := mariadbv1alpha1.SecretKeySelector{
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSAdminCASecretKey().Name,
},
Key: pki.CACertKey,
}
listenerCAKeySelector := mariadbv1alpha1.SecretKeySelector{
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSListenerCASecretKey().Name,
},
Key: pki.CACertKey,
}
serverCAKeySelector := mariadbv1alpha1.SecretKeySelector{
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSServerCASecretKey().Name,
},
Key: pki.CACertKey,
}
caKeySelectors := []mariadbv1alpha1.SecretKeySelector{
caBundleKeySelector,
adminCAKeySelector,
listenerCAKeySelector,
serverCAKeySelector,
}
var caBundles [][]byte
for _, caKeySelector := range caKeySelectors {
ca, err := r.RefResolver.SecretKeyRef(ctx, caKeySelector, mxs.Namespace)
if err != nil {
if !apierrors.IsNotFound(err) {
return fmt.Errorf("error getting CA Secret \"%s\": %v", caKeySelector.Name, err)
}
logger.V(1).Info("CA Secret not found", "secret-name", caKeySelector.Name)
}
caBundles = append(caBundles, []byte(ca))
}
bundle, err := pki.BundleCertificatePEMs(
caBundles,
pki.WithLogger(logger),
pki.WithSkipExpired(true),
)
if err != nil {
return fmt.Errorf("error creating CA bundle: %v", err)
}
secretReq := secret.SecretRequest{
Metadata: []*mariadbv1alpha1.Metadata{mxs.Spec.InheritMetadata},
Owner: mxs,
Key: types.NamespacedName{
Name: caBundleKeySelector.Name,
Namespace: mxs.Namespace,
},
Data: map[string][]byte{
caBundleKeySelector.Key: bundle,
},
}
return r.SecretReconciler.Reconcile(ctx, &secretReq)
}
func (r *MaxScaleReconciler) getTLSAnnotations(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) (map[string]string, error) {
if !mxs.IsTLSEnabled() {
return nil, nil
}
annotations, err := r.getTLSAdminAnnotations(ctx, mxs)
if err != nil {
return nil, fmt.Errorf("error getting client annotations: %v", err)
}
secretSelectorsByAnn := map[string]mariadbv1alpha1.SecretKeySelector{
metadata.TLSListenerCertAnnotation: {
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSListenerCertSecretKey().Name,
},
Key: pki.TLSCertKey,
},
metadata.TLSServerCertAnnotation: {
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSServerCertSecretKey().Name,
},
Key: pki.TLSCertKey,
},
}
for annotation, secretKeySelector := range secretSelectorsByAnn {
cert, err := r.RefResolver.SecretKeyRef(ctx, secretKeySelector, mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting Secret \"%s\": %v", secretKeySelector.Name, err)
}
annotations[annotation] = hash.Hash(cert)
}
return annotations, nil
}
func (r *MaxScaleReconciler) getTLSAdminAnnotations(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) (map[string]string, error) {
if !mxs.IsTLSEnabled() {
return nil, nil
}
annotations := make(map[string]string)
ca, err := r.RefResolver.SecretKeyRef(ctx, mxs.TLSCABundleSecretKeyRef(), mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting CA bundle: %v", err)
}
annotations[metadata.TLSCAAnnotation] = hash.Hash(ca)
adminCertKeySelector := mariadbv1alpha1.SecretKeySelector{
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSAdminCertSecretKey().Name,
},
Key: pki.TLSCertKey,
}
adminCert, err := r.RefResolver.SecretKeyRef(ctx, adminCertKeySelector, mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting admin cert: %v", err)
}
annotations[metadata.TLSAdminCertAnnotation] = hash.Hash(adminCert)
return annotations, nil
}
func (r *MaxScaleReconciler) getTLSStatus(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) (*mariadbv1alpha1.MaxScaleTLSStatus, error) {
if !mxs.IsTLSEnabled() {
return nil, nil
}
var tlsStatus mariadbv1alpha1.MaxScaleTLSStatus
certStatus, err := getCertificateStatus(ctx, r.RefResolver, mxs.TLSCABundleSecretKeyRef(), mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting CA bundle status: %v", err)
}
tlsStatus.CABundle = certStatus
secretKeySelector := mariadbv1alpha1.SecretKeySelector{
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSAdminCertSecretKey().Name,
},
Key: pki.TLSCertKey,
}
certStatus, err = getCertificateStatus(ctx, r.RefResolver, secretKeySelector, mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting admin certificate status: %v", err)
}
tlsStatus.AdminCert = ptr.To(certStatus[0])
secretKeySelector = mariadbv1alpha1.SecretKeySelector{
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSListenerCertSecretKey().Name,
},
Key: pki.TLSCertKey,
}
certStatus, err = getCertificateStatus(ctx, r.RefResolver, secretKeySelector, mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting listener certificate status: %v", err)
}
tlsStatus.ListenerCert = ptr.To(certStatus[0])
secretKeySelector = mariadbv1alpha1.SecretKeySelector{
LocalObjectReference: mariadbv1alpha1.LocalObjectReference{
Name: mxs.TLSServerCertSecretKey().Name,
},
Key: pki.TLSCertKey,
}
certStatus, err = getCertificateStatus(ctx, r.RefResolver, secretKeySelector, mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting server certificate status: %v", err)
}
tlsStatus.ServerCert = ptr.To(certStatus[0])
return &tlsStatus, nil
}