1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 17:37:12 +00:00
kyverno/pkg/imageverifiers/cosign/opts.go
Vishal Choudhary 221c559247
feat: cosign verifier for new image verifier crd (#12196)
* 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>
2025-02-21 09:03:53 +08:00

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
}