From cdd016158c4a17fb007d41e5d3625999be80a5e8 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Tue, 26 Apr 2022 11:17:06 -0400 Subject: [PATCH 01/10] Add signaturePullSecrets support Signed-off-by: Denny Hoang --- config/300-clusterimagepolicy.yaml | 8 ++++ .../v1alpha1/clusterimagepolicy_types.go | 2 + .../v1alpha1/zz_generated.deepcopy.go | 9 ++++- pkg/cosign/kubernetes/webhook/validator.go | 38 +++++++++++++++---- .../kubernetes/webhook/validator_test.go | 8 ++-- .../clusterimagepolicy_test.go | 30 +++++++++++++++ 6 files changed, 82 insertions(+), 13 deletions(-) diff --git a/config/300-clusterimagepolicy.yaml b/config/300-clusterimagepolicy.yaml index cd5545806f1..c04545f7ebf 100644 --- a/config/300-clusterimagepolicy.yaml +++ b/config/300-clusterimagepolicy.yaml @@ -139,6 +139,14 @@ spec: properties: oci: type: string + signaturePullSecrets: + type: array + items: + type: object + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string images: type: array items: diff --git a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go index bad7c36483b..c7f7292a210 100644 --- a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go +++ b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go @@ -115,6 +115,8 @@ type KeyRef struct { type Source struct { // +optional OCI string `json:"oci,omitempty"` + // +optional + SignaturePullSecrets []v1.LocalObjectReference `json:"signaturePullSecrets,omitempty"` } // TLog specifies the URL to a transparency log that holds diff --git a/pkg/apis/cosigned/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/cosigned/v1alpha1/zz_generated.deepcopy.go index c6926846691..4bfa48469a3 100644 --- a/pkg/apis/cosigned/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/cosigned/v1alpha1/zz_generated.deepcopy.go @@ -62,7 +62,9 @@ func (in *Authority) DeepCopyInto(out *Authority) { if in.Sources != nil { in, out := &in.Sources, &out.Sources *out = make([]Source, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.CTLog != nil { in, out := &in.CTLog, &out.CTLog @@ -311,6 +313,11 @@ func (in *Policy) DeepCopy() *Policy { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Source) DeepCopyInto(out *Source) { *out = *in + if in.SignaturePullSecrets != nil { + in, out := &in.SignaturePullSecrets, &out.SignaturePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } return } diff --git a/pkg/cosign/kubernetes/webhook/validator.go b/pkg/cosign/kubernetes/webhook/validator.go index 54440b133fd..c74e4401ca9 100644 --- a/pkg/cosign/kubernetes/webhook/validator.go +++ b/pkg/cosign/kubernetes/webhook/validator.go @@ -76,7 +76,7 @@ func (v *Validator) ValidatePodSpecable(ctx context.Context, wp *duckv1.WithPod) ServiceAccountName: wp.Spec.Template.Spec.ServiceAccountName, ImagePullSecrets: imagePullSecrets, } - return v.validatePodSpec(ctx, &wp.Spec.Template.Spec, opt).ViaField("spec.template.spec") + return v.validatePodSpec(ctx, wp.Namespace, &wp.Spec.Template.Spec, opt).ViaField("spec.template.spec") } // ValidatePod implements duckv1.PodValidator @@ -94,7 +94,7 @@ func (v *Validator) ValidatePod(ctx context.Context, p *duckv1.Pod) *apis.FieldE ServiceAccountName: p.Spec.ServiceAccountName, ImagePullSecrets: imagePullSecrets, } - return v.validatePodSpec(ctx, &p.Spec, opt).ViaField("spec") + return v.validatePodSpec(ctx, p.Namespace, &p.Spec, opt).ViaField("spec") } // ValidateCronJob implements duckv1.CronJobValidator @@ -112,10 +112,10 @@ func (v *Validator) ValidateCronJob(ctx context.Context, c *duckv1.CronJob) *api ServiceAccountName: c.Spec.JobTemplate.Spec.Template.Spec.ServiceAccountName, ImagePullSecrets: imagePullSecrets, } - return v.validatePodSpec(ctx, &c.Spec.JobTemplate.Spec.Template.Spec, opt).ViaField("spec.jobTemplate.spec.template.spec") + return v.validatePodSpec(ctx, c.Namespace, &c.Spec.JobTemplate.Spec.Template.Spec, opt).ViaField("spec.jobTemplate.spec.template.spec") } -func (v *Validator) validatePodSpec(ctx context.Context, ps *corev1.PodSpec, opt k8schain.Options) (errs *apis.FieldError) { +func (v *Validator) validatePodSpec(ctx context.Context, namespace string, ps *corev1.PodSpec, opt k8schain.Options) (errs *apis.FieldError) { kc, err := k8schain.New(ctx, v.client, opt) if err != nil { logging.FromContext(ctx).Warnf("Unable to build k8schain: %v", err) @@ -171,7 +171,7 @@ func (v *Validator) validatePodSpec(ctx context.Context, ps *corev1.PodSpec, opt // If there is at least one policy that matches, that means it // has to be satisfied. if len(policies) > 0 { - signatures, fieldErrors := validatePolicies(ctx, ref, policies, ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(kc))) + signatures, fieldErrors := validatePolicies(ctx, namespace, ref, policies, ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(kc))) if len(signatures) != len(policies) { logging.FromContext(ctx).Warnf("Failed to validate at least one policy for %s", ref.Name()) @@ -235,7 +235,7 @@ func (v *Validator) validatePodSpec(ctx context.Context, ps *corev1.PodSpec, opt // Note that if an image does not match any policies, it's perfectly // reasonable that the return value is 0, nil since there were no errors, but // the image was not validated against any matching policy and hence authority. -func validatePolicies(ctx context.Context, ref name.Reference, policies map[string]webhookcip.ClusterImagePolicy, remoteOpts ...ociremote.Option) (map[string]*PolicyResult, map[string][]error) { +func validatePolicies(ctx context.Context, namespace string, ref name.Reference, policies map[string]webhookcip.ClusterImagePolicy, remoteOpts ...ociremote.Option) (map[string]*PolicyResult, map[string][]error) { type retChannelType struct { name string policyResult *PolicyResult @@ -258,7 +258,7 @@ func validatePolicies(ctx context.Context, ref name.Reference, policies map[stri go func() { result := retChannelType{name: cipName} - result.policyResult, result.errors = ValidatePolicy(ctx, ref, cip, remoteOpts...) + result.policyResult, result.errors = ValidatePolicy(ctx, namespace, ref, cip, remoteOpts...) if len(result.errors) == 0 { // Ok, at least one Authority on the policy passed. If there's a CIP level // policy, apply it against the results of the successful Authorities @@ -314,7 +314,7 @@ func validatePolicies(ctx context.Context, ref name.Reference, policies map[stri // signatures OR attestations if atttestations were specified. // Returns PolicyResult, or errors encountered if none of the authorities // passed. -func ValidatePolicy(ctx context.Context, ref name.Reference, cip webhookcip.ClusterImagePolicy, remoteOpts ...ociremote.Option) (*PolicyResult, []error) { +func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, cip webhookcip.ClusterImagePolicy, remoteOpts ...ociremote.Option) (*PolicyResult, []error) { // Each gofunc creates and puts one of these into a results channel. // Once each gofunc finishes, we go through the channel and pull out // the results. @@ -335,6 +335,28 @@ func ValidatePolicy(ctx context.Context, ref name.Reference, cip webhookcip.Clus authorityRemoteOpts := remoteOpts authorityRemoteOpts = append(authorityRemoteOpts, authority.RemoteOpts...) + for _, source := range authority.Sources { + if len(source.SignaturePullSecrets) > 0 { + + signaturePullSecrets := make([]string, 0, len(source.SignaturePullSecrets)) + for _, s := range source.SignaturePullSecrets { + signaturePullSecrets = append(signaturePullSecrets, s.Name) + } + + opt := k8schain.Options{ + Namespace: namespace, + ImagePullSecrets: signaturePullSecrets, + } + + kc, err := k8schain.New(ctx, kubeclient.Get(ctx), opt) + if err != nil { + result.err = err + } + + authorityRemoteOpts = append(authorityRemoteOpts, ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(kc))) + } + } + if len(authority.Attestations) > 0 { // We're doing the verify-attestations path, so validate (.att) validatedAttestations, err := ValidatePolicyAttestationsForAuthority(ctx, ref, authority, authorityRemoteOpts...) diff --git a/pkg/cosign/kubernetes/webhook/validator_test.go b/pkg/cosign/kubernetes/webhook/validator_test.go index 4dedd275ac5..64b2c57ee19 100644 --- a/pkg/cosign/kubernetes/webhook/validator_test.go +++ b/pkg/cosign/kubernetes/webhook/validator_test.go @@ -480,7 +480,7 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== } // Check the core mechanics - got := v.validatePodSpec(testContext, test.ps, k8schain.Options{}) + got := v.validatePodSpec(testContext, system.Namespace(), test.ps, k8schain.Options{}) if (got != nil) != (test.want != nil) { t.Errorf("validatePodSpec() = %v, wanted %v", got, test.want) } else if got != nil && got.Error() != test.want.Error() { @@ -1373,7 +1373,7 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== if test.customContext != nil { testContext = test.customContext } - got, gotErrs := ValidatePolicy(testContext, digest, test.policy) + got, gotErrs := ValidatePolicy(testContext, system.Namespace(), digest, test.policy) validateErrors(t, test.wantErrs, gotErrs) if !reflect.DeepEqual(test.want, got) { t.Errorf("unexpected PolicyResult, want: %+v got: %+v", test.want, got) @@ -1418,7 +1418,7 @@ func TestValidatePolicyCancelled(t *testing.T) { } wantErrs := []string{"context was canceled before validation completed"} cancelFunc() - _, gotErrs := ValidatePolicy(testContext, digest, cip) + _, gotErrs := ValidatePolicy(testContext, system.Namespace(), digest, cip) validateErrors(t, wantErrs, gotErrs) } @@ -1445,6 +1445,6 @@ func TestValidatePoliciesCancelled(t *testing.T) { } wantErrs := []string{"context was canceled before validation completed"} cancelFunc() - _, gotErrs := validatePolicies(testContext, digest, map[string]webhookcip.ClusterImagePolicy{"testcip": cip}) + _, gotErrs := validatePolicies(testContext, system.Namespace(), digest, map[string]webhookcip.ClusterImagePolicy{"testcip": cip}) validateErrors(t, wantErrs, gotErrs["internalerror"]) } diff --git a/pkg/reconciler/clusterimagepolicy/clusterimagepolicy_test.go b/pkg/reconciler/clusterimagepolicy/clusterimagepolicy_test.go index ee6e667be52..95e7ab97a0b 100644 --- a/pkg/reconciler/clusterimagepolicy/clusterimagepolicy_test.go +++ b/pkg/reconciler/clusterimagepolicy/clusterimagepolicy_test.go @@ -83,6 +83,8 @@ RCTfQ5s1kD+hGMSE1rH7s46hmXEeyhnlRnaGF8eMU/SBJE/2NKPnxE7WzQ== // This is the patch for inlined secret for keyless cakey ref data inlinedSecretKeylessPatch = `[{"op":"replace","path":"/data/test-cip-2","value":"{\"images\":[{\"glob\":\"ghcr.io/example/*\"}],\"authorities\":[{\"name\":\"authority-0\",\"keyless\":{\"ca-cert\":{\"data\":\"-----BEGIN PUBLIC KEY-----\\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExB6+H6054/W1SJgs5JR6AJr6J35J\\nRCTfQ5s1kD+hGMSE1rH7s46hmXEeyhnlRnaGF8eMU/SBJE/2NKPnxE7WzQ==\\n-----END PUBLIC KEY-----\"}}}]}"}]` + + replaceCIPKeySourcePatch = `[{"op":"replace","path":"/data/test-cip","value":"{\"images\":[{\"glob\":\"ghcr.io/example/*\"}],\"authorities\":[{\"name\":\"authority-0\",\"key\":{\"data\":\"-----BEGIN PUBLIC KEY-----\\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExB6+H6054/W1SJgs5JR6AJr6J35J\\nRCTfQ5s1kD+hGMSE1rH7s46hmXEeyhnlRnaGF8eMU/SBJE/2NKPnxE7WzQ==\\n-----END PUBLIC KEY-----\"},\"source\":[{\"oci\":\"example.com/alternative/signature\",\"signaturePullSecrets\":[{\"name\":\"signaturePullSecretName\"}]}]}]}"}]` ) func TestReconcile(t *testing.T) { @@ -527,6 +529,34 @@ func TestReconcile(t *testing.T) { WantPatches: []clientgotesting.PatchActionImpl{ patchKMS(mainContext, t, fakeKMSKey), }, + }, { + Name: "Key with data, source, and signature pull secrets", + Key: testKey, + + SkipNamespaceValidation: true, // Cluster scoped + Objects: []runtime.Object{ + NewClusterImagePolicy(cipName, + WithFinalizer, + WithImagePattern(v1alpha1.ImagePattern{ + Glob: glob, + }), + WithAuthority(v1alpha1.Authority{ + Key: &v1alpha1.KeyRef{ + Data: validPublicKeyData, + }, + Sources: []v1alpha1.Source{{ + OCI: "example.com/alternative/signature", + SignaturePullSecrets: []corev1.LocalObjectReference{ + {Name: "signaturePullSecretName"}, + }, + }}, + }), + ), + makeConfigMap(), + }, + WantPatches: []clientgotesting.PatchActionImpl{ + makePatch(replaceCIPKeySourcePatch), + }, }, {}} logger := logtesting.TestLogger(t) From f4e0162c505e3ef8b646cf1c6cff85b229d89f81 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Tue, 26 Apr 2022 12:04:49 -0400 Subject: [PATCH 02/10] Abstract signaturePullSecrets remoteOpts Signed-off-by: Denny Hoang --- .../clusterimagepolicy_types.go | 39 +++++++++++++++++-- pkg/cosign/kubernetes/webhook/validator.go | 23 +---------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go index cbda0c7c0fb..0bcec8c51ce 100644 --- a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go +++ b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go @@ -15,17 +15,21 @@ package clusterimagepolicy import ( + "context" "crypto" "crypto/x509" "encoding/json" "encoding/pem" + "github.com/google/go-containerregistry/pkg/authn/k8schain" "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/pkg/errors" "github.com/sigstore/cosign/pkg/apis/cosigned/v1alpha1" - "github.com/sigstore/cosign/pkg/oci/remote" - + ociremote "github.com/sigstore/cosign/pkg/oci/remote" "knative.dev/pkg/apis" + kubeclient "knative.dev/pkg/client/injection/kube/client" + "knative.dev/pkg/logging" ) // ClusterImagePolicy defines the images that go through verification @@ -58,7 +62,7 @@ type Authority struct { // RemoteOpts are not marshalled because they are an unsupported type // RemoteOpts will be populated by the Authority UnmarshalJSON override // +optional - RemoteOpts []remote.Option `json:"-"` + RemoteOpts []ociremote.Option `json:"-"` // +optional Attestations []AttestationPolicy `json:"attestations,omitempty"` } @@ -139,7 +143,7 @@ func (a *Authority) UnmarshalJSON(data []byte) error { if targetRepoOverride, err := name.NewRepository(source.OCI); err != nil { return errors.Wrap(err, "failed to determine source") } else if (targetRepoOverride != name.Repository{}) { - rawAuthority.RemoteOpts = append(rawAuthority.RemoteOpts, remote.WithTargetRepository(targetRepoOverride)) + rawAuthority.RemoteOpts = append(rawAuthority.RemoteOpts, ociremote.WithTargetRepository(targetRepoOverride)) } } } @@ -149,6 +153,33 @@ func (a *Authority) UnmarshalJSON(data []byte) error { return nil } +func (a *Authority) SourceSignaturePullSecretsOpts(ctx context.Context, namespace string) []ociremote.Option { + var ret []ociremote.Option + for _, source := range a.Sources { + if len(source.SignaturePullSecrets) > 0 { + + signaturePullSecrets := make([]string, 0, len(source.SignaturePullSecrets)) + for _, s := range source.SignaturePullSecrets { + signaturePullSecrets = append(signaturePullSecrets, s.Name) + } + + opt := k8schain.Options{ + Namespace: namespace, + ImagePullSecrets: signaturePullSecrets, + } + + kc, err := k8schain.New(ctx, kubeclient.Get(ctx), opt) + if err != nil { + logging.FromContext(ctx).Errorf("failed creating keychain: %+v", err) + } + + ret = append(ret, ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(kc))) + } + } + + return ret +} + func ConvertClusterImagePolicyV1alpha1ToWebhook(in *v1alpha1.ClusterImagePolicy) *ClusterImagePolicy { copyIn := in.DeepCopy() diff --git a/pkg/cosign/kubernetes/webhook/validator.go b/pkg/cosign/kubernetes/webhook/validator.go index c74e4401ca9..40f4a9780d0 100644 --- a/pkg/cosign/kubernetes/webhook/validator.go +++ b/pkg/cosign/kubernetes/webhook/validator.go @@ -334,28 +334,7 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c // Assignment for appendAssign lint error authorityRemoteOpts := remoteOpts authorityRemoteOpts = append(authorityRemoteOpts, authority.RemoteOpts...) - - for _, source := range authority.Sources { - if len(source.SignaturePullSecrets) > 0 { - - signaturePullSecrets := make([]string, 0, len(source.SignaturePullSecrets)) - for _, s := range source.SignaturePullSecrets { - signaturePullSecrets = append(signaturePullSecrets, s.Name) - } - - opt := k8schain.Options{ - Namespace: namespace, - ImagePullSecrets: signaturePullSecrets, - } - - kc, err := k8schain.New(ctx, kubeclient.Get(ctx), opt) - if err != nil { - result.err = err - } - - authorityRemoteOpts = append(authorityRemoteOpts, ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(kc))) - } - } + authorityRemoteOpts = append(authorityRemoteOpts, authority.SourceSignaturePullSecretsOpts(ctx, namespace)...) if len(authority.Attestations) > 0 { // We're doing the verify-attestations path, so validate (.att) From ac5ab2e83b3ade06dcc216af3a14b0a541b867c3 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Tue, 26 Apr 2022 15:24:56 -0400 Subject: [PATCH 03/10] Add validation and signaturePullSecrets test cases Signed-off-by: Denny Hoang --- pkg/apis/config/image_policies_test.go | 39 +++++++++++++++ pkg/apis/config/store_test.go | 3 ++ .../testdata/config-image-policies.yaml | 20 ++++++++ .../v1alpha1/clusterimagepolicy_validation.go | 12 ++++- .../clusterimagepolicy_validation_test.go | 48 ++++++++++++++++++- 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/pkg/apis/config/image_policies_test.go b/pkg/apis/config/image_policies_test.go index 0336bb80afe..02c9c062e5b 100644 --- a/pkg/apis/config/image_policies_test.go +++ b/pkg/apis/config/image_policies_test.go @@ -154,6 +154,45 @@ func TestGetAuthorities(t *testing.T) { if got := c[matchedPolicy].Authorities[0].Attestations[0].Data; got != want { t.Errorf("Did not get what I wanted %q, got %+v", want, got) } + + // Test source oci and signaturePullSecrets + c, err = defaults.GetMatchingPolicies("sourceocionly") + checkGetMatches(t, c, err) + if len(c) != 1 { + t.Errorf("Wanted 1 match, got %d", len(c)) + } + matchedPolicy = "cluster-image-policy-source-oci" + if got := len(c[matchedPolicy].Authorities); got != 1 { + t.Errorf("Did not get what I wanted %d, got %d", 1, got) + } + if got := len(c[matchedPolicy].Authorities[0].Sources); got != 1 { + t.Errorf("Did not get what I wanted %d, got %d", 1, got) + } + + want = "example.registry.com/alternative/signature" + if got := c[matchedPolicy].Authorities[0].Sources[0].OCI; got != want { + t.Errorf("Did not get what I wanted %q, got %+v", want, got) + } + + c, err = defaults.GetMatchingPolicies("sourceocisignaturepullsecrets") + checkGetMatches(t, c, err) + if len(c) != 1 { + t.Errorf("Wanted 1 match, got %d", len(c)) + } + matchedPolicy = "cluster-image-policy-source-oci-signature-pull-secrets" + if got := len(c[matchedPolicy].Authorities); got != 1 { + t.Errorf("Did not get what I wanted %d, got %d", 1, got) + } + if got := len(c[matchedPolicy].Authorities[0].Sources); got != 1 { + t.Errorf("Did not get what I wanted %d, got %d", 1, got) + } + if got := len(c[matchedPolicy].Authorities[0].Sources[0].SignaturePullSecrets); got != 1 { + t.Errorf("Did not get what I wanted %d, got %d", 1, got) + } + want = "examplePullSecret" + if got := c[matchedPolicy].Authorities[0].Sources[0].SignaturePullSecrets[0].Name; got != want { + t.Errorf("Did not get what I wanted %q, got %+v", want, got) + } } func checkGetMatches(t *testing.T, c map[string]webhookcip.ClusterImagePolicy, err error) { diff --git a/pkg/apis/config/store_test.go b/pkg/apis/config/store_test.go index 767548920e8..cfb163f7298 100644 --- a/pkg/apis/config/store_test.go +++ b/pkg/apis/config/store_test.go @@ -20,6 +20,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sigstore/cosign/pkg/oci/remote" "k8s.io/apimachinery/pkg/api/resource" logtesting "knative.dev/pkg/logging/testing" @@ -28,6 +29,8 @@ import ( var ignoreStuff = cmp.Options{ cmpopts.IgnoreUnexported(resource.Quantity{}), + // Ignore functional remote options + cmpopts.IgnoreTypes((remote.Option)(nil)), } func TestStoreLoadWithContext(t *testing.T) { diff --git a/pkg/apis/config/testdata/config-image-policies.yaml b/pkg/apis/config/testdata/config-image-policies.yaml index 23a0d0f6c79..30238e47680 100644 --- a/pkg/apis/config/testdata/config-image-policies.yaml +++ b/pkg/apis/config/testdata/config-image-policies.yaml @@ -105,3 +105,23 @@ data: policy: type: cue data: "cip level cue here" + cluster-image-policy-source-oci: | + images: + - regex: .*sourceocionly.* + authorities: + - name: attestation-0 + key: + data: inlinedata here + source: + - oci: "example.registry.com/alternative/signature" + cluster-image-policy-source-oci-signature-pull-secrets: | + images: + - regex: .*sourceocisignaturepullsecrets.* + authorities: + - name: attestation-0 + key: + data: inlinedata here + source: + - oci: "example.registry.com/alternative/signature" + signaturePullSecrets: + - name: examplePullSecret diff --git a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation.go b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation.go index a307f33a834..0f5aff7bea0 100644 --- a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation.go +++ b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation.go @@ -84,8 +84,8 @@ func (authority *Authority) Validate(ctx context.Context) *apis.FieldError { errs = errs.Also(authority.Keyless.Validate(ctx).ViaField("keyless")) } - for _, source := range authority.Sources { - errs = errs.Also(source.Validate(ctx).ViaField("source")) + for i, source := range authority.Sources { + errs = errs.Also(source.Validate(ctx).ViaFieldIndex("source", i)) } for _, att := range authority.Attestations { @@ -144,6 +144,14 @@ func (source *Source) Validate(ctx context.Context) *apis.FieldError { if source.OCI == "" { errs = errs.Also(apis.ErrMissingField("oci")) } + + if len(source.SignaturePullSecrets) > 0 { + for i, secret := range source.SignaturePullSecrets { + if secret.Name == "" { + errs = errs.Also(apis.ErrMissingField("name")).ViaFieldIndex("signaturePullSecrets", i) + } + } + } return errs } diff --git a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation_test.go b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation_test.go index 9848f0a15e5..9610ca2d1ff 100644 --- a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation_test.go +++ b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_validation_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" "knative.dev/pkg/apis" ) @@ -415,7 +416,7 @@ func TestAuthoritiesValidation(t *testing.T) { { name: "Should fail when source oci is empty", expectErr: true, - errorString: "missing field(s): spec.authorities[0].source.oci", + errorString: "missing field(s): spec.authorities[0].source[0].oci", policy: ClusterImagePolicy{ Spec: ClusterImagePolicySpec{ Images: []ImagePattern{{Regex: ".*"}}, @@ -486,6 +487,51 @@ func TestAuthoritiesValidation(t *testing.T) { }, }, }, + { + name: "Should fail with signaturePullSecret name empty", + expectErr: true, + errorString: "missing field(s): spec.authorities[0].source[0].signaturePullSecrets[0].name", + policy: ClusterImagePolicy{ + Spec: ClusterImagePolicySpec{ + Images: []ImagePattern{{Regex: ".*"}}, + Authorities: []Authority{ + { + Key: &KeyRef{KMS: "kms://key/path"}, + Sources: []Source{ + { + OCI: "registry1", + SignaturePullSecrets: []v1.LocalObjectReference{ + {Name: ""}, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Should pass with signaturePullSecret name filled", + expectErr: false, + policy: ClusterImagePolicy{ + Spec: ClusterImagePolicySpec{ + Images: []ImagePattern{{Regex: ".*"}}, + Authorities: []Authority{ + { + Key: &KeyRef{KMS: "kms://key/path"}, + Sources: []Source{ + { + OCI: "registry1", + SignaturePullSecrets: []v1.LocalObjectReference{ + {Name: "testPullSecrets"}, + }, + }, + }, + }, + }, + }, + }, + }, } for _, test := range tests { From bd9b24199fc521a58499f9be88bf603a3de51542 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Tue, 26 Apr 2022 15:42:02 -0400 Subject: [PATCH 04/10] Test Authorities RemoteOpts count Signed-off-by: Denny Hoang --- pkg/apis/config/image_policies_test.go | 38 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/pkg/apis/config/image_policies_test.go b/pkg/apis/config/image_policies_test.go index 02c9c062e5b..3b3eead05a9 100644 --- a/pkg/apis/config/image_policies_test.go +++ b/pkg/apis/config/image_policies_test.go @@ -155,37 +155,29 @@ func TestGetAuthorities(t *testing.T) { t.Errorf("Did not get what I wanted %q, got %+v", want, got) } - // Test source oci and signaturePullSecrets + // Test source oci + matchedPolicy = "cluster-image-policy-source-oci" c, err = defaults.GetMatchingPolicies("sourceocionly") checkGetMatches(t, c, err) if len(c) != 1 { t.Errorf("Wanted 1 match, got %d", len(c)) } - matchedPolicy = "cluster-image-policy-source-oci" - if got := len(c[matchedPolicy].Authorities); got != 1 { - t.Errorf("Did not get what I wanted %d, got %d", 1, got) - } - if got := len(c[matchedPolicy].Authorities[0].Sources); got != 1 { - t.Errorf("Did not get what I wanted %d, got %d", 1, got) - } + checkSourceOCI(t, c[matchedPolicy].Authorities) want = "example.registry.com/alternative/signature" if got := c[matchedPolicy].Authorities[0].Sources[0].OCI; got != want { t.Errorf("Did not get what I wanted %q, got %+v", want, got) } + // Test source signaturePullSecrets + matchedPolicy = "cluster-image-policy-source-oci-signature-pull-secrets" c, err = defaults.GetMatchingPolicies("sourceocisignaturepullsecrets") checkGetMatches(t, c, err) if len(c) != 1 { t.Errorf("Wanted 1 match, got %d", len(c)) } - matchedPolicy = "cluster-image-policy-source-oci-signature-pull-secrets" - if got := len(c[matchedPolicy].Authorities); got != 1 { - t.Errorf("Did not get what I wanted %d, got %d", 1, got) - } - if got := len(c[matchedPolicy].Authorities[0].Sources); got != 1 { - t.Errorf("Did not get what I wanted %d, got %d", 1, got) - } + + checkSourceOCI(t, c[matchedPolicy].Authorities) if got := len(c[matchedPolicy].Authorities[0].Sources[0].SignaturePullSecrets); got != 1 { t.Errorf("Did not get what I wanted %d, got %d", 1, got) } @@ -230,3 +222,19 @@ func checkPublicKey(t *testing.T, gotKey crypto.PublicKey) { t.Errorf("Did not get what I wanted %s, got %s", inlineKeyData, string(pemBytes)) } } + +func checkSourceOCI(t *testing.T, authority []webhookcip.Authority) { + t.Helper() + + if got := len(authority); got != 1 { + t.Errorf("Did not get what I wanted %d, got %d", 1, got) + } + if got := len(authority[0].Sources); got != 1 { + t.Errorf("Did not get what I wanted %d, got %d", 1, got) + } + + want := len(authority[0].Sources) + if got := len(authority[0].RemoteOpts); got != want { + t.Errorf("Did not get what I wanted %d, got %d", want, got) + } +} From 1877ea662aedfbdbc7c8cccbdbb63277c91782d1 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Tue, 26 Apr 2022 15:56:14 -0400 Subject: [PATCH 05/10] Comment on not storing in Authority RemoteOpts Signed-off-by: Denny Hoang --- .../webhook/clusterimagepolicy/clusterimagepolicy_types.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go index 0bcec8c51ce..e2069f2faa4 100644 --- a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go +++ b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go @@ -153,6 +153,8 @@ func (a *Authority) UnmarshalJSON(data []byte) error { return nil } +// SourceSignaturePullSecretsOpts creates the signaturePullSecrets remoteOpts +// This is not stored in the Authority under RemoteOpts as the namespace can be different func (a *Authority) SourceSignaturePullSecretsOpts(ctx context.Context, namespace string) []ociremote.Option { var ret []ociremote.Option for _, source := range a.Sources { From e5e10606cad664705f1f553ca19ece62e18ea0d3 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Tue, 26 Apr 2022 15:58:03 -0400 Subject: [PATCH 06/10] Fix lint issue Signed-off-by: Denny Hoang --- .../webhook/clusterimagepolicy/clusterimagepolicy_types.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go index e2069f2faa4..e5d6cc9fa26 100644 --- a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go +++ b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go @@ -159,7 +159,6 @@ func (a *Authority) SourceSignaturePullSecretsOpts(ctx context.Context, namespac var ret []ociremote.Option for _, source := range a.Sources { if len(source.SignaturePullSecrets) > 0 { - signaturePullSecrets := make([]string, 0, len(source.SignaturePullSecrets)) for _, s := range source.SignaturePullSecrets { signaturePullSecrets = append(signaturePullSecrets, s.Name) From f4063d6b7703d9788722dea7dcbfc0db4c4ed05c Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Wed, 27 Apr 2022 14:29:14 -0400 Subject: [PATCH 07/10] Add podSpec signaturePullSecrets test Signed-off-by: Denny Hoang --- .../clusterimagepolicy_types.go | 5 +- pkg/cosign/kubernetes/webhook/validator.go | 17 +++--- .../kubernetes/webhook/validator_test.go | 53 +++++++++++++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go index e5d6cc9fa26..d283519705d 100644 --- a/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go +++ b/pkg/cosign/kubernetes/webhook/clusterimagepolicy/clusterimagepolicy_types.go @@ -155,7 +155,7 @@ func (a *Authority) UnmarshalJSON(data []byte) error { // SourceSignaturePullSecretsOpts creates the signaturePullSecrets remoteOpts // This is not stored in the Authority under RemoteOpts as the namespace can be different -func (a *Authority) SourceSignaturePullSecretsOpts(ctx context.Context, namespace string) []ociremote.Option { +func (a *Authority) SourceSignaturePullSecretsOpts(ctx context.Context, namespace string) ([]ociremote.Option, error) { var ret []ociremote.Option for _, source := range a.Sources { if len(source.SignaturePullSecrets) > 0 { @@ -172,13 +172,14 @@ func (a *Authority) SourceSignaturePullSecretsOpts(ctx context.Context, namespac kc, err := k8schain.New(ctx, kubeclient.Get(ctx), opt) if err != nil { logging.FromContext(ctx).Errorf("failed creating keychain: %+v", err) + return nil, err } ret = append(ret, ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(kc))) } } - return ret + return ret, nil } func ConvertClusterImagePolicyV1alpha1ToWebhook(in *v1alpha1.ClusterImagePolicy) *ClusterImagePolicy { diff --git a/pkg/cosign/kubernetes/webhook/validator.go b/pkg/cosign/kubernetes/webhook/validator.go index 40f4a9780d0..3e8fe3965a5 100644 --- a/pkg/cosign/kubernetes/webhook/validator.go +++ b/pkg/cosign/kubernetes/webhook/validator.go @@ -322,7 +322,7 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c name string attestations map[string][]PolicySignature signatures []PolicySignature - err error + errs []error } results := make(chan retChannelType, len(cip.Authorities)) for _, authority := range cip.Authorities { @@ -334,20 +334,25 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c // Assignment for appendAssign lint error authorityRemoteOpts := remoteOpts authorityRemoteOpts = append(authorityRemoteOpts, authority.RemoteOpts...) - authorityRemoteOpts = append(authorityRemoteOpts, authority.SourceSignaturePullSecretsOpts(ctx, namespace)...) + + signaturePullSecretsOpts, err := authority.SourceSignaturePullSecretsOpts(ctx, namespace) + if err != nil { + result.errs = append(result.errs, err) + } + authorityRemoteOpts = append(authorityRemoteOpts, signaturePullSecretsOpts...) if len(authority.Attestations) > 0 { // We're doing the verify-attestations path, so validate (.att) validatedAttestations, err := ValidatePolicyAttestationsForAuthority(ctx, ref, authority, authorityRemoteOpts...) if err != nil { - result.err = err + result.errs = append(result.errs, err) } else { result.attestations = validatedAttestations } } else { validatedSignatures, err := ValidatePolicySignaturesForAuthority(ctx, ref, authority, authorityRemoteOpts...) if err != nil { - result.err = err + result.errs = append(result.errs, err) } else { result.signatures = validatedSignatures } @@ -371,8 +376,8 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c continue } switch { - case result.err != nil: - authorityErrors = append(authorityErrors, result.err) + case len(result.errs) > 0: + authorityErrors = append(authorityErrors, result.errs...) case len(result.signatures) > 0: policyResult.AuthorityMatches[result.name] = AuthorityMatch{Signatures: result.signatures} case len(result.attestations) > 0: diff --git a/pkg/cosign/kubernetes/webhook/validator_test.go b/pkg/cosign/kubernetes/webhook/validator_test.go index 64b2c57ee19..778bfffbbf7 100644 --- a/pkg/cosign/kubernetes/webhook/validator_test.go +++ b/pkg/cosign/kubernetes/webhook/validator_test.go @@ -43,6 +43,7 @@ import ( "github.com/sigstore/cosign/pkg/oci/static" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "knative.dev/pkg/apis" @@ -468,6 +469,58 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== return errs }(), cvs: fail, + }, { + name: "simple, error, authority source signaturePullSecrets, invalid secret", + ps: &corev1.PodSpec{ + InitContainers: []corev1.Container{{ + Name: "setup-stuff", + Image: digest.String(), + }}, + Containers: []corev1.Container{{ + Name: "user-container", + Image: digest.String(), + }}, + }, + customContext: config.ToContext(ctx, + &config.Config{ + ImagePolicyConfig: &config.ImagePolicyConfig{ + Policies: map[string]webhookcip.ClusterImagePolicy{ + "cluster-image-policy": { + Images: []v1alpha1.ImagePattern{{ + Regex: ".*", + }}, + Authorities: []webhookcip.Authority{ + { + Key: &webhookcip.KeyRef{ + Data: authorityKeyCosignPubString, + PublicKeys: []crypto.PublicKey{authorityKeyCosignPub}, + }, + Sources: []v1alpha1.Source{{ + OCI: "example.com/alternative/signature", + SignaturePullSecrets: []v1.LocalObjectReference{{ + Name: "non-existing-secret", + }}, + }}, + }, + }, + }, + }, + }, + }, + ), + want: func() *apis.FieldError { + var errs *apis.FieldError + fe := apis.ErrGeneric("failed policy: cluster-image-policy", "image").ViaFieldIndex("initContainers", 0) + fe.Details = fmt.Sprintf("%s secrets \"non-existing-secret\" not found", digest.String()) + errs = errs.Also(fe) + + fe2 := apis.ErrGeneric("failed policy: cluster-image-policy", "image").ViaFieldIndex("containers", 0) + fe2.Details = fmt.Sprintf("%s secrets \"non-existing-secret\" not found", digest.String()) + errs = errs.Also(fe2) + + return errs + }(), + cvs: authorityPublicKeyCVS, }} for _, test := range tests { From 65bd3fd20b3df6f65895c68cbfbced9735fbee1a Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Wed, 27 Apr 2022 15:30:13 -0400 Subject: [PATCH 08/10] Add valid signaturePullSecrets test Signed-off-by: Denny Hoang --- .../kubernetes/webhook/validator_test.go | 67 ++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/pkg/cosign/kubernetes/webhook/validator_test.go b/pkg/cosign/kubernetes/webhook/validator_test.go index 778bfffbbf7..feae66c2c66 100644 --- a/pkg/cosign/kubernetes/webhook/validator_test.go +++ b/pkg/cosign/kubernetes/webhook/validator_test.go @@ -43,7 +43,6 @@ import ( "github.com/sigstore/cosign/pkg/oci/static" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "knative.dev/pkg/apis" @@ -123,11 +122,23 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== }) kc := fakekube.Get(ctx) - kc.CoreV1().ServiceAccounts("default").Create(ctx, &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default", - }, - }, metav1.CreateOptions{}) + // Setup service acc and fakeSignaturePullSecrets for "default" and "cosign-system" namespace + for _, ns := range []string{"default", system.Namespace()} { + kc.CoreV1().ServiceAccounts(ns).Create(ctx, &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + }, metav1.CreateOptions{}) + + kc.CoreV1().Secrets(ns).Create(ctx, &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fakeSignaturePullSecrets", + }, + Data: map[string][]byte{ + "dockerconfigjson": []byte(`{"auths":{"https://index.docker.io/v1/":{"username":"username","password":"password","auth":"dXNlcm5hbWU6cGFzc3dvcmQ="}}`), + }, + }, metav1.CreateOptions{}) + } v := NewValidator(ctx, secretName) @@ -470,7 +481,7 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== }(), cvs: fail, }, { - name: "simple, error, authority source signaturePullSecrets, invalid secret", + name: "simple, error, authority source signaturePullSecrets, non exisiting secret", ps: &corev1.PodSpec{ InitContainers: []corev1.Container{{ Name: "setup-stuff", @@ -497,7 +508,7 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== }, Sources: []v1alpha1.Source{{ OCI: "example.com/alternative/signature", - SignaturePullSecrets: []v1.LocalObjectReference{{ + SignaturePullSecrets: []corev1.LocalObjectReference{{ Name: "non-existing-secret", }}, }}, @@ -521,6 +532,46 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== return errs }(), cvs: authorityPublicKeyCVS, + }, { + name: "simple, no error, authority source signaturePullSecrets, valid secret", + ps: &corev1.PodSpec{ + InitContainers: []corev1.Container{{ + Name: "setup-stuff", + Image: digest.String(), + }}, + Containers: []corev1.Container{{ + Name: "user-container", + Image: digest.String(), + }}, + }, + customContext: config.ToContext(ctx, + &config.Config{ + ImagePolicyConfig: &config.ImagePolicyConfig{ + Policies: map[string]webhookcip.ClusterImagePolicy{ + "cluster-image-policy": { + Images: []v1alpha1.ImagePattern{{ + Regex: ".*", + }}, + Authorities: []webhookcip.Authority{ + { + Key: &webhookcip.KeyRef{ + Data: authorityKeyCosignPubString, + PublicKeys: []crypto.PublicKey{authorityKeyCosignPub}, + }, + Sources: []v1alpha1.Source{{ + OCI: "example.com/alternative/signature", + SignaturePullSecrets: []corev1.LocalObjectReference{{ + Name: "fakeSignaturePullSecrets", + }}, + }}, + }, + }, + }, + }, + }, + }, + ), + cvs: authorityPublicKeyCVS, }} for _, test := range tests { From 350e17e5ac619e6c26995207eacc7f3ae71a8be8 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Thu, 28 Apr 2022 09:49:06 -0400 Subject: [PATCH 09/10] early return err; add signaturePullSecrets comment Signed-off-by: Denny Hoang --- .../cosigned/v1alpha1/clusterimagepolicy_types.go | 3 +++ pkg/cosign/kubernetes/webhook/validator.go | 14 ++++++++------ pkg/cosign/kubernetes/webhook/validator_test.go | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go index c7f7292a210..c4a6329e279 100644 --- a/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go +++ b/pkg/apis/cosigned/v1alpha1/clusterimagepolicy_types.go @@ -115,6 +115,9 @@ type KeyRef struct { type Source struct { // +optional OCI string `json:"oci,omitempty"` + // SignaturePullSecrets is an optional list of references to secrets in the + // same namespace as the deploying resource for pulling any of the signatures + // used by this Source. // +optional SignaturePullSecrets []v1.LocalObjectReference `json:"signaturePullSecrets,omitempty"` } diff --git a/pkg/cosign/kubernetes/webhook/validator.go b/pkg/cosign/kubernetes/webhook/validator.go index 3e8fe3965a5..d1a2454fd1a 100644 --- a/pkg/cosign/kubernetes/webhook/validator.go +++ b/pkg/cosign/kubernetes/webhook/validator.go @@ -322,7 +322,7 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c name string attestations map[string][]PolicySignature signatures []PolicySignature - errs []error + err error } results := make(chan retChannelType, len(cip.Authorities)) for _, authority := range cip.Authorities { @@ -337,7 +337,9 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c signaturePullSecretsOpts, err := authority.SourceSignaturePullSecretsOpts(ctx, namespace) if err != nil { - result.errs = append(result.errs, err) + result.err = err + results <- result + return } authorityRemoteOpts = append(authorityRemoteOpts, signaturePullSecretsOpts...) @@ -345,14 +347,14 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c // We're doing the verify-attestations path, so validate (.att) validatedAttestations, err := ValidatePolicyAttestationsForAuthority(ctx, ref, authority, authorityRemoteOpts...) if err != nil { - result.errs = append(result.errs, err) + result.err = err } else { result.attestations = validatedAttestations } } else { validatedSignatures, err := ValidatePolicySignaturesForAuthority(ctx, ref, authority, authorityRemoteOpts...) if err != nil { - result.errs = append(result.errs, err) + result.err = err } else { result.signatures = validatedSignatures } @@ -376,8 +378,8 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c continue } switch { - case len(result.errs) > 0: - authorityErrors = append(authorityErrors, result.errs...) + case result.err != nil: + authorityErrors = append(authorityErrors, result.err) case len(result.signatures) > 0: policyResult.AuthorityMatches[result.name] = AuthorityMatch{Signatures: result.signatures} case len(result.attestations) > 0: diff --git a/pkg/cosign/kubernetes/webhook/validator_test.go b/pkg/cosign/kubernetes/webhook/validator_test.go index feae66c2c66..183ef8e7bd1 100644 --- a/pkg/cosign/kubernetes/webhook/validator_test.go +++ b/pkg/cosign/kubernetes/webhook/validator_test.go @@ -531,7 +531,7 @@ UoJou2P8sbDxpLiE/v3yLw1/jyOrCPWYHWFXnyyeGlkgSVefG54tNoK7Uw== return errs }(), - cvs: authorityPublicKeyCVS, + cvs: fail, }, { name: "simple, no error, authority source signaturePullSecrets, valid secret", ps: &corev1.PodSpec{ From d085a5fadd63f6bc00814e3a521a80412ddaf547 Mon Sep 17 00:00:00 2001 From: Denny Hoang Date: Thu, 28 Apr 2022 10:45:59 -0400 Subject: [PATCH 10/10] codegen update Signed-off-by: Denny Hoang --- config/300-clusterimagepolicy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/300-clusterimagepolicy.yaml b/config/300-clusterimagepolicy.yaml index c04545f7ebf..bf4524c41ad 100644 --- a/config/300-clusterimagepolicy.yaml +++ b/config/300-clusterimagepolicy.yaml @@ -140,6 +140,7 @@ spec: oci: type: string signaturePullSecrets: + description: SignaturePullSecrets is an optional list of references to secrets in the same namespace as the deploying resource for pulling any of the signatures used by this Source. type: array items: type: object