Skip to content
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

feat(cert): add cryostat CA cert to target namespaces #661

Merged
merged 7 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
61 changes: 61 additions & 0 deletions internal/controllers/certmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,48 @@ func (r *Reconciler) setupTLS(ctx context.Context, cr *model.CryostatInstance) (
return nil, err
}

secret, err := r.GetCertificateSecret(ctx, caCert)
if err != nil {
return nil, err
}
// Copy Cryostat CA secret in each target namespace
for _, ns := range cr.TargetNamespaces {
if ns != cr.InstallNamespace {
namespaceSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: caCert.Spec.SecretName,
Namespace: ns,
},
Type: corev1.SecretTypeOpaque,
}
err = r.createOrUpdateSecret(ctx, namespaceSecret, cr.Object, func() error {
if namespaceSecret.Data == nil {
namespaceSecret.Data = map[string][]byte{}
}
namespaceSecret.Data[corev1.TLSCertKey] = secret.Data[corev1.TLSCertKey]
return nil
})
if err != nil {
return nil, err
}
}
}
// Delete any Cryostat CA secrets in target namespaces that are no longer requested
for _, ns := range toDelete(cr) {
if ns != cr.InstallNamespace {
namespaceSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: caCert.Spec.SecretName,
Namespace: ns,
},
}
err = r.deleteSecret(ctx, namespaceSecret)
if err != nil {
return nil, err
}
}
}

// Get the Cryostat CA certificate bytes from certificate secret
caBytes, err := r.getCertficateBytes(ctx, caCert)
if err != nil {
Expand All @@ -131,6 +173,25 @@ func (r *Reconciler) setupTLS(ctx context.Context, cr *model.CryostatInstance) (
return tlsConfig, nil
}

func (r *Reconciler) finalizeTLS(ctx context.Context, cr *model.CryostatInstance) error {
caCert := resources.NewCryostatCACert(cr)
for _, ns := range cr.TargetNamespaces {
if ns != cr.InstallNamespace {
namespaceSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: caCert.Spec.SecretName,
Namespace: ns,
},
}
err := r.deleteSecret(ctx, namespaceSecret)
if err != nil {
return err
}
}
}
return nil
}

