From 1a2354bbbbdbd37ecafa1cf8699af717872061af Mon Sep 17 00:00:00 2001 From: Tomas Turek Date: Wed, 3 Jul 2024 17:13:39 +0200 Subject: [PATCH] Inject CA trust bundle into managed containers and set SSL_CERT_DIR --- api/v1alpha1/rekor_types_test.go | 24 +- api/v1alpha1/trillian_types_test.go | 18 +- .../controller/annotations/annotations.go | 19 ++ .../annotations/annotations_test.go | 78 ++++++ .../controller/common/utils/set_trusted_ca.go | 68 +++++ .../common/utils/set_trusted_ca_test.go | 248 ++++++++++++++++++ .../controller/ctlog/actions/deployment.go | 5 + .../fulcio/utils/fulcio_deployment.go | 56 +--- .../fulcio/utils/fulcio_deployment_test.go | 37 ++- .../rekor/actions/server/deployment.go | 4 + .../securesign/actions/ensure_ctlog.go | 2 + .../securesign/actions/ensure_fulcio.go | 2 + .../securesign/actions/ensure_rekor.go | 2 + .../securesign/actions/ensure_trillian.go | 2 + .../securesign/actions/ensure_tuf.go | 2 + .../actions/segment_backup_cronjob.go | 4 + .../securesign/actions/segment_backup_job.go | 4 + .../trillian/actions/logserver/deployment.go | 2 + .../trillian/actions/logsigner/deployment.go | 2 + 19 files changed, 506 insertions(+), 73 deletions(-) create mode 100644 internal/controller/annotations/annotations_test.go create mode 100644 internal/controller/common/utils/set_trusted_ca.go create mode 100644 internal/controller/common/utils/set_trusted_ca_test.go diff --git a/api/v1alpha1/rekor_types_test.go b/api/v1alpha1/rekor_types_test.go index d3bc5fd04..f554241a3 100644 --- a/api/v1alpha1/rekor_types_test.go +++ b/api/v1alpha1/rekor_types_test.go @@ -3,12 +3,12 @@ package v1alpha1 import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/securesign/operator/internal/controller/common/utils" "golang.org/x/net/context" _ "k8s.io/api/apps/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" k8sresource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" ) var _ = Describe("Rekor", func() { @@ -77,27 +77,27 @@ var _ = Describe("Rekor", func() { When("changing Rekor Search UI", func() { It("enabled false->true", func() { created := generateRekorObject("rekor-ui-1") - created.Spec.RekorSearchUI.Enabled = utils.Pointer(false) + created.Spec.RekorSearchUI.Enabled = pointer.Bool(false) Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) fetched := &Rekor{} Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) Expect(fetched).To(Equal(created)) - fetched.Spec.RekorSearchUI.Enabled = utils.Pointer(true) + fetched.Spec.RekorSearchUI.Enabled = pointer.Bool(true) Expect(k8sClient.Update(context.Background(), fetched)).To(Succeed()) }) It("enabled true->false", func() { created := generateRekorObject("rekor-ui-2") - created.Spec.RekorSearchUI.Enabled = utils.Pointer(true) + created.Spec.RekorSearchUI.Enabled = pointer.Bool(true) Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) fetched := &Rekor{} Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) Expect(fetched).To(Equal(created)) - fetched.Spec.RekorSearchUI.Enabled = utils.Pointer(false) + fetched.Spec.RekorSearchUI.Enabled = pointer.Bool(false) Expect(apierrors.IsInvalid(k8sClient.Update(context.Background(), fetched))).To(BeTrue()) Expect(k8sClient.Update(context.Background(), fetched)). To(MatchError(ContainSubstring("Feature cannot be disabled"))) @@ -150,7 +150,7 @@ var _ = Describe("Rekor", func() { invalidObject := &Rekor{} Expect(k8sClient.Get(context.Background(), getKey(validObject), invalidObject)).To(Succeed()) - invalidObject.Spec.Pvc.Retain = utils.Pointer(false) + invalidObject.Spec.Pvc.Retain = pointer.Bool(false) Expect(apierrors.IsInvalid(k8sClient.Update(context.Background(), invalidObject))).To(BeTrue()) Expect(k8sClient.Update(context.Background(), invalidObject)). @@ -214,10 +214,10 @@ var _ = Describe("Rekor", func() { Host: "hostname", }, RekorSearchUI: RekorSearchUI{ - Enabled: utils.Pointer(true), + Enabled: pointer.Bool(true), }, BackFillRedis: BackFillRedis{ - Enabled: utils.Pointer(true), + Enabled: pointer.Bool(true), Schedule: "* */2 * * 0-3", }, TreeID: &tree, @@ -225,7 +225,7 @@ var _ = Describe("Rekor", func() { Name: "name", Size: &storage, StorageClass: "name", - Retain: utils.Pointer(true), + Retain: pointer.Bool(true), }, Signer: RekorSigner{ KMS: "secret", @@ -294,18 +294,18 @@ func generateRekorObject(name string) *Rekor { }, Spec: RekorSpec{ BackFillRedis: BackFillRedis{ - Enabled: utils.Pointer(true), + Enabled: pointer.Bool(true), Schedule: "0 0 * * *", }, Signer: RekorSigner{ KMS: "secret", }, Pvc: Pvc{ - Retain: utils.Pointer(true), + Retain: pointer.Bool(true), Size: &storage, }, Trillian: TrillianService{ - Port: utils.Pointer(int32(8091)), + Port: pointer.Int32(int32(8091)), }, }, } diff --git a/api/v1alpha1/trillian_types_test.go b/api/v1alpha1/trillian_types_test.go index 94d14bd06..41dcd0d03 100644 --- a/api/v1alpha1/trillian_types_test.go +++ b/api/v1alpha1/trillian_types_test.go @@ -3,12 +3,12 @@ package v1alpha1 import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/securesign/operator/internal/controller/common/utils" "golang.org/x/net/context" _ "k8s.io/api/apps/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" k8sresource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" ) var _ = Describe("Trillian", func() { @@ -58,7 +58,7 @@ var _ = Describe("Trillian", func() { invalidObject := &Trillian{} Expect(k8sClient.Get(context.Background(), getKey(validObject), invalidObject)).To(Succeed()) - invalidObject.Spec.Db.Create = utils.Pointer(false) + invalidObject.Spec.Db.Create = pointer.Bool(false) Expect(apierrors.IsInvalid(k8sClient.Update(context.Background(), invalidObject))).To(BeTrue()) Expect(k8sClient.Update(context.Background(), invalidObject)). @@ -71,7 +71,7 @@ var _ = Describe("Trillian", func() { invalidObject := &Trillian{} Expect(k8sClient.Get(context.Background(), getKey(validObject), invalidObject)).To(Succeed()) - invalidObject.Spec.Db.Pvc.Retain = utils.Pointer(false) + invalidObject.Spec.Db.Pvc.Retain = pointer.Bool(false) Expect(apierrors.IsInvalid(k8sClient.Update(context.Background(), invalidObject))).To(BeTrue()) Expect(k8sClient.Update(context.Background(), invalidObject)). @@ -82,7 +82,7 @@ var _ = Describe("Trillian", func() { It("true", func() { By("databaseSecretRef is empty", func() { validObject := generateTrillianObject("database-secret-1") - validObject.Spec.Db.Create = utils.Pointer(true) + validObject.Spec.Db.Create = pointer.Bool(true) validObject.Spec.Db.DatabaseSecretRef = nil Expect(k8sClient.Create(context.Background(), validObject)).To(Succeed()) }) @@ -91,7 +91,7 @@ var _ = Describe("Trillian", func() { It("false", func() { By("databaseSecretRef is mandatory", func() { invalidObject := generateTrillianObject("database-secret-2") - invalidObject.Spec.Db.Create = utils.Pointer(false) + invalidObject.Spec.Db.Create = pointer.Bool(false) invalidObject.Spec.Db.DatabaseSecretRef = nil Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidObject))).To(BeTrue()) Expect(k8sClient.Create(context.Background(), invalidObject)). @@ -146,9 +146,9 @@ var _ = Describe("Trillian", func() { }, Spec: TrillianSpec{ Db: TrillianDB{ - Create: utils.Pointer(true), + Create: pointer.Bool(true), Pvc: Pvc{ - Retain: utils.Pointer(true), + Retain: pointer.Bool(true), Name: "storage", StorageClass: "storage-class", Size: &storage, @@ -208,9 +208,9 @@ func generateTrillianObject(name string) *Trillian { }, Spec: TrillianSpec{ Db: TrillianDB{ - Create: utils.Pointer(true), + Create: pointer.Bool(true), Pvc: Pvc{ - Retain: utils.Pointer(true), + Retain: pointer.Bool(true), Size: &storage, }, }, diff --git a/internal/controller/annotations/annotations.go b/internal/controller/annotations/annotations.go index d24b6cf49..6b60d147e 100644 --- a/internal/controller/annotations/annotations.go +++ b/internal/controller/annotations/annotations.go @@ -6,4 +6,23 @@ const ( // Metrics Annotation is used to control the sending of analytic metrics of the installed services managed by the operator. Metrics = "rhtas.redhat.com/metrics" + + // TrustedCA Annotation to specify name of ConfigMap with additional bundle of trusted CA + TrustedCA = "rhtas.redhat.com/trusted-ca" ) + +var inheritable = []string{ + TrustedCA, +} + +func FilterInheritable(annotations map[string]string) map[string]string { + result := make(map[string]string, 0) + for key, value := range annotations { + for _, ia := range inheritable { + if key == ia { + result[key] = value + } + } + } + return result +} diff --git a/internal/controller/annotations/annotations_test.go b/internal/controller/annotations/annotations_test.go new file mode 100644 index 000000000..5828ad0f2 --- /dev/null +++ b/internal/controller/annotations/annotations_test.go @@ -0,0 +1,78 @@ +package annotations + +import ( + "maps" + "reflect" + "strconv" + "testing" +) + +func TestFilterInheritable(t *testing.T) { + allIn := make(map[string]string) + allCopy := make(map[string]string) + for i, a := range inheritable { + allIn[a] = strconv.FormatInt(int64(i), 10) + } + maps.Copy(allCopy, allIn) + + type args struct { + annotations map[string]string + } + tests := []struct { + name string + args args + want map[string]string + }{ + { + name: "empty", + args: args{ + annotations: make(map[string]string), + }, + want: map[string]string{}, + }, + { + name: "nil", + args: args{ + annotations: nil, + }, + want: map[string]string{}, + }, + { + name: "no inheritable", + args: args{ + annotations: map[string]string{ + "name1": "value", + "name2": "value", + }, + }, + want: map[string]string{}, + }, + { + name: "all inheritable", + args: args{ + annotations: allIn, + }, + want: allCopy, + }, + { + name: "one inheritable", + args: args{ + annotations: map[string]string{ + "name1": "value", + "name2": "value", + TrustedCA: "ca", + }, + }, + want: map[string]string{ + TrustedCA: "ca", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := FilterInheritable(tt.args.annotations); !reflect.DeepEqual(got, tt.want) { + t.Errorf("FilterInheritable() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/controller/common/utils/set_trusted_ca.go b/internal/controller/common/utils/set_trusted_ca.go new file mode 100644 index 000000000..5d3c26fd9 --- /dev/null +++ b/internal/controller/common/utils/set_trusted_ca.go @@ -0,0 +1,68 @@ +package utils + +import ( + "errors" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/annotations" + corev1 "k8s.io/api/core/v1" +) + +// SetTrustedCA mount config map with trusted CA bundle to all deployment's containers. +func SetTrustedCA(template *corev1.PodTemplateSpec, lor *v1alpha1.LocalObjectReference) error { + if template == nil { + return errors.New("SetTrustedCA: PodTemplateSpec is not set") + } + + for i, container := range template.Spec.Containers { + if template.Spec.Containers[i].Env == nil { + template.Spec.Containers[i].Env = make([]corev1.EnvVar, 0) + } + template.Spec.Containers[i].Env = append(container.Env, corev1.EnvVar{ + Name: "SSL_CERT_DIR", + Value: "/var/run/configs/tas/ca-trust:/var/run/secrets/kubernetes.io/serviceaccount", + }) + + if template.Spec.Containers[i].VolumeMounts == nil { + template.Spec.Containers[i].VolumeMounts = make([]corev1.VolumeMount, 0) + } + template.Spec.Containers[i].VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: "ca-trust", + MountPath: "/var/run/configs/tas/ca-trust", + ReadOnly: true, + }) + } + + projections := make([]corev1.VolumeProjection, 0) + if lor != nil { + projections = append(projections, corev1.VolumeProjection{ + ConfigMap: &corev1.ConfigMapProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: lor.Name, + }, + }, + }) + } + + if template.Spec.Volumes == nil { + template.Spec.Volumes = make([]corev1.Volume, 0) + } + template.Spec.Volumes = append(template.Spec.Volumes, corev1.Volume{ + Name: "ca-trust", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: projections, + DefaultMode: Pointer(int32(420)), + }, + }, + }) + return nil +} + +func TrustedCAAnnotationToReference(anns map[string]string) *v1alpha1.LocalObjectReference { + if v, ok := anns[annotations.TrustedCA]; ok { + return &v1alpha1.LocalObjectReference{ + Name: v, + } + } + return nil +} diff --git a/internal/controller/common/utils/set_trusted_ca_test.go b/internal/controller/common/utils/set_trusted_ca_test.go new file mode 100644 index 000000000..dba5cbbba --- /dev/null +++ b/internal/controller/common/utils/set_trusted_ca_test.go @@ -0,0 +1,248 @@ +package utils + +import ( + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gstruct" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/annotations" + corev1 "k8s.io/api/core/v1" + "reflect" + "testing" +) + +func TestSetTrustedCA(t *testing.T) { + g := NewWithT(t) + deployment := func() *corev1.PodTemplateSpec { + return &corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "empty", + }, + { + Name: "env", + Env: []corev1.EnvVar{ + { + Name: "NAME", + Value: "VALUE", + }, + }, + }, + { + Name: "volume", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "mount", + MountPath: "/mount/path/", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "mount", + }, + }, + }, + } + } + + type asserts func(*corev1.PodTemplateSpec, error) + type args struct { + dep *corev1.PodTemplateSpec + lor *v1alpha1.LocalObjectReference + } + tests := []struct { + name string + args args + error bool + want asserts + }{ + { + name: "nil LocalObjectReference", + args: args{ + dep: deployment(), + lor: nil, + }, + want: func(spec *corev1.PodTemplateSpec, _ error) { + + g.Expect(spec.Spec.Containers).ShouldNot(BeNil()) + g.Expect(spec.Spec.Containers).Should(HaveLen(3)) + g.Expect(spec.Spec.Containers[0].Name).Should(BeEquivalentTo("empty")) + g.Expect(spec.Spec.Containers[0].Env).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[0].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) + g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + + g.Expect(spec.Spec.Containers[1].Name).Should(BeEquivalentTo("env")) + g.Expect(spec.Spec.Containers[1].Env).Should(HaveLen(2)) + g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("NAME"), + }))) + g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) + g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + + g.Expect(spec.Spec.Containers[2].Name).Should(BeEquivalentTo("volume")) + g.Expect(spec.Spec.Containers[2].Env).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[2].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) + g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(HaveLen(2)) + g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("mount"), + }))) + g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + + g.Expect(spec.Spec.Volumes).Should(HaveLen(2)) + g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("mount"), + }))) + g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + g.Expect(spec.Spec.Volumes[1].VolumeSource.Projected.Sources).Should(HaveLen(0)) + }, + }, + { + name: "mount config map", + args: args{ + dep: deployment(), + lor: &v1alpha1.LocalObjectReference{Name: "trusted"}, + }, + want: func(spec *corev1.PodTemplateSpec, _ error) { + + g.Expect(spec.Spec.Containers).ShouldNot(BeNil()) + g.Expect(spec.Spec.Containers).Should(HaveLen(3)) + g.Expect(spec.Spec.Containers[0].Name).Should(BeEquivalentTo("empty")) + g.Expect(spec.Spec.Containers[0].Env).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[0].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) + g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[0].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + + g.Expect(spec.Spec.Containers[1].Name).Should(BeEquivalentTo("env")) + g.Expect(spec.Spec.Containers[1].Env).Should(HaveLen(2)) + g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("NAME"), + }))) + g.Expect(spec.Spec.Containers[1].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) + g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[1].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + + g.Expect(spec.Spec.Containers[2].Name).Should(BeEquivalentTo("volume")) + g.Expect(spec.Spec.Containers[2].Env).Should(HaveLen(1)) + g.Expect(spec.Spec.Containers[2].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) + g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(HaveLen(2)) + g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("mount"), + }))) + g.Expect(spec.Spec.Containers[2].VolumeMounts).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + + g.Expect(spec.Spec.Volumes).Should(HaveLen(2)) + g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("mount"), + }))) + g.Expect(spec.Spec.Volumes).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("ca-trust"), + }))) + g.Expect(spec.Spec.Volumes[1].VolumeSource.Projected.Sources).Should(HaveLen(1)) + g.Expect(spec.Spec.Volumes[1].VolumeSource.Projected.Sources[0].ConfigMap.LocalObjectReference.Name).Should(Equal("trusted")) + }, + }, + { + name: "nil Deployment", + args: args{ + dep: nil, + lor: nil, + }, + error: true, + want: func(d *corev1.PodTemplateSpec, err error) { + g.Expect(d).Should(BeNil()) + g.Expect(err).Should(MatchError(ContainSubstring("PodTemplateSpec is not set"))) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := SetTrustedCA(tt.args.dep, tt.args.lor) + if tt.error { + g.Expect(err).ShouldNot(BeNil()) + } else { + g.Expect(err).Should(BeNil()) + } + tt.want(tt.args.dep, err) + }) + } +} + +func TestTrustedCAAnnotationToReference(t *testing.T) { + type args struct { + anns map[string]string + } + tests := []struct { + name string + args args + want *v1alpha1.LocalObjectReference + }{ + { + name: "nil", + args: args{ + anns: nil, + }, + want: nil, + }, + { + name: "empty", + args: args{ + anns: make(map[string]string, 0), + }, + want: nil, + }, + { + name: "not existing", + args: args{ + anns: map[string]string{ + "annotation": "value", + }, + }, + want: nil, + }, + { + name: "existing", + args: args{map[string]string{ + "annotation": "value", + annotations.TrustedCA: "trusted", + }}, + want: &v1alpha1.LocalObjectReference{Name: "trusted"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := TrustedCAAnnotationToReference(tt.args.anns); !reflect.DeepEqual(got, tt.want) { + t.Errorf("TrustedCAAnnotationToReference() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/controller/ctlog/actions/deployment.go b/internal/controller/ctlog/actions/deployment.go index 554717431..294d03e9a 100644 --- a/internal/controller/ctlog/actions/deployment.go +++ b/internal/controller/ctlog/actions/deployment.go @@ -3,6 +3,7 @@ package actions import ( "context" "fmt" + cutils "github.com/securesign/operator/internal/controller/common/utils" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -50,6 +51,10 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.CTlog) return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could create server Deployment: %w", err), instance) } } + err = cutils.SetTrustedCA(&dp.Spec.Template, cutils.TrustedCAAnnotationToReference(instance.Annotations)) + if err != nil { + return i.Failed(err) + } if err = controllerutil.SetControllerReference(instance, dp, i.Client.Scheme()); err != nil { return i.Failed(fmt.Errorf("could not set controller reference for Deployment: %w", err)) diff --git a/internal/controller/fulcio/utils/fulcio_deployment.go b/internal/controller/fulcio/utils/fulcio_deployment.go index d3a55c573..72446c9f6 100644 --- a/internal/controller/fulcio/utils/fulcio_deployment.go +++ b/internal/controller/fulcio/utils/fulcio_deployment.go @@ -41,11 +41,6 @@ func CreateDeployment(instance *v1alpha1.Fulcio, deploymentName string, sa strin fmt.Sprintf("--ct-log-url=http://ctlog.%s.svc/trusted-artifact-signer", instance.Namespace)} env := make([]corev1.EnvVar, 0) - env = append(env, corev1.EnvVar{ - Name: "SSL_CERT_DIR", - Value: "/var/run/fulcio", - }) - if instance.Status.Certificate.PrivateKeyPasswordRef != nil { env = append(env, corev1.EnvVar{ Name: "PASSWORD", @@ -61,33 +56,6 @@ func CreateDeployment(instance *v1alpha1.Fulcio, deploymentName string, sa strin args = append(args, "--fileca-key-passwd", "$(PASSWORD)") } - oidcInfo := make([]corev1.VolumeProjection, 0) - // Integration with https://kubernetes.default.svc" OIDC issuer and ctlog service - oidcInfo = append(oidcInfo, corev1.VolumeProjection{ - ConfigMap: &corev1.ConfigMapProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "kube-root-ca.crt", - }, - Items: []corev1.KeyToPath{ - { - Key: "ca.crt", - Path: "ca.crt", - Mode: utils.Pointer(int32(0444)), - }, - }, - }, - }) - - if instance.Spec.TrustedCA != nil { - oidcInfo = append(oidcInfo, corev1.VolumeProjection{ - ConfigMap: &corev1.ConfigMapProjection{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: instance.Spec.TrustedCA.Name, - }, - }, - }) - } - dep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: deploymentName, @@ -157,11 +125,6 @@ func CreateDeployment(instance *v1alpha1.Fulcio, deploymentName string, sa strin Name: "fulcio-config", MountPath: "/etc/fulcio-config", }, - { - Name: "oidc-info", - MountPath: "/var/run/fulcio", - ReadOnly: true, - }, { Name: "fulcio-cert", MountPath: "/var/run/fulcio-secrets", @@ -182,14 +145,6 @@ func CreateDeployment(instance *v1alpha1.Fulcio, deploymentName string, sa strin }, }, }, - { - Name: "oidc-info", - VolumeSource: corev1.VolumeSource{ - Projected: &corev1.ProjectedVolumeSource{ - Sources: oidcInfo, - }, - }, - }, { Name: "fulcio-cert", VolumeSource: corev1.VolumeSource{ @@ -231,5 +186,16 @@ func CreateDeployment(instance *v1alpha1.Fulcio, deploymentName string, sa strin }, } utils.SetProxyEnvs(dep) + + caRef := utils.TrustedCAAnnotationToReference(instance.Annotations) + // override if spec.trustedCA is defined + if instance.Spec.TrustedCA != nil { + caRef = instance.Spec.TrustedCA + } + err := utils.SetTrustedCA(&dep.Spec.Template, caRef) + if err != nil { + return nil, err + } + return dep, nil } diff --git a/internal/controller/fulcio/utils/fulcio_deployment_test.go b/internal/controller/fulcio/utils/fulcio_deployment_test.go index a194687e3..d39368d4d 100644 --- a/internal/controller/fulcio/utils/fulcio_deployment_test.go +++ b/internal/controller/fulcio/utils/fulcio_deployment_test.go @@ -1,6 +1,7 @@ package utils import ( + "github.com/securesign/operator/internal/controller/annotations" "testing" . "github.com/onsi/gomega" @@ -37,10 +38,9 @@ func TestSimpleDeploymen(t *testing.T) { })), "PASSWORD env should not be set") // oidc-info volume - oidcVolume := findVolume("oidc-info", deployment.Spec.Template.Spec.Volumes) + oidcVolume := findVolume("ca-trust", deployment.Spec.Template.Spec.Volumes) g.Expect(oidcVolume).ShouldNot(BeNil()) - g.Expect(len(oidcVolume.VolumeSource.Projected.Sources)).Should(Equal(1)) - g.Expect(oidcVolume.VolumeSource.Projected.Sources[0].ConfigMap.Name).Should(Equal("kube-root-ca.crt")) + g.Expect(len(oidcVolume.VolumeSource.Projected.Sources)).Should(Equal(0)) } func TestPrivateKeyPassword(t *testing.T) { @@ -61,6 +61,9 @@ func TestPrivateKeyPassword(t *testing.T) { g.Expect(deployment.Spec.Template.Spec.Containers[0].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ "Name": Equal("PASSWORD"), })), "PASSWORD env should be set") + g.Expect(deployment.Spec.Template.Spec.Containers[0].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) } func TestTrustedCA(t *testing.T) { @@ -77,11 +80,31 @@ func TestTrustedCA(t *testing.T) { "Name": Equal("SSL_CERT_DIR"), }))) - oidcVolume := findVolume("oidc-info", deployment.Spec.Template.Spec.Volumes) + oidcVolume := findVolume("ca-trust", deployment.Spec.Template.Spec.Volumes) g.Expect(oidcVolume).ShouldNot(BeNil()) - g.Expect(len(oidcVolume.VolumeSource.Projected.Sources)).Should(Equal(2)) - g.Expect(oidcVolume.VolumeSource.Projected.Sources[0].ConfigMap.Name).Should(Equal("kube-root-ca.crt")) - g.Expect(oidcVolume.VolumeSource.Projected.Sources[1].ConfigMap.Name).Should(Equal("trusted")) + g.Expect(len(oidcVolume.VolumeSource.Projected.Sources)).Should(Equal(1)) + g.Expect(oidcVolume.VolumeSource.Projected.Sources[0].ConfigMap.Name).Should(Equal("trusted")) +} + +func TestTrustedCAByAnnotation(t *testing.T) { + g := NewWithT(t) + + instance := createInstance() + instance.Annotations = make(map[string]string) + instance.Annotations[annotations.TrustedCA] = "trusted-annotation" + labels := constants.LabelsFor(componentName, deploymentName, instance.Name) + deployment, err := CreateDeployment(instance, deploymentName, rbacName, labels) + g.Expect(err).ShouldNot(HaveOccurred()) + g.Expect(deployment).ShouldNot(BeNil()) + + g.Expect(deployment.Spec.Template.Spec.Containers[0].Env).Should(ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ + "Name": Equal("SSL_CERT_DIR"), + }))) + + oidcVolume := findVolume("ca-trust", deployment.Spec.Template.Spec.Volumes) + g.Expect(oidcVolume).ShouldNot(BeNil()) + g.Expect(len(oidcVolume.VolumeSource.Projected.Sources)).Should(Equal(1)) + g.Expect(oidcVolume.VolumeSource.Projected.Sources[0].ConfigMap.Name).Should(Equal("trusted-annotation")) } func TestMissingPrivateKey(t *testing.T) { diff --git a/internal/controller/rekor/actions/server/deployment.go b/internal/controller/rekor/actions/server/deployment.go index c5352407e..e50b7636a 100644 --- a/internal/controller/rekor/actions/server/deployment.go +++ b/internal/controller/rekor/actions/server/deployment.go @@ -3,6 +3,7 @@ package server import ( "context" "fmt" + cutils "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/constants" @@ -46,6 +47,9 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Rekor) } i.Logger.V(1).Info("trillian logserver", "address", insCopy.Spec.Trillian.Address) dp, err := utils.CreateRekorDeployment(insCopy, actions.ServerDeploymentName, actions.RBACName, labels) + if err == nil { + err = cutils.SetTrustedCA(&dp.Spec.Template, cutils.TrustedCAAnnotationToReference(instance.Annotations)) + } if err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ diff --git a/internal/controller/securesign/actions/ensure_ctlog.go b/internal/controller/securesign/actions/ensure_ctlog.go index 5fac59c3a..98299673b 100644 --- a/internal/controller/securesign/actions/ensure_ctlog.go +++ b/internal/controller/securesign/actions/ensure_ctlog.go @@ -2,6 +2,7 @@ package actions import ( "context" + "github.com/securesign/operator/internal/controller/annotations" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -39,6 +40,7 @@ func (i ctlogAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secures ctlog.Name = instance.Name ctlog.Namespace = instance.Namespace ctlog.Labels = constants.LabelsFor(actions.ComponentName, ctlog.Name, instance.Name) + ctlog.Annotations = annotations.FilterInheritable(instance.Annotations) ctlog.Spec = instance.Spec.Ctlog diff --git a/internal/controller/securesign/actions/ensure_fulcio.go b/internal/controller/securesign/actions/ensure_fulcio.go index 5583099a5..f4c75520b 100644 --- a/internal/controller/securesign/actions/ensure_fulcio.go +++ b/internal/controller/securesign/actions/ensure_fulcio.go @@ -2,6 +2,7 @@ package actions import ( "context" + "github.com/securesign/operator/internal/controller/annotations" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -39,6 +40,7 @@ func (i fulcioAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secure fulcio.Name = instance.Name fulcio.Namespace = instance.Namespace fulcio.Labels = constants.LabelsFor(actions.ComponentName, fulcio.Name, instance.Name) + fulcio.Annotations = annotations.FilterInheritable(instance.Annotations) fulcio.Spec = instance.Spec.Fulcio diff --git a/internal/controller/securesign/actions/ensure_rekor.go b/internal/controller/securesign/actions/ensure_rekor.go index 2def39a27..d3ce550af 100644 --- a/internal/controller/securesign/actions/ensure_rekor.go +++ b/internal/controller/securesign/actions/ensure_rekor.go @@ -2,6 +2,7 @@ package actions import ( "context" + "github.com/securesign/operator/internal/controller/annotations" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -38,6 +39,7 @@ func (i rekorAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secures rekor.Name = instance.Name rekor.Namespace = instance.Namespace rekor.Labels = constants.LabelsFor("rekor", rekor.Name, instance.Name) + rekor.Annotations = annotations.FilterInheritable(instance.Annotations) rekor.Spec = instance.Spec.Rekor diff --git a/internal/controller/securesign/actions/ensure_trillian.go b/internal/controller/securesign/actions/ensure_trillian.go index 1341637b0..cbc291640 100644 --- a/internal/controller/securesign/actions/ensure_trillian.go +++ b/internal/controller/securesign/actions/ensure_trillian.go @@ -2,6 +2,7 @@ package actions import ( "context" + "github.com/securesign/operator/internal/controller/annotations" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -38,6 +39,7 @@ func (i trillianAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Secu trillian.Name = instance.Name trillian.Namespace = instance.Namespace trillian.Labels = constants.LabelsFor("trillian", trillian.Name, instance.Name) + trillian.Annotations = annotations.FilterInheritable(instance.Annotations) trillian.Spec = instance.Spec.Trillian diff --git a/internal/controller/securesign/actions/ensure_tuf.go b/internal/controller/securesign/actions/ensure_tuf.go index 0936c7432..4ea9d1d0c 100644 --- a/internal/controller/securesign/actions/ensure_tuf.go +++ b/internal/controller/securesign/actions/ensure_tuf.go @@ -2,6 +2,7 @@ package actions import ( "context" + "github.com/securesign/operator/internal/controller/annotations" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -39,6 +40,7 @@ func (i tufAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Securesig tuf.Name = instance.Name tuf.Namespace = instance.Namespace tuf.Labels = constants.LabelsFor(actions.ComponentName, tuf.Name, instance.Name) + tuf.Annotations = annotations.FilterInheritable(instance.Annotations) tuf.Spec = instance.Spec.Tuf diff --git a/internal/controller/securesign/actions/segment_backup_cronjob.go b/internal/controller/securesign/actions/segment_backup_cronjob.go index f4ca9e677..920c28926 100644 --- a/internal/controller/securesign/actions/segment_backup_cronjob.go +++ b/internal/controller/securesign/actions/segment_backup_cronjob.go @@ -78,6 +78,10 @@ func (i segmentBackupCronJob) Handle(ctx context.Context, instance *rhtasv1alpha Name: "RUN_TYPE", Value: "nightly", }, + { + Name: "REQUESTS_CA_BUNDLE", + Value: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", + }, }, }, }, diff --git a/internal/controller/securesign/actions/segment_backup_job.go b/internal/controller/securesign/actions/segment_backup_job.go index b02100797..4f7b63204 100644 --- a/internal/controller/securesign/actions/segment_backup_job.go +++ b/internal/controller/securesign/actions/segment_backup_job.go @@ -56,6 +56,10 @@ func (i segmentBackupJob) Handle(ctx context.Context, instance *rhtasv1alpha1.Se Name: "RUN_TYPE", Value: "installation", }, + { + Name: "REQUESTS_CA_BUNDLE", + Value: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", + }, } job := kubernetes.CreateJob(instance.Namespace, SegmentBackupJobName, labels, constants.SegmentBackupImage, SegmentRBACName, parallelism, completions, activeDeadlineSeconds, backoffLimit, command, env) diff --git a/internal/controller/trillian/actions/logserver/deployment.go b/internal/controller/trillian/actions/logserver/deployment.go index ab1f03379..a11071466 100644 --- a/internal/controller/trillian/actions/logserver/deployment.go +++ b/internal/controller/trillian/actions/logserver/deployment.go @@ -3,6 +3,7 @@ package logserver import ( "context" "fmt" + "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/constants" @@ -50,6 +51,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli Protocol: corev1.ProtocolTCP, ContainerPort: 8090, }) + err = utils.SetTrustedCA(&server.Spec.Template, utils.TrustedCAAnnotationToReference(instance.Annotations)) if err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ Type: actions.ServerCondition, diff --git a/internal/controller/trillian/actions/logsigner/deployment.go b/internal/controller/trillian/actions/logsigner/deployment.go index 4f6b03598..fb04b3097 100644 --- a/internal/controller/trillian/actions/logsigner/deployment.go +++ b/internal/controller/trillian/actions/logsigner/deployment.go @@ -3,6 +3,7 @@ package logsigner import ( "context" "fmt" + "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/common/action" "github.com/securesign/operator/internal/controller/constants" @@ -44,6 +45,7 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Trilli actions.RBACName, labels) signer.Spec.Template.Spec.Containers[0].Args = append(signer.Spec.Template.Spec.Containers[0].Args, "--force_master=true") + err = utils.SetTrustedCA(&signer.Spec.Template, utils.TrustedCAAnnotationToReference(instance.Annotations)) if err != nil { meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{ Type: actions.SignerCondition,