mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-05-21 16:43:48 +00:00
163 lines
5.6 KiB
Go
163 lines
5.6 KiB
Go
package certrotationcontroller
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/openshift/library-go/pkg/controller/factory"
|
|
"github.com/openshift/library-go/pkg/operator/events"
|
|
errorhelpers "github.com/openshift/library-go/pkg/operator/v1helpers"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/klog/v2"
|
|
|
|
operatorinformer "github.com/open-cluster-management/api/client/operator/informers/externalversions/operator/v1"
|
|
operatorlister "github.com/open-cluster-management/api/client/operator/listers/operator/v1"
|
|
"github.com/open-cluster-management/registration-operator/pkg/certrotation"
|
|
"github.com/open-cluster-management/registration-operator/pkg/helpers"
|
|
)
|
|
|
|
const (
|
|
signerSecret = "signer-secret"
|
|
caBundleConfigmap = "ca-bundle-configmap"
|
|
signerNamePrefix = "cluster-manager-webhook"
|
|
)
|
|
|
|
// Follow the rules below to set the value of SigningCertValidity/TargetCertValidity/ResyncInterval:
|
|
//
|
|
// 1) SigningCertValidity * 1/5 * 1/5 > ResyncInterval * 2
|
|
// 2) TargetCertValidity * 1/5 > ResyncInterval * 2
|
|
var SigningCertValidity = time.Hour * 24 * 365
|
|
var TargetCertValidity = time.Hour * 24 * 30
|
|
var ResyncInterval = time.Minute * 5
|
|
|
|
// certRotationController does:
|
|
//
|
|
// 1) continuously create a self-signed signing CA (via SigningRotation).
|
|
// It creates the next one when a given percentage of the validity of the old CA has passed.
|
|
// 2) maintain a CA bundle with all not yet expired CA certs.
|
|
// 3) continuously create target cert/key pairs signed by the latest signing CA
|
|
// It creates the next one when a given percentage of the validity of the previous cert has
|
|
// passed, or when a new CA has been created.
|
|
type certRotationController struct {
|
|
signingRotation certrotation.SigningRotation
|
|
caBundleRotation certrotation.CABundleRotation
|
|
targetRotations []certrotation.TargetRotation
|
|
kubeClient kubernetes.Interface
|
|
clusterManagerLister operatorlister.ClusterManagerLister
|
|
}
|
|
|
|
func NewCertRotationController(
|
|
kubeClient kubernetes.Interface,
|
|
secretInformer corev1informers.SecretInformer,
|
|
configMapInformer corev1informers.ConfigMapInformer,
|
|
clusterManagerInformer operatorinformer.ClusterManagerInformer,
|
|
recorder events.Recorder,
|
|
) factory.Controller {
|
|
signingRotation := certrotation.SigningRotation{
|
|
Namespace: helpers.ClusterManagerNamespace,
|
|
Name: signerSecret,
|
|
SignerNamePrefix: signerNamePrefix,
|
|
Validity: SigningCertValidity,
|
|
Lister: secretInformer.Lister(),
|
|
Client: kubeClient.CoreV1(),
|
|
EventRecorder: recorder,
|
|
}
|
|
caBundleRotation := certrotation.CABundleRotation{
|
|
Namespace: helpers.ClusterManagerNamespace,
|
|
Name: caBundleConfigmap,
|
|
Lister: configMapInformer.Lister(),
|
|
Client: kubeClient.CoreV1(),
|
|
EventRecorder: recorder,
|
|
}
|
|
targetRotations := []certrotation.TargetRotation{
|
|
{
|
|
Namespace: helpers.ClusterManagerNamespace,
|
|
Name: helpers.RegistrationWebhookSecret,
|
|
Validity: TargetCertValidity,
|
|
HostNames: []string{fmt.Sprintf("%s.%s.svc", helpers.RegistrationWebhookService, helpers.ClusterManagerNamespace)},
|
|
Lister: secretInformer.Lister(),
|
|
Client: kubeClient.CoreV1(),
|
|
EventRecorder: recorder,
|
|
},
|
|
{
|
|
Namespace: helpers.ClusterManagerNamespace,
|
|
Name: helpers.WorkWebhookSecret,
|
|
Validity: TargetCertValidity,
|
|
HostNames: []string{fmt.Sprintf("%s.%s.svc", helpers.WorkWebhookService, helpers.ClusterManagerNamespace)},
|
|
Lister: secretInformer.Lister(),
|
|
Client: kubeClient.CoreV1(),
|
|
EventRecorder: recorder,
|
|
},
|
|
}
|
|
|
|
c := &certRotationController{
|
|
signingRotation: signingRotation,
|
|
caBundleRotation: caBundleRotation,
|
|
targetRotations: targetRotations,
|
|
kubeClient: kubeClient,
|
|
clusterManagerLister: clusterManagerInformer.Lister(),
|
|
}
|
|
return factory.New().
|
|
ResyncEvery(ResyncInterval).
|
|
WithSync(c.sync).
|
|
WithInformers(
|
|
secretInformer.Informer(),
|
|
configMapInformer.Informer(),
|
|
clusterManagerInformer.Informer(),
|
|
).
|
|
ToController("CertRotationController", recorder)
|
|
}
|
|
|
|
func (c certRotationController) sync(ctx context.Context, syncCtx factory.SyncContext) error {
|
|
// do nothing if there is no cluster manager
|
|
clustermanagers, err := c.clusterManagerLister.List(labels.Everything())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(clustermanagers) == 0 {
|
|
klog.V(4).Infof("No ClusterManager found")
|
|
return nil
|
|
}
|
|
|
|
klog.Infof("Reconciling ClusterManager %q", clustermanagers[0].Name)
|
|
// do nothing if the cluster manager is deleting
|
|
if !clustermanagers[0].DeletionTimestamp.IsZero() {
|
|
return nil
|
|
}
|
|
|
|
// check if namespace exists or not
|
|
_, err = c.kubeClient.CoreV1().Namespaces().Get(ctx, helpers.ClusterManagerNamespace, metav1.GetOptions{})
|
|
if errors.IsNotFound(err) {
|
|
return fmt.Errorf("namespace %q does not exist yet", helpers.ClusterManagerNamespace)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// reconcile cert/key pair for signer
|
|
signingCertKeyPair, err := c.signingRotation.EnsureSigningCertKeyPair()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// reconcile ca bundle
|
|
cabundleCerts, err := c.caBundleRotation.EnsureConfigMapCABundle(signingCertKeyPair)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// reconcile target cert/key pairs
|
|
errs := []error{}
|
|
for _, targetRotation := range c.targetRotations {
|
|
if err := targetRotation.EnsureTargetCertKeyPair(signingCertKeyPair, cabundleCerts); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
return errorhelpers.NewMultiLineAggregate(errs)
|
|
}
|