diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go index dee84edc3..6af53b237 100644 --- a/api/v1alpha1/common.go +++ b/api/v1alpha1/common.go @@ -58,6 +58,19 @@ type TrillianService struct { Port *int32 `json:"port,omitempty"` } +// TufService configuration to connect TUF server +type TufService struct { + // Address to TUF Server End point + //+optional + Address string `json:"address,omitempty"` + // Port of TUF Server End point + //+kubebuilder:validation:Minimum:=1 + //+kubebuilder:validation:Maximum:=65535 + //+kubebuilder:default:=8080 + //+optional + Port *int32 `json:"port,omitempty"` +} + // CtlogService configuration to connect Ctlog server type CtlogService struct { // Address to Ctlog Log Server End point diff --git a/api/v1alpha1/rekor_types.go b/api/v1alpha1/rekor_types.go index b9fdab91a..b2b3f295d 100644 --- a/api/v1alpha1/rekor_types.go +++ b/api/v1alpha1/rekor_types.go @@ -19,6 +19,10 @@ type RekorSpec struct { // Trillian service configuration //+kubebuilder:default:={port: 8091} Trillian TrillianService `json:"trillian,omitempty"` + // TUF service configuration + //+kubebuilder:default:={port: 8080} + //+optional + Tuf TufService `json:"tuf,omitempty"` // Define whether you want to export service or not ExternalAccess ExternalAccess `json:"externalAccess,omitempty"` //Enable Service monitors for rekor diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index b082eb7b7..79e44b0cd 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -920,6 +920,7 @@ func (in *RekorSpec) DeepCopyInto(out *RekorSpec) { **out = **in } in.Trillian.DeepCopyInto(&out.Trillian) + in.Tuf.DeepCopyInto(&out.Tuf) in.ExternalAccess.DeepCopyInto(&out.ExternalAccess) out.Monitoring = in.Monitoring in.RekorSearchUI.DeepCopyInto(&out.RekorSearchUI) @@ -1773,6 +1774,26 @@ func (in *TufPvc) DeepCopy() *TufPvc { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TufService) DeepCopyInto(out *TufService) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TufService. +func (in *TufService) DeepCopy() *TufService { + if in == nil { + return nil + } + out := new(TufService) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TufSpec) DeepCopyInto(out *TufSpec) { *out = *in diff --git a/config/crd/bases/rhtas.redhat.com_rekors.yaml b/config/crd/bases/rhtas.redhat.com_rekors.yaml index 185d9ca60..ba0a79267 100644 --- a/config/crd/bases/rhtas.redhat.com_rekors.yaml +++ b/config/crd/bases/rhtas.redhat.com_rekors.yaml @@ -2705,6 +2705,22 @@ spec: - name type: object x-kubernetes-map-type: atomic + tuf: + default: + port: 8080 + description: TUF service configuration + properties: + address: + description: Address to TUF Server End point + type: string + port: + default: 8080 + description: Port of TUF Server End point + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object type: object status: description: RekorStatus defines the observed state of Rekor diff --git a/config/crd/bases/rhtas.redhat.com_securesigns.yaml b/config/crd/bases/rhtas.redhat.com_securesigns.yaml index 81653c326..eb607e86b 100644 --- a/config/crd/bases/rhtas.redhat.com_securesigns.yaml +++ b/config/crd/bases/rhtas.redhat.com_securesigns.yaml @@ -5325,6 +5325,22 @@ spec: - name type: object x-kubernetes-map-type: atomic + tuf: + default: + port: 8080 + description: TUF service configuration + properties: + address: + description: Address to TUF Server End point + type: string + port: + default: 8080 + description: Port of TUF Server End point + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object type: object trillian: description: TrillianSpec defines the desired state of Trillian diff --git a/config/default/images.env b/config/default/images.env index 765d70e56..6a3b11a8f 100644 --- a/config/default/images.env +++ b/config/default/images.env @@ -14,4 +14,4 @@ RELATED_IMAGE_HTTP_SERVER=registry.redhat.io/ubi9/httpd-24@sha256:ab5885d4368f83 RELATED_IMAGE_SEGMENT_REPORTING=registry.redhat.io/rhtas/segment-reporting-rhel9@sha256:e1790a0cac5eadef484e10d8f3f7ef6af9bdfabec4ab9fcc35c5ebd42b0205b3 RELATED_IMAGE_TIMESTAMP_AUTHORITY=registry.redhat.io/rhtas/timestamp-authority-rhel9@sha256:be623422f3f636c39397a66416b02a79f1d59cf593ca258e1701d1728755dde9 RELATED_IMAGE_CLIENT_SERVER=registry.redhat.io/rhtas/client-server-rhel9@sha256:c81aaa8f300021d7cdbb964524fc5e89ea2c79fdab5507f0ec036bf96b219332 -RELATED_IMAGE_REKOR_MONITOR=registry.redhat.io/rhtas/rekor-monitor-rhel9@sha256:1944eff9f103d84380b9efac6adec9cb22613643968e51f07db58df977b6b982 +RELATED_IMAGE_REKOR_MONITOR=registry.redhat.io/rhtas/rekor-monitor-rhel9@sha256:b7f9f8b24fe7db4e124f9e5e9289bc2d180a810e253f48feb7e1177bbef6d4d0 diff --git a/internal/controller/rekor/actions/monitor/statefulset.go b/internal/controller/rekor/actions/monitor/statefulset.go index 76706ae02..4f77b6d92 100644 --- a/internal/controller/rekor/actions/monitor/statefulset.go +++ b/internal/controller/rekor/actions/monitor/statefulset.go @@ -8,9 +8,11 @@ import ( "github.com/securesign/operator/internal/images" + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/action" "github.com/securesign/operator/internal/constants" "github.com/securesign/operator/internal/controller/rekor/actions" + tufConstants "github.com/securesign/operator/internal/controller/tuf/constants" "github.com/securesign/operator/internal/labels" cutils "github.com/securesign/operator/internal/utils" "github.com/securesign/operator/internal/utils/kubernetes" @@ -21,11 +23,13 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" ) -const storageVolumeName = "monitor-storage" +const ( + storageVolumeName = "monitor-storage" + tufRepoVolumeName = "tuf-repository" + mountPath = "/data" +) func NewStatefulSetAction() action.Action[*rhtasv1alpha1.Rekor] { return &statefulSetAction{} @@ -50,6 +54,11 @@ func (i statefulSetAction) Handle(ctx context.Context, instance *rhtasv1alpha1.R result controllerutil.OperationResult ) + switch instance.Spec.Tuf.Address { + case "": + instance.Spec.Tuf.Address = fmt.Sprintf("%s.%s.svc", tufConstants.DeploymentName, instance.Namespace) + } + tufServerHost := fmt.Sprintf("http://%s", instance.Spec.Tuf.Address) rekorServerHost := fmt.Sprintf("http://%s.%s.svc", actions.ServerComponentName, instance.Namespace) labels := labels.For(actions.MonitorComponentName, actions.MonitorStatefulSetName, instance.Name) @@ -60,8 +69,8 @@ func (i statefulSetAction) Handle(ctx context.Context, instance *rhtasv1alpha1.R Namespace: instance.Namespace, }, }, - i.ensureMonitorStatefulSet(instance, actions.RBACName, labels, rekorServerHost), - i.ensureInitContainer(rekorServerHost), + i.ensureMonitorStatefulSet(instance, actions.RBACName, labels, rekorServerHost, tufServerHost), + i.ensureInitContainer(rekorServerHost, tufServerHost), ensure.ControllerReference[*v1.StatefulSet](instance, i.Client), ensure.Labels[*v1.StatefulSet](slices.Collect(maps.Keys(labels)), labels), func(object *v1.StatefulSet) error { @@ -90,7 +99,7 @@ func (i statefulSetAction) Handle(ctx context.Context, instance *rhtasv1alpha1.R return i.Continue() } -func (i statefulSetAction) ensureMonitorStatefulSet(instance *rhtasv1alpha1.Rekor, sa string, labels map[string]string, rekorServerHost string) func(*v1.StatefulSet) error { +func (i statefulSetAction) ensureMonitorStatefulSet(instance *rhtasv1alpha1.Rekor, sa string, labels map[string]string, rekorServerHost string, tufServerHost string) func(*v1.StatefulSet) error { return func(ss *v1.StatefulSet) error { spec := &ss.Spec @@ -110,7 +119,9 @@ func (i statefulSetAction) ensureMonitorStatefulSet(instance *rhtasv1alpha1.Reko container.Command = []string{ "/bin/sh", "-c", - fmt.Sprintf(`/rekor_monitor --file=/data/checkpoint_log.txt --once=false --interval=%s --url=%s`, interval.String(), rekorServerHost), + fmt.Sprintf( + `/rekor_monitor --file=/data/checkpoint_log.txt --once=false --interval=%s --url=%s --tuf-repository=%s --tuf-root-path="%s/root.json"`, + interval.String(), rekorServerHost, tufServerHost, mountPath), } container.Ports = []core.ContainerPort{ @@ -120,9 +131,15 @@ func (i statefulSetAction) ensureMonitorStatefulSet(instance *rhtasv1alpha1.Reko Protocol: core.ProtocolTCP, }, } + container.Env = []core.EnvVar{ + { + Name: "HOME", + Value: mountPath, + }, + } volumeMount := kubernetes.FindVolumeMountByNameOrCreate(container, storageVolumeName) - volumeMount.MountPath = "/data" + volumeMount.MountPath = mountPath spec.VolumeClaimTemplates = []core.PersistentVolumeClaim{ { @@ -141,19 +158,35 @@ func (i statefulSetAction) ensureMonitorStatefulSet(instance *rhtasv1alpha1.Reko }, }, } + return nil } } -func (i statefulSetAction) ensureInitContainer(rekorServerHost string) func(*v1.StatefulSet) error { +func (i statefulSetAction) ensureInitContainer(rekorServerHost string, tufHost string) func(*v1.StatefulSet) error { return func(ss *v1.StatefulSet) error { - initContainer := kubernetes.FindInitContainerByNameOrCreate(&ss.Spec.Template.Spec, "wait-for-rekor-server") + initContainer := kubernetes.FindInitContainerByNameOrCreate(&ss.Spec.Template.Spec, "tuf-init") initContainer.Image = images.Registry.Get(images.RekorMonitor) - + volumeMount := kubernetes.FindVolumeMountByNameOrCreate(initContainer, storageVolumeName) + volumeMount.MountPath = mountPath initContainer.Command = []string{ "/bin/sh", "-c", - fmt.Sprintf(`until curl -sf %s > /dev/null 2>&1; do echo 'Waiting for rekor-server to be ready...'; sleep 5; done`, rekorServerHost), + fmt.Sprintf(` + echo "Waiting for rekor-server..."; + until curl -sf %s > /dev/null 2>&1; do + echo "rekor-server not ready..."; + sleep 5; + done; + echo "Waiting for TUF server..."; + until curl %s > /dev/null 2>&1; do + echo "TUF server not ready..."; + sleep 5; + done; + echo "Downloading root.json"; + curl %s/root.json > %s/root.json + echo "tuf-init completed." + `, rekorServerHost, tufHost, tufHost, mountPath), } return nil diff --git a/test/e2e/rekor_monitor_log_test.go b/test/e2e/rekor_monitor_log_test.go index 1cd1e78bf..c632abef7 100644 --- a/test/e2e/rekor_monitor_log_test.go +++ b/test/e2e/rekor_monitor_log_test.go @@ -254,8 +254,8 @@ var _ = Describe("Rekor Monitor Log", Ordered, func() { g.Expect(err).ToNot(HaveOccurred()) g.Expect(strings.Contains(logContent, "Root hash consistency verified")).To(BeFalse(), fmt.Sprintf("Expected 'Root hash consistency verified' NOT to be in logs, but got: %s", logContent)) - g.Expect(strings.Contains(logContent, "empty log")).To(BeTrue(), - fmt.Sprintf("Expected 'empty log' to be in logs, but got: %s", logContent)) + g.Expect(strings.Contains(logContent, "skipping write of checkpoint: size is 0")).To(BeTrue(), + fmt.Sprintf("Expected 'skipping write of checkpoint: size is 0' to be in logs, but got: %s", logContent)) }, 30*time.Second, 1*time.Second).Should(Succeed(), "Monitor log should be empty and not contain root hash consistency verification") }) diff --git a/test/e2e/rekor_monitor_test.go b/test/e2e/rekor_monitor_test.go index caef96abf..d98038d66 100644 --- a/test/e2e/rekor_monitor_test.go +++ b/test/e2e/rekor_monitor_test.go @@ -14,14 +14,13 @@ import ( "github.com/securesign/operator/internal/labels" "github.com/securesign/operator/test/e2e/support" "github.com/securesign/operator/test/e2e/support/steps" - rekorSupport "github.com/securesign/operator/test/e2e/support/tas/rekor" - "github.com/securesign/operator/test/e2e/support/tas/trillian" + "github.com/securesign/operator/test/e2e/support/tas" + "github.com/securesign/operator/test/e2e/support/tas/securesign" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -30,10 +29,9 @@ var _ = Describe("Rekor Monitor", Ordered, func() { var ( namespace *v1.Namespace - trillianCR *v1alpha1.Trillian - rekorCR *v1alpha1.Rekor rekorMonitorPod v1.Pod rekorMonitorContainer v1.Container + s *v1alpha1.Securesign ) BeforeAll(steps.CreateNamespace(cli, func(new *v1.Namespace) { @@ -41,47 +39,20 @@ var _ = Describe("Rekor Monitor", Ordered, func() { })) BeforeAll(func(ctx SpecContext) { - trillianCR = &v1alpha1.Trillian{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-trillian", - Namespace: namespace.Name, + s = securesign.Create(namespace.Name, "test", + securesign.WithDefaults(), + securesign.WithMonitoring(), + func(v *v1alpha1.Securesign) { + v.Spec.Rekor.Monitoring.TLog.Enabled = true + v.Spec.Rekor.Monitoring.TLog.Interval = metav1.Duration{Duration: time.Second * 10} }, - Spec: v1alpha1.TrillianSpec{ - Db: v1alpha1.TrillianDB{Create: ptr.To(true)}, - }, - } - Expect(cli.Create(ctx, trillianCR)).To(Succeed()) - - By("Waiting for Trillian to be ready") - trillian.Verify(ctx, cli, namespace.Name, trillianCR.Name, true) + ) }) BeforeAll(func(ctx SpecContext) { - rekorCR = &v1alpha1.Rekor{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-rekor-monitor", - Namespace: namespace.Name, - }, - Spec: v1alpha1.RekorSpec{ - Monitoring: v1alpha1.MonitoringWithTLogConfig{ - MonitoringConfig: v1alpha1.MonitoringConfig{ - Enabled: true, - }, - TLog: v1alpha1.TlogMonitoring{ - Enabled: true, - Interval: metav1.Duration{Duration: time.Minute * 10}, - }, - }, - Trillian: v1alpha1.TrillianService{ - Address: fmt.Sprintf("trillian-logserver.%s.svc.cluster.local", namespace.Name), - Port: ptr.To(int32(8091)), - }, - }, - } - Expect(cli.Create(ctx, rekorCR)).To(Succeed()) - - By("Waiting for Rekor to be ready") - rekorSupport.Verify(ctx, cli, namespace.Name, rekorCR.Name, true) + Expect(cli.Create(ctx, s)).To(Succeed()) + By("Waiting for all TAS components to be ready") + tas.VerifyAllComponents(ctx, cli, s, true) }) Describe("Monitor Pod Deployment", func() { @@ -123,7 +94,7 @@ var _ = Describe("Rekor Monitor", Ordered, func() { updated := &v1alpha1.Rekor{} err := cli.Get(ctx, types.NamespacedName{ Namespace: namespace.Name, - Name: rekorCR.Name, + Name: s.Name, }, updated) g.Expect(err).ToNot(HaveOccurred())