From e3f2b5eec6f35476809bc4cd487108df34fb0933 Mon Sep 17 00:00:00 2001 From: Tomas Turek Date: Fri, 9 Aug 2024 16:38:15 +0200 Subject: [PATCH] refactor: split config update based on component refactor: check if latest generation of deployment has been observed --- .github/workflows/main.yml | 2 +- .gitignore | 1 + Makefile | 4 +- .../tsa/actions/generate_signer_test.go | 11 +- .../controller/tsa/tsa_hot_update_test.go | 5 +- test/.DS_Store | Bin 8196 -> 0 bytes test/e2e/.DS_Store | Bin 6148 -> 0 bytes test/e2e/byodb_test.go | 63 +- test/e2e/common_install_test.go | 126 ++-- test/e2e/config_update_test.go | 579 ------------------ test/e2e/key_autodiscovery_test.go | 100 +-- test/e2e/provided_certs_test.go | 98 +-- test/e2e/support/cert.go | 90 +++ test/e2e/support/common.go | 278 --------- test/e2e/support/tas/ctlog.go | 55 -- test/e2e/support/tas/ctlog/ctlog.go | 70 +++ test/e2e/support/tas/{ => fulcio}/fulcio.go | 41 +- test/e2e/support/tas/rekor.go | 64 -- test/e2e/support/tas/rekor/rekor.go | 78 +++ test/e2e/support/tas/rekor/search_ui.go | 18 + .../tas/{ => securesign}/securesign.go | 8 +- test/e2e/support/tas/tas.go | 77 +++ test/e2e/support/tas/trillian.go | 44 -- test/e2e/support/tas/trillian/trillian.go | 55 ++ test/e2e/support/tas/tsa.go | 88 --- test/e2e/support/tas/tsa/tsa.go | 235 +++++++ test/e2e/support/tas/{ => tuf}/tuf.go | 20 +- test/e2e/update/ctlog_test.go | 158 +++++ test/e2e/update/fulcio_test.go | 235 +++++++ test/e2e/update/rekor_test.go | 146 +++++ test/e2e/update/suite_test.go | 159 +++++ test/e2e/update/tsa_test.go | 254 ++++++++ test/e2e/upgrade_test.go | 157 ++--- 33 files changed, 1825 insertions(+), 1494 deletions(-) delete mode 100644 test/.DS_Store delete mode 100644 test/e2e/.DS_Store delete mode 100644 test/e2e/config_update_test.go create mode 100644 test/e2e/support/cert.go delete mode 100644 test/e2e/support/tas/ctlog.go create mode 100644 test/e2e/support/tas/ctlog/ctlog.go rename test/e2e/support/tas/{ => fulcio}/fulcio.go (50%) delete mode 100644 test/e2e/support/tas/rekor.go create mode 100644 test/e2e/support/tas/rekor/rekor.go create mode 100644 test/e2e/support/tas/rekor/search_ui.go rename test/e2e/support/tas/{ => securesign}/securesign.go (68%) create mode 100644 test/e2e/support/tas/tas.go delete mode 100644 test/e2e/support/tas/trillian.go create mode 100644 test/e2e/support/tas/trillian/trillian.go delete mode 100644 test/e2e/support/tas/tsa.go create mode 100644 test/e2e/support/tas/tsa/tsa.go rename test/e2e/support/tas/{ => tuf}/tuf.go (59%) create mode 100644 test/e2e/update/ctlog_test.go create mode 100644 test/e2e/update/fulcio_test.go create mode 100644 test/e2e/update/rekor_test.go create mode 100644 test/e2e/update/suite_test.go create mode 100644 test/e2e/update/tsa_test.go diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6b5e039ea..6a95a3690 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -315,7 +315,7 @@ jobs: TEST_BASE_CATALOG: registry.redhat.io/redhat/redhat-operator-index:v4.14 TEST_TARGET_CATALOG: ${{ env.CATALOG_IMG }} OPENSHIFT: false - run: go test ./test/e2e/... -tags=upgrade -timeout 20m + run: go test -p 1 ./test/e2e/... -tags=upgrade -timeout 20m - name: Archive test artifacts uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 19aa23985..9d0272da6 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ go.work *.swp *.swo *~ +.DS_Store # K8s dump from e2e tests k8s-dump-*.tar.gz diff --git a/Makefile b/Makefile index c45f984fd..d9040f993 100644 --- a/Makefile +++ b/Makefile @@ -130,12 +130,12 @@ test: manifests generate fmt vet envtest ## Run tests. # Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. .PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. test-e2e: - go test ./test/e2e/... -v -ginkgo.v -tags=integration -timeout 20m + go test -p 1 ./test/e2e/... -tags=integration -timeout 20m # Switch images from `registry.redhat.io` images to the dev images .PHONY: dev-images dev-images: - sed -E -f ci/dev-images.sed -i internal/controller/constants/images.go + sed -E -f ci/dev-images.sed -i internal/controller/constants/images.go GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint GOLANGCI_LINT_VERSION ?= v1.54.2 diff --git a/internal/controller/tsa/actions/generate_signer_test.go b/internal/controller/tsa/actions/generate_signer_test.go index 8bcdf5bcf..9949e8c51 100644 --- a/internal/controller/tsa/actions/generate_signer_test.go +++ b/internal/controller/tsa/actions/generate_signer_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/securesign/operator/test/e2e/support/tas/tsa" + . "github.com/onsi/gomega" rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/action" @@ -11,7 +13,6 @@ import ( "github.com/securesign/operator/internal/controller/constants" testAction "github.com/securesign/operator/internal/testing/action" common "github.com/securesign/operator/internal/testing/common/tsa" - "github.com/securesign/operator/test/e2e/support" "k8s.io/apimachinery/pkg/api/meta" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -183,7 +184,7 @@ func Test_SignerHandle(t *testing.T) { obj := []client.Object{} client := testAction.FakeClientBuilder().WithObjects(instance).Build() - secret := support.InitTsaSecrets(instance.Namespace, "tsa-test-secret") + secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") obj = append(obj, secret) return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), obj...) }, @@ -239,7 +240,7 @@ func Test_SignerHandle(t *testing.T) { } obj := []client.Object{} client := testAction.FakeClientBuilder().WithObjects(instance).Build() - secret := support.InitTsaSecrets(instance.Namespace, "tsa-test-secret") + secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") obj = append(obj, secret) return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), obj...) }, @@ -263,7 +264,7 @@ func Test_SignerHandle(t *testing.T) { setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { instance.Status.Conditions[0].Reason = constants.Pending client := testAction.FakeClientBuilder().WithObjects(instance).Build() - secret := support.InitTsaSecrets(instance.Namespace, "tsa-test-secret") + secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), secret) }, testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { @@ -332,7 +333,7 @@ func Test_SignerHandle(t *testing.T) { setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) { instance.Status.Conditions[0].Reason = constants.Pending client := testAction.FakeClientBuilder().WithObjects(instance).Build() - secret := support.InitTsaSecrets(instance.Namespace, "tsa-test-secret") + secret := tsa.CreateSecrets(instance.Namespace, "tsa-test-secret") return common.TsaTestSetup(instance, t, client, NewGenerateSignerAction(), secret) }, testCase: func(g Gomega, a action.Action[*rhtasv1alpha1.TimestampAuthority], client client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool { diff --git a/internal/controller/tsa/tsa_hot_update_test.go b/internal/controller/tsa/tsa_hot_update_test.go index 949eaf229..7237b14a9 100644 --- a/internal/controller/tsa/tsa_hot_update_test.go +++ b/internal/controller/tsa/tsa_hot_update_test.go @@ -20,6 +20,8 @@ import ( "context" "time" + "github.com/securesign/operator/test/e2e/support/tas/tsa" + k8sTest "github.com/securesign/operator/internal/testing/kubernetes" . "github.com/onsi/ginkgo/v2" @@ -27,7 +29,6 @@ import ( rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/constants" "github.com/securesign/operator/internal/controller/tsa/actions" - "github.com/securesign/operator/test/e2e/support" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -186,7 +187,7 @@ var _ = Describe("Timestamp Authority hot update", func() { }).Should(Equal(constants.Pending)) By("Creating new certificate chain and signer keys") - secret := support.InitTsaSecrets(Namespace, "tsa-test-secret") + secret := tsa.CreateSecrets(Namespace, "tsa-test-secret") Expect(k8sClient.Create(context.TODO(), secret)).NotTo(HaveOccurred()) By("Status field changed for cert chain") diff --git a/test/.DS_Store b/test/.DS_Store deleted file mode 100644 index 137a2d511af3725b1692a6cd5199f91262eebc22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMTWl0n7(U;$rE@Ho0SdG%tBVB!w8GL-?w6%qC`F*cw)6tJ%@VLDTGW_MA7 zsfk97NHprB_rwQdh(4HjB{A_qH1Po%qR|KAgHI;B_~0d8{xfH`rCeSx@e<}F=ltjY z&;Oq}|3BY9XU|#27&?l^D#jWZV=7fawK{5UP{g>X*A*p{)DQ*PGjAkkyE)?C&y8HC z9SNccL=lK05Je!0Koo)hLIh~f7Dc_uzAs0kHi|$Lf!i_y;`@-GDrhRCqml-n4r+oU z0MTj^2Zj2Y=nzeWG!@cONoj-%q$yEpitva5X-@J)sFw=qsHD=IAv}B_d@{ll3WBH8 z{E0xFAthC*}aOHtd*I$^3~^e+w%Nm@-mdF>Y6FFYOPwQ-k&+a8Pi)u50HieLZ);GRs-5zSDJl-Ez!AU|<<0sT}IJ9K$VlxJAeC0^5unP`S#p zTH~oxtCFi%wItftwx4cEoLb+SY)PzcTXXs}SLZaX+_*n`+#Yt^r=$l6zXEL9H6ERr zaL4fSc8-=OR6`GKuhc1TD5~b?Mx=*dRXpUpF4jLVIF!}2qG#?mZQZv@W?J`6FRRV) z$l30^>)cl`OXOsK-nEOv4voyr>vq0K%H}rP&R7qb-hgkO^hIyTa|?cFpg3TWt&nB4 zS;D?&#J60>J75+(qMV;c_G|rF3q|Ss*WtM?r&N@-F{0FrrRh7?( zB^Gj3d-sq*4(xFaF{2TT#A1oXs(Mh!I}0YYQp{t^mXm?r>yb0zGFPnRT=RO5(+ifS zylu70HKWhgbEX}N)~zSez5ONWaVXrpMWxlZ4CLM6Ve-e5;#fzgs%oD+AfCNj_xzp_ z(-~h?*G?MyY{)yN7tEeAVJx)i+O4X=dm_0o+HP3BTL?n3HxY{sipjPahG~%J4Rhyd zu}rK_)h?2aW;rE`P)#pFf?I7$Etj|o!rGKdO0Hg#@mB0vjjV-jVLR9m%QJ_S*%|gU zJIh{VZ?JRhJ$9a5V4t%u*q7`Q`<8vreq=wf-`SsF0Pa8?reZo`ScJt`f>vzACTzx5 zq_GEk(Sv_$G7w{5Z#w&OY@8AP`h>vg{7jO}mFoqv+ z1;5}|{H9bZHA=0bDluh&vRGNJw8`}=l@vNS?oZHB(nKcmCA`s7PTVM8;yb!_cK<8+ z^7T}aAv0&quAg&f)6#od*KACUgGj=zg0!m<9DsHb)C154Z~0oZ1*yar#QO-<=E;d% zlDSpMD6LlDR~-}DckzXBt%h(f*+qOwT$@6em+Z25b6is>)g-$jp5$5;B@z#8+v3e! zgDRP|d_C8yDWxR4iErT=6Im|#ZSl^yCK5#b`#^n_y~*Atpngg~z0AI0-?1y~XZ8nF z0_I#apb-lYCvYysGBl$F?O2O-*g#NCV+Xp>ja|r~kAOOWLF8b-#1XhSL2&g^B(RR+ z5j={=a2C(tSv-g51K7Tfx2}QVuE{XO#}hCtWeSe%I!8%T0qtbvh`Xf-R7tA_asJnmaOzA`nI3{}BOHr!(meTIfgc5S?vP>QEta0I5eyTod6qA$7#XO3Q&OMsNTWytWHgU2haSh?%S^{5wXc4?Gtr~$U&0Wx{vHPBK|^99A~V?fcUmL=s1IqC6dZHDnSc%Pdx&O;&dDB*%?`;jU>Ud@1M+-u zktBu&8;$DdK&GAmz!uC(pv&uB#<&JUgN;UbAWVk>btp4W45q_jcTK&}V53oo6Elww zW?E+Eg~C+p_`a*E6ALxk+%jMpxXr+!E_USo|KiW@|Jy;fWErpw>=gs7brD@mFeG!g zZVgV}wJy>}Bq_39qfvz*GuN?F", 0)) - ctlogGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: ctlog.DeploymentName}) - Expect(ctlogGeneration).Should(BeNumerically(">", 0)) - fulcioGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: fulcio.DeploymentName}) - Expect(fulcioGeneration).Should(BeNumerically(">", 0)) - - Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(securesign), securesign)).To(Succeed()) - securesign.Spec.Fulcio.Certificate = v1alpha1.FulcioCert{ - PrivateKeyRef: &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "my-fulcio-secret", - }, - Key: "private", - }, - PrivateKeyPasswordRef: &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "my-fulcio-secret", - }, - Key: "password", - }, - CARef: &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "my-fulcio-secret", - }, - Key: "cert", - }, - } - - Expect(cli.Update(ctx, securesign)).To(Succeed()) - Eventually(func() string { - fulcio := tas.GetFulcio(ctx, cli, namespace.Name, securesign.Name)() - return meta.FindStatusCondition(fulcio.Status.Conditions, constants.Ready).Reason - }).Should(Equal(constants.Pending)) - - Expect(cli.Create(ctx, support.InitFulcioSecret(namespace.Name, "my-fulcio-secret"))).To(Succeed()) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tuf.DeploymentName}) - }).Should(BeNumerically(">", tufGeneration)) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: ctlog.DeploymentName}) - }).Should(BeNumerically(">", ctlogGeneration)) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: fulcio.DeploymentName}) - }).Should(BeNumerically(">", fulcioGeneration)) - - tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyFulcio(ctx, cli, namespace.Name, securesign.Name) - }) - - It("Verify new configuration", func() { - var fulcioPod *v1.Pod - Eventually(func(g Gomega) { - fulcioPod = tas.GetFulcioServerPod(ctx, cli, namespace.Name)() - g.Expect(fulcioPod).ToNot(BeNil()) - }).Should(Succeed()) - Expect(fulcioPod.Spec.Volumes).To(ContainElements(And( - WithTransform(func(v v1.Volume) string { return v.Name }, Equal("fulcio-cert")), - WithTransform(func(v v1.Volume) string { return v.VolumeSource.Projected.Sources[0].Secret.Name }, Equal("my-fulcio-secret"))))) - - }) - - }) - - Describe("Fulcio Config update", func() { - It("Pods are restarted after update", func() { - By("Storing current deployment observed generations") - fulcioGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: fulcio.DeploymentName}) - Expect(fulcioGeneration).Should(BeNumerically(">", 0)) - - Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(securesign), securesign)).To(Succeed()) - securesign.Spec.Fulcio.Config.OIDCIssuers = []v1alpha1.OIDCIssuer{ - { - ClientID: support.OidcClientID(), - IssuerURL: support.OidcIssuerUrl(), - Issuer: support.OidcIssuerUrl(), - Type: "email", - }, - { - ClientID: "fake", - IssuerURL: "fake", - Issuer: "fake", - Type: "email", - }, - } - Expect(cli.Update(ctx, securesign)).To(Succeed()) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: fulcio.DeploymentName}) - }).Should(BeNumerically(">", fulcioGeneration)) - - tas.VerifyFulcio(ctx, cli, namespace.Name, securesign.Name) - }) - It("Verify new configuration", func() { - var fulcio *v1alpha1.Fulcio - var fulcioPod *v1.Pod - Eventually(func(g Gomega) { - fulcio = tas.GetFulcio(ctx, cli, namespace.Name, securesign.Name)() - g.Expect(fulcio).NotTo(BeNil()) - fulcioPod = tas.GetFulcioServerPod(ctx, cli, namespace.Name)() - g.Expect(fulcioPod).NotTo(BeNil()) - }).Should(Succeed()) - - Expect(fulcioPod.Spec.Volumes[0].VolumeSource.ConfigMap.Name).To(Equal(fulcio.Status.ServerConfigRef.Name)) - - cm := &v1.ConfigMap{} - Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: fulcio.Status.ServerConfigRef.Name}, cm)).To(Succeed()) - config := &actions.FulcioMapConfig{} - Expect(json.Unmarshal([]byte(cm.Data["config.json"]), config)).To(Succeed()) - Expect(config.OIDCIssuers).To(HaveKey("fake")) - }) - }) - - Describe("Inject Rekor signer", func() { - It("Pods are restarted after update", func() { - By("Storing current deployment observed generations") - tufGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tuf.DeploymentName}) - Expect(tufGeneration).Should(BeNumerically(">", 0)) - rekorGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: rekor.ServerComponentName}) - Expect(rekorGeneration).Should(BeNumerically(">", 0)) - - Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(securesign), securesign)).To(Succeed()) - securesign.Spec.Rekor.Signer = v1alpha1.RekorSigner{ - KMS: "secret", - KeyRef: &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "my-rekor-secret", - }, - Key: "private", - }, - } - - Expect(cli.Update(ctx, securesign)).To(Succeed()) - Eventually(func() string { - rekor := tas.GetRekor(ctx, cli, namespace.Name, securesign.Name)() - return meta.FindStatusCondition(rekor.Status.Conditions, constants.Ready).Reason - }).Should(Equal(constants.Pending)) - - Expect(cli.Create(ctx, support.InitRekorSecret(namespace.Name, "my-rekor-secret"))).To(Succeed()) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tuf.DeploymentName}) - }).Should(BeNumerically(">", tufGeneration)) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: rekor.ServerComponentName}) - }).Should(BeNumerically(">", rekorGeneration)) - - tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyRekor(ctx, cli, namespace.Name, securesign.Name) - }) - It("Verify new configuration", func() { - var rekor *v1.Pod - Eventually(func(g Gomega) { - rekor = tas.GetRekorServerPod(ctx, cli, namespace.Name)() - g.Expect(rekor).NotTo(BeNil()) - }).Should(Succeed()) - Expect(rekor.Spec.Volumes).To(ContainElements(And( - WithTransform(func(v v1.Volume) string { return v.Name }, Equal("rekor-private-key-volume")), - WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal("my-rekor-secret"))))) - - }) - }) - - Describe("Inject CTL secret", func() { - It("Pods are restarted after update", func() { - By("Storing current deployment observed generations") - tufGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tuf.DeploymentName}) - Expect(tufGeneration).Should(BeNumerically(">", 0)) - ctlogGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: ctlog.DeploymentName}) - Expect(ctlogGeneration).Should(BeNumerically(">", 0)) - - Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(securesign), securesign)).To(Succeed()) - securesign.Spec.Ctlog.PrivateKeyRef = &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "my-ctlog-secret", - }, - Key: "private", - } - securesign.Spec.Ctlog.PublicKeyRef = &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "my-ctlog-secret", - }, - Key: "public", - } - - Expect(cli.Update(ctx, securesign)).To(Succeed()) - Eventually(func() string { - ctl := tas.GetCTLog(ctx, cli, namespace.Name, securesign.Name)() - return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason - }).Should(Equal(constants.Creating)) - Expect(cli.Create(ctx, support.InitCTSecret(namespace.Name, "my-ctlog-secret"))).To(Succeed()) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tuf.DeploymentName}) - }).Should(BeNumerically(">", tufGeneration)) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: ctlog.DeploymentName}) - }).Should(BeNumerically(">", ctlogGeneration)) - - tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyCTLog(ctx, cli, namespace.Name, securesign.Name) - }) - It("Verify new configuration", func() { - var ctl *v1alpha1.CTlog - var ctlPod *v1.Pod - Eventually(func(g Gomega) { - ctl = tas.GetCTLog(ctx, cli, namespace.Name, securesign.Name)() - g.Expect(ctl).NotTo(BeNil()) - ctlPod = tas.GetCTLogServerPod(ctx, cli, namespace.Name)() - g.Expect(ctlPod).NotTo(BeNil()) - }).Should(Succeed()) - - Expect(ctlPod.Spec.Volumes).To(ContainElements(And( - WithTransform(func(v v1.Volume) string { return v.Name }, Equal("keys")), - WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal(ctl.Status.ServerConfigRef.Name))))) - - existing := &v1.Secret{} - expected := &v1.Secret{} - Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: ctl.Status.ServerConfigRef.Name}, existing)).To(Succeed()) - Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: "my-ctlog-secret"}, expected)).To(Succeed()) - - Expect(existing.Data["public"]).To(Equal(existing.Data["public"])) - }) - }) - - Describe("Inject tsa signer and certificate chain", func() { - It("Pods are restarted after update", func() { - By("Storing current deployment observed generations") - tufGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tuf.DeploymentName}) - Expect(tufGeneration).Should(BeNumerically(">", 0)) - tsaGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tsa.DeploymentName}) - Expect(tsaGeneration).Should(BeNumerically(">", 0)) - Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(securesign), securesign)).To(Succeed()) - securesign.Spec.TimestampAuthority.Signer = v1alpha1.TimestampAuthoritySigner{ - CertificateChain: v1alpha1.CertificateChain{ - CertificateChainRef: &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "test-tsa-secret", - }, - Key: "certificateChain", - }, - }, - File: &v1alpha1.File{ - PrivateKeyRef: &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "test-tsa-secret", - }, - Key: "leafPrivateKey", - }, - PasswordRef: &v1alpha1.SecretKeySelector{ - LocalObjectReference: v1alpha1.LocalObjectReference{ - Name: "test-tsa-secret", - }, - Key: "leafPrivateKeyPassword", - }, - }, - } - Expect(cli.Update(ctx, securesign)).To(Succeed()) - Eventually(func() string { - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() - return meta.FindStatusCondition(tsa.Status.Conditions, constants.Ready).Reason - }).Should(Equal(constants.Pending)) - - Expect(cli.Create(ctx, support.InitTsaSecrets(namespace.Name, "test-tsa-secret"))).To(Succeed()) - - tsaPod := tas.GetTSAServerPod(ctx, cli, namespace.Name)() - Eventually(func() error { - return cli.Get(ctx, runtimeCli.ObjectKeyFromObject(tsaPod), &v1.Pod{}) - }).Should(HaveOccurred()) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tuf.DeploymentName}) - }).Should(BeNumerically(">", tufGeneration)) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tsa.DeploymentName}) - }).Should(BeNumerically(">", tsaGeneration)) - - tas.VerifyTSA(ctx, cli, namespace.Name, securesign.Name) - }) - It("Verify new configuration", func() { - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() - tsaPod := tas.GetTSAServerPod(ctx, cli, namespace.Name)() - - Expect(tsaPod.Spec.Volumes).To(ContainElement(And( - WithTransform(func(v v1.Volume) string { return v.Name }, Equal("tsa-cert-chain")), - WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal("test-tsa-secret")), - ))) - - Expect(tsaPod.Spec.Volumes).To(ContainElement(And( - WithTransform(func(v v1.Volume) string { return v.Name }, Equal("tsa-file-signer-config")), - WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal("test-tsa-secret")), - ))) - - certChainSecret := &v1.Secret{} - privateKeySecret := &v1.Secret{} - expectedSecret := &v1.Secret{} - Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: tsa.Status.Signer.CertificateChain.CertificateChainRef.Name}, certChainSecret)).To(Succeed()) - Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: tsa.Status.Signer.File.PrivateKeyRef.Name}, privateKeySecret)).To(Succeed()) - Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: "test-tsa-secret"}, expectedSecret)).To(Succeed()) - }) - }) - - Describe("Update NTP config", func() { - It("Pods are restarted after update", func() { - By("Storing current deployment observed generations") - tsaGeneration := getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tsa.DeploymentName}) - Expect(tsaGeneration).Should(BeNumerically(">", 0)) - - Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(securesign), securesign)).To(Succeed()) - securesign.Spec.TimestampAuthority.NTPMonitoring = v1alpha1.NTPMonitoring{ - Enabled: true, - Config: &v1alpha1.NtpMonitoringConfig{ - RequestAttempts: 3, - RequestTimeout: 5, - NumServers: 4, - ServerThreshold: 3, - MaxTimeDelta: 6, - Period: 40, - Servers: []string{"time.apple.com", "time.google.com", "time-a-b.nist.gov", "time-b-b.nist.gov", "gbg1.ntp.se"}, - }, - } - Expect(cli.Update(ctx, securesign)).To(Succeed()) - - Eventually(func() int64 { - return getDeploymentGeneration(types.NamespacedName{Namespace: namespace.Name, Name: tsa.DeploymentName}) - }).Should(BeNumerically(">", tsaGeneration)) - - tas.VerifyTSA(ctx, cli, namespace.Name, securesign.Name) - }) - It("Verify new configuration", func() { - var tsa *v1alpha1.TimestampAuthority - var tsaPod *v1.Pod - Eventually(func(g Gomega) { - tsa = tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() - g.Expect(tsa).NotTo(BeNil()) - tsaPod = tas.GetTSAServerPod(ctx, cli, namespace.Name)() - g.Expect(tsaPod).NotTo(BeNil()) - }).Should(Succeed()) - - Expect(tsaPod.Spec.Volumes).To(ContainElements(And( - WithTransform(func(v v1.Volume) string { return v.Name }, Equal("ntp-config")), - WithTransform(func(v v1.Volume) string { return v.VolumeSource.ConfigMap.Name }, Equal(tsa.Status.NTPMonitoring.Config.NtpConfigRef.Name))))) - - cm := &v1.ConfigMap{} - Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: tsa.Status.NTPMonitoring.Config.NtpConfigRef.Name}, cm)).To(Succeed()) - config := &tsaUtils.NtpConfig{} - Expect(yaml.Unmarshal([]byte(cm.Data["ntp-config.yaml"]), config)).To(Succeed()) - Expect(config.Period).To(Equal(40)) - }) - }) - - It("Use cosign cli", func() { - fulcio := tas.GetFulcio(ctx, cli, namespace.Name, securesign.Name)() - Expect(fulcio).ToNot(BeNil()) - - rekor := tas.GetRekor(ctx, cli, namespace.Name, securesign.Name)() - Expect(rekor).ToNot(BeNil()) - - tuf := tas.GetTuf(ctx, cli, namespace.Name, securesign.Name)() - Expect(tuf).ToNot(BeNil()) - - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() - Expect(tsa).ToNot(BeNil()) - - Eventually(func() error { - return tas.GetTSACertificateChain(ctx, cli, tsa.Namespace, tsa.Name, tsa.Status.Url) - }).Should(Succeed()) - - oidcToken, err := support.OidcToken(ctx) - Expect(err).ToNot(HaveOccurred()) - Expect(oidcToken).ToNot(BeEmpty()) - - // sleep for a while to be sure everything has settled down - time.Sleep(time.Duration(10) * time.Second) - - Expect(clients.Execute("cosign", "initialize", "--mirror="+tuf.Status.Url, "--root="+tuf.Status.Url+"/root.json")).To(Succeed()) - - Expect(clients.Execute( - "cosign", "sign", "-y", - "--fulcio-url="+fulcio.Status.Url, - "--rekor-url="+rekor.Status.Url, - "--timestamp-server-url="+tsa.Status.Url+"/api/v1/timestamp", - "--oidc-issuer="+support.OidcIssuerUrl(), - "--oidc-client-id="+support.OidcClientID(), - "--identity-token="+oidcToken, - targetImageName, - )).To(Succeed()) - - Expect(clients.Execute( - "cosign", "verify", - "--rekor-url="+rekor.Status.Url, - "--timestamp-certificate-chain=ts_chain.pem", - "--certificate-identity-regexp", ".*@redhat", - "--certificate-oidc-issuer-regexp", ".*keycloak.*", - targetImageName, - )).To(Succeed()) - }) -}) diff --git a/test/e2e/key_autodiscovery_test.go b/test/e2e/key_autodiscovery_test.go index 7b586c258..0316c35dd 100644 --- a/test/e2e/key_autodiscovery_test.go +++ b/test/e2e/key_autodiscovery_test.go @@ -4,17 +4,22 @@ package e2e import ( "context" - "time" - "github.com/securesign/operator/internal/controller/common/utils" + "github.com/securesign/operator/test/e2e/support/tas/tsa" + + "k8s.io/utils/ptr" + + "github.com/securesign/operator/test/e2e/support/tas" + "github.com/securesign/operator/test/e2e/support/tas/ctlog" + "github.com/securesign/operator/test/e2e/support/tas/fulcio" + "github.com/securesign/operator/test/e2e/support/tas/rekor" + "github.com/securesign/operator/test/e2e/support/tas/tuf" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/test/e2e/support" - "github.com/securesign/operator/test/e2e/support/tas" - clients "github.com/securesign/operator/test/e2e/support/tas/cli" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -25,7 +30,7 @@ var _ = Describe("Securesign key autodiscovery test", Ordered, func() { var targetImageName string var namespace *v1.Namespace - var securesign *v1alpha1.Securesign + var s *v1alpha1.Securesign AfterEach(func() { if CurrentSpecReport().Failed() && support.IsCIEnvironment() { @@ -39,7 +44,7 @@ var _ = Describe("Securesign key autodiscovery test", Ordered, func() { _ = cli.Delete(ctx, namespace) }) - securesign = &v1alpha1.Securesign{ + s = &v1alpha1.Securesign{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace.Name, Name: "test", @@ -118,7 +123,10 @@ var _ = Describe("Securesign key autodiscovery test", Ordered, func() { }, }, Trillian: v1alpha1.TrillianSpec{Db: v1alpha1.TrillianDB{ - Create: utils.Pointer(true), + Create: ptr.To(true), + Pvc: v1alpha1.Pvc{ + Retain: ptr.To(false), + }, }}, TimestampAuthority: v1alpha1.TimestampAuthoritySpec{ ExternalAccess: v1alpha1.ExternalAccess{ @@ -171,50 +179,45 @@ var _ = Describe("Securesign key autodiscovery test", Ordered, func() { Describe("Install with provided certificates", func() { BeforeAll(func() { - Expect(cli.Create(ctx, support.InitCTSecret(namespace.Name, "my-ctlog-secret"))).To(Succeed()) - Expect(cli.Create(ctx, support.InitFulcioSecret(namespace.Name, "my-fulcio-secret"))).To(Succeed()) - Expect(cli.Create(ctx, support.InitRekorSecret(namespace.Name, "my-rekor-secret"))).To(Succeed()) - Expect(cli.Create(ctx, support.InitTsaSecrets(namespace.Name, "test-tsa-secret"))).To(Succeed()) - Expect(cli.Create(ctx, securesign)).To(Succeed()) + Expect(cli.Create(ctx, ctlog.CreateSecret(namespace.Name, "my-ctlog-secret"))).To(Succeed()) + Expect(cli.Create(ctx, fulcio.CreateSecret(namespace.Name, "my-fulcio-secret"))).To(Succeed()) + Expect(cli.Create(ctx, rekor.CreateSecret(namespace.Name, "my-rekor-secret"))).To(Succeed()) + Expect(cli.Create(ctx, tsa.CreateSecrets(namespace.Name, "test-tsa-secret"))).To(Succeed()) + Expect(cli.Create(ctx, s)).To(Succeed()) }) It("All components are running", func() { - tas.VerifySecuresign(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyRekor(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyFulcio(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyCTLog(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name, true) - tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyTSA(ctx, cli, namespace.Name, securesign.Name) + tas.VerifyAllComponents(ctx, cli, s, true) }) It("Verify TUF keys", func() { - tuf := tas.GetTuf(ctx, cli, namespace.Name, securesign.Name)() - Expect(tuf.Status.Keys).To(HaveEach(WithTransform(func(k v1alpha1.TufKey) string { return k.SecretRef.Name }, Not(BeEmpty())))) + t := tuf.Get(ctx, cli, namespace.Name, s.Name)() + Expect(t).ToNot(BeNil()) + Expect(t.Status.Keys).To(HaveEach(WithTransform(func(k v1alpha1.TufKey) string { return k.SecretRef.Name }, Not(BeEmpty())))) var ( expected, actual []byte err error ) - for _, k := range tuf.Status.Keys { + for _, k := range t.Status.Keys { actual, err = kubernetes.GetSecretData(cli, namespace.Name, k.SecretRef) Expect(err).To(Not(HaveOccurred())) switch k.Name { case "fulcio_v1.crt.pem": - expected, err = kubernetes.GetSecretData(cli, namespace.Name, securesign.Spec.Fulcio.Certificate.CARef) + expected, err = kubernetes.GetSecretData(cli, namespace.Name, s.Spec.Fulcio.Certificate.CARef) Expect(err).To(Not(HaveOccurred())) case "rekor.pub": - expectedKeyRef := securesign.Spec.Rekor.Signer.KeyRef.DeepCopy() + expectedKeyRef := s.Spec.Rekor.Signer.KeyRef.DeepCopy() expectedKeyRef.Key = "public" expected, err = kubernetes.GetSecretData(cli, namespace.Name, expectedKeyRef) Expect(err).To(Not(HaveOccurred())) case "ctfe.pub": - expectedKeyRef := securesign.Spec.Ctlog.PrivateKeyRef.DeepCopy() + expectedKeyRef := s.Spec.Ctlog.PrivateKeyRef.DeepCopy() expectedKeyRef.Key = "public" expected, err = kubernetes.GetSecretData(cli, namespace.Name, expectedKeyRef) Expect(err).To(Not(HaveOccurred())) case "tsa.certchain.pem": - expectedKeyRef := securesign.Spec.TimestampAuthority.Signer.CertificateChain.CertificateChainRef.DeepCopy() + expectedKeyRef := s.Spec.TimestampAuthority.Signer.CertificateChain.CertificateChainRef.DeepCopy() expectedKeyRef.Key = "certificateChain" expected, err = kubernetes.GetSecretData(cli, namespace.Name, expectedKeyRef) Expect(err).To(Not(HaveOccurred())) @@ -224,50 +227,7 @@ var _ = Describe("Securesign key autodiscovery test", Ordered, func() { }) It("Use cosign cli", func() { - fulcio := tas.GetFulcio(ctx, cli, namespace.Name, securesign.Name)() - Expect(fulcio).ToNot(BeNil()) - - rekor := tas.GetRekor(ctx, cli, namespace.Name, securesign.Name)() - Expect(rekor).ToNot(BeNil()) - - tuf := tas.GetTuf(ctx, cli, namespace.Name, securesign.Name)() - Expect(tuf).ToNot(BeNil()) - - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() - Expect(tsa).ToNot(BeNil()) - - Eventually(func() error { - return tas.GetTSACertificateChain(ctx, cli, tsa.Namespace, tsa.Name, tsa.Status.Url) - }).Should(Succeed()) - - oidcToken, err := support.OidcToken(ctx) - Expect(err).ToNot(HaveOccurred()) - Expect(oidcToken).ToNot(BeEmpty()) - - // sleep for a while to be sure everything has settled down - time.Sleep(time.Duration(10) * time.Second) - - Expect(clients.Execute("cosign", "initialize", "--mirror="+tuf.Status.Url, "--root="+tuf.Status.Url+"/root.json")).To(Succeed()) - - Expect(clients.Execute( - "cosign", "sign", "-y", - "--fulcio-url="+fulcio.Status.Url, - "--rekor-url="+rekor.Status.Url, - "--timestamp-server-url="+tsa.Status.Url+"/api/v1/timestamp", - "--oidc-issuer="+support.OidcIssuerUrl(), - "--oidc-client-id="+support.OidcClientID(), - "--identity-token="+oidcToken, - targetImageName, - )).To(Succeed()) - - Expect(clients.Execute( - "cosign", "verify", - "--rekor-url="+rekor.Status.Url, - "--timestamp-certificate-chain=ts_chain.pem", - "--certificate-identity-regexp", ".*@redhat", - "--certificate-oidc-issuer-regexp", ".*keycloak.*", - targetImageName, - )).To(Succeed()) + tas.VerifyByCosign(ctx, cli, s, targetImageName) }) }) }) diff --git a/test/e2e/provided_certs_test.go b/test/e2e/provided_certs_test.go index 8a59b9df9..747b52488 100644 --- a/test/e2e/provided_certs_test.go +++ b/test/e2e/provided_certs_test.go @@ -4,16 +4,20 @@ package e2e import ( "context" - "time" - "github.com/securesign/operator/internal/controller/common/utils" + "github.com/securesign/operator/test/e2e/support/tas/tsa" + + "k8s.io/utils/ptr" + + "github.com/securesign/operator/test/e2e/support/tas" + "github.com/securesign/operator/test/e2e/support/tas/ctlog" + "github.com/securesign/operator/test/e2e/support/tas/fulcio" + "github.com/securesign/operator/test/e2e/support/tas/rekor" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" "github.com/securesign/operator/test/e2e/support" - "github.com/securesign/operator/test/e2e/support/tas" - clients "github.com/securesign/operator/test/e2e/support/tas/cli" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -24,7 +28,7 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { var targetImageName string var namespace *v1.Namespace - var securesign *v1alpha1.Securesign + var s *v1alpha1.Securesign AfterEach(func() { if CurrentSpecReport().Failed() && support.IsCIEnvironment() { @@ -38,7 +42,7 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { _ = cli.Delete(ctx, namespace) }) - securesign = &v1alpha1.Securesign{ + s = &v1alpha1.Securesign{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace.Name, Name: "test", @@ -155,7 +159,10 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { }, }, Trillian: v1alpha1.TrillianSpec{Db: v1alpha1.TrillianDB{ - Create: utils.Pointer(true), + Create: ptr.To(true), + Pvc: v1alpha1.Pvc{ + Retain: ptr.To(false), + }, }}, TimestampAuthority: v1alpha1.TimestampAuthoritySpec{ ExternalAccess: v1alpha1.ExternalAccess{ @@ -208,16 +215,16 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { Describe("Install with provided certificates", func() { BeforeAll(func() { - Expect(cli.Create(ctx, support.InitCTSecret(namespace.Name, "my-ctlog-secret"))).To(Succeed()) - Expect(cli.Create(ctx, support.InitFulcioSecret(namespace.Name, "my-fulcio-secret"))).To(Succeed()) - Expect(cli.Create(ctx, support.InitRekorSecret(namespace.Name, "my-rekor-secret"))).To(Succeed()) - Expect(cli.Create(ctx, support.InitTsaSecrets(namespace.Name, "test-tsa-secret"))).To(Succeed()) - Expect(cli.Create(ctx, securesign)).To(Succeed()) + Expect(cli.Create(ctx, ctlog.CreateSecret(namespace.Name, "my-ctlog-secret"))).To(Succeed()) + Expect(cli.Create(ctx, fulcio.CreateSecret(namespace.Name, "my-fulcio-secret"))).To(Succeed()) + Expect(cli.Create(ctx, rekor.CreateSecret(namespace.Name, "my-rekor-secret"))).To(Succeed()) + Expect(cli.Create(ctx, tsa.CreateSecrets(namespace.Name, "test-tsa-secret"))).To(Succeed()) + Expect(cli.Create(ctx, s)).To(Succeed()) }) - It("fulcio is running with mounted certs", func() { - tas.VerifyFulcio(ctx, cli, namespace.Name, securesign.Name) - server := tas.GetFulcioServerPod(ctx, cli, namespace.Name)() + It("Fulcio is running with mounted certs", func() { + fulcio.Verify(ctx, cli, namespace.Name, s.Name) + server := fulcio.GetServerPod(ctx, cli, namespace.Name)() Expect(server).NotTo(BeNil()) sp := []v1.SecretProjection{} @@ -238,9 +245,9 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { }) - It("rekor is running with mounted certs", func() { - tas.VerifyRekor(ctx, cli, namespace.Name, securesign.Name) - server := tas.GetRekorServerPod(ctx, cli, namespace.Name)() + It("Rekor is running with mounted certs", func() { + rekor.Verify(ctx, cli, namespace.Name, s.Name) + server := rekor.GetServerPod(ctx, cli, namespace.Name)() Expect(server).NotTo(BeNil()) Expect(server.Spec.Volumes).To( ContainElement( @@ -255,8 +262,8 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { }) It("tsa is running with mounted certs", func() { - tas.VerifyTSA(ctx, cli, namespace.Name, securesign.Name) - tsa := tas.GetTSAServerPod(ctx, cli, namespace.Name)() + tsa.Verify(ctx, cli, namespace.Name, s.Name) + tsa := tsa.GetServerPod(ctx, cli, namespace.Name)() Expect(tsa).NotTo(BeNil()) Expect(tsa.Spec.Volumes).To( ContainElement( @@ -270,58 +277,11 @@ var _ = Describe("Securesign install with provided certs", Ordered, func() { }) It("All other components are running", func() { - tas.VerifySecuresign(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyCTLog(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyTrillian(ctx, cli, namespace.Name, securesign.Name, true) - tas.VerifyTuf(ctx, cli, namespace.Name, securesign.Name) - tas.VerifyTSA(ctx, cli, namespace.Name, securesign.Name) + tas.VerifyAllComponents(ctx, cli, s, true) }) It("Use cosign cli", func() { - fulcio := tas.GetFulcio(ctx, cli, namespace.Name, securesign.Name)() - Expect(fulcio).ToNot(BeNil()) - - rekor := tas.GetRekor(ctx, cli, namespace.Name, securesign.Name)() - Expect(rekor).ToNot(BeNil()) - - tuf := tas.GetTuf(ctx, cli, namespace.Name, securesign.Name)() - Expect(tuf).ToNot(BeNil()) - - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() - Expect(tsa).ToNot(BeNil()) - - Eventually(func() error { - return tas.GetTSACertificateChain(ctx, cli, tsa.Namespace, tsa.Name, tsa.Status.Url) - }).Should(Succeed()) - - oidcToken, err := support.OidcToken(ctx) - Expect(err).ToNot(HaveOccurred()) - Expect(oidcToken).ToNot(BeEmpty()) - - // sleep for a while to be sure everything has settled down - time.Sleep(time.Duration(10) * time.Second) - - Expect(clients.Execute("cosign", "initialize", "--mirror="+tuf.Status.Url, "--root="+tuf.Status.Url+"/root.json")).To(Succeed()) - - Expect(clients.Execute( - "cosign", "sign", "-y", - "--fulcio-url="+fulcio.Status.Url, - "--rekor-url="+rekor.Status.Url, - "--timestamp-server-url="+tsa.Status.Url+"/api/v1/timestamp", - "--oidc-issuer="+support.OidcIssuerUrl(), - "--oidc-client-id="+support.OidcClientID(), - "--identity-token="+oidcToken, - targetImageName, - )).To(Succeed()) - - Expect(clients.Execute( - "cosign", "verify", - "--rekor-url="+rekor.Status.Url, - "--timestamp-certificate-chain=ts_chain.pem", - "--certificate-identity-regexp", ".*@redhat", - "--certificate-oidc-issuer-regexp", ".*keycloak.*", - targetImageName, - )).To(Succeed()) + tas.VerifyByCosign(ctx, cli, s, targetImageName) }) }) }) diff --git a/test/e2e/support/cert.go b/test/e2e/support/cert.go new file mode 100644 index 000000000..4e6a59c21 --- /dev/null +++ b/test/e2e/support/cert.go @@ -0,0 +1,90 @@ +package support + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "time" +) + +const CertPassword = "LetMeIn123" + +func CreateCertificates(passwordProtected bool) ([]byte, []byte, []byte, error) { + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, nil, err + } + + // private + privateKeyBytes, err := x509.MarshalECPrivateKey(key) + if err != nil { + return nil, nil, nil, err + } + var block *pem.Block + if passwordProtected { + block, err = x509.EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", privateKeyBytes, []byte(CertPassword), x509.PEMCipher3DES) //nolint:staticcheck + if err != nil { + return nil, nil, nil, err + } + } else { + block = &pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: privateKeyBytes, + } + } + privateKeyPem := pem.EncodeToMemory(block) + + // public key + publicKeyBytes, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + if err != nil { + return nil, nil, nil, err + } + publicKeyPem := pem.EncodeToMemory( + &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyBytes, + }, + ) + + notBefore := time.Now() + notAfter := notBefore.Add(365 * 24 * 10 * time.Hour) + + issuer := pkix.Name{ + CommonName: "local", + Country: []string{"CR"}, + Organization: []string{"RedHat"}, + Province: []string{"Czech Republic"}, + Locality: []string{"Brno"}, + OrganizationalUnit: []string{"QE"}, + } + //Create certificate templet + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: issuer, + SignatureAlgorithm: x509.ECDSAWithSHA256, + NotBefore: notBefore, + NotAfter: notAfter, + BasicConstraintsValid: true, + IsCA: true, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + Issuer: issuer, + } + //Create certificate using templet + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) + if err != nil { + return nil, nil, nil, err + + } + //pem encoding of certificate + root := pem.EncodeToMemory( + &pem.Block{ + Type: "CERTIFICATE", + Bytes: derBytes, + }, + ) + return publicKeyPem, privateKeyPem, root, err +} diff --git a/test/e2e/support/common.go b/test/e2e/support/common.go index dfb7fd320..c64935dec 100644 --- a/test/e2e/support/common.go +++ b/test/e2e/support/common.go @@ -2,23 +2,14 @@ package support import ( "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/pem" "fmt" "log" - "math/big" "os" "path/filepath" "reflect" "regexp" "strconv" "strings" - "time" v12 "k8s.io/api/apps/v1" v13 "k8s.io/api/batch/v1" @@ -31,7 +22,6 @@ import ( "github.com/onsi/ginkgo/v2/dsl/core" . "github.com/onsi/gomega" "github.com/securesign/operator/api/v1alpha1" - tsaUtils "github.com/securesign/operator/internal/controller/tsa/utils" "gopkg.in/yaml.v2" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -40,10 +30,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - CertPassword = "LetMeIn123" -) - func IsCIEnvironment() bool { if val, present := os.LookupEnv("CI"); present { b, _ := strconv.ParseBool(val) @@ -189,267 +175,3 @@ func toYAMLNoManagedFields(value runtime.Object) string { return fmt.Sprintf("%s\n", out) } - -func InitFulcioSecret(ns string, name string) *v1.Secret { - public, private, root, err := InitCertificates(true) - if err != nil { - return nil - } - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Data: map[string][]byte{ - "password": []byte(CertPassword), - "private": private, - "public": public, - "cert": root, - }, - } -} - -func InitRekorSecret(ns string, name string) *v1.Secret { - public, private, _, err := InitCertificates(false) - if err != nil { - return nil - } - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Data: map[string][]byte{ - "private": private, - "public": public, - }, - } -} - -func InitCTSecret(ns string, name string) *v1.Secret { - public, private, _, err := InitCertificates(false) - if err != nil { - return nil - } - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Data: map[string][]byte{ - "private": private, - "public": public, - }, - } -} - -func InitCertificates(passwordProtected bool) ([]byte, []byte, []byte, error) { - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, nil, nil, err - } - - // private - privateKeyBytes, err := x509.MarshalECPrivateKey(key) - if err != nil { - return nil, nil, nil, err - } - var block *pem.Block - if passwordProtected { - block, err = x509.EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", privateKeyBytes, []byte(CertPassword), x509.PEMCipher3DES) //nolint:staticcheck - if err != nil { - return nil, nil, nil, err - } - } else { - block = &pem.Block{ - Type: "EC PRIVATE KEY", - Bytes: privateKeyBytes, - } - } - privateKeyPem := pem.EncodeToMemory(block) - - // public key - publicKeyBytes, err := x509.MarshalPKIXPublicKey(&key.PublicKey) - if err != nil { - return nil, nil, nil, err - } - publicKeyPem := pem.EncodeToMemory( - &pem.Block{ - Type: "PUBLIC KEY", - Bytes: publicKeyBytes, - }, - ) - - notBefore := time.Now() - notAfter := notBefore.Add(365 * 24 * 10 * time.Hour) - - issuer := pkix.Name{ - CommonName: "local", - Country: []string{"CR"}, - Organization: []string{"RedHat"}, - Province: []string{"Czech Republic"}, - Locality: []string{"Brno"}, - OrganizationalUnit: []string{"QE"}, - } - //Create certificate templet - template := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: issuer, - SignatureAlgorithm: x509.ECDSAWithSHA256, - NotBefore: notBefore, - NotAfter: notAfter, - BasicConstraintsValid: true, - IsCA: true, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - Issuer: issuer, - } - //Create certificate using templet - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) - if err != nil { - return nil, nil, nil, err - - } - //pem encoding of certificate - root := pem.EncodeToMemory( - &pem.Block{ - Type: "CERTIFICATE", - Bytes: derBytes, - }, - ) - return publicKeyPem, privateKeyPem, root, err -} - -func InitTsaSecrets(ns string, name string) *v1.Secret { - config := &tsaUtils.TsaCertChainConfig{ - RootPrivateKeyPassword: []byte(CertPassword), - IntermediatePrivateKeyPasswords: [][]byte{[]byte(CertPassword)}, - LeafPrivateKeyPassword: []byte(CertPassword), - } - _, rootPrivateKey, rootCA, err := InitCertificates(true) - if err != nil { - return nil - } - config.RootPrivateKey = rootPrivateKey - - intermediatePublicKey, intermediatePrivateKey, _, err := InitCertificates(true) - if err != nil { - return nil - } - config.IntermediatePrivateKeys = append(config.IntermediatePrivateKeys, intermediatePrivateKey) - - leafPublicKey, leafPrivateKey, _, err := InitCertificates(true) - if err != nil { - return nil - } - config.LeafPrivateKey = leafPrivateKey - - notBefore := time.Now() - notAfter := notBefore.Add(365 * 24 * 10 * time.Hour) - oidExtendedKeyUsage := asn1.ObjectIdentifier{2, 5, 29, 37} - oidTimeStamping := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} - ekuValues, err := asn1.Marshal([]asn1.ObjectIdentifier{oidTimeStamping}) - if err != nil { - return nil - } - - ekuExtension := pkix.Extension{ - Id: oidExtendedKeyUsage, - Critical: true, - Value: ekuValues, - } - - issuer := pkix.Name{ - CommonName: "local", - Country: []string{"CR"}, - Organization: []string{"RedHat"}, - Province: []string{"Czech Republic"}, - Locality: []string{"Brno"}, - OrganizationalUnit: []string{"QE"}, - } - - certTemplate := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: issuer, - BasicConstraintsValid: true, - IsCA: true, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}, - ExtraExtensions: []pkix.Extension{ekuExtension}, - Issuer: issuer, - NotBefore: notBefore, - NotAfter: notAfter, - } - - block, _ := pem.Decode(rootPrivateKey) - keyBytes := block.Bytes - if x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck - keyBytes, err = x509.DecryptPEMBlock(block, []byte(CertPassword)) //nolint:staticcheck - if err != nil { - return nil - } - } - - rootPrivKey, err := x509.ParseECPrivateKey(keyBytes) - if err != nil { - return nil - } - - block, _ = pem.Decode(intermediatePublicKey) - keyBytes = block.Bytes - interPubKey, err := x509.ParsePKIXPublicKey(keyBytes) - if err != nil { - return nil - } - - block, _ = pem.Decode(rootCA) - rootCert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil - } - - intermediateCert, err := x509.CreateCertificate(rand.Reader, &certTemplate, rootCert, interPubKey, rootPrivKey) - if err != nil { - return nil - } - - block, _ = pem.Decode(leafPublicKey) - keyBytes = block.Bytes - leafPuKey, err := x509.ParsePKIXPublicKey(keyBytes) - if err != nil { - return nil - } - - leafCert, err := x509.CreateCertificate(rand.Reader, &certTemplate, rootCert, leafPuKey, rootPrivKey) - if err != nil { - return nil - } - - intermediatePEM := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: intermediateCert, - }) - - leafPEM := pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: leafCert, - }) - certificateChain := append(leafPEM, intermediatePEM...) - certificateChain = append(certificateChain, rootCA...) - config.CertificateChain = certificateChain - - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Data: map[string][]byte{ - "rootPrivateKey": config.RootPrivateKey, - "rootPrivateKeyPassword": config.RootPrivateKeyPassword, - "interPrivateKey-0": config.IntermediatePrivateKeys[0], - "interPrivateKeyPassword-0": config.IntermediatePrivateKeyPasswords[0], - "leafPrivateKey": config.LeafPrivateKey, - "leafPrivateKeyPassword": config.LeafPrivateKeyPassword, - "certificateChain": config.CertificateChain, - }, - } -} diff --git a/test/e2e/support/tas/ctlog.go b/test/e2e/support/tas/ctlog.go deleted file mode 100644 index 7b8dc7023..000000000 --- a/test/e2e/support/tas/ctlog.go +++ /dev/null @@ -1,55 +0,0 @@ -package tas - -import ( - "context" - - . "github.com/onsi/gomega" - "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/internal/controller/common/utils/kubernetes" - "github.com/securesign/operator/internal/controller/constants" - "github.com/securesign/operator/internal/controller/ctlog/actions" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func VerifyCTLog(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(func(g Gomega) bool { - instance := &v1alpha1.CTlog{} - g.Expect(cli.Get(ctx, types.NamespacedName{ - Namespace: namespace, - Name: name, - }, instance)).To(Succeed()) - return meta.IsStatusConditionTrue(instance.Status.Conditions, constants.Ready) - }).Should(BeTrue()) - - list := &v1.PodList{} - - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) -} - -func GetCTLogServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { - return func() *v1.Pod { - list := &v1.PodList{} - _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName, kubernetes.NameLabel: "ctlog"}) - if len(list.Items) != 1 { - return nil - } - return &list.Items[0] - } -} - -func GetCTLog(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.CTlog { - return func() *v1alpha1.CTlog { - instance := &v1alpha1.CTlog{} - Expect(cli.Get(ctx, types.NamespacedName{ - Namespace: ns, - Name: name, - }, instance)).To(Succeed()) - return instance - } -} diff --git a/test/e2e/support/tas/ctlog/ctlog.go b/test/e2e/support/tas/ctlog/ctlog.go new file mode 100644 index 000000000..0f7b4e969 --- /dev/null +++ b/test/e2e/support/tas/ctlog/ctlog.go @@ -0,0 +1,70 @@ +package ctlog + +import ( + "context" + + "github.com/securesign/operator/test/e2e/support" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/constants" + "github.com/securesign/operator/internal/controller/ctlog/actions" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func Verify(ctx context.Context, cli client.Client, namespace string, name string) { + Eventually(Get(ctx, cli, namespace, name)).Should( + WithTransform(func(f *v1alpha1.CTlog) bool { + return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) + }, BeTrue())) + + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.ComponentName, + }) + }).Should(BeTrue()) +} + +func GetServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { + return func() *v1.Pod { + list := &v1.PodList{} + _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName, kubernetes.NameLabel: "ctlog"}) + if len(list.Items) != 1 { + return nil + } + return &list.Items[0] + } +} + +func Get(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.CTlog { + return func() *v1alpha1.CTlog { + instance := &v1alpha1.CTlog{} + Expect(cli.Get(ctx, types.NamespacedName{ + Namespace: ns, + Name: name, + }, instance)).To(Succeed()) + return instance + } +} + +func CreateSecret(ns string, name string) *v1.Secret { + public, private, _, err := support.CreateCertificates(false) + if err != nil { + return nil + } + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Data: map[string][]byte{ + "private": private, + "public": public, + }, + } +} diff --git a/test/e2e/support/tas/fulcio.go b/test/e2e/support/tas/fulcio/fulcio.go similarity index 50% rename from test/e2e/support/tas/fulcio.go rename to test/e2e/support/tas/fulcio/fulcio.go index 7b8f3cd2b..4510a4ce5 100644 --- a/test/e2e/support/tas/fulcio.go +++ b/test/e2e/support/tas/fulcio/fulcio.go @@ -1,4 +1,4 @@ -package tas +package fulcio import ( "context" @@ -8,26 +8,28 @@ import ( "github.com/securesign/operator/internal/controller/common/utils/kubernetes" "github.com/securesign/operator/internal/controller/constants" "github.com/securesign/operator/internal/controller/fulcio/actions" + "github.com/securesign/operator/test/e2e/support" 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" "sigs.k8s.io/controller-runtime/pkg/client" ) -func VerifyFulcio(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(GetFulcio(ctx, cli, namespace, name)).Should( +func Verify(ctx context.Context, cli client.Client, namespace string, name string) { + Eventually(Get(ctx, cli, namespace, name)).Should( WithTransform(func(f *v1alpha1.Fulcio) bool { return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) }, BeTrue())) - list := &v1.PodList{} - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.ComponentName, + }) + }).Should(BeTrue()) } -func GetFulcioServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { +func GetServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { return func() *v1.Pod { list := &v1.PodList{} _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName, kubernetes.NameLabel: "fulcio-server"}) @@ -38,7 +40,7 @@ func GetFulcioServerPod(ctx context.Context, cli client.Client, ns string) func( } } -func GetFulcio(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Fulcio { +func Get(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Fulcio { return func() *v1alpha1.Fulcio { instance := &v1alpha1.Fulcio{} _ = cli.Get(ctx, types.NamespacedName{ @@ -48,3 +50,22 @@ func GetFulcio(ctx context.Context, cli client.Client, ns string, name string) f return instance } } + +func CreateSecret(ns string, name string) *v1.Secret { + public, private, root, err := support.CreateCertificates(true) + if err != nil { + return nil + } + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Data: map[string][]byte{ + "password": []byte(support.CertPassword), + "private": private, + "public": public, + "cert": root, + }, + } +} diff --git a/test/e2e/support/tas/rekor.go b/test/e2e/support/tas/rekor.go deleted file mode 100644 index 65c71ab07..000000000 --- a/test/e2e/support/tas/rekor.go +++ /dev/null @@ -1,64 +0,0 @@ -package tas - -import ( - "context" - - . "github.com/onsi/gomega" - "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/internal/controller/common/utils/kubernetes" - "github.com/securesign/operator/internal/controller/constants" - "github.com/securesign/operator/internal/controller/rekor/actions" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func VerifyRekor(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(GetRekor(ctx, cli, namespace, name)).Should( - WithTransform(func(f *v1alpha1.Rekor) bool { - return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) - }, BeTrue())) - - list := &v1.PodList{} - - // server - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ServerComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) - - // redis - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.RedisComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) -} - -func GetRekorServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { - return func() *v1.Pod { - list := &v1.PodList{} - _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: actions.ServerComponentName, kubernetes.NameLabel: "rekor-server"}) - if len(list.Items) != 1 { - return nil - } - return &list.Items[0] - } -} - -func GetRekor(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Rekor { - return func() *v1alpha1.Rekor { - instance := &v1alpha1.Rekor{} - _ = cli.Get(ctx, types.NamespacedName{ - Namespace: ns, - Name: name, - }, instance) - return instance - } -} - -func VerifyRekorSearchUI(ctx context.Context, cli client.Client, namespace string, name string) { - list := &v1.PodList{} - Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.UIComponentName})).To(Succeed()) - Expect(list.Items).To(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) -} diff --git a/test/e2e/support/tas/rekor/rekor.go b/test/e2e/support/tas/rekor/rekor.go new file mode 100644 index 000000000..a15a7c161 --- /dev/null +++ b/test/e2e/support/tas/rekor/rekor.go @@ -0,0 +1,78 @@ +package rekor + +import ( + "context" + + "github.com/securesign/operator/test/e2e/support" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/constants" + "github.com/securesign/operator/internal/controller/rekor/actions" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func Verify(ctx context.Context, cli client.Client, namespace string, name string) { + Eventually(Get(ctx, cli, namespace, name)).Should( + WithTransform(func(f *v1alpha1.Rekor) bool { + return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) + }, BeTrue())) + + // server + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.ServerComponentName, + }) + }).Should(BeTrue()) + + // redis + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.RedisComponentName, + }) + }).Should(BeTrue()) +} + +func GetServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { + return func() *v1.Pod { + list := &v1.PodList{} + _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: actions.ServerComponentName, kubernetes.NameLabel: "rekor-server"}) + if len(list.Items) != 1 { + return nil + } + return &list.Items[0] + } +} + +func Get(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Rekor { + return func() *v1alpha1.Rekor { + instance := &v1alpha1.Rekor{} + _ = cli.Get(ctx, types.NamespacedName{ + Namespace: ns, + Name: name, + }, instance) + return instance + } +} + +func CreateSecret(ns string, name string) *v1.Secret { + public, private, _, err := support.CreateCertificates(false) + if err != nil { + return nil + } + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Data: map[string][]byte{ + "private": private, + "public": public, + }, + } +} diff --git a/test/e2e/support/tas/rekor/search_ui.go b/test/e2e/support/tas/rekor/search_ui.go new file mode 100644 index 000000000..c82657333 --- /dev/null +++ b/test/e2e/support/tas/rekor/search_ui.go @@ -0,0 +1,18 @@ +package rekor + +import ( + "context" + + . "github.com/onsi/gomega" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/rekor/actions" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func VerifySearchUI(ctx context.Context, cli client.Client, namespace string) { + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.UIComponentName, + }) + }).Should(BeTrue()) +} diff --git a/test/e2e/support/tas/securesign.go b/test/e2e/support/tas/securesign/securesign.go similarity index 68% rename from test/e2e/support/tas/securesign.go rename to test/e2e/support/tas/securesign/securesign.go index 71b70375c..18eb98322 100644 --- a/test/e2e/support/tas/securesign.go +++ b/test/e2e/support/tas/securesign/securesign.go @@ -1,4 +1,4 @@ -package tas +package securesign import ( "context" @@ -11,14 +11,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func VerifySecuresign(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(GetSecuresign(ctx, cli, namespace, name)).Should( +func Verify(ctx context.Context, cli client.Client, namespace string, name string) { + Eventually(Get(ctx, cli, namespace, name)).Should( WithTransform(func(f *v1alpha1.Securesign) bool { return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) }, BeTrue())) } -func GetSecuresign(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Securesign { +func Get(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Securesign { return func() *v1alpha1.Securesign { instance := &v1alpha1.Securesign{} _ = cli.Get(ctx, types.NamespacedName{ diff --git a/test/e2e/support/tas/tas.go b/test/e2e/support/tas/tas.go new file mode 100644 index 000000000..8792edccd --- /dev/null +++ b/test/e2e/support/tas/tas.go @@ -0,0 +1,77 @@ +package tas + +import ( + "context" + "time" + + "github.com/securesign/operator/test/e2e/support/tas/tsa" + + . "github.com/onsi/gomega" + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/test/e2e/support" + clients "github.com/securesign/operator/test/e2e/support/tas/cli" + "github.com/securesign/operator/test/e2e/support/tas/ctlog" + "github.com/securesign/operator/test/e2e/support/tas/fulcio" + "github.com/securesign/operator/test/e2e/support/tas/rekor" + "github.com/securesign/operator/test/e2e/support/tas/securesign" + "github.com/securesign/operator/test/e2e/support/tas/trillian" + "github.com/securesign/operator/test/e2e/support/tas/tuf" + runtimeCli "sigs.k8s.io/controller-runtime/pkg/client" +) + +func VerifyAllComponents(ctx context.Context, cli runtimeCli.Client, s *rhtasv1alpha1.Securesign, dbPresent bool) { + securesign.Verify(ctx, cli, s.Namespace, s.Name) + trillian.Verify(ctx, cli, s.Namespace, s.Name, dbPresent) + ctlog.Verify(ctx, cli, s.Namespace, s.Name) + tuf.Verify(ctx, cli, s.Namespace, s.Name) + rekor.Verify(ctx, cli, s.Namespace, s.Name) + fulcio.Verify(ctx, cli, s.Namespace, s.Name) + tsa.Verify(ctx, cli, s.Namespace, s.Name) +} + +func VerifyByCosign(ctx context.Context, cli runtimeCli.Client, s *rhtasv1alpha1.Securesign, targetImageName string) { + f := fulcio.Get(ctx, cli, s.Namespace, s.Name)() + Expect(f).ToNot(BeNil()) + + r := rekor.Get(ctx, cli, s.Namespace, s.Name)() + Expect(r).ToNot(BeNil()) + + t := tuf.Get(ctx, cli, s.Namespace, s.Name)() + Expect(t).ToNot(BeNil()) + + ts := tsa.Get(ctx, cli, s.Namespace, s.Name)() + Expect(ts).ToNot(BeNil()) + + Eventually(func() error { + return tsa.GetCertificateChain(ctx, cli, s.Namespace, s.Name, ts.Status.Url) + }).Should(Succeed()) + + oidcToken, err := support.OidcToken(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(oidcToken).ToNot(BeEmpty()) + + // sleep for a while to be sure everything has settled down + time.Sleep(time.Duration(10) * time.Second) + + Expect(clients.Execute("cosign", "initialize", "--mirror="+t.Status.Url, "--root="+t.Status.Url+"/root.json")).To(Succeed()) + + Expect(clients.Execute( + "cosign", "sign", "-y", + "--fulcio-url="+f.Status.Url, + "--rekor-url="+r.Status.Url, + "--timestamp-server-url="+ts.Status.Url+"/api/v1/timestamp", + "--oidc-issuer="+support.OidcIssuerUrl(), + "--oidc-client-id="+support.OidcClientID(), + "--identity-token="+oidcToken, + targetImageName, + )).To(Succeed()) + + Expect(clients.Execute( + "cosign", "verify", + "--rekor-url="+r.Status.Url, + "--timestamp-certificate-chain=ts_chain.pem", + "--certificate-identity-regexp", ".*@redhat", + "--certificate-oidc-issuer-regexp", ".*keycloak.*", + targetImageName, + )).To(Succeed()) +} diff --git a/test/e2e/support/tas/trillian.go b/test/e2e/support/tas/trillian.go deleted file mode 100644 index 81f26a6d1..000000000 --- a/test/e2e/support/tas/trillian.go +++ /dev/null @@ -1,44 +0,0 @@ -package tas - -import ( - "context" - - . "github.com/onsi/gomega" - "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/internal/controller/common/utils/kubernetes" - "github.com/securesign/operator/internal/controller/constants" - "github.com/securesign/operator/internal/controller/trillian/actions" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func VerifyTrillian(ctx context.Context, cli client.Client, namespace string, name string, dbPresent bool) { - Eventually(func(g Gomega) bool { - instance := &v1alpha1.Trillian{} - g.Expect(cli.Get(ctx, types.NamespacedName{ - Namespace: namespace, - Name: name, - }, instance)).To(Succeed()) - return meta.IsStatusConditionTrue(instance.Status.Conditions, constants.Ready) - }).Should(BeTrue()) - - list := &v1.PodList{} - if dbPresent { - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.DbComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) - } - - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.LogServerComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) - - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.LogSignerComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) -} diff --git a/test/e2e/support/tas/trillian/trillian.go b/test/e2e/support/tas/trillian/trillian.go new file mode 100644 index 000000000..c34666899 --- /dev/null +++ b/test/e2e/support/tas/trillian/trillian.go @@ -0,0 +1,55 @@ +package trillian + +import ( + "context" + + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/constants" + "github.com/securesign/operator/internal/controller/trillian/actions" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func Verify(ctx context.Context, cli client.Client, namespace string, name string, dbPresent bool) { + Eventually(Get(ctx, cli, namespace, name)).Should( + WithTransform(func(f *v1alpha1.Trillian) bool { + return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) + }, BeTrue())) + + if dbPresent { + // trillian-db + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.DbComponentName, + }) + }).Should(BeTrue()) + } + + // log server + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.LogServerComponentName, + }) + }).Should(BeTrue()) + + // log signer + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.LogSignerComponentName, + }) + }).Should(BeTrue()) +} + +func Get(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Trillian { + return func() *v1alpha1.Trillian { + instance := &v1alpha1.Trillian{} + Expect(cli.Get(ctx, types.NamespacedName{ + Namespace: ns, + Name: name, + }, instance)).To(Succeed()) + return instance + } +} diff --git a/test/e2e/support/tas/tsa.go b/test/e2e/support/tas/tsa.go deleted file mode 100644 index c31ea5526..000000000 --- a/test/e2e/support/tas/tsa.go +++ /dev/null @@ -1,88 +0,0 @@ -package tas - -import ( - "context" - "fmt" - "io" - "net/http" - "os" - - . "github.com/onsi/gomega" - "github.com/securesign/operator/api/v1alpha1" - "github.com/securesign/operator/internal/controller/common/utils/kubernetes" - "github.com/securesign/operator/internal/controller/constants" - "github.com/securesign/operator/internal/controller/tsa/actions" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func VerifyTSA(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(GetTSA(ctx, cli, namespace, name)).Should( - WithTransform(func(f *v1alpha1.TimestampAuthority) bool { - return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) - }, BeTrue())) - - list := &v1.PodList{} - Eventually(func() []v1.Pod { - Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) -} - -func GetTSAServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { - return func() *v1.Pod { - list := &v1.PodList{} - _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName, kubernetes.NameLabel: "tsa-server"}) - if len(list.Items) != 1 { - return nil - } - return &list.Items[0] - } -} - -func GetTSA(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.TimestampAuthority { - return func() *v1alpha1.TimestampAuthority { - instance := &v1alpha1.TimestampAuthority{} - _ = cli.Get(ctx, types.NamespacedName{ - Namespace: ns, - Name: name, - }, instance) - return instance - } -} - -func GetTSACertificateChain(ctx context.Context, cli client.Client, ns string, name string, url string) error { - var resp *http.Response - var err error - req, err := http.NewRequestWithContext(ctx, "GET", url+"/api/v1/timestamp/certchain", nil) - if err != nil { - return fmt.Errorf("failed to create HTTP request: %w", err) - } - - resp, err = http.DefaultClient.Do(req) - if err != nil { - return fmt.Errorf("failed to perform HTTP request: %w", err) - } - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected HTTP status: %s", resp.Status) - } - - defer func() { - if closeErr := resp.Body.Close(); closeErr != nil { - fmt.Println("Error closing response body:", closeErr) - } - }() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - err = os.WriteFile("ts_chain.pem", body, 0644) - if err != nil { - return fmt.Errorf("failed to write file: %w", err) - } - - return nil -} diff --git a/test/e2e/support/tas/tsa/tsa.go b/test/e2e/support/tas/tsa/tsa.go new file mode 100644 index 000000000..f4ab29639 --- /dev/null +++ b/test/e2e/support/tas/tsa/tsa.go @@ -0,0 +1,235 @@ +package tsa + +import ( + "context" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "fmt" + "io" + "math/big" + "net/http" + "os" + "time" + + tsaUtils "github.com/securesign/operator/internal/controller/tsa/utils" + "github.com/securesign/operator/test/e2e/support" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/common/utils/kubernetes" + "github.com/securesign/operator/internal/controller/constants" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func Verify(ctx context.Context, cli client.Client, namespace string, name string) { + Eventually(Get(ctx, cli, namespace, name)).Should( + WithTransform(func(f *v1alpha1.TimestampAuthority) bool { + return meta.IsStatusConditionTrue(f.Status.Conditions, constants.Ready) + }, BeTrue())) + + // server + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: "timestamp-authority", + }) + }).Should(BeTrue()) +} + +func Get(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.TimestampAuthority { + return func() *v1alpha1.TimestampAuthority { + instance := &v1alpha1.TimestampAuthority{} + Expect(cli.Get(ctx, types.NamespacedName{ + Namespace: ns, + Name: name, + }, instance)).To(Succeed()) + return instance + } +} + +func GetServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { + return func() *v1.Pod { + list := &v1.PodList{} + _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: "timestamp-authority", kubernetes.NameLabel: "tsa-server"}) + if len(list.Items) != 1 { + return nil + } + return &list.Items[0] + } +} + +func GetCertificateChain(ctx context.Context, cli client.Client, ns string, name string, url string) error { + var resp *http.Response + var err error + req, err := http.NewRequestWithContext(ctx, "GET", url+"/api/v1/timestamp/certchain", nil) + if err != nil { + return fmt.Errorf("failed to create HTTP request: %w", err) + } + + resp, err = http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to perform HTTP request: %w", err) + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected HTTP status: %s", resp.Status) + } + + defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + fmt.Println("Error closing response body:", closeErr) + } + }() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %w", err) + } + err = os.WriteFile("ts_chain.pem", body, 0644) + if err != nil { + return fmt.Errorf("failed to write file: %w", err) + } + + return nil +} + +func CreateSecrets(ns string, name string) *v1.Secret { + + config := &tsaUtils.TsaCertChainConfig{ + RootPrivateKeyPassword: []byte(support.CertPassword), + IntermediatePrivateKeyPasswords: [][]byte{[]byte(support.CertPassword)}, + LeafPrivateKeyPassword: []byte(support.CertPassword), + } + _, rootPrivateKey, rootCA, err := support.CreateCertificates(true) + if err != nil { + return nil + } + config.RootPrivateKey = rootPrivateKey + + intermediatePublicKey, intermediatePrivateKey, _, err := support.CreateCertificates(true) + if err != nil { + return nil + } + config.IntermediatePrivateKeys = append(config.IntermediatePrivateKeys, intermediatePrivateKey) + + leafPublicKey, leafPrivateKey, _, err := support.CreateCertificates(true) + if err != nil { + return nil + } + config.LeafPrivateKey = leafPrivateKey + + notBefore := time.Now() + notAfter := notBefore.Add(365 * 24 * 10 * time.Hour) + oidExtendedKeyUsage := asn1.ObjectIdentifier{2, 5, 29, 37} + oidTimeStamping := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} + ekuValues, err := asn1.Marshal([]asn1.ObjectIdentifier{oidTimeStamping}) + if err != nil { + return nil + } + + ekuExtension := pkix.Extension{ + Id: oidExtendedKeyUsage, + Critical: true, + Value: ekuValues, + } + + issuer := pkix.Name{ + CommonName: "local", + Country: []string{"CR"}, + Organization: []string{"RedHat"}, + Province: []string{"Czech Republic"}, + Locality: []string{"Brno"}, + OrganizationalUnit: []string{"QE"}, + } + + certTemplate := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: issuer, + BasicConstraintsValid: true, + IsCA: true, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}, + ExtraExtensions: []pkix.Extension{ekuExtension}, + Issuer: issuer, + NotBefore: notBefore, + NotAfter: notAfter, + } + + block, _ := pem.Decode(rootPrivateKey) + keyBytes := block.Bytes + if x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck + keyBytes, err = x509.DecryptPEMBlock(block, []byte(support.CertPassword)) //nolint:staticcheck + if err != nil { + return nil + } + } + + rootPrivKey, err := x509.ParseECPrivateKey(keyBytes) + if err != nil { + return nil + } + + block, _ = pem.Decode(intermediatePublicKey) + keyBytes = block.Bytes + interPubKey, err := x509.ParsePKIXPublicKey(keyBytes) + if err != nil { + return nil + } + + block, _ = pem.Decode(rootCA) + rootCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil + } + + intermediateCert, err := x509.CreateCertificate(rand.Reader, &certTemplate, rootCert, interPubKey, rootPrivKey) + if err != nil { + return nil + } + + block, _ = pem.Decode(leafPublicKey) + keyBytes = block.Bytes + leafPuKey, err := x509.ParsePKIXPublicKey(keyBytes) + if err != nil { + return nil + } + + leafCert, err := x509.CreateCertificate(rand.Reader, &certTemplate, rootCert, leafPuKey, rootPrivKey) + if err != nil { + return nil + } + + intermediatePEM := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: intermediateCert, + }) + + leafPEM := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: leafCert, + }) + certificateChain := append(leafPEM, intermediatePEM...) + certificateChain = append(certificateChain, rootCA...) + config.CertificateChain = certificateChain + + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Data: map[string][]byte{ + "rootPrivateKey": config.RootPrivateKey, + "rootPrivateKeyPassword": config.RootPrivateKeyPassword, + "interPrivateKey-0": config.IntermediatePrivateKeys[0], + "interPrivateKeyPassword-0": config.IntermediatePrivateKeyPasswords[0], + "leafPrivateKey": config.LeafPrivateKey, + "leafPrivateKeyPassword": config.LeafPrivateKeyPassword, + "certificateChain": config.CertificateChain, + }, + } +} diff --git a/test/e2e/support/tas/tuf.go b/test/e2e/support/tas/tuf/tuf.go similarity index 59% rename from test/e2e/support/tas/tuf.go rename to test/e2e/support/tas/tuf/tuf.go index 2e4748d5b..5ff383c83 100644 --- a/test/e2e/support/tas/tuf.go +++ b/test/e2e/support/tas/tuf/tuf.go @@ -1,4 +1,4 @@ -package tas +package tuf import ( "context" @@ -14,20 +14,20 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func VerifyTuf(ctx context.Context, cli client.Client, namespace string, name string) { - Eventually(GetTuf(ctx, cli, namespace, name)).Should( +func Verify(ctx context.Context, cli client.Client, namespace string, name string) { + Eventually(Get(ctx, cli, namespace, name)).Should( WithTransform(func(f *v1alpha1.Tuf) string { return meta.FindStatusCondition(f.Status.Conditions, constants.Ready).Reason }, Equal(constants.Ready))) - list := &v1.PodList{} - Eventually(func(g Gomega) []v1.Pod { - g.Expect(cli.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName})).To(Succeed()) - return list.Items - }).Should(And(Not(BeEmpty()), HaveEach(WithTransform(func(p v1.Pod) v1.PodPhase { return p.Status.Phase }, Equal(v1.PodRunning))))) + Eventually(func(g Gomega) (bool, error) { + return kubernetes.DeploymentIsRunning(ctx, cli, namespace, map[string]string{ + kubernetes.ComponentLabel: actions.ComponentName, + }) + }).Should(BeTrue()) } -func GetTuf(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Tuf { +func Get(ctx context.Context, cli client.Client, ns string, name string) func() *v1alpha1.Tuf { return func() *v1alpha1.Tuf { instance := &v1alpha1.Tuf{} _ = cli.Get(ctx, types.NamespacedName{ @@ -38,7 +38,7 @@ func GetTuf(ctx context.Context, cli client.Client, ns string, name string) func } } -func GetTufServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { +func GetServerPod(ctx context.Context, cli client.Client, ns string) func() *v1.Pod { return func() *v1.Pod { list := &v1.PodList{} _ = cli.List(ctx, list, client.InNamespace(ns), client.MatchingLabels{kubernetes.ComponentLabel: actions.ComponentName}) diff --git a/test/e2e/update/ctlog_test.go b/test/e2e/update/ctlog_test.go new file mode 100644 index 000000000..ac118fbde --- /dev/null +++ b/test/e2e/update/ctlog_test.go @@ -0,0 +1,158 @@ +//go:build integration + +package update + +import ( + "context" + "time" + + "github.com/securesign/operator/test/e2e/support/tas" + + "github.com/securesign/operator/test/e2e/support/tas/ctlog" + "github.com/securesign/operator/test/e2e/support/tas/tuf" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/constants" + ctlogAction "github.com/securesign/operator/internal/controller/ctlog/actions" + tufAction "github.com/securesign/operator/internal/controller/tuf/actions" + "github.com/securesign/operator/test/e2e/support" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + runtimeCli "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("CTlog update", Ordered, func() { + SetDefaultEventuallyTimeout(time.Duration(5) * time.Minute) + cli, _ := CreateClient() + ctx := context.TODO() + + var targetImageName string + var namespace *v1.Namespace + var s *v1alpha1.Securesign + + AfterEach(func() { + if CurrentSpecReport().Failed() && support.IsCIEnvironment() { + support.DumpNamespace(ctx, cli, namespace.Name) + } + }) + + BeforeAll(func() { + namespace = support.CreateTestNamespace(ctx, cli) + DeferCleanup(func() { + _ = cli.Delete(ctx, namespace) + }) + s = securesignResource(namespace) + }) + + BeforeAll(func() { + targetImageName = support.PrepareImage(ctx) + }) + + Describe("Install with autogenerated certificates", func() { + BeforeAll(func() { + Expect(cli.Create(ctx, s)).To(Succeed()) + }) + + It("All other components are running", func() { + tas.VerifyAllComponents(ctx, cli, s, true) + }) + }) + + Describe("Change private key", func() { + var tufGeneration, ctlogGeneration int64 + + It("stored current deployment observed generations ", func() { + tufGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}, + ) + Expect(tufGeneration).Should(BeNumerically(">", 0)) + ctlogGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: ctlogAction.DeploymentName}, + ) + Expect(ctlogGeneration).Should(BeNumerically(">", 0)) + }) + + It("modified ctlog.privateKeyRef and ctlog.publicKeyRef", func() { + Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(s), s)).To(Succeed()) + s.Spec.Ctlog.PrivateKeyRef = &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "my-ctlog-secret", + }, + Key: "private", + } + s.Spec.Ctlog.PublicKeyRef = &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "my-ctlog-secret", + }, + Key: "public", + } + Expect(cli.Update(ctx, s)).To(Succeed()) + }) + + It("has status Creating: waiting on my-ctlog-secret", func() { + Eventually(func(g Gomega) string { + ctl := ctlog.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason + }).Should(Equal(constants.Creating)) + }) + + It("created my-ctlog-secret", func() { + Expect(cli.Create(ctx, ctlog.CreateSecret(namespace.Name, "my-ctlog-secret"))).Should(Succeed()) + }) + + It("has status Ready", func() { + Eventually(func(g Gomega) string { + ctl := ctlog.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason + }).Should(Equal(constants.Ready)) + }) + + It("updated CTlog deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: ctlogAction.DeploymentName}) + }).Should(BeNumerically(">", ctlogGeneration)) + }) + + It("updated TUF deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}) + }).Should(BeNumerically(">", tufGeneration)) + }) + + It("verify CTlog and TUF", func() { + ctlog.Verify(ctx, cli, namespace.Name, s.Name) + tuf.Verify(ctx, cli, namespace.Name, s.Name) + }) + + It("verify new configuration", func() { + var ctl *v1alpha1.CTlog + var ctlPod *v1.Pod + Eventually(func(g Gomega) { + ctl = ctlog.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + ctlPod = ctlog.GetServerPod(ctx, cli, namespace.Name)() + g.Expect(ctlPod).NotTo(BeNil()) + }).Should(Succeed()) + + Expect(ctlPod.Spec.Volumes).To(ContainElements(And( + WithTransform(func(v v1.Volume) string { return v.Name }, Equal("keys")), + WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal(ctl.Status.ServerConfigRef.Name))))) + + existing := &v1.Secret{} + expected := &v1.Secret{} + Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: ctl.Status.ServerConfigRef.Name}, existing)).To(Succeed()) + Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: "my-ctlog-secret"}, expected)).To(Succeed()) + + Expect(existing.Data["public"]).To(Equal(expected.Data["public"])) + }) + + It("verify by cosign", func() { + tas.VerifyByCosign(ctx, cli, s, targetImageName) + }) + }) +}) diff --git a/test/e2e/update/fulcio_test.go b/test/e2e/update/fulcio_test.go new file mode 100644 index 000000000..ed4c18cb2 --- /dev/null +++ b/test/e2e/update/fulcio_test.go @@ -0,0 +1,235 @@ +//go:build integration + +package update + +import ( + "context" + "encoding/json" + "time" + + "github.com/securesign/operator/test/e2e/support/tas" + + fulcioAction "github.com/securesign/operator/internal/controller/fulcio/actions" + "github.com/securesign/operator/test/e2e/support/tas/ctlog" + "github.com/securesign/operator/test/e2e/support/tas/fulcio" + "github.com/securesign/operator/test/e2e/support/tas/tuf" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/constants" + ctlogAction "github.com/securesign/operator/internal/controller/ctlog/actions" + tufAction "github.com/securesign/operator/internal/controller/tuf/actions" + "github.com/securesign/operator/test/e2e/support" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + runtimeCli "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("Fulcio update", Ordered, func() { + SetDefaultEventuallyTimeout(time.Duration(5) * time.Minute) + cli, _ := CreateClient() + ctx := context.TODO() + + var targetImageName string + var namespace *v1.Namespace + var s *v1alpha1.Securesign + + AfterEach(func() { + if CurrentSpecReport().Failed() && support.IsCIEnvironment() { + support.DumpNamespace(ctx, cli, namespace.Name) + } + }) + + BeforeAll(func() { + namespace = support.CreateTestNamespace(ctx, cli) + DeferCleanup(func() { + _ = cli.Delete(ctx, namespace) + }) + s = securesignResource(namespace) + }) + + BeforeAll(func() { + targetImageName = support.PrepareImage(ctx) + }) + + Describe("Install with autogenerated certificates", func() { + BeforeAll(func() { + Expect(cli.Create(ctx, s)).To(Succeed()) + }) + + It("All other components are running", func() { + tas.VerifyAllComponents(ctx, cli, s, true) + }) + }) + + Describe("Change certificate private key and root CA", func() { + var tufGeneration, ctlogGeneration, fulcioGeneration int64 + + It("stored current deployment observed generations ", func() { + tufGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}, + ) + Expect(tufGeneration).Should(BeNumerically(">", 0)) + ctlogGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: ctlogAction.DeploymentName}, + ) + Expect(ctlogGeneration).Should(BeNumerically(">", 0)) + fulcioGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: fulcioAction.DeploymentName}, + ) + Expect(fulcioGeneration).Should(BeNumerically(">", 0)) + }) + + It("modified fulcio.certificate", func() { + Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(s), s)).To(Succeed()) + s.Spec.Fulcio.Certificate = v1alpha1.FulcioCert{ + PrivateKeyRef: &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "my-fulcio-secret", + }, + Key: "private", + }, + PrivateKeyPasswordRef: &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "my-fulcio-secret", + }, + Key: "password", + }, + CARef: &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "my-fulcio-secret", + }, + Key: "cert", + }, + } + Expect(cli.Update(ctx, s)).To(Succeed()) + }) + + It("has status FulcioCertAvailable == Failure: waiting on my-fulcio-secret", func() { + Eventually(func(g Gomega) string { + ctl := fulcio.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + c := meta.FindStatusCondition(ctl.Status.Conditions, fulcioAction.CertCondition) + g.Expect(c).ToNot(BeNil()) + return c.Reason + }).Should(Equal(constants.Failure)) + }) + + It("created my-fulcio-secret", func() { + Expect(cli.Create(ctx, fulcio.CreateSecret(namespace.Name, "my-fulcio-secret"))).Should(Succeed()) + }) + + It("has status Ready", func() { + Eventually(func(g Gomega) string { + ctl := fulcio.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason + }).Should(Equal(constants.Ready)) + }) + + It("updated Fulcio deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: fulcioAction.DeploymentName}) + }).Should(BeNumerically(">", fulcioGeneration)) + }) + + It("updated CTlog deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: ctlogAction.DeploymentName}) + }).Should(BeNumerically(">", ctlogGeneration)) + }) + + It("updated TUF deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}) + }).Should(BeNumerically(">", tufGeneration)) + }) + + It("verify CTlog and TUF", func() { + fulcio.Verify(ctx, cli, namespace.Name, s.Name) + ctlog.Verify(ctx, cli, namespace.Name, s.Name) + tuf.Verify(ctx, cli, namespace.Name, s.Name) + }) + + It("verify new configuration", func() { + var fulcioPod *v1.Pod + Eventually(func(g Gomega) { + fulcioPod = fulcio.GetServerPod(ctx, cli, namespace.Name)() + g.Expect(fulcioPod).ToNot(BeNil()) + }).Should(Succeed()) + Expect(fulcioPod.Spec.Volumes).To(ContainElements(And( + WithTransform(func(v v1.Volume) string { return v.Name }, Equal("fulcio-cert")), + WithTransform(func(v v1.Volume) string { return v.VolumeSource.Projected.Sources[0].Secret.Name }, Equal("my-fulcio-secret"))))) + + }) + + It("verify by cosign", func() { + tas.VerifyByCosign(ctx, cli, s, targetImageName) + }) + }) + + Describe("Changes OIDC configuration", func() { + var fulcioGeneration int64 + + It("stored current deployment observed generations ", func() { + fulcioGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: fulcioAction.DeploymentName}, + ) + Expect(fulcioGeneration).Should(BeNumerically(">", 0)) + }) + + It("adds new OIDCIssuers", func() { + Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(s), s)).To(Succeed()) + s.Spec.Fulcio.Config.OIDCIssuers = append(s.Spec.Fulcio.Config.OIDCIssuers, v1alpha1.OIDCIssuer{ + ClientID: "fake", + IssuerURL: "fake", + Issuer: "fake", + Type: "email", + }) + Expect(cli.Update(ctx, s)).To(Succeed()) + }) + + It("has status Ready", func() { + Eventually(func(g Gomega) string { + ctl := fulcio.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason + }).Should(Equal(constants.Ready)) + }) + + It("updated Fulcio deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: fulcioAction.DeploymentName}) + }).Should(BeNumerically(">", fulcioGeneration)) + }) + + It("verify CTlog and TUF", func() { + fulcio.Verify(ctx, cli, namespace.Name, s.Name) + }) + + It("verify new configuration", func() { + var f *v1alpha1.Fulcio + var fulcioPod *v1.Pod + Eventually(func(g Gomega) { + f = fulcio.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(f).NotTo(BeNil()) + fulcioPod = fulcio.GetServerPod(ctx, cli, namespace.Name)() + g.Expect(fulcioPod).NotTo(BeNil()) + }).Should(Succeed()) + + Expect(fulcioPod.Spec.Volumes[0].VolumeSource.ConfigMap.Name).To(Equal(f.Status.ServerConfigRef.Name)) + + cm := &v1.ConfigMap{} + Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: f.Status.ServerConfigRef.Name}, cm)).To(Succeed()) + config := &fulcioAction.FulcioMapConfig{} + Expect(json.Unmarshal([]byte(cm.Data["config.json"]), config)).To(Succeed()) + Expect(config.OIDCIssuers).To(HaveKey("fake")) + }) + + It("verify by cosign", func() { + tas.VerifyByCosign(ctx, cli, s, targetImageName) + }) + }) +}) diff --git a/test/e2e/update/rekor_test.go b/test/e2e/update/rekor_test.go new file mode 100644 index 000000000..88cc8dde9 --- /dev/null +++ b/test/e2e/update/rekor_test.go @@ -0,0 +1,146 @@ +//go:build integration + +package update + +import ( + "context" + "time" + + "github.com/securesign/operator/test/e2e/support/tas" + + rekorAction "github.com/securesign/operator/internal/controller/rekor/actions" + "github.com/securesign/operator/test/e2e/support/tas/rekor" + "github.com/securesign/operator/test/e2e/support/tas/tuf" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/constants" + tufAction "github.com/securesign/operator/internal/controller/tuf/actions" + "github.com/securesign/operator/test/e2e/support" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + runtimeCli "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("Rekor update", Ordered, func() { + SetDefaultEventuallyTimeout(time.Duration(5) * time.Minute) + cli, _ := CreateClient() + ctx := context.TODO() + + var targetImageName string + var namespace *v1.Namespace + var s *v1alpha1.Securesign + + AfterEach(func() { + if CurrentSpecReport().Failed() && support.IsCIEnvironment() { + support.DumpNamespace(ctx, cli, namespace.Name) + } + }) + + BeforeAll(func() { + namespace = support.CreateTestNamespace(ctx, cli) + DeferCleanup(func() { + _ = cli.Delete(ctx, namespace) + }) + s = securesignResource(namespace) + }) + + BeforeAll(func() { + targetImageName = support.PrepareImage(ctx) + }) + + Describe("Install with autogenerated certificates", func() { + BeforeAll(func() { + Expect(cli.Create(ctx, s)).To(Succeed()) + }) + + It("All other components are running", func() { + tas.VerifyAllComponents(ctx, cli, s, true) + }) + }) + + Describe("Change signer key", func() { + var tufGeneration, rekorGeneration int64 + + It("stored current deployment observed generations ", func() { + tufGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}, + ) + Expect(tufGeneration).Should(BeNumerically(">", 0)) + rekorGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: rekorAction.ServerDeploymentName}, + ) + Expect(rekorGeneration).Should(BeNumerically(">", 0)) + }) + + It("modified signer.keyRef", func() { + Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(s), s)).To(Succeed()) + s.Spec.Rekor.Signer = v1alpha1.RekorSigner{ + KMS: "secret", + KeyRef: &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "my-rekor-secret", + }, + Key: "private", + }, + } + Expect(cli.Update(ctx, s)).To(Succeed()) + }) + + It("has status SignerAvailable == Failure: waiting on my-rekor-secret", func() { + Eventually(func(g Gomega) string { + ctl := rekor.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + c := meta.FindStatusCondition(ctl.Status.Conditions, rekorAction.SignerCondition) + g.Expect(c).ToNot(BeNil()) + return c.Reason + }).Should(Equal(constants.Failure)) + }) + + It("created my-rekor-secret", func() { + Expect(cli.Create(ctx, rekor.CreateSecret(namespace.Name, "my-rekor-secret"))).Should(Succeed()) + }) + + It("has status Ready", func() { + Eventually(func(g Gomega) string { + ctl := rekor.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason + }).Should(Equal(constants.Ready)) + }) + + It("updated Rekor deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: rekorAction.ServerDeploymentName}) + }).Should(BeNumerically(">", rekorGeneration)) + }) + + It("updated TUF deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}) + }).Should(BeNumerically(">", tufGeneration)) + }) + + It("verify CTlog and TUF", func() { + rekor.Verify(ctx, cli, namespace.Name, s.Name) + tuf.Verify(ctx, cli, namespace.Name, s.Name) + }) + + It("verify new configuration", func() { + var r *v1.Pod + Eventually(func(g Gomega) { + r = rekor.GetServerPod(ctx, cli, namespace.Name)() + g.Expect(r).NotTo(BeNil()) + }).Should(Succeed()) + Expect(r.Spec.Volumes).To(ContainElements(And( + WithTransform(func(v v1.Volume) string { return v.Name }, Equal("rekor-private-key-volume")), + WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal("my-rekor-secret"))))) + }) + + It("verify by cosign", func() { + tas.VerifyByCosign(ctx, cli, s, targetImageName) + }) + }) +}) diff --git a/test/e2e/update/suite_test.go b/test/e2e/update/suite_test.go new file mode 100644 index 000000000..92c6414d0 --- /dev/null +++ b/test/e2e/update/suite_test.go @@ -0,0 +1,159 @@ +//go:build integration + +package update + +import ( + "context" + "testing" + "time" + + "k8s.io/utils/ptr" + + "github.com/securesign/operator/internal/controller/common/utils" + "github.com/securesign/operator/test/e2e/support" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" + routev1 "github.com/openshift/api/route/v1" + olm "github.com/operator-framework/api/pkg/operators/v1" + olmAlpha "github.com/operator-framework/api/pkg/operators/v1alpha1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + runtimeCli "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +func TestUpdateComponents(t *testing.T) { + RegisterFailHandler(Fail) + log.SetLogger(GinkgoLogr) + SetDefaultEventuallyTimeout(time.Duration(1) * time.Minute) + RunSpecs(t, "Update components E2E Suite") + + // print whole stack in case of failure + format.MaxLength = 0 +} + +func CreateClient() (runtimeCli.Client, error) { + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(monitoringv1.AddToScheme(scheme)) + utilruntime.Must(rhtasv1alpha1.AddToScheme(scheme)) + utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(olmAlpha.AddToScheme(scheme)) + utilruntime.Must(olm.AddToScheme(scheme)) + + cfg, err := config.GetConfig() + if err != nil { + return nil, err + } + + return runtimeCli.New(cfg, runtimeCli.Options{Scheme: scheme}) +} + +func securesignResource(namespace *v1.Namespace) *rhtasv1alpha1.Securesign { + return &rhtasv1alpha1.Securesign{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace.Name, + Name: "test", + Annotations: map[string]string{ + "rhtas.redhat.com/metrics": "false", + }, + }, + Spec: rhtasv1alpha1.SecuresignSpec{ + Rekor: rhtasv1alpha1.RekorSpec{ + ExternalAccess: rhtasv1alpha1.ExternalAccess{ + Enabled: true, + }, + RekorSearchUI: rhtasv1alpha1.RekorSearchUI{ + Enabled: utils.Pointer(false), + }, + }, + Fulcio: rhtasv1alpha1.FulcioSpec{ + ExternalAccess: rhtasv1alpha1.ExternalAccess{ + Enabled: true, + }, + Config: rhtasv1alpha1.FulcioConfig{ + OIDCIssuers: []rhtasv1alpha1.OIDCIssuer{ + { + ClientID: support.OidcClientID(), + IssuerURL: support.OidcIssuerUrl(), + Issuer: support.OidcIssuerUrl(), + Type: "email", + }, + }}, + Certificate: rhtasv1alpha1.FulcioCert{ + OrganizationName: "MyOrg", + OrganizationEmail: "my@email.org", + CommonName: "fulcio", + }, + }, + Ctlog: rhtasv1alpha1.CTlogSpec{}, + Tuf: rhtasv1alpha1.TufSpec{ + ExternalAccess: rhtasv1alpha1.ExternalAccess{ + Enabled: true, + }, + }, + Trillian: rhtasv1alpha1.TrillianSpec{Db: rhtasv1alpha1.TrillianDB{ + Create: utils.Pointer(true), + Pvc: rhtasv1alpha1.Pvc{ + Retain: ptr.To(false), + }, + }}, + TimestampAuthority: rhtasv1alpha1.TimestampAuthoritySpec{ + ExternalAccess: rhtasv1alpha1.ExternalAccess{ + Enabled: true, + }, + Signer: rhtasv1alpha1.TimestampAuthoritySigner{ + CertificateChain: rhtasv1alpha1.CertificateChain{ + RootCA: rhtasv1alpha1.TsaCertificateAuthority{ + OrganizationName: "MyOrg", + OrganizationEmail: "my@email.org", + CommonName: "tsa.hostname", + }, + IntermediateCA: []rhtasv1alpha1.TsaCertificateAuthority{ + { + OrganizationName: "MyOrg", + OrganizationEmail: "my@email.org", + CommonName: "tsa.hostname", + }, + }, + LeafCA: rhtasv1alpha1.TsaCertificateAuthority{ + OrganizationName: "MyOrg", + OrganizationEmail: "my@email.org", + CommonName: "tsa.hostname", + }, + }, + }, + NTPMonitoring: rhtasv1alpha1.NTPMonitoring{ + Enabled: true, + Config: &rhtasv1alpha1.NtpMonitoringConfig{ + RequestAttempts: 3, + RequestTimeout: 5, + NumServers: 4, + ServerThreshold: 3, + MaxTimeDelta: 6, + Period: 60, + Servers: []string{"time.apple.com", "time.google.com", "time-a-b.nist.gov", "time-b-b.nist.gov", "gbg1.ntp.se"}, + }, + }, + }, + }, + } +} + +func getDeploymentGeneration(ctx context.Context, cli runtimeCli.Client, nn types.NamespacedName) int64 { + deployment := appsv1.Deployment{} + if err := cli.Get(ctx, nn, &deployment); err != nil { + return -1 + } + return deployment.Status.ObservedGeneration +} diff --git a/test/e2e/update/tsa_test.go b/test/e2e/update/tsa_test.go new file mode 100644 index 000000000..e75abf285 --- /dev/null +++ b/test/e2e/update/tsa_test.go @@ -0,0 +1,254 @@ +//go:build integration + +package update + +import ( + "context" + "time" + + tsaAction "github.com/securesign/operator/internal/controller/tsa/actions" + tsaUtils "github.com/securesign/operator/internal/controller/tsa/utils" + "github.com/securesign/operator/test/e2e/support/tas/tsa" + "sigs.k8s.io/yaml" + + "github.com/securesign/operator/test/e2e/support/tas" + "github.com/securesign/operator/test/e2e/support/tas/rekor" + "github.com/securesign/operator/test/e2e/support/tas/tuf" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/securesign/operator/api/v1alpha1" + "github.com/securesign/operator/internal/controller/constants" + tufAction "github.com/securesign/operator/internal/controller/tuf/actions" + "github.com/securesign/operator/test/e2e/support" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/types" + runtimeCli "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("TSA update", Ordered, func() { + SetDefaultEventuallyTimeout(time.Duration(5) * time.Minute) + cli, _ := CreateClient() + ctx := context.TODO() + + var targetImageName string + var namespace *v1.Namespace + var s *v1alpha1.Securesign + + AfterEach(func() { + if CurrentSpecReport().Failed() && support.IsCIEnvironment() { + support.DumpNamespace(ctx, cli, namespace.Name) + } + }) + + BeforeAll(func() { + namespace = support.CreateTestNamespace(ctx, cli) + DeferCleanup(func() { + _ = cli.Delete(ctx, namespace) + }) + s = securesignResource(namespace) + }) + + BeforeAll(func() { + targetImageName = support.PrepareImage(ctx) + }) + + Describe("Install with autogenerated certificates", func() { + BeforeAll(func() { + Expect(cli.Create(ctx, s)).To(Succeed()) + }) + + It("All other components are running", func() { + tas.VerifyAllComponents(ctx, cli, s, true) + }) + }) + + Describe("modify tsa signer and certificate chain", func() { + + var tufGeneration, tsaGeneration int64 + + It("stored current deployment observed generations ", func() { + tufGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}, + ) + Expect(tufGeneration).Should(BeNumerically(">", 0)) + tsaGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: tsaAction.DeploymentName}, + ) + Expect(tsaGeneration).Should(BeNumerically(">", 0)) + }) + + It("modified signer and certificate chain", func() { + Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(s), s)).To(Succeed()) + s.Spec.TimestampAuthority.Signer = v1alpha1.TimestampAuthoritySigner{ + CertificateChain: v1alpha1.CertificateChain{ + CertificateChainRef: &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "test-tsa-secret", + }, + Key: "certificateChain", + }, + }, + File: &v1alpha1.File{ + PrivateKeyRef: &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "test-tsa-secret", + }, + Key: "leafPrivateKey", + }, + PasswordRef: &v1alpha1.SecretKeySelector{ + LocalObjectReference: v1alpha1.LocalObjectReference{ + Name: "test-tsa-secret", + }, + Key: "leafPrivateKeyPassword", + }, + }, + } + Expect(cli.Update(ctx, s)).To(Succeed()) + }) + + It("has status Pending: waiting on test-tsa-secret", func() { + Eventually(func(g Gomega) string { + ctl := tsa.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + c := meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready) + g.Expect(c).ToNot(BeNil()) + return c.Reason + }).Should(Equal(constants.Pending)) + }) + + It("created test-tsa-secret", func() { + Expect(cli.Create(ctx, tsa.CreateSecrets(namespace.Name, "test-tsa-secret"))).Should(Succeed()) + }) + + It("has status Ready", func() { + Eventually(func(g Gomega) string { + ctl := rekor.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason + }).Should(Equal(constants.Ready)) + }) + + It("updated TSA deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: tsaAction.DeploymentName}) + }).Should(BeNumerically(">", tsaGeneration)) + }) + + It("updated TUF deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: tufAction.DeploymentName}) + }).Should(BeNumerically(">", tufGeneration)) + }) + + It("verify TSA and TUF", func() { + tsa.Verify(ctx, cli, namespace.Name, s.Name) + tuf.Verify(ctx, cli, namespace.Name, s.Name) + }) + + It("verify new configuration", func() { + var pod *v1.Pod + var t *v1alpha1.TimestampAuthority + Eventually(func(g Gomega) { + pod = tsa.GetServerPod(ctx, cli, namespace.Name)() + g.Expect(pod).ToNot(BeNil()) + t = tsa.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(t).ToNot(BeNil()) + }).Should(Succeed()) + + Expect(pod.Spec.Volumes).To(ContainElement(And( + WithTransform(func(v v1.Volume) string { return v.Name }, Equal("tsa-cert-chain")), + WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal("test-tsa-secret")), + ))) + + Expect(pod.Spec.Volumes).To(ContainElement(And( + WithTransform(func(v v1.Volume) string { return v.Name }, Equal("tsa-file-signer-config")), + WithTransform(func(v v1.Volume) string { return v.VolumeSource.Secret.SecretName }, Equal("test-tsa-secret")), + ))) + + certChainSecret := &v1.Secret{} + privateKeySecret := &v1.Secret{} + expectedSecret := &v1.Secret{} + Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: t.Status.Signer.CertificateChain.CertificateChainRef.Name}, certChainSecret)).To(Succeed()) + Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: t.Status.Signer.File.PrivateKeyRef.Name}, privateKeySecret)).To(Succeed()) + Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: "test-tsa-secret"}, expectedSecret)).To(Succeed()) + }) + + It("verify by cosign", func() { + tas.VerifyByCosign(ctx, cli, s, targetImageName) + }) + }) + + Describe("Update NTP config", func() { + + var tsaGeneration int64 + + It("stored current deployment observed generations ", func() { + tsaGeneration = getDeploymentGeneration(ctx, cli, + types.NamespacedName{Namespace: namespace.Name, Name: tsaAction.DeploymentName}, + ) + Expect(tsaGeneration).Should(BeNumerically(">", 0)) + }) + + It("modified NTP config", func() { + Expect(cli.Get(ctx, runtimeCli.ObjectKeyFromObject(s), s)).To(Succeed()) + s.Spec.TimestampAuthority.NTPMonitoring = v1alpha1.NTPMonitoring{ + Enabled: true, + Config: &v1alpha1.NtpMonitoringConfig{ + RequestAttempts: 3, + RequestTimeout: 5, + NumServers: 4, + ServerThreshold: 3, + MaxTimeDelta: 6, + Period: 40, + Servers: []string{"time.apple.com", "time.google.com", "time-a-b.nist.gov", "time-b-b.nist.gov", "gbg1.ntp.se"}, + }, + } + Expect(cli.Update(ctx, s)).To(Succeed()) + }) + + It("has status Ready", func() { + Eventually(func(g Gomega) string { + ctl := rekor.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(ctl).NotTo(BeNil()) + return meta.FindStatusCondition(ctl.Status.Conditions, constants.Ready).Reason + }).Should(Equal(constants.Ready)) + }) + + It("updated TSA deployment", func() { + Eventually(func() int64 { + return getDeploymentGeneration(ctx, cli, types.NamespacedName{Namespace: namespace.Name, Name: tsaAction.DeploymentName}) + }).Should(BeNumerically(">", tsaGeneration)) + }) + + It("verify TSA", func() { + tsa.Verify(ctx, cli, namespace.Name, s.Name) + }) + + It("verify new configuration", func() { + var pod *v1.Pod + var t *v1alpha1.TimestampAuthority + Eventually(func(g Gomega) { + pod = tsa.GetServerPod(ctx, cli, namespace.Name)() + g.Expect(pod).ToNot(BeNil()) + t = tsa.Get(ctx, cli, namespace.Name, s.Name)() + g.Expect(t).ToNot(BeNil()) + }).Should(Succeed()) + + Expect(pod.Spec.Volumes).To(ContainElements(And( + WithTransform(func(v v1.Volume) string { return v.Name }, Equal("ntp-config")), + WithTransform(func(v v1.Volume) string { return v.VolumeSource.ConfigMap.Name }, Equal(t.Status.NTPMonitoring.Config.NtpConfigRef.Name))))) + + cm := &v1.ConfigMap{} + Expect(cli.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: t.Status.NTPMonitoring.Config.NtpConfigRef.Name}, cm)).To(Succeed()) + config := &tsaUtils.NtpConfig{} + Expect(yaml.Unmarshal([]byte(cm.Data["ntp-config.yaml"]), config)).To(Succeed()) + Expect(config.Period).To(Equal(40)) + }) + + It("verify by cosign", func() { + tas.VerifyByCosign(ctx, cli, s, targetImageName) + }) + }) +}) diff --git a/test/e2e/upgrade_test.go b/test/e2e/upgrade_test.go index f56b013c8..706917a3c 100644 --- a/test/e2e/upgrade_test.go +++ b/test/e2e/upgrade_test.go @@ -10,6 +10,16 @@ import ( "strings" "time" + "github.com/securesign/operator/test/e2e/support/tas/ctlog" + + "github.com/securesign/operator/test/e2e/support/tas/tsa" + + "github.com/securesign/operator/test/e2e/support/tas/fulcio" + "github.com/securesign/operator/test/e2e/support/tas/rekor" + "github.com/securesign/operator/test/e2e/support/tas/securesign" + "github.com/securesign/operator/test/e2e/support/tas/trillian" + "github.com/securesign/operator/test/e2e/support/tas/tuf" + semver "github.com/blang/semver/v4" "github.com/onsi/ginkgo/v2/dsl/core" "github.com/onsi/gomega/matchers" @@ -18,12 +28,11 @@ import ( "github.com/securesign/operator/internal/controller/common/utils" "github.com/securesign/operator/internal/controller/constants" ctl "github.com/securesign/operator/internal/controller/ctlog/actions" - fulcio "github.com/securesign/operator/internal/controller/fulcio/actions" - rekor "github.com/securesign/operator/internal/controller/rekor/actions" + fulcioAction "github.com/securesign/operator/internal/controller/fulcio/actions" + rekorAction "github.com/securesign/operator/internal/controller/rekor/actions" "github.com/securesign/operator/internal/controller/securesign/actions" - trillian "github.com/securesign/operator/internal/controller/trillian/actions" - tuf "github.com/securesign/operator/internal/controller/tuf/actions" - "github.com/securesign/operator/test/e2e/support/tas" + trillianAction "github.com/securesign/operator/internal/controller/trillian/actions" + tufAction "github.com/securesign/operator/internal/controller/tuf/actions" clients "github.com/securesign/operator/test/e2e/support/tas/cli" v13 "k8s.io/api/apps/v1" @@ -203,7 +212,7 @@ var _ = Describe("Operator upgrade", Ordered, func() { Certificate: tasv1alpha.FulcioCert{ OrganizationName: "MyOrg", OrganizationEmail: "my@email.org", - CommonName: "fulcio", + CommonName: "fulcioAction", }, }, Ctlog: tasv1alpha.CTlogSpec{}, @@ -220,23 +229,23 @@ var _ = Describe("Operator upgrade", Ordered, func() { gomega.Expect(cli.Create(ctx, securesignDeployment)).To(gomega.Succeed()) - tas.VerifySecuresign(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyFulcio(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyRekor(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyTrillian(ctx, cli, namespace.Name, securesignDeployment.Name, true) - tas.VerifyCTLog(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyTuf(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyRekorSearchUI(ctx, cli, namespace.Name, securesignDeployment.Name) + securesign.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + fulcio.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + rekor.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + trillian.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name, true) + ctlog.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + tuf.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + rekor.VerifySearchUI(ctx, cli, securesignDeployment.Namespace) }) It("Sign image with cosign cli", func() { - rfulcio = tas.GetFulcio(ctx, cli, namespace.Name, securesignDeployment.Name)() + rfulcio = fulcio.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() gomega.Expect(rfulcio).ToNot(gomega.BeNil()) - rrekor = tas.GetRekor(ctx, cli, namespace.Name, securesignDeployment.Name)() + rrekor = rekor.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() gomega.Expect(rrekor).ToNot(gomega.BeNil()) - rtuf = tas.GetTuf(ctx, cli, namespace.Name, securesignDeployment.Name)() + rtuf = tuf.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() gomega.Expect(rtuf).ToNot(gomega.BeNil()) var err error @@ -251,8 +260,8 @@ var _ = Describe("Operator upgrade", Ordered, func() { gomega.Expect(clients.Execute( "cosign", "sign", "-y", - "--fulcio-url="+rfulcio.Status.Url, - "--rekor-url="+rrekor.Status.Url, + "--fulcioAction-url="+rfulcio.Status.Url, + "--rekorAction-url="+rrekor.Status.Url, "--oidc-issuer="+support.OidcIssuerUrl(), "--oidc-client-id="+support.OidcClientID(), "--identity-token="+oidcToken, @@ -295,13 +304,13 @@ var _ = Describe("Operator upgrade", Ordered, func() { gomega.Expect(updated.GT(base)).To(gomega.BeTrue()) for k, v := range map[string]string{ - fulcio.DeploymentName: constants.FulcioServerImage, - ctl.DeploymentName: constants.CTLogImage, - tuf.DeploymentName: constants.TufImage, - rekor.ServerDeploymentName: constants.RekorServerImage, - rekor.SearchUiDeploymentName: constants.RekorSearchUiImage, - trillian.LogsignerDeploymentName: constants.TrillianLogSignerImage, - trillian.LogserverDeploymentName: constants.TrillianServerImage, + fulcioAction.DeploymentName: constants.FulcioServerImage, + ctl.DeploymentName: constants.CTLogImage, + tufAction.DeploymentName: constants.TufImage, + rekorAction.ServerDeploymentName: constants.RekorServerImage, + rekorAction.SearchUiDeploymentName: constants.RekorSearchUiImage, + trillianAction.LogsignerDeploymentName: constants.TrillianLogSignerImage, + trillianAction.LogserverDeploymentName: constants.TrillianServerImage, } { gomega.Eventually(func(g gomega.Gomega) string { d := &v13.Deployment{} @@ -314,19 +323,19 @@ var _ = Describe("Operator upgrade", Ordered, func() { }).Should(gomega.Equal(v), fmt.Sprintf("Expected %s deployment image to be equal to %s", k, v)) } - tas.VerifySecuresign(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyFulcio(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyRekor(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyTrillian(ctx, cli, namespace.Name, securesignDeployment.Name, true) - tas.VerifyCTLog(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyTuf(ctx, cli, namespace.Name, securesignDeployment.Name) - tas.VerifyRekorSearchUI(ctx, cli, namespace.Name, securesignDeployment.Name) + securesign.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + fulcio.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + rekor.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + trillian.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name, true) + ctlog.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + tuf.Verify(ctx, cli, securesignDeployment.Namespace, securesignDeployment.Name) + rekor.VerifySearchUI(ctx, cli, securesignDeployment.Namespace) }) It("Verify image signature after upgrade", func() { gomega.Expect(clients.Execute( "cosign", "verify", - "--rekor-url="+rrekor.Status.Url, + "--rekorAction-url="+rrekor.Status.Url, "--certificate-identity-regexp", ".*@redhat", "--certificate-oidc-issuer-regexp", ".*keycloak.*", prevImageName, @@ -336,8 +345,8 @@ var _ = Describe("Operator upgrade", Ordered, func() { It("Sign and Verify new image after upgrade", func() { gomega.Expect(clients.Execute( "cosign", "sign", "-y", - "--fulcio-url="+rfulcio.Status.Url, - "--rekor-url="+rrekor.Status.Url, + "--fulcioAction-url="+rfulcio.Status.Url, + "--rekorAction-url="+rrekor.Status.Url, "--oidc-issuer="+support.OidcIssuerUrl(), "--oidc-client-id="+support.OidcClientID(), "--identity-token="+oidcToken, @@ -346,7 +355,7 @@ var _ = Describe("Operator upgrade", Ordered, func() { gomega.Expect(clients.Execute( "cosign", "verify", - "--rekor-url="+rrekor.Status.Url, + "--rekorAction-url="+rrekor.Status.Url, "--certificate-identity-regexp", ".*@redhat", "--certificate-oidc-issuer-regexp", ".*keycloak.*", newImageName, @@ -354,11 +363,11 @@ var _ = Describe("Operator upgrade", Ordered, func() { }) It("Install Timestamp Authority after upgrade", func() { - securesign := tas.GetSecuresign(ctx, cli, namespace.Name, securesignDeployment.Name)() - gomega.Expect(securesign).ToNot(gomega.BeNil()) - gomega.Expect(meta.FindStatusCondition(securesign.GetConditions(), actions.TSACondition).Reason).To(gomega.Equal(constants.NotDefined)) + s := securesign.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() + gomega.Expect(s).ToNot(gomega.BeNil()) + gomega.Expect(meta.FindStatusCondition(s.GetConditions(), actions.TSACondition).Reason).To(gomega.Equal(constants.NotDefined)) - securesign.Spec.TimestampAuthority = tasv1alpha.TimestampAuthoritySpec{ + s.Spec.TimestampAuthority = tasv1alpha.TimestampAuthoritySpec{ ExternalAccess: tasv1alpha.ExternalAccess{ Enabled: true, }, @@ -396,21 +405,21 @@ var _ = Describe("Operator upgrade", Ordered, func() { }, }, } - gomega.Expect(cli.Update(ctx, securesign)).Should(gomega.Succeed()) + gomega.Expect(cli.Update(ctx, s)).Should(gomega.Succeed()) }) It("tsa should reach a ready state", func() { gomega.Eventually(func() string { - securesign := tas.GetSecuresign(ctx, cli, namespace.Name, securesignDeployment.Name)() - gomega.Expect(securesign).ToNot(gomega.BeNil()) - return meta.FindStatusCondition(securesign.Status.Conditions, actions.TSACondition).Reason + s := securesign.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() + gomega.Expect(s).ToNot(gomega.BeNil()) + return meta.FindStatusCondition(s.Status.Conditions, actions.TSACondition).Reason }).Should(gomega.Equal(constants.Ready)) }) It("operator should generate TSA secret", func() { - securesign := tas.GetSecuresign(ctx, cli, namespace.Name, securesignDeployment.Name)() - gomega.Expect(securesign).ToNot(gomega.BeNil()) - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() + s := securesign.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() + gomega.Expect(s).ToNot(gomega.BeNil()) + tsa := tsa.Get(ctx, cli, namespace.Name, s.Name)() gomega.Eventually(func() *v1.Secret { scr := &v1.Secret{} @@ -430,10 +439,10 @@ var _ = Describe("Operator upgrade", Ordered, func() { }) It("tsa is running with operator generated certs and keys", func() { - securesign := tas.GetSecuresign(ctx, cli, namespace.Name, securesignDeployment.Name)() - gomega.Expect(securesign).ToNot(gomega.BeNil()) - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() - server := tas.GetTSAServerPod(ctx, cli, namespace.Name)() + s := securesign.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() + gomega.Expect(s).ToNot(gomega.BeNil()) + t := tsa.Get(ctx, cli, namespace.Name, s.Name)() + server := tsa.GetServerPod(ctx, cli, namespace.Name)() gomega.Expect(server).NotTo(gomega.BeNil()) gomega.Expect(server.Spec.Volumes).To( @@ -443,7 +452,7 @@ var _ = Describe("Operator upgrade", Ordered, func() { return volume.VolumeSource.Secret.SecretName } return "" - }, gomega.Equal(tsa.Status.Signer.CertificateChain.CertificateChainRef.Name))), + }, gomega.Equal(t.Status.Signer.CertificateChain.CertificateChainRef.Name))), ) gomega.Expect(server.Spec.Volumes).To( gomega.ContainElement( @@ -452,16 +461,16 @@ var _ = Describe("Operator upgrade", Ordered, func() { return volume.VolumeSource.Secret.SecretName } return "" - }, gomega.Equal(tsa.Status.Signer.File.PrivateKeyRef.Name))), + }, gomega.Equal(t.Status.Signer.File.PrivateKeyRef.Name))), ) }) It("ntp monitoring config created", func() { - securesign := tas.GetSecuresign(ctx, cli, namespace.Name, securesignDeployment.Name)() - gomega.Expect(securesign).ToNot(gomega.BeNil()) - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() + s := securesign.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() + gomega.Expect(s).ToNot(gomega.BeNil()) + t := tsa.Get(ctx, cli, namespace.Name, s.Name)() - server := tas.GetTSAServerPod(ctx, cli, namespace.Name)() + server := tsa.GetServerPod(ctx, cli, namespace.Name)() gomega.Expect(server).NotTo(gomega.BeNil()) gomega.Expect(server.Spec.Volumes).To( gomega.ContainElement( @@ -470,38 +479,38 @@ var _ = Describe("Operator upgrade", Ordered, func() { return volume.VolumeSource.ConfigMap.Name } return "" - }, gomega.Equal(tsa.Status.NTPMonitoring.Config.NtpConfigRef.Name))), + }, gomega.Equal(t.Status.NTPMonitoring.Config.NtpConfigRef.Name))), ) }) It("Update tuf root with tsa certificate chain", func() { - securesign := tas.GetSecuresign(ctx, cli, namespace.Name, securesignDeployment.Name)() - gomega.Expect(securesign).ToNot(gomega.BeNil()) + s := securesign.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() + gomega.Expect(s).ToNot(gomega.BeNil()) - securesign.Spec.Tuf.Keys = append(securesign.Spec.Tuf.Keys, tasv1alpha.TufKey{Name: "tsa.certchain.pem"}) - gomega.Expect(cli.Update(ctx, securesign)).To(gomega.Succeed()) + s.Spec.Tuf.Keys = append(s.Spec.Tuf.Keys, tasv1alpha.TufKey{Name: "tsa.certchain.pem"}) + gomega.Expect(cli.Update(ctx, s)).To(gomega.Succeed()) gomega.Eventually(func() string { - tuf := tas.GetTuf(ctx, cli, namespace.Name, securesign.Name)() - return meta.FindStatusCondition(tuf.Status.Conditions, constants.Ready).Reason + t := tuf.Get(ctx, cli, namespace.Name, s.Name)() + return meta.FindStatusCondition(t.Status.Conditions, constants.Ready).Reason }).Should(gomega.Equal(constants.Ready)) }) It("Sign and Verify image after tsa install", func() { - securesign := tas.GetSecuresign(ctx, cli, namespace.Name, securesignDeployment.Name)() - gomega.Expect(securesign).ToNot(gomega.BeNil()) - tsa := tas.GetTSA(ctx, cli, namespace.Name, securesign.Name)() + s := securesign.Get(ctx, cli, namespace.Name, securesignDeployment.Name)() + gomega.Expect(s).ToNot(gomega.BeNil()) + t := tsa.Get(ctx, cli, namespace.Name, s.Name)() - gomega.Expect(tsa).ToNot(gomega.BeNil()) + gomega.Expect(t).ToNot(gomega.BeNil()) gomega.Eventually(func() error { - return tas.GetTSACertificateChain(ctx, cli, tsa.Namespace, tsa.Name, tsa.Status.Url) + return tsa.GetCertificateChain(ctx, cli, t.Namespace, t.Name, t.Status.Url) }).Should(gomega.Succeed()) gomega.Expect(clients.Execute("cosign", "initialize", "--mirror="+rtuf.Status.Url, "--root="+rtuf.Status.Url+"/root.json")).To(gomega.Succeed()) gomega.Expect(clients.Execute( "cosign", "verify", - "--rekor-url="+rrekor.Status.Url, + "--rekorAction-url="+rrekor.Status.Url, "--certificate-identity-regexp", ".*@redhat", "--certificate-oidc-issuer-regexp", ".*keycloak.*", prevImageName, @@ -509,9 +518,9 @@ var _ = Describe("Operator upgrade", Ordered, func() { gomega.Expect(clients.Execute( "cosign", "sign", "-y", - "--fulcio-url="+rfulcio.Status.Url, - "--rekor-url="+rrekor.Status.Url, - "--timestamp-server-url="+tsa.Status.Url+"/api/v1/timestamp", + "--fulcioAction-url="+rfulcio.Status.Url, + "--rekorAction-url="+rrekor.Status.Url, + "--timestamp-server-url="+t.Status.Url+"/api/v1/timestamp", "--oidc-issuer="+support.OidcIssuerUrl(), "--oidc-client-id="+support.OidcClientID(), "--identity-token="+oidcToken, @@ -520,7 +529,7 @@ var _ = Describe("Operator upgrade", Ordered, func() { gomega.Expect(clients.Execute( "cosign", "verify", - "--rekor-url="+rrekor.Status.Url, + "--rekorAction-url="+rrekor.Status.Url, "--timestamp-certificate-chain=ts_chain.pem", "--certificate-identity-regexp", ".*@redhat", "--certificate-oidc-issuer-regexp", ".*keycloak.*",