Skip to content

Commit

Permalink
Handle context cancelled properly + tests. (sigstore#1796)
Browse files Browse the repository at this point in the history
Signed-off-by: Ville Aikas <vaikas@chainguard.dev>
  • Loading branch information
vaikas authored and mlieberman85 committed May 6, 2022
1 parent 0834ac2 commit 8ac2f57
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 34 deletions.
59 changes: 35 additions & 24 deletions pkg/cosign/kubernetes/webhook/validator.go
Expand Up @@ -287,19 +287,25 @@ func validatePolicies(ctx context.Context, ref name.Reference, policies map[stri
ret := map[string][]error{}

for i := 0; i < len(policies); i++ {
result, ok := <-results
if !ok {
ret["internalerror"] = append(ret["internalerror"], fmt.Errorf("results channel failed to produce a result"))
}
switch {
case len(result.errors) > 0:
ret[result.name] = append(ret[result.name], result.errors...)
case len(result.policyResult.AuthorityMatches) > 0:
policyResults[result.name] = result.policyResult
default:
ret[result.name] = append(ret[result.name], fmt.Errorf("failed to process policy: %s", result.name))
select {
case <-ctx.Done():
ret["internalerror"] = append(ret["internalerror"], fmt.Errorf("context was canceled before validation completed"))
case result, ok := <-results:
if !ok {
ret["internalerror"] = append(ret["internalerror"], fmt.Errorf("results channel failed to produce a result"))
continue
}
switch {
case len(result.errors) > 0:
ret[result.name] = append(ret[result.name], result.errors...)
case len(result.policyResult.AuthorityMatches) > 0:
policyResults[result.name] = result.policyResult
default:
ret[result.name] = append(ret[result.name], fmt.Errorf("failed to process policy: %s", result.name))
}
}
}

return policyResults, ret
}

Expand Down Expand Up @@ -355,19 +361,24 @@ func ValidatePolicy(ctx context.Context, ref name.Reference, cip webhookcip.Clus
// return it.
policyResult := &PolicyResult{AuthorityMatches: make(map[string]AuthorityMatch)}
for i := 0; i < len(cip.Authorities); i++ {
result, ok := <-results
if !ok {
authorityErrors = append(authorityErrors, fmt.Errorf("results channel failed to produce a result"))
}
switch {
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:
policyResult.AuthorityMatches[result.name] = AuthorityMatch{Attestations: result.attestations}
default:
authorityErrors = append(authorityErrors, fmt.Errorf("failed to process authority: %s", result.name))
select {
case <-ctx.Done():
authorityErrors = append(authorityErrors, fmt.Errorf("context was canceled before validation completed"))
case result, ok := <-results:
if !ok {
authorityErrors = append(authorityErrors, fmt.Errorf("results channel failed to produce a result"))
continue
}
switch {
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:
policyResult.AuthorityMatches[result.name] = AuthorityMatch{Attestations: result.attestations}
default:
authorityErrors = append(authorityErrors, fmt.Errorf("failed to process authority: %s", result.name))
}
}
}
if len(authorityErrors) > 0 {
Expand Down
70 changes: 60 additions & 10 deletions pkg/cosign/kubernetes/webhook/validator_test.go
Expand Up @@ -56,6 +56,12 @@ import (
const (
fulcioRootCert = "-----BEGIN CERTIFICATE-----\nMIICNzCCAd2gAwIBAgITPLBoBQhl1hqFND9S+SGWbfzaRTAKBggqhkjOPQQDAjBo\nMQswCQYDVQQGEwJVSzESMBAGA1UECBMJV2lsdHNoaXJlMRMwEQYDVQQHEwpDaGlw\ncGVuaGFtMQ8wDQYDVQQKEwZSZWRIYXQxDDAKBgNVBAsTA0NUTzERMA8GA1UEAxMI\ndGVzdGNlcnQwHhcNMjEwMzEyMjMyNDQ5WhcNMzEwMjI4MjMyNDQ5WjBoMQswCQYD\nVQQGEwJVSzESMBAGA1UECBMJV2lsdHNoaXJlMRMwEQYDVQQHEwpDaGlwcGVuaGFt\nMQ8wDQYDVQQKEwZSZWRIYXQxDDAKBgNVBAsTA0NUTzERMA8GA1UEAxMIdGVzdGNl\ncnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQRn+Alyof6xP3GQClSwgV0NFuY\nYEwmKP/WLWr/LwB6LUYzt5v49RlqG83KuaJSpeOj7G7MVABdpIZYWwqAiZV3o2Yw\nZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQU\nT8Jwm6JuVb0dsiuHUROiHOOVHVkwHwYDVR0jBBgwFoAUT8Jwm6JuVb0dsiuHUROi\nHOOVHVkwCgYIKoZIzj0EAwIDSAAwRQIhAJkNZmP6sKA+8EebRXFkBa9DPjacBpTc\nOljJotvKidRhAiAuNrIazKEw2G4dw8x1z6EYk9G+7fJP5m93bjm/JfMBtA==\n-----END CERTIFICATE-----"
rekorResponse = "bad response"

// Random public key (cosign generate-key-pair) 2022-03-18
authorityKeyCosignPubString = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENAyijLvRu5QpCPp2uOj8C79ZW1VJ
SID/4H61ZiRzN4nqONzp+ZF22qQTk3MFO3D0/ZKmWHAosIf2pf2GHH7myA==
-----END PUBLIC KEY-----`
)

func TestValidatePodSpec(t *testing.T) {
Expand Down Expand Up @@ -91,11 +97,6 @@ func TestValidatePodSpec(t *testing.T) {
}

var authorityKeyCosignPub *ecdsa.PublicKey
// Random public key (cosign generate-key-pair) 2022-03-18
authorityKeyCosignPubString := `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENAyijLvRu5QpCPp2uOj8C79ZW1VJ
SID/4H61ZiRzN4nqONzp+ZF22qQTk3MFO3D0/ZKmWHAosIf2pf2GHH7myA==
-----END PUBLIC KEY-----`

pems := parsePems([]byte(authorityKeyCosignPubString))
if len(pems) > 0 {
Expand Down Expand Up @@ -1175,11 +1176,6 @@ func TestValidatePolicy(t *testing.T) {
}
t.Logf("rekorURL: %s", rekorURL.String())
var authorityKeyCosignPub *ecdsa.PublicKey
// Random public key (cosign generate-key-pair) 2022-03-18
authorityKeyCosignPubString := `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENAyijLvRu5QpCPp2uOj8C79ZW1VJ
SID/4H61ZiRzN4nqONzp+ZF22qQTk3MFO3D0/ZKmWHAosIf2pf2GHH7myA==
-----END PUBLIC KEY-----`

pems := parsePems([]byte(authorityKeyCosignPubString))
if len(pems) > 0 {
Expand Down Expand Up @@ -1350,3 +1346,57 @@ func validateErrors(t *testing.T, wantErr []string, got []error) {
}
}
}

func TestValidatePolicyCancelled(t *testing.T) {
var authorityKeyCosignPub *ecdsa.PublicKey
pems := parsePems([]byte(authorityKeyCosignPubString))
if len(pems) > 0 {
key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes)
authorityKeyCosignPub = key.(*ecdsa.PublicKey)
} else {
t.Errorf("Error parsing authority key from string")
}
// Resolved via crane digest on 2021/09/25
digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4")

testContext, cancelFunc := context.WithCancel(context.Background())
cip := webhookcip.ClusterImagePolicy{
Authorities: []webhookcip.Authority{{
Name: "authority-0",
Key: &webhookcip.KeyRef{
PublicKeys: []crypto.PublicKey{authorityKeyCosignPub},
},
}},
}
wantErrs := []string{"context was canceled before validation completed"}
cancelFunc()
_, gotErrs := ValidatePolicy(testContext, digest, cip)
validateErrors(t, wantErrs, gotErrs)
}

func TestValidatePoliciesCancelled(t *testing.T) {
var authorityKeyCosignPub *ecdsa.PublicKey
pems := parsePems([]byte(authorityKeyCosignPubString))
if len(pems) > 0 {
key, _ := x509.ParsePKIXPublicKey(pems[0].Bytes)
authorityKeyCosignPub = key.(*ecdsa.PublicKey)
} else {
t.Errorf("Error parsing authority key from string")
}
// Resolved via crane digest on 2021/09/25
digest := name.MustParseReference("gcr.io/distroless/static:nonroot@sha256:be5d77c62dbe7fedfb0a4e5ec2f91078080800ab1f18358e5f31fcc8faa023c4")

testContext, cancelFunc := context.WithCancel(context.Background())
cip := webhookcip.ClusterImagePolicy{
Authorities: []webhookcip.Authority{{
Name: "authority-0",
Key: &webhookcip.KeyRef{
PublicKeys: []crypto.PublicKey{authorityKeyCosignPub},
},
}},
}
wantErrs := []string{"context was canceled before validation completed"}
cancelFunc()
_, gotErrs := validatePolicies(testContext, digest, map[string]webhookcip.ClusterImagePolicy{"testcip": cip})
validateErrors(t, wantErrs, gotErrs["internalerror"])
}

0 comments on commit 8ac2f57

Please sign in to comment.