From fc06fb1e0f0eda19e2c3c503a0283ae3500d6805 Mon Sep 17 00:00:00 2001 From: Douglas Camata <159076+douglascamata@users.noreply.github.com> Date: Thu, 6 Jun 2024 12:00:04 +0200 Subject: [PATCH] [ACM-10269] Stamp CSR's organization units with the hash of the CA bundle (#1464) * Stamp the observability addon CSR with the hash of the CA Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Stamp all CSR with the hash of the CA Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Adds certs test helpers Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Go mod tidy Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> * Add missing copyright notice Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> --------- Signed-off-by: Douglas Camata <159076+douglascamata@users.noreply.github.com> Signed-off-by: Periklis Tsirakidis --- go.mod | 2 +- .../pkg/certificates/cert_agent.go | 25 ++++++-- .../pkg/certificates/cert_agent_test.go | 63 ++++++++++++++++++- .../pkg/certificates/cert_controller.go | 14 ++--- .../pkg/certificates/test_helpers.go | 27 ++++++++ 5 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 operators/multiclusterobservability/pkg/certificates/test_helpers.go diff --git a/go.mod b/go.mod index 1ac8ecf810..0f1db30b09 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/openshift/client-go v0.0.0-20230120202327-72f107311084 github.com/openshift/cluster-monitoring-operator v0.0.0-20230118025836-20fcb9f6ef4e github.com/openshift/hypershift v0.1.11 + github.com/openshift/library-go v0.0.0-20230120214501-9bc305884fcb github.com/prometheus-community/prom-label-proxy v0.6.0 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.58.0 github.com/prometheus-operator/prometheus-operator/pkg/client v0.53.1 @@ -116,7 +117,6 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/openshift/library-go v0.0.0-20230120214501-9bc305884fcb // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect diff --git a/operators/multiclusterobservability/pkg/certificates/cert_agent.go b/operators/multiclusterobservability/pkg/certificates/cert_agent.go index af4999b54d..db4458026f 100644 --- a/operators/multiclusterobservability/pkg/certificates/cert_agent.go +++ b/operators/multiclusterobservability/pkg/certificates/cert_agent.go @@ -5,7 +5,11 @@ package certificates import ( + "crypto/sha256" + "fmt" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "open-cluster-management.io/addon-framework/pkg/agent" addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" @@ -17,7 +21,9 @@ const ( agentName = "observability" ) -type ObservabilityAgent struct{} +type ObservabilityAgent struct { + client client.Client +} func (o *ObservabilityAgent) Manifests( cluster *clusterv1.ManagedCluster, @@ -30,7 +36,7 @@ func (o *ObservabilityAgent) GetAgentAddonOptions() agent.AgentAddonOptions { return agent.AgentAddonOptions{ AddonName: addonName, Registration: &agent.RegistrationOption{ - CSRConfigurations: observabilitySignerConfigurations(), + CSRConfigurations: observabilitySignerConfigurations(o.client), CSRApproveCheck: approve, PermissionConfig: func(cluster *clusterv1.ManagedCluster, addon *addonapiv1alpha1.ManagedClusterAddOn) error { return nil @@ -40,7 +46,7 @@ func (o *ObservabilityAgent) GetAgentAddonOptions() agent.AgentAddonOptions { } } -func observabilitySignerConfigurations() func(cluster *clusterv1.ManagedCluster) []addonapiv1alpha1.RegistrationConfig { +func observabilitySignerConfigurations(client client.Client) func(cluster *clusterv1.ManagedCluster) []addonapiv1alpha1.RegistrationConfig { return func(cluster *clusterv1.ManagedCluster) []addonapiv1alpha1.RegistrationConfig { observabilityConfig := addonapiv1alpha1.RegistrationConfig{ SignerName: "open-cluster-management.io/observability-signer", @@ -49,6 +55,17 @@ func observabilitySignerConfigurations() func(cluster *clusterv1.ManagedCluster) OrganizationUnits: []string{"acm"}, }, } - return append(agent.KubeClientSignerConfigurations(addonName, agentName)(cluster), observabilityConfig) + kubeClientSignerConfigurations := agent.KubeClientSignerConfigurations(addonName, agentName) + registrationConfigs := append(kubeClientSignerConfigurations(cluster), observabilityConfig) + + _, _, caCertBytes, err := getCA(client, true) + if err == nil { + caHashStamp := fmt.Sprintf("ca-hash-%x", sha256.Sum256(caCertBytes)) + for i := range registrationConfigs { + registrationConfigs[i].Subject.OrganizationUnits = append(registrationConfigs[i].Subject.OrganizationUnits, caHashStamp) + } + } + + return registrationConfigs } } diff --git a/operators/multiclusterobservability/pkg/certificates/cert_agent_test.go b/operators/multiclusterobservability/pkg/certificates/cert_agent_test.go index 99b2f3125d..f984ebb016 100644 --- a/operators/multiclusterobservability/pkg/certificates/cert_agent_test.go +++ b/operators/multiclusterobservability/pkg/certificates/cert_agent_test.go @@ -5,15 +5,45 @@ package certificates import ( + "crypto/sha256" + "fmt" "testing" + "time" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "open-cluster-management.io/api/addon/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" clusterv1 "open-cluster-management.io/api/cluster/v1" + + "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" ) func TestCertAgent(t *testing.T) { - agent := &ObservabilityAgent{} + cert, key, err := NewSigningCertKeyPair("testing-mco", 365*24*time.Hour) + if err != nil { + t.Fatal(err) + } + + caSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: serverCACerts, + Namespace: config.GetDefaultNamespace(), + }, + Data: map[string][]byte{ + "tls.crt": cert, + "tls.key": key, + }, + } + + scheme := runtime.NewScheme() + scheme.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Secret{}) + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(caSecret).Build() + + agent := &ObservabilityAgent{client: client} agent.Manifests(nil, nil) options := agent.GetAgentAddonOptions() cluster := &clusterv1.ManagedCluster{ @@ -22,7 +52,34 @@ func TestCertAgent(t *testing.T) { }, } configs := options.Registration.CSRConfigurations(cluster) - if len(configs) != 2 { - t.Fatal("Wrong CSRConfigurations") + expectedCSRs := 2 + if len(configs) != expectedCSRs { + t.Fatalf("expected %d CSRs, found %d", expectedCSRs, len(configs)) + } + + caHashOrgUnit := fmt.Sprintf("ca-hash-%x", sha256.Sum256(cert)) + + kubeAPISignerExpectedRegConfig := v1alpha1.RegistrationConfig{ + SignerName: "kubernetes.io/kube-apiserver-client", + Subject: v1alpha1.Subject{ + User: "system:open-cluster-management:cluster:test:addon:observability-controller:agent:observability", + Groups: []string{ + "system:open-cluster-management:cluster:test:addon:observability-controller", + "system:open-cluster-management:addon:observability-controller", + "system:authenticated", + }, + OrganizationUnits: []string{caHashOrgUnit}, + }, + } + assert.Contains(t, configs, kubeAPISignerExpectedRegConfig) + + obsSignerExpectedRegConfig := v1alpha1.RegistrationConfig{ + SignerName: "open-cluster-management.io/observability-signer", + Subject: v1alpha1.Subject{ + User: "managed-cluster-observability", + Groups: nil, + OrganizationUnits: []string{"acm", caHashOrgUnit}, + }, } + assert.Contains(t, configs, obsSignerExpectedRegConfig) } diff --git a/operators/multiclusterobservability/pkg/certificates/cert_controller.go b/operators/multiclusterobservability/pkg/certificates/cert_controller.go index e8d287f309..7ee9c4046f 100644 --- a/operators/multiclusterobservability/pkg/certificates/cert_controller.go +++ b/operators/multiclusterobservability/pkg/certificates/cert_controller.go @@ -13,8 +13,10 @@ import ( "reflect" "time" - operatorconfig "github.com/stolostron/multicluster-observability-operator/operators/pkg/config" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "open-cluster-management.io/addon-framework/pkg/addonmanager" + + operatorconfig "github.com/stolostron/multicluster-observability-operator/operators/pkg/config" "golang.org/x/exp/slices" appv1 "k8s.io/api/apps/v1" @@ -27,8 +29,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "open-cluster-management.io/addon-framework/pkg/addonmanager" - mcov1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2" "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" ) @@ -55,7 +55,7 @@ func Start(c client.Client, ingressCtlCrdExists bool) { log.Error(err, "Failed to init addon manager") os.Exit(1) } - agent := &ObservabilityAgent{} + agent := &ObservabilityAgent{client: c} err = addonMgr.AddAgent(agent) if err != nil { log.Error(err, "Failed to add agent for addon manager") @@ -84,10 +84,8 @@ func Start(c client.Client, ingressCtlCrdExists bool) { &v1.Secret{}, time.Minute*60, cache.ResourceEventHandlerFuncs{ - AddFunc: onAdd(c), - + AddFunc: onAdd(c), DeleteFunc: onDelete(c), - UpdateFunc: onUpdate(c, ingressCtlCrdExists), }, ) @@ -246,7 +244,7 @@ func onUpdate(c client.Client, ingressCtlCrdExists bool) func(oldObj, newObj int } case name == hubMetricsCollectorMtlsCert: // ACM 8509: Special case for hub metrics collector - //Delete the MTLS secret and the placement controller will reconcile to create a new one + // Delete the MTLS secret and the placement controller will reconcile to create a new one HubMtlsSecret := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: operatorconfig.HubMetricsCollectorMtlsCert, diff --git a/operators/multiclusterobservability/pkg/certificates/test_helpers.go b/operators/multiclusterobservability/pkg/certificates/test_helpers.go new file mode 100644 index 0000000000..2e8c21f84a --- /dev/null +++ b/operators/multiclusterobservability/pkg/certificates/test_helpers.go @@ -0,0 +1,27 @@ +// Copyright (c) Red Hat, Inc. +// Copyright Contributors to the Open Cluster Management project +// Licensed under the Apache License 2.0 + +package certificates + +import ( + "bytes" + "time" + + "github.com/openshift/library-go/pkg/crypto" +) + +func NewSigningCertKeyPair(signerName string, validity time.Duration) (certData, keyData []byte, err error) { + ca, err := crypto.MakeSelfSignedCAConfigForDuration(signerName, validity) + if err != nil { + return nil, nil, err + } + + certBytes := &bytes.Buffer{} + keyBytes := &bytes.Buffer{} + if err := ca.WriteCertConfig(certBytes, keyBytes); err != nil { + return nil, nil, err + } + + return certBytes.Bytes(), keyBytes.Bytes(), nil +}