Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
change: fix interpretation of IARs to check until first allowance
Browse files Browse the repository at this point in the history
Signed-off-by: Thorsten Klein <tk@thklein.io>
  • Loading branch information
iwilltry42 committed May 2, 2023
1 parent 4127910 commit 9afb2ea
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 30 deletions.
38 changes: 23 additions & 15 deletions pkg/cosign/cosign.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,37 @@ type VerifyOpts struct {
// It's outsourced here, so we can ensure that it's used as few times as possible to reduce the number of potential
// GET requests to the registry which would count against potential rate limits.
func EnsureReferences(ctx context.Context, c client.Reader, img string, opts *VerifyOpts) error {
// --- image name to digest hash
imgRef, err := name.ParseReference(img)
if err != nil {
return fmt.Errorf("failed to parse image %s: %w", img, err)
if opts == nil {
opts = &VerifyOpts{}
}

// in the best case, we have a digest ref already, so we don't need to do any external request
if imgDigest, ok := imgRef.(name.Digest); ok {
opts.ImageRef = imgDigest
} else {
imgDigest, err := crane.Digest(imgRef.Name(), opts.CraneOpts...) // this uses HEAD to determine the digest, but falls back to GET if HEAD fails
if opts.ImageRef.Name() == "" {
// --- image name to digest hash
imgRef, err := name.ParseReference(img)
if err != nil {
return fmt.Errorf("failed to resolve image digest: %w", err)
return fmt.Errorf("failed to parse image %s: %w", img, err)
}

opts.ImageRef = imgRef.Context().Digest(imgDigest)
// in the best case, we have a digest ref already, so we don't need to do any external request
if imgDigest, ok := imgRef.(name.Digest); ok {
opts.ImageRef = imgDigest
} else {
imgDigest, err := crane.Digest(imgRef.Name(), opts.CraneOpts...) // this uses HEAD to determine the digest, but falls back to GET if HEAD fails
if err != nil {
return fmt.Errorf("failed to resolve image digest: %w", err)
}

opts.ImageRef = imgRef.Context().Digest(imgDigest)
}
}

signatureRef, err := ensureSignatureArtifact(ctx, c, opts.Namespace, opts.ImageRef, opts.NoCache, opts.OciRemoteOpts, opts.CraneOpts)
if err != nil {
return fmt.Errorf("failed to ensure signature artifact: %w", err)
if opts.SignatureRef.Name() == "" {
signatureRef, err := ensureSignatureArtifact(ctx, c, opts.Namespace, opts.ImageRef, opts.NoCache, opts.OciRemoteOpts, opts.CraneOpts)
if err != nil {
return fmt.Errorf("failed to ensure signature artifact: %w", err)
}
opts.SignatureRef = signatureRef
}
opts.SignatureRef = signatureRef

return nil
}
Expand Down
25 changes: 11 additions & 14 deletions pkg/imageallowrules/imageallowrules.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,11 @@ func CheckImageAgainstRules(ctx context.Context, c client.Reader, namespace stri
CraneOpts: []crane.Option{crane.WithContext(ctx), crane.WithAuthFromKeychain(keychain)},
}

if err := cosign.EnsureReferences(ctx, c, image, &verifyOpts); err != nil {
return fmt.Errorf("error ensuring references for image %s: %w", image, err)
}

ref, err := name.ParseReference(image)
if err != nil {
return fmt.Errorf("error parsing image reference %s: %w", image, err)
}

allowed := false

iarLoop:
for _, imageAllowRule := range imageAllowRules {
// Check if the image is in scope of the ImageAllowRule
Expand All @@ -94,7 +88,12 @@ iarLoop:
}

// > Signatures
// Any verification error or failed verification issue will skip on to the next IAR
for _, rule := range imageAllowRule.Signatures.Rules {
if err := cosign.EnsureReferences(ctx, c, image, &verifyOpts); err != nil {
return fmt.Errorf("error ensuring references for image %s: %w", image, err)
}

verifyOpts.AnnotationRules = rule.Annotations

// allOf: all signatures must pass verification
Expand Down Expand Up @@ -142,13 +141,11 @@ iarLoop:
}
}
}
}

if !allowed {
return &ErrImageNotAllowed{Image: image}
return nil
}

return nil
return &ErrImageNotAllowed{Image: image}
}

func imageCovered(image name.Reference, iar v1.ImageAllowRuleInstance) bool {
Expand Down Expand Up @@ -192,7 +189,7 @@ func imageCovered(image name.Reference, iar v1.ImageAllowRuleInstance) bool {

// matchContext matches the image context against the context pattern, similar to globbing
func matchContext(contextPattern string, imageContext string) error {
re, _, err := imagepattern.NewMatcher(contextPattern, &imagepattern.MatcherOpts{DoubleStarPattern: `[0-9A-Za-z_.-/:]{0,}`})
re, _, err := imagepattern.NewMatcher(contextPattern, &imagepattern.MatcherOpts{DoubleStarPattern: `[0-9A-Za-z_./:-]{0,}`})
if err != nil {
return fmt.Errorf("error parsing context pattern %s: %w", contextPattern, err)
}
Expand All @@ -201,12 +198,12 @@ func matchContext(contextPattern string, imageContext string) error {
return nil
}

return fmt.Errorf("image context %s does not match pattern %s", imageContext, contextPattern)
return fmt.Errorf("image context %s does not match pattern %s (regex: `%s`)", imageContext, contextPattern, re.String())
}

// matchTag matches the image tag against the tag pattern, similar to auto-upgrade pattern
func matchTag(tagPattern string, imageTag string) error {
re, _, err := imagepattern.NewMatcher(tagPattern, &imagepattern.MatcherOpts{DoubleStarPattern: `[0-9A-Za-z_.-/:]{0,}`})
re, _, err := imagepattern.NewMatcher(tagPattern, &imagepattern.MatcherOpts{DoubleStarPattern: `[0-9A-Za-z_./:-]{0,}`})
if err != nil {
return fmt.Errorf("error parsing tag pattern %s: %w", tagPattern, err)
}
Expand All @@ -215,5 +212,5 @@ func matchTag(tagPattern string, imageTag string) error {
return nil
}

return fmt.Errorf("image tag %s does not match pattern %s", imageTag, tagPattern)
return fmt.Errorf("image tag %s does not match pattern %s (regex: `%s`)", imageTag, tagPattern, re.String())
}
2 changes: 1 addition & 1 deletion pkg/server/registry/apigroups/acorn/apps/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (s *Validator) Validate(ctx context.Context, obj runtime.Object) (result fi

if !disableCheckImageAllowRules {
if err := s.checkImageAllowed(ctx, params.Namespace, params.Spec.Image); err != nil {
result = append(result, field.Invalid(field.NewPath("spec", "image"), params.Spec.Image, fmt.Sprintf("disallowed by imageAllowRules: %s", err.Error())))
result = append(result, field.Invalid(field.NewPath("spec", "image"), params.Spec.Image, err.Error()))
return
}
}
Expand Down

0 comments on commit 9afb2ea

Please sign in to comment.