Skip to content

Commit

Permalink
fix: cosign keyless attestations
Browse files Browse the repository at this point in the history
Signed-off-by: Vishal Choudhary <[email protected]>
  • Loading branch information
vishal-chdhry committed Sep 10, 2024
1 parent 5ec3406 commit 9364b39
Show file tree
Hide file tree
Showing 20 changed files with 159 additions and 26 deletions.
4 changes: 3 additions & 1 deletion .crds/nirmata.io_imageverificationpolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,9 @@ spec:
ImageReferences is a list of matching image reference patterns. At least one pattern in the
list must match the image for the rule to apply. Each image reference consists of a registry
address, repository, image, and tag (defaults to latest). Wildcards ('*' and '?') are allowed.
type: string
items:
type: string
type: array
notary:
description: Notary is an array of attributes used to
verify notary signatures
Expand Down
2 changes: 1 addition & 1 deletion .schemas/openapi/v2/schema.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .schemas/openapi/v3/apis/nirmata.io/v1alpha1.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions cmd/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ func Test_Verify(t *testing.T) {
resourcePath: "./examples/cosign-keyless/bad-payload.json",
outputPath: "./examples/cosign-keyless/bad-out.txt",
},
{
name: "cosign keyless attestation pass",
policyPath: "./examples/cosign-keyless-attestations/policy.yaml",
resourcePath: "./examples/cosign-keyless-attestations/payload.json",
outputPath: "./examples/cosign-keyless-attestations/out.txt",
},
{
name: "notary attestation pass",
policyPath: "./examples/notary-attestation-verification/policy.yaml",
Expand Down
3 changes: 2 additions & 1 deletion cmd/examples/aws-signer-image-verification/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: "*"
- imageReferences:
- "*"
externalService:
- apiCall:
method: POST
Expand Down
4 changes: 4 additions & 0 deletions cmd/examples/cosign-keyless-attestations/out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Verification Result:
Results for policy: test
Results for rule: cosign-keyless
Verifying image: ghcr.io/chipzoller/zulu:v0.0.14, result: PASS
42 changes: 42 additions & 0 deletions cmd/examples/cosign-keyless-attestations/payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"containerDefinitions": [
{
"command": [
"/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' > /usr/local/apache2/htdocs/index.html && httpd-foreground\""
],
"entryPoint": [
"sh",
"-c"
],
"essential": true,
"image": "ghcr.io/chipzoller/zulu:v0.0.14",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group" : "/ecs/fargate-task-definition",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"name": "sample-fargate-app",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
}
]
}
],
"cpu": "256",
"executionRoleArn": "arn:aws:iam::012345678910:role/ecsTaskExecutionRole",
"family": "fargate-task-definition",
"memory": "512",
"networkMode": "awsvpc",
"runtimePlatform": {
"operatingSystemFamily": "LINUX"
},
"requiresCompatibilities": [
"FARGATE"
]
}
31 changes: 31 additions & 0 deletions cmd/examples/cosign-keyless-attestations/policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: nirmata.io/v1alpha1
kind: ImageVerificationPolicy
metadata:
name: test
spec:
rules:
- name: cosign-keyless
match:
any:
- (length(containerDefinitions) > `0`): true
imageExtractors:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences:
- ghcr.io/*
cosign:
- keyless:
issuer: https://token.actions.githubusercontent.com
subject: https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/heads/main
rekor:
url: https://rekor.sigstore.dev
ignoreSCT: true
intotoAttestations:
- type: https://slsa.dev/provenance/v0.2
conditions:
- all:
- key: '{{ regex_match(''^https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/heads/main'',''{{
builder.id}}'') }}'
operator: Equals
value: true
3 changes: 2 additions & 1 deletion cmd/examples/cosign-keyless/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: ghcr.io/*
- imageReferences:
- ghcr.io/*
cosign:
- keyless:
issuer: https://accounts.google.com
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: ghcr.io/kyverno/test-verify-image*
- imageReferences:
- ghcr.io/kyverno/test-verify-image*
externalService:
- apiCall:
method: POST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: ghcr.io/kyverno/test-verify-image*
- imageReferences:
- ghcr.io/kyverno/test-verify-image*
externalService:
- apiCall:
method: POST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: ghcr.io/kyverno/test-verify-image*
- imageReferences:
- ghcr.io/kyverno/test-verify-image*
externalService:
- apiCall:
method: POST
Expand Down
3 changes: 2 additions & 1 deletion cmd/examples/external-api-image-verification/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: ghcr.io/kyverno/test-verify-image*
- imageReferences:
- ghcr.io/kyverno/test-verify-image*
externalService:
- apiCall:
method: POST
Expand Down
3 changes: 2 additions & 1 deletion cmd/examples/notary-attestation-verification/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: ghcr.io/kyverno/test-verify-image*
- imageReferences:
- ghcr.io/kyverno/test-verify-image*
notary:
- certs: |-
-----BEGIN CERTIFICATE-----
Expand Down
3 changes: 2 additions & 1 deletion cmd/examples/notary-image-verification/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ spec:
- name: test
path: /containerDefinitions/*/image/
verify:
- imageReferences: ghcr.io/kyverno/test-verify-image*
- imageReferences:
- ghcr.io/kyverno/test-verify-image*
notary:
- certs: |-
-----BEGIN CERTIFICATE-----
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/v1alpha1/policy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ type VerificationRule struct {
// ImageReferences is a list of matching image reference patterns. At least one pattern in the
// list must match the image for the rule to apply. Each image reference consists of a registry
// address, repository, image, and tag (defaults to latest). Wildcards ('*' and '?') are allowed.
ImageReferences string `json:"imageReferences"`
ImageReferences []string `json:"imageReferences"`

// Cosign is an array of attributes used to verify cosign signatures
// +optional
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pkg/data/crds/nirmata.io_imageverificationpolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,9 @@ spec:
ImageReferences is a list of matching image reference patterns. At least one pattern in the
list must match the image for the rule to apply. Each image reference consists of a registry
address, repository, image, and tag (defaults to latest). Wildcards ('*' and '?') are allowed.
type: string
items:
type: string
type: array
notary:
description: Notary is an array of attributes used to
verify notary signatures
Expand Down
11 changes: 6 additions & 5 deletions pkg/imageverifier/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ func substituteVariablesInRule(rule *v1alpha1.ImageVerificationRule, jsonCtx eng
}
}

