From 74bd852c1dc430fb6504d494dfc8c364b01d22ed Mon Sep 17 00:00:00 2001 From: Max Jonas Werner Date: Tue, 16 Apr 2024 19:32:55 +0200 Subject: [PATCH] Add flags for issuer/subject OCI signature verification This change introduces two new flags to `create source oci` for providing the values to the `OCIRepository.spec.verify.matchOIDCIdentity.(issuer,subject)` fields. Signed-off-by: Max Jonas Werner --- cmd/flux/create_source_oci.go | 49 ++++++++++++++----- cmd/flux/create_source_oci_test.go | 27 +++++++++- .../export_with_complete_verification.golden | 16 ++++++ .../testdata/oci/export_with_issuer.golden | 16 ++++++ .../testdata/oci/export_with_subject.golden | 16 ++++++ 5 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 cmd/flux/testdata/oci/export_with_complete_verification.golden create mode 100644 cmd/flux/testdata/oci/export_with_issuer.golden create mode 100644 cmd/flux/testdata/oci/export_with_subject.golden diff --git a/cmd/flux/create_source_oci.go b/cmd/flux/create_source_oci.go index 6393380c42..afa9b36ad4 100644 --- a/cmd/flux/create_source_oci.go +++ b/cmd/flux/create_source_oci.go @@ -43,25 +43,36 @@ var createSourceOCIRepositoryCmd = &cobra.Command{ Example: ` # Create an OCIRepository for a public container image flux create source oci podinfo \ --url=oci://ghcr.io/stefanprodan/manifests/podinfo \ - --tag=6.1.6 \ + --tag=6.6.2 \ --interval=10m + + # Create an OCIRepository with signature verification + flux create source oci podinfo \ + --url=oci://ghcr.io/stefanprodan/manifests/podinfo \ + --tag=6.6.2 \ + --interval=10m \ + --verify-provider=cosign \ + --verify-subject="^https://github.com/stefanprodan/podinfo/.github/workflows/release.yml@refs/tags/6.6.2$" \ + --verify-issuer="^https://token.actions.githubusercontent.com$" `, RunE: createSourceOCIRepositoryCmdRun, } type sourceOCIRepositoryFlags struct { - url string - tag string - semver string - digest string - secretRef string - serviceAccount string - certSecretRef string - verifyProvider flags.SourceOCIVerifyProvider - verifySecretRef string - ignorePaths []string - provider flags.SourceOCIProvider - insecure bool + url string + tag string + semver string + digest string + secretRef string + serviceAccount string + certSecretRef string + verifyProvider flags.SourceOCIVerifyProvider + verifySecretRef string + verifyOIDCIssuer string + verifySubject string + ignorePaths []string + provider flags.SourceOCIProvider + insecure bool } var sourceOCIRepositoryArgs = newSourceOCIFlags() @@ -83,6 +94,8 @@ func init() { createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.certSecretRef, "cert-ref", "", "the name of a secret to use for TLS certificates") createSourceOCIRepositoryCmd.Flags().Var(&sourceOCIRepositoryArgs.verifyProvider, "verify-provider", sourceOCIRepositoryArgs.verifyProvider.Description()) createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.verifySecretRef, "verify-secret-ref", "", "the name of a secret to use for signature verification") + createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.verifySubject, "verify-subject", "", "regular expression to use for the OIDC subject during signature verification") + createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.verifyOIDCIssuer, "verify-issuer", "", "regular expression to use for the OIDC issuer during signature verification") createSourceOCIRepositoryCmd.Flags().StringSliceVar(&sourceOCIRepositoryArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore resources (can specify multiple paths with commas: path1,path2)") createSourceOCIRepositoryCmd.Flags().BoolVar(&sourceOCIRepositoryArgs.insecure, "insecure", false, "for when connecting to a non-TLS registries over plain HTTP") @@ -168,8 +181,18 @@ func createSourceOCIRepositoryCmdRun(cmd *cobra.Command, args []string) error { Name: secretName, } } + verifyIssuer := sourceOCIRepositoryArgs.verifyOIDCIssuer + verifySubject := sourceOCIRepositoryArgs.verifySubject + if verifyIssuer != "" || verifySubject != "" { + repository.Spec.Verify.MatchOIDCIdentity = []sourcev1.OIDCIdentityMatch{{ + Issuer: verifyIssuer, + Subject: verifySubject, + }} + } } else if sourceOCIRepositoryArgs.verifySecretRef != "" { return fmt.Errorf("a verification provider must be specified when a secret is specified") + } else if sourceOCIRepositoryArgs.verifyOIDCIssuer != "" || sourceOCIRepositoryArgs.verifySubject != "" { + return fmt.Errorf("a verification provider must be specified when OIDC issuer/subject is specified") } if createArgs.export { diff --git a/cmd/flux/create_source_oci_test.go b/cmd/flux/create_source_oci_test.go index 743632a6e2..da08d9f640 100644 --- a/cmd/flux/create_source_oci_test.go +++ b/cmd/flux/create_source_oci_test.go @@ -37,10 +37,35 @@ func TestCreateSourceOCI(t *testing.T) { assertFunc: assertError("url is required"), }, { - name: "verify provider not specified", + name: "verify secret specified but provider missing", args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --verify-secret-ref=cosign-pub", assertFunc: assertError("a verification provider must be specified when a secret is specified"), }, + { + name: "verify issuer specified but provider missing", + args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --verify-issuer=github.com", + assertFunc: assertError("a verification provider must be specified when OIDC issuer/subject is specified"), + }, + { + name: "verify identity specified but provider missing", + args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --verify-subject=developer", + assertFunc: assertError("a verification provider must be specified when OIDC issuer/subject is specified"), + }, + { + name: "verify issuer specified but subject missing", + args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --verify-issuer=github --verify-provider=cosign --export", + assertFunc: assertGoldenFile("./testdata/oci/export_with_issuer.golden"), + }, + { + name: "all verify fields set", + args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --verify-issuer=github verify-subject=stefanprodan --verify-provider=cosign --export", + assertFunc: assertGoldenFile("./testdata/oci/export_with_issuer.golden"), + }, + { + name: "verify subject specified but issuer missing", + args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --verify-subject=stefanprodan --verify-provider=cosign --export", + assertFunc: assertGoldenFile("./testdata/oci/export_with_subject.golden"), + }, { name: "export manifest", args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --interval 10m --export", diff --git a/cmd/flux/testdata/oci/export_with_complete_verification.golden b/cmd/flux/testdata/oci/export_with_complete_verification.golden new file mode 100644 index 0000000000..8b862a83dd --- /dev/null +++ b/cmd/flux/testdata/oci/export_with_complete_verification.golden @@ -0,0 +1,16 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: OCIRepository +metadata: + name: podinfo + namespace: flux-system +spec: + interval: 0s + ref: + tag: 6.3.5 + url: oci://ghcr.io/stefanprodan/manifests/podinfo + verify: + matchOIDCIdentity: + - issuer: github + subject: stefanprodan + provider: cosign diff --git a/cmd/flux/testdata/oci/export_with_issuer.golden b/cmd/flux/testdata/oci/export_with_issuer.golden new file mode 100644 index 0000000000..3abaf36a1b --- /dev/null +++ b/cmd/flux/testdata/oci/export_with_issuer.golden @@ -0,0 +1,16 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: OCIRepository +metadata: + name: podinfo + namespace: flux-system +spec: + interval: 0s + ref: + tag: 6.3.5 + url: oci://ghcr.io/stefanprodan/manifests/podinfo + verify: + matchOIDCIdentity: + - issuer: github + subject: "" + provider: cosign diff --git a/cmd/flux/testdata/oci/export_with_subject.golden b/cmd/flux/testdata/oci/export_with_subject.golden new file mode 100644 index 0000000000..93eca53b88 --- /dev/null +++ b/cmd/flux/testdata/oci/export_with_subject.golden @@ -0,0 +1,16 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: OCIRepository +metadata: + name: podinfo + namespace: flux-system +spec: + interval: 0s + ref: + tag: 6.3.5 + url: oci://ghcr.io/stefanprodan/manifests/podinfo + verify: + matchOIDCIdentity: + - issuer: "" + subject: stefanprodan + provider: cosign