mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-30 19:35:06 +00:00
Fix keyless attest (#3219)
* allow root cert for keyless attestations checks Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add logs and improve var names Signed-off-by: Jim Bugwadia <jim@nirmata.com> * make fmt Signed-off-by: Jim Bugwadia <jim@nirmata.com> * handle err in sig loading Signed-off-by: Jim Bugwadia <jim@nirmata.com> Co-authored-by: Sambhav Kothari <sambhavs.email@gmail.com>
This commit is contained in:
parent
14111aaa05
commit
bd1a145678
3 changed files with 34 additions and 29 deletions
|
@ -9,6 +9,8 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/rekor"
|
||||
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio"
|
||||
|
@ -145,23 +147,32 @@ func getFulcioRoots(roots []byte) (*x509.CertPool, error) {
|
|||
|
||||
// FetchAttestations retrieves signed attestations and decodes them into in-toto statements
|
||||
// https://github.com/in-toto/attestation/blob/main/spec/README.md#statement
|
||||
func FetchAttestations(imageRef string, key string, repository string, log logr.Logger) ([]map[string]interface{}, error) {
|
||||
func FetchAttestations(imageRef string, imageVerify *v1.ImageVerification, log logr.Logger) ([]map[string]interface{}, error) {
|
||||
ctx := context.Background()
|
||||
var pubKey signature.Verifier
|
||||
var err error
|
||||
|
||||
if strings.HasPrefix(key, "-----BEGIN PUBLIC KEY-----") {
|
||||
pubKey, err = decodePEM([]byte(key))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode pem")
|
||||
cosignOpts := &cosign.CheckOpts{
|
||||
ClaimVerifier: cosign.IntotoSubjectClaimVerifier,
|
||||
}
|
||||
|
||||
if imageVerify.Key != "" {
|
||||
if strings.HasPrefix(imageVerify.Key, "-----BEGIN PUBLIC KEY-----") {
|
||||
cosignOpts.SigVerifier, err = decodePEM([]byte(imageVerify.Key))
|
||||
} else {
|
||||
cosignOpts.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, imageVerify.Key)
|
||||
}
|
||||
} else {
|
||||
pubKey, err = sigs.PublicKeyFromKeyRef(ctx, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "loading public key")
|
||||
cosignOpts.CertEmail = ""
|
||||
cosignOpts.RootCerts, err = getFulcioRoots([]byte(imageVerify.Roots))
|
||||
if err == nil {
|
||||
cosignOpts.RekorClient, err = rekor.NewClient("https://rekor.sigstore.dev")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "loading credentials")
|
||||
}
|
||||
|
||||
var opts []remote.Option
|
||||
ro := options.RegistryOptions{}
|
||||
|
||||
|
@ -170,20 +181,14 @@ func FetchAttestations(imageRef string, key string, repository string, log logr.
|
|||
return nil, errors.Wrap(err, "constructing client options")
|
||||
}
|
||||
opts = append(opts, remote.WithRemoteOptions(gcrremote.WithAuthFromKeychain(registryclient.DefaultKeychain)))
|
||||
if repository != "" {
|
||||
signatureRepo, err := name.NewRepository(repository)
|
||||
if imageVerify.Repository != "" {
|
||||
signatureRepo, err := name.NewRepository(imageVerify.Repository)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse signature repository %s", repository)
|
||||
return nil, errors.Wrapf(err, "failed to parse signature repository %s", imageVerify.Repository)
|
||||
}
|
||||
opts = append(opts, remote.WithTargetRepository(signatureRepo))
|
||||
}
|
||||
|
||||
cosignOpts := &cosign.CheckOpts{
|
||||
ClaimVerifier: cosign.IntotoSubjectClaimVerifier,
|
||||
SigVerifier: pubKey,
|
||||
RegistryClientOpts: opts,
|
||||
}
|
||||
|
||||
ref, err := name.ParseReference(imageRef)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse image")
|
||||
|
|
|
@ -125,8 +125,6 @@ type imageVerifier struct {
|
|||
|
||||
func (iv *imageVerifier) verify(imageVerify *v1.ImageVerification, images map[string]*context.ImageInfo) {
|
||||
imagePattern := imageVerify.Image
|
||||
key := imageVerify.Key
|
||||
repository := getSignatureRepository(imageVerify)
|
||||
|
||||
for _, imageInfo := range images {
|
||||
image := imageInfo.String()
|
||||
|
@ -150,7 +148,7 @@ func (iv *imageVerifier) verify(imageVerify *v1.ImageVerification, images map[st
|
|||
iv.patchDigest(imageInfo, digest, ruleResp)
|
||||
}
|
||||
} else {
|
||||
ruleResp = iv.attestImage(repository, key, imageInfo, imageVerify.Attestations)
|
||||
ruleResp = iv.attestImage(imageVerify, imageInfo)
|
||||
}
|
||||
|
||||
iv.resp.PolicyResponse.Rules = append(iv.resp.PolicyResponse.Rules, *ruleResp)
|
||||
|
@ -235,11 +233,11 @@ func makeAddDigestPatch(imageInfo *context.ImageInfo, digest string) ([]byte, er
|
|||
return json.Marshal(patch)
|
||||
}
|
||||
|
||||
func (iv *imageVerifier) attestImage(repository, key string, imageInfo *context.ImageInfo, attestationChecks []*v1.Attestation) *response.RuleResponse {
|
||||
func (iv *imageVerifier) attestImage(imageVerify *v1.ImageVerification, imageInfo *context.ImageInfo) *response.RuleResponse {
|
||||
image := imageInfo.String()
|
||||
start := time.Now()
|
||||
|
||||
statements, err := cosign.FetchAttestations(image, key, repository, iv.logger)
|
||||
statements, err := cosign.FetchAttestations(image, imageVerify, iv.logger)
|
||||
if err != nil {
|
||||
iv.logger.Info("failed to fetch attestations", "image", image, "error", err, "duration", time.Since(start).Seconds())
|
||||
return ruleError(iv.rule, utils.ImageVerify, fmt.Sprintf("failed to fetch attestations for %s", image), err)
|
||||
|
@ -248,7 +246,7 @@ func (iv *imageVerifier) attestImage(repository, key string, imageInfo *context.
|
|||
iv.logger.V(4).Info("received attestations", "statements", statements)
|
||||
statementsByPredicate := buildStatementMap(statements)
|
||||
|
||||
for _, ac := range attestationChecks {
|
||||
for _, ac := range imageVerify.Attestations {
|
||||
statements := statementsByPredicate[ac.PredicateType]
|
||||
if statements == nil {
|
||||
msg := fmt.Sprintf("predicate type %s not found", ac.PredicateType)
|
||||
|
|
|
@ -512,13 +512,13 @@ func buildPolicyResults(engineResponses []*response.EngineResponse, testResults
|
|||
}
|
||||
|
||||
var result report.PolicyReportResult
|
||||
var resultsKey []string
|
||||
var resultsKeys []string
|
||||
var resultKey string
|
||||
resultsKey = GetAllPossibleResultsKey("", info.PolicyName, rule.Name, infoResult.Resource.Namespace, infoResult.Resource.Kind, infoResult.Resource.Name)
|
||||
for _, resultK := range resultsKey {
|
||||
if val, ok := results[resultK]; ok {
|
||||
resultsKeys = GetAllPossibleResultsKey("", info.PolicyName, rule.Name, infoResult.Resource.Namespace, infoResult.Resource.Kind, infoResult.Resource.Name)
|
||||
for _, key := range resultsKeys {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
resultKey = resultK
|
||||
resultKey = key
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
@ -779,6 +779,7 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
if val, ok := resps[resultKey]; ok {
|
||||
testRes = val
|
||||
} else {
|
||||
log.Log.V(2).Info("result not found", "key", resultKey)
|
||||
res.Result = boldYellow.Sprintf("Not found")
|
||||
rc.Fail++
|
||||
table = append(table, res)
|
||||
|
@ -799,6 +800,7 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
rc.Pass++
|
||||
}
|
||||
} else {
|
||||
log.Log.V(2).Info("result mismatch", "expected", testRes.Result, "received", v.Result, "key", resultKey)
|
||||
res.Result = boldRed.Sprintf("Fail")
|
||||
rc.Fail++
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue