mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-09 17:37:12 +00:00
* feat: cosign verifier for new image verifier crd Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * chore(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp (#12170) Bumps [github.com/sigstore/sigstore/pkg/signature/kms/gcp](https://github.com/sigstore/sigstore) from 1.8.12 to 1.8.14. - [Release notes](https://github.com/sigstore/sigstore/releases) - [Commits](https://github.com/sigstore/sigstore/compare/v1.8.12...v1.8.14) --- updated-dependencies: - dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/gcp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: shuting <shuting@nirmata.com> * feat: add MutatingPolicies CRD (#12150) Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> * README: fix markdown syntax (#12176) Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com> Co-authored-by: shuting <shuting@nirmata.com> * chore(deps): bump sigs.k8s.io/controller-runtime from 0.20.1 to 0.20.2 (#12180) Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.20.1 to 0.20.2. - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.20.1...v0.20.2) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: cel policies nits (#12184) Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * use serviceAccountName instead of deprecated serviceAccount (#12158) Signed-off-by: Francesco Ilario <filario@redhat.com> Co-authored-by: shuting <shuting@nirmata.com> * chore(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/azure (#12179) Bumps [github.com/sigstore/sigstore/pkg/signature/kms/azure](https://github.com/sigstore/sigstore) from 1.8.12 to 1.8.14. - [Release notes](https://github.com/sigstore/sigstore/releases) - [Commits](https://github.com/sigstore/sigstore/compare/v1.8.12...v1.8.14) --- updated-dependencies: - dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/azure dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * chore(deps): bump github.com/awslabs/amazon-ecr-credential-helper/ecr-login (#12178) Bumps [github.com/awslabs/amazon-ecr-credential-helper/ecr-login](https://github.com/awslabs/amazon-ecr-credential-helper) from 0.0.0-20241227172826-c97b94eac159 to 0.9.1. - [Release notes](https://github.com/awslabs/amazon-ecr-credential-helper/releases) - [Changelog](https://github.com/awslabs/amazon-ecr-credential-helper/blob/main/CHANGELOG.md) - [Commits](https://github.com/awslabs/amazon-ecr-credential-helper/commits/v0.9.1) --- updated-dependencies: - dependency-name: github.com/awslabs/amazon-ecr-credential-helper/ecr-login dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: add notary verifier with tsa support (#12160) * feat: add notary repository Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: add notary verifier Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: tests Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: more tests Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * fix: more tests Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * fix: ci Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> * feat: update types Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> --------- Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> Co-authored-by: shuting <shuting@nirmata.com> * fix: codegen (#12195) Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * feat(gctx): add jmespath caching through projections (#11833) feat(gctx): move ready check to runtime Signed-off-by: Khaled Emara <khaled.emara@nirmata.com> Co-authored-by: shuting <shuting@nirmata.com> * fix: publish codecov reports (#12197) Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * chore: format conformance.yaml workflow file (#12194) Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix: add result count for VPs in the CLI (#12193) Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * feat: implement functions Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> --------- Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com> Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Signed-off-by: Francesco Ilario <filario@redhat.com> Signed-off-by: Khaled Emara <khaled.emara@nirmata.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: shuting <shuting@nirmata.com> Co-authored-by: Mariam Fahmy <mariam.fahmy@nirmata.com> Co-authored-by: Koichi Shiraishi <zchee.io@gmail.com> Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Co-authored-by: Francesco Ilario <filario@redhat.com> Co-authored-by: Khaled Emara <khaled.emara@nirmata.com>
270 lines
9.3 KiB
Go
270 lines
9.3 KiB
Go
package cosign
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"fmt"
|
|
|
|
"github.com/google/go-containerregistry/pkg/name"
|
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
|
"github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1"
|
|
"github.com/kyverno/kyverno/pkg/imagedataloader"
|
|
"github.com/sigstore/cosign/v2/pkg/blob"
|
|
"github.com/sigstore/cosign/v2/pkg/cosign"
|
|
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
|
|
sigs "github.com/sigstore/cosign/v2/pkg/signature"
|
|
rekor "github.com/sigstore/rekor/pkg/client"
|
|
"github.com/sigstore/rekor/pkg/generated/client"
|
|
"github.com/sigstore/sigstore/pkg/fulcioroots"
|
|
"github.com/sigstore/sigstore/pkg/signature"
|
|
"github.com/sigstore/sigstore/pkg/tuf"
|
|
k8scorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
)
|
|
|
|
func checkOptions(ctx context.Context, att *v1alpha1.Cosign, baseROpts []remote.Option, baseNOpts []name.Option, secretLister k8scorev1.SecretInterface) (*cosign.CheckOpts, error) {
|
|
if err := initializeTuf(ctx, att.TUF); err != nil {
|
|
return nil, err
|
|
}
|
|
cosignRemoteOpts := []ociremote.Option{}
|
|
|
|
if att.Source != nil {
|
|
remoteOpts, err := sourceRemoteOpts(ctx, secretLister, att.Source)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
baseROpts = append(baseROpts, remoteOpts...)
|
|
if len(att.Source.Repository) > 0 {
|
|
signatureRepo, err := name.NewRepository(att.Source.Repository)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse signature repository %s: %w", att.Source.Repository, err)
|
|
}
|
|
|
|
cosignRemoteOpts = append(cosignRemoteOpts, ociremote.WithTargetRepository(signatureRepo))
|
|
}
|
|
if len(att.Source.TagPrefix) != 0 {
|
|
cosignRemoteOpts = append(cosignRemoteOpts, ociremote.WithPrefix(att.Source.TagPrefix))
|
|
}
|
|
}
|
|
cosignRemoteOpts = append(cosignRemoteOpts, ociremote.WithRemoteOptions(baseROpts...), ociremote.WithNameOptions(baseNOpts...))
|
|
|
|
opts := &cosign.CheckOpts{
|
|
RegistryClientOpts: cosignRemoteOpts,
|
|
}
|
|
|
|
rekorClient, rekorPubKeys, ctlogPubKey, err := getRekor(ctx, att.CTLog)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getting Rekor public keys: %w", err)
|
|
}
|
|
opts.RekorClient = rekorClient
|
|
opts.RekorPubKeys = rekorPubKeys
|
|
opts.CTLogPubKeys = ctlogPubKey
|
|
|
|
if opts.RekorClient == nil {
|
|
if opts.RekorPubKeys != nil {
|
|
opts.Offline = true
|
|
}
|
|
}
|
|
|
|
if att.CTLog != nil {
|
|
opts.IgnoreSCT = att.CTLog.InsecureIgnoreSCT
|
|
opts.IgnoreTlog = att.CTLog.InsecureIgnoreTlog
|
|
if att.CTLog.TSACertChain != "" {
|
|
leaves, intermediates, roots, err := splitCertChain([]byte(att.CTLog.TSACertChain))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error splitting tsa certificates: %w", err)
|
|
}
|
|
if len(leaves) > 1 {
|
|
return nil, fmt.Errorf("certificate chain must contain at most one TSA certificate")
|
|
}
|
|
if len(leaves) == 1 {
|
|
opts.TSACertificate = leaves[0]
|
|
}
|
|
opts.TSAIntermediateCertificates = intermediates
|
|
opts.TSARootCertificates = roots
|
|
}
|
|
}
|
|
|
|
if att.Keyless != nil {
|
|
for _, id := range att.Keyless.Identities {
|
|
opts.Identities = append(opts.Identities,
|
|
cosign.Identity{
|
|
Issuer: id.Issuer,
|
|
Subject: id.Subject,
|
|
IssuerRegExp: id.IssuerRegExp,
|
|
SubjectRegExp: id.SubjectRegExp,
|
|
})
|
|
}
|
|
fulcioRoots, fulcioIntermediates, err := getFulcio(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getting Fulcio certs: %w", err)
|
|
}
|
|
opts.RootCerts = fulcioRoots
|
|
opts.IntermediateCerts = fulcioIntermediates
|
|
if att.Keyless.Roots != "" {
|
|
cp, err := certPoolFromBytes([]byte(att.Keyless.Roots))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load Root certificates: %w", err)
|
|
}
|
|
opts.RootCerts = cp
|
|
}
|
|
} else if att.Key != nil {
|
|
if len(att.Key.Data) > 0 {
|
|
opts.SigVerifier, err = decodePEM([]byte(att.Key.Data), signatureAlgorithmMap[att.Key.HashAlgorithm])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load public key from PEM: %w", err)
|
|
}
|
|
} else if att.Key.SecretRef != nil {
|
|
key := fmt.Sprintf("k8s://%s/%s", att.Key.SecretRef.Namespace, att.Key.SecretRef.Name)
|
|
opts.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, key, signatureAlgorithmMap[att.Key.HashAlgorithm])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load public key from %s: %w", key, err)
|
|
}
|
|
} else if len(att.Key.KMS) != 0 {
|
|
opts.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, att.Key.KMS, signatureAlgorithmMap[att.Key.HashAlgorithm])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load public key from %s: %w", att.Key.KMS, err)
|
|
}
|
|
}
|
|
} else if att.Certificate != nil {
|
|
if att.Certificate.Certificate != "" {
|
|
// load cert and optionally a cert chain as a verifier
|
|
cert, err := certFromBytes([]byte(att.Certificate.Certificate))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load certificate from %s: %w", att.Certificate.Certificate, err)
|
|
}
|
|
|
|
if att.Certificate.CertificateChain == "" {
|
|
opts.SigVerifier, err = signature.LoadVerifier(cert.PublicKey, signatureAlgorithmMap[att.Key.HashAlgorithm])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load signature from certificate: %w", err)
|
|
}
|
|
} else {
|
|
// Verify certificate with chain
|
|
chain, err := certChainFromBytes([]byte(att.Certificate.CertificateChain))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load load certificate chain: %w", err)
|
|
}
|
|
opts.SigVerifier, err = cosign.ValidateAndUnpackCertWithChain(cert, chain, opts)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load validate certificate chain: %w", err)
|
|
}
|
|
}
|
|
}
|
|
if att.Certificate.CertificateChain != "" {
|
|
// load cert chain as roots
|
|
cp, err := certPoolFromBytes([]byte(att.Certificate.CertificateChain))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load certificates: %w", err)
|
|
}
|
|
opts.RootCerts = cp
|
|
}
|
|
}
|
|
return opts, nil
|
|
}
|
|
|
|
func initializeTuf(ctx context.Context, t *v1alpha1.TUF) error {
|
|
if t != nil {
|
|
var root []byte
|
|
var err error
|
|
if t.Root.Path != "" {
|
|
root, err = blob.LoadFileOrURL(t.Root.Path)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to read alternate TUF root file %v : %w", t, err)
|
|
}
|
|
} else if t.Root.Data != "" {
|
|
root, err = base64.StdEncoding.DecodeString(t.Root.Data)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to base64 decode TUF root %v : %w", t, err)
|
|
}
|
|
}
|
|
|
|
if err := tuf.Initialize(ctx, t.Mirror, root); err != nil {
|
|
return fmt.Errorf("Failed to initialize TUF client from %v : %w", t, err)
|
|
}
|
|
} else {
|
|
if err := tuf.Initialize(ctx, tuf.DefaultRemoteRoot, nil); err != nil {
|
|
return fmt.Errorf("Failed to initialize TUF client from %v : %w", t, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func sourceRemoteOpts(ctx context.Context, secretLister k8scorev1.SecretInterface, src *v1alpha1.Source) ([]remote.Option, error) {
|
|
opts := make([]remote.Option, 0)
|
|
if len(src.SignaturePullSecrets) > 0 {
|
|
signaturePullSecrets := make([]string, 0, len(src.SignaturePullSecrets))
|
|
for _, s := range src.SignaturePullSecrets {
|
|
signaturePullSecrets = append(signaturePullSecrets, s.Name)
|
|
}
|
|
kc, err := imagedataloader.NewAutoRefreshSecretsKeychain(secretLister, signaturePullSecrets...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
opts = append(opts, remote.WithAuthFromKeychain(kc))
|
|
}
|
|
return opts, nil
|
|
}
|
|
|
|
func getRekor(ctx context.Context, ctlog *v1alpha1.CTLog) (*client.Rekor, *cosign.TrustedTransparencyLogPubKeys, *cosign.TrustedTransparencyLogPubKeys, error) {
|
|
// In keyless, if no TrustRoot was defined and CTLog is nil, then default to rekor pub keys as done in cosign
|
|
if ctlog == nil {
|
|
rekorPubKeys, err := cosign.GetRekorPubs(ctx)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("getting Rekor public keys: %w", err)
|
|
}
|
|
ctlogPubKey, err := cosign.GetCTLogPubs(ctx)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("getting Rekor public keys: %w", err)
|
|
}
|
|
return nil, rekorPubKeys, ctlogPubKey, nil
|
|
}
|
|
|
|
if len(ctlog.URL) == 0 {
|
|
return nil, nil, nil, fmt.Errorf("rekor URL must be provided")
|
|
}
|
|
rekorClient, err := rekor.GetRekorClient(ctlog.URL)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("error creating Rekor client: %w", err)
|
|
}
|
|
|
|
var rekorPubKey *cosign.TrustedTransparencyLogPubKeys
|
|
var ctlogPubKey *cosign.TrustedTransparencyLogPubKeys
|
|
if ctlog.RekorPubKey == "" {
|
|
if rekorPubKey, err = cosign.GetRekorPubs(ctx); err != nil {
|
|
return nil, nil, nil, fmt.Errorf("failed to get rekor public keys: %w", err)
|
|
}
|
|
} else {
|
|
key := cosign.NewTrustedTransparencyLogPubKeys()
|
|
if err := key.AddTransparencyLogPubKey([]byte(ctlog.RekorPubKey), tuf.Active); err != nil {
|
|
return nil, nil, nil, fmt.Errorf("failed to parse rekor public keys: %w", err)
|
|
}
|
|
rekorPubKey = &key
|
|
}
|
|
|
|
if ctlog.CTLogPubKey == "" {
|
|
if ctlogPubKey, err = cosign.GetCTLogPubs(ctx); err != nil {
|
|
return nil, nil, nil, fmt.Errorf("failed to get ctlog public keys: %w", err)
|
|
}
|
|
} else {
|
|
key := cosign.NewTrustedTransparencyLogPubKeys()
|
|
if err := key.AddTransparencyLogPubKey([]byte(ctlog.CTLogPubKey), tuf.Active); err != nil {
|
|
return nil, nil, nil, fmt.Errorf("failed to parse ctlog public keys: %w", err)
|
|
}
|
|
ctlogPubKey = &key
|
|
}
|
|
|
|
return rekorClient, rekorPubKey, ctlogPubKey, nil
|
|
}
|
|
|
|
func getFulcio(ctx context.Context) (*x509.CertPool, *x509.CertPool, error) {
|
|
roots, err := fulcioroots.Get()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to fetch Fulcio roots: %w", err)
|
|
}
|
|
intermediates, err := fulcioroots.GetIntermediates()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to fetch Fulcio intermediates: %w", err)
|
|
}
|
|
return roots, intermediates, nil
|
|
}
|