This repository has been archived by the owner on Mar 16, 2024. It is now read-only.
/
signatures.go
83 lines (76 loc) · 2.89 KB
/
signatures.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package signatures
import (
"context"
"fmt"
"github.com/acorn-io/baaah/pkg/merr"
internalv1 "github.com/acorn-io/runtime/pkg/apis/internal.acorn.io/v1"
acornsign "github.com/acorn-io/runtime/pkg/cosign"
"github.com/acorn-io/runtime/pkg/imageselector/signatures/annotations"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/sigstore/cosign/v2/pkg/cosign"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type MatchImageSignatureOpts struct {
NoCache bool
}
func VerifySignatureRule(ctx context.Context, c client.Reader, namespace string, image string, rule internalv1.SignatureRules, opts MatchImageSignatureOpts, remoteOpts ...remote.Option) error {
// TODO(@iwilltry42): Move this out of here again or only leave default here and merge incoming?
// ... alternatively, re-do the function signature to avoid unnecessary external calls in EnsureReferences
verifyOpts := acornsign.VerifyOpts{
Namespace: namespace,
AnnotationRules: nil,
Key: "",
SignatureAlgorithm: "sha256",
RemoteOpts: remoteOpts,
NoCache: opts.NoCache,
}
if err := acornsign.EnsureReferences(ctx, c, image, namespace, &verifyOpts); err != nil {
return fmt.Errorf(".signatures: %w", err)
}
// We're using Kubernetes' label selector logic here, but we need to override the error handling
// since the annotations we're matching on are less restricted than Kubernetes labels
sel, err := annotations.GenerateSelector(rule.Annotations, annotations.DefaultAnnotationOpts)
if err != nil {
return fmt.Errorf("failed to parse annotation rule: %w", err)
}
verifyOpts.AnnotationRules = sel
// allOf: all signatures must pass verification
if len(rule.SignedBy.AllOf) != 0 {
for allOfRuleIndex, signer := range rule.SignedBy.AllOf {
verifyOpts.Key = signer
err := acornsign.VerifySignature(ctx, verifyOpts)
if err != nil {
if _, ok := err.(*cosign.VerificationError); !ok {
return fmt.Errorf(".signatures.allOf.%d: %w", allOfRuleIndex, err)
}
return err // failed or errored in allOf -> noping out
}
}
}
// anyOf: only one signature must pass verification
var anyOfErrs []error
if len(rule.SignedBy.AnyOf) != 0 {
anyOfOK := false
for anyOfRuleIndex, signer := range rule.SignedBy.AnyOf {
verifyOpts.Key = signer
err := acornsign.VerifySignature(ctx, verifyOpts)
if err == nil {
anyOfOK = true
break
} else {
if _, ok := err.(*cosign.VerificationError); !ok {
e := fmt.Errorf(".signatures.anyOf.%d: %w", anyOfRuleIndex, err)
anyOfErrs = append(anyOfErrs, e)
}
}
}
if !anyOfOK {
if len(anyOfErrs) == len(rule.SignedBy.AnyOf) {
// we had errors for all anyOf rules (not failed verification, but actual errors)
return fmt.Errorf(".signatures.anyOf.*: %w", merr.NewErrors(anyOfErrs...))
}
return fmt.Errorf(".signature.anyOf: failed") // failed or errored in all anyOf, try next IAR
}
}
return nil
}