var err error
ruleCopy, err = variables.SubstituteAllInType(logr.Discard(), jsonCtx, ruleCopy)
if err != nil {
return nil, err
}

for i := range ruleCopy.Rules {
if ruleCopy.Rules[i].Cosign != nil {
for j := range ruleCopy.Rules[i].Cosign {
Expand All @@ -136,11 +142,6 @@ func substituteVariablesInRule(rule *v1alpha1.ImageVerificationRule, jsonCtx eng
}
}
}
var err error
ruleCopy, err = variables.SubstituteAllInType(logr.Discard(), jsonCtx, ruleCopy)
if err != nil {
return nil, err
}

if ruleCopy.Rules[i].Notary != nil {
for j := range ruleCopy.Rules[i].Notary {
Expand Down
48 changes: 40 additions & 8 deletions pkg/imageverifier/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ func NewVerifier(rules v1alpha1.VerificationRules, client dclient.Interface, jso
}
}

func match(imgRefs []string, image string) bool {
for _, s := range imgRefs {
if wildcard.Match(s, image) {
return true
}
}
return false
}

func (i *imageVerifier) Verify(image string) VerificationResult {
verificationResult := VerificationResult{
VerificationResponses: make([]VerificationResponse, len(i.rules)),
Expand All @@ -59,7 +68,7 @@ func (i *imageVerifier) Verify(image string) VerificationResult {
VerificationRule: policy,
Failures: make([]error, 0),
}
if !wildcard.Match(policy.ImageReferences, image) {
if !match(policy.ImageReferences, image) {
skippedCount += 1
continue
}
Expand Down Expand Up @@ -124,9 +133,11 @@ func (i *imageVerifier) cosignVerification(pol *v1alpha1.Cosign, image string) e
return err
}

_, err = i.cosignVerifier.VerifySignature(context.TODO(), *opts)
if err != nil {
return err
if len(pol.InToToAttestations) == 0 {
_, err = i.cosignVerifier.VerifySignature(context.TODO(), *opts)
if err != nil {
return err
}
}

for _, att := range pol.InToToAttestations {
Expand All @@ -136,23 +147,44 @@ func (i *imageVerifier) cosignVerification(pol *v1alpha1.Cosign, image string) e

o := *opts
o.Type = att.Type
_, err := i.cosignVerifier.FetchAttestations(context.Background(), o)
resp, err := i.cosignVerifier.FetchAttestations(context.Background(), o)
if err != nil {
return err
}
resp = i.filterStatements(att.Type, resp)
val, msg, err := i.verifyAttestationConditions(att.Conditions, resp)
if err != nil {
return fmt.Errorf("failed to check attestations: %w", err)
}
if !val {
return fmt.Errorf("attestation checks failed for %s and predicate %s: %s", image, att.Type, msg)
}
}
return nil
}

func (i *imageVerifier) filterStatements(predicateType string, resp *images.Response) *images.Response {
statements := make([]map[string]interface{}, 0)
for _, v := range resp.Statements {
if v["type"] == predicateType {
statements = append(statements, v)
}
}
resp.Statements = statements
return resp
}

func (i *imageVerifier) notaryVerification(pol *v1alpha1.Notary, image string) error {
opts, err := notaryVerificationOpts(pol, image)
if err != nil {
return err
}

_, err = i.notaryVerifier.VerifySignature(context.Background(), *opts)
if err != nil {
return err
if len(pol.Attestations) == 0 {
_, err = i.notaryVerifier.VerifySignature(context.Background(), *opts)
if err != nil {
return err
}
}

for _, att := range pol.Attestations {
Expand Down

0 comments on commit 9364b39

Please sign in to comment.