func (r *Reconciler) setCertSecretOwner(ctx context.Context, owner metav1.Object, certs ...*certv1.Certificate) error {
// Make Cryostat CR controller of secrets created by cert-manager
for _, cert := range certs {
Expand Down
19 changes: 18 additions & 1 deletion internal/controllers/clustercryostat_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cryostatio/cryostat-operator/internal/controllers"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
)
Expand Down Expand Up @@ -62,6 +63,10 @@ var _ = Describe("ClusterCryostatController", func() {
t.expectMainDeployment()
})

It("should create CA Cert secret in each namespace", func() {
t.expectCertificates()
})

It("should create RBAC in each namespace", func() {
t.expectRBAC()
})
Expand All @@ -80,7 +85,9 @@ var _ = Describe("ClusterCryostatController", func() {
*cr.TargetNamespaceStatus = targetNamespaces
t.objs = append(t.objs, cr.Object,
t.NewRoleBinding(targetNamespaces[0]),
t.NewRoleBinding(targetNamespaces[1]))
t.NewRoleBinding(targetNamespaces[1]),
t.NewCACertSecret(targetNamespaces[0]),
t.NewCACertSecret(targetNamespaces[1]))
})
It("should create the expected main deployment", func() {
t.expectMainDeployment()
Expand All @@ -94,6 +101,16 @@ var _ = Describe("ClusterCryostatController", func() {
Expect(err).ToNot(BeNil())
Expect(errors.IsNotFound(err)).To(BeTrue())
})
It("leave CA Cert secret for the first namespace", func() {
t.expectCertificates()
})
It("should remove CA Cert secret from the second namespace", func() {
caCert := t.NewCACert()
secret := &corev1.Secret{}
err := t.Client.Get(context.Background(), types.NamespacedName{Name: caCert.Name, Namespace: targetNamespaces[1]}, secret)
Expect(err).ToNot(BeNil())
Expect(errors.IsNotFound(err)).To(BeTrue())
ebaron marked this conversation as resolved.
Show resolved Hide resolved
})
It("should update the target namespaces in Status", func() {
t.expectTargetNamespaces()
})
Expand Down
4 changes: 2 additions & 2 deletions internal/controllers/common/finalizer_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
)

// AddFinalizer adds the provided finalizer to the object and updates it in the cluster
func AddFinalizer(ctx context.Context, client client.Client, obj controllerutil.Object, finalizer string) error {
func AddFinalizer(ctx context.Context, client client.Client, obj client.Object, finalizer string) error {
log.Info("adding finalizer to object", "namespace", obj.GetNamespace(), "name", obj.GetName())
controllerutil.AddFinalizer(obj, finalizer)
err := client.Update(ctx, obj)
Expand All @@ -35,7 +35,7 @@ func AddFinalizer(ctx context.Context, client client.Client, obj controllerutil.
}

// RemoveFinalizer removes the provided finalizer from the object and updates it in the cluster
func RemoveFinalizer(ctx context.Context, client client.Client, obj controllerutil.Object, finalizer string) error {
func RemoveFinalizer(ctx context.Context, client client.Client, obj client.Object, finalizer string) error {
log.Info("removing finalizer from object", "namespace", obj.GetNamespace(), "name", obj.GetName())
controllerutil.RemoveFinalizer(obj, finalizer)
err := client.Update(ctx, obj)
Expand Down
19 changes: 13 additions & 6 deletions internal/controllers/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ func (r *Reconciler) reconcile(ctx context.Context, cr *model.CryostatInstance)
return reconcile.Result{}, err
}

// Finalizer for CA Cert secrets
err = r.finalizeTLS(ctx, cr)
if err != nil {
return reconcile.Result{}, err
}

err = common.RemoveFinalizer(ctx, r.Client, cr.Object, cryostatFinalizer)
if err != nil {
return reconcile.Result{}, err
Expand Down Expand Up @@ -196,8 +202,15 @@ func (r *Reconciler) reconcile(ctx context.Context, cr *model.CryostatInstance)
return reconcile.Result{}, err
}

// Reconcile RBAC resources for Cryostat
err = r.reconcileRBAC(ctx, cr)
if err != nil {
return reconcile.Result{}, err
}

// Set up TLS using cert-manager, if available
var tlsConfig *resources.TLSConfig

if r.IsCertManagerEnabled(cr) {
tlsConfig, err = r.setupTLS(ctx, cr)
if err != nil {
Expand Down Expand Up @@ -230,12 +243,6 @@ func (r *Reconciler) reconcile(ctx context.Context, cr *model.CryostatInstance)
}
}

// Reconcile RBAC resources for Cryostat
err = r.reconcileRBAC(ctx, cr)
if err != nil {
return reconcile.Result{}, err
}

serviceSpecs := &resources.ServiceSpecs{
InsightsURL: r.InsightsProxy,
}
Expand Down
16 changes: 15 additions & 1 deletion internal/controllers/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2336,7 +2336,21 @@ func (t *cryostatTestInput) expectCertificates() {
err := t.Client.Get(context.Background(), types.NamespacedName{Name: expectedSecret.Name, Namespace: expectedSecret.Namespace}, secret)
Expect(err).ToNot(HaveOccurred())
t.checkMetadata(secret, expectedSecret)
Expect(secret.StringData).To(Equal(secret.StringData))
Expect(secret.StringData).To(Equal(expectedSecret.StringData))

// Check CA Cert secrets in each target namespace
Expect(t.TargetNamespaces).ToNot(BeEmpty())
for _, ns := range t.TargetNamespaces {
if ns != t.Namespace {
namespaceSecret := t.NewCACertSecret(ns)
secret := &corev1.Secret{}
err := t.Client.Get(context.Background(), types.NamespacedName{Name: namespaceSecret.Name, Namespace: ns}, secret)
Expect(err).ToNot(HaveOccurred())
t.checkMetadata(secret, namespaceSecret)
Expect(secret.Data).To(Equal(namespaceSecret.Data))
Expect(secret.Type).To(Equal(namespaceSecret.Type))
}
}
}

func (t *cryostatTestInput) expectRBAC() {
Expand Down
3 changes: 2 additions & 1 deletion internal/test/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ func (c *testClient) createCertSecret(ctx context.Context, cert *certv1.Certific
Namespace: cert.Namespace,
},
Data: map[string][]byte{
corev1.TLSCertKey: []byte(cert.Name + "-bytes"),
corev1.TLSCertKey: []byte(cert.Name + "-bytes"),
corev1.TLSPrivateKeyKey: []byte(cert.Name + "-key"),
},
}
err := c.Create(ctx, secret)
Expand Down
13 changes: 13 additions & 0 deletions internal/test/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,19 @@ func (r *TestResources) NewTestService() *corev1.Service {
}
}

func (r *TestResources) NewCACertSecret(ns string) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: r.Name + "-ca",
Namespace: ns,
},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{
corev1.TLSCertKey: []byte(r.Name + "-ca-bytes"),
},
}
}

func (r *TestResources) NewGrafanaSecret() *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Expand Down
Loading