package v1alpha1 import ( "strings" "github.com/kyverno/kyverno/api/kyverno" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // +genclient // +genclient:nonNamespaced // +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:path=imageverificationpolicies,scope="Cluster",shortName=ivpol,categories=kyverno // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" // +kubebuilder:printcolumn:name="READY",type=string,JSONPath=`.status.conditionStatus.ready` // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type ImageVerificationPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec ImageVerificationPolicySpec `json:"spec"` // Status contains policy runtime data. // +optional Status IvpolStatus `json:"status,omitempty"` } type IvpolStatus struct { // +optional ConditionStatus ConditionStatus `json:"conditionStatus,omitempty"` // +optional Autogen IvpolAutogenStatus `json:"autogen,omitempty"` } type IvpolAutogenStatus struct { // +optional Rules []*IvpolAutogen `json:"rules,omitempty"` } type IvpolAutogen struct { Name string `json:"name,omitempty"` Spec ImageVerificationPolicySpec `json:"spec"` } func (s *ImageVerificationPolicy) GetName() string { name := s.Name if s.Annotations == nil { if _, found := s.Annotations[kyverno.AnnotationAutogenControllers]; found { if strings.HasPrefix(name, "autogen-cronjobs-") { return strings.TrimPrefix(name, "autogen-cronjobs-") } else if strings.HasPrefix(name, "autogen-") { return strings.TrimPrefix(name, "autogen-") } } } return name } func (s *ImageVerificationPolicy) GetMatchConstraints() admissionregistrationv1.MatchResources { if s.Spec.MatchConstraints == nil { return admissionregistrationv1.MatchResources{} } return *s.Spec.MatchConstraints } func (s *ImageVerificationPolicy) GetMatchConditions() []admissionregistrationv1.MatchCondition { return s.Spec.MatchConditions } func (s *ImageVerificationPolicy) GetWebhookConfiguration() *WebhookConfiguration { return s.Spec.WebhookConfiguration } func (s *ImageVerificationPolicy) GetFailurePolicy() admissionregistrationv1.FailurePolicyType { if s.Spec.FailurePolicy == nil { return admissionregistrationv1.Fail } return *s.Spec.FailurePolicy } func (s *ImageVerificationPolicy) GetVariables() []admissionregistrationv1.Variable { return s.Spec.Variables } func (s *ImageVerificationPolicy) GetSpec() *ImageVerificationPolicySpec { return &s.Spec } func (s *ImageVerificationPolicy) GetStatus() *IvpolStatus { return &s.Status } func (s *ImageVerificationPolicy) GetKind() string { return "ImageVerificationPolicy" } // AdmissionEnabled checks if admission is set to true func (s ImageVerificationPolicySpec) AdmissionEnabled() bool { if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Admission == nil || s.EvaluationConfiguration.Admission.Enabled == nil { return true } return *s.EvaluationConfiguration.Admission.Enabled } // BackgroundEnabled checks if background is set to true func (s ImageVerificationPolicySpec) BackgroundEnabled() bool { if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Background == nil || s.EvaluationConfiguration.Background.Enabled == nil { return true } return *s.EvaluationConfiguration.Background.Enabled } func (status *IvpolStatus) SetReadyByCondition(c PolicyConditionType, s metav1.ConditionStatus, message string) { reason := "Succeeded" if s != metav1.ConditionTrue { reason = "Failed" } newCondition := metav1.Condition{ Type: string(c), Reason: reason, Status: s, Message: message, } meta.SetStatusCondition(&status.ConditionStatus.Conditions, newCondition) } func (status *IvpolStatus) GetConditionStatus() *ConditionStatus { return &status.ConditionStatus } // +kubebuilder:object:root=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ImageVerificationPolicyList is a list of ImageVerificationPolicy instances type ImageVerificationPolicyList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` Items []ImageVerificationPolicy `json:"items"` } // CredentialsProvidersType provides the list of credential providers required. // +kubebuilder:validation:Enum=default;amazon;azure;google;github type CredentialsProvidersType string const ( DEFAULT CredentialsProvidersType = "default" AWS CredentialsProvidersType = "amazon" ACR CredentialsProvidersType = "azure" GCP CredentialsProvidersType = "google" GHCR CredentialsProvidersType = "github" ) // ImageVerificationPolicySpec is the specification of the desired behavior of the ImageVerificationPolicy. type ImageVerificationPolicySpec struct { // MatchConstraints specifies what resources this policy is designed to validate. // +optional MatchConstraints *admissionregistrationv1.MatchResources `json:"matchConstraints"` // FailurePolicy defines how to handle failures for the admission policy. Failures can // occur from CEL expression parse errors, type check errors, runtime errors and invalid // or mis-configured policy definitions or bindings. // +optional FailurePolicy *admissionregistrationv1.FailurePolicyType `json:"failurePolicy"` // ValidationAction specifies the action to be taken when the matched resource violates the policy. // Required. // +listType=set ValidationAction []admissionregistrationv1.ValidationAction `json:"validationActions,omitempty"` // MatchConditions is a list of conditions that must be met for a request to be validated. // Match conditions filter requests that have already been matched by the rules, // namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. // There are a maximum of 64 match conditions allowed. // +optional MatchConditions []admissionregistrationv1.MatchCondition `json:"matchConditions,omitempty"` // Variables contain definitions of variables that can be used in composition of other expressions. // Each variable is defined as a named CEL expression. // +optional Variables []admissionregistrationv1.Variable `json:"variables,omitempty"` // ImagesRules is a list of Glob and CELExpressions to match images. // Any image that matches one of the rules is considered for validation // Any image that does not match a rule is skipped, even when they are passed as arguments to // image verification functions // +optional ImageRules []ImageRule `json:"imageRules"` // MutateDigest enables replacement of image tags with digests. // Defaults to true. // +kubebuilder:default=true // +optional MutateDigest *bool `json:"mutateDigest"` // VerifyDigest validates that images have a digest. // +kubebuilder:default=true // +optional VerifyDigest *bool `json:"verifyDigest"` // Required validates that images are verified i.e. have matched passed a signature or attestation check. // +kubebuilder:default=true // +optional Required *bool `json:"required"` // Credentials provides credentials that will be used for authentication with registry. // +kubebuilder:validation:Optional Credentials *Credentials `json:"credentials,omitempty"` // Images is a list of CEL expression to extract images from the resource // +optional Images []Image `json:"images,omitempty"` // Attestors provides a list of trusted authorities. Attestors []Attestor `json:"attestors"` // Attestations provides a list of image metadata to verify // +optional Attestations []Attestation `json:"attestations"` // Verifications contain CEL expressions which is used to apply the image verification checks. // +listType=atomic Verifications []admissionregistrationv1.Validation `json:"verifications"` // WebhookConfiguration defines the configuration for the webhook. // +optional WebhookConfiguration *WebhookConfiguration `json:"webhookConfiguration,omitempty"` // EvaluationConfiguration defines the configuration for the policy evaluation. // +optional EvaluationConfiguration *EvaluationConfiguration `json:"evaluation,omitempty"` } // ImageRule defines a Glob or a CEL expression for matching images type ImageRule struct { // Glob defines a globbing pattern for matching images // +optional Glob string `json:"glob"` // Cel defines CEL Expressions for matching images // +optional CELExpression string `json:"cel"` } type Image struct { // Name is the name for this imageList. It is used to refer to the images in verification block as images.<name> Name string `json:"name"` // Expression defines CEL expression to extact images from the resource. Expression string `json:"expression"` } type Credentials struct { // AllowInsecureRegistry allows insecure access to a registry. // +optional AllowInsecureRegistry bool `json:"allowInsecureRegistry,omitempty"` // Providers specifies a list of OCI Registry names, whose authentication providers are provided. // It can be of one of these values: default,google,azure,amazon,github. // +optional Providers []CredentialsProvidersType `json:"providers,omitempty"` // Secrets specifies a list of secrets that are provided for credentials. // Secrets must live in the Kyverno namespace. // +optional Secrets []string `json:"secrets,omitempty"` } // Attestor is an identity that confirms or verifies the authenticity of an image or an attestation type Attestor struct { // Name is the name for this attestor. It is used to refer to the attestor in verification Name string `json:"name"` // Cosign defines attestor configuration for Cosign based signatures // +optional Cosign *Cosign `json:"cosign,omitempty"` // Notary defines attestor configuration for Notary based signatures // +optional Notary *Notary `json:"notary,omitempty"` } func (a Attestor) GetKey() string { return a.Name } func (a Attestor) IsCosign() bool { return a.Cosign != nil } func (a Attestor) IsNotary() bool { return a.Notary != nil } // Cosign defines attestor configuration for Cosign based signatures type Cosign struct { // Key defines the type of key to validate the image. // +optional Key *Key `json:"key,omitempty"` // Keyless sets the configuration to verify the authority against a Fulcio instance. // +optional Keyless *Keyless `json:"keyless,omitempty"` // Certificate defines the configuration for local signature verification // +optional Certificate *Certificate `json:"certificate,omitempty"` // Sources sets the configuration to specify the sources from where to consume the signature and attestations. // +optional Source *Source `json:"source,omitempty"` // CTLog sets the configuration to verify the authority against a Rekor instance. // +optional CTLog *CTLog `json:"ctlog,omitempty"` // TUF defines the configuration to fetch sigstore root // +optional TUF *TUF `json:"tuf,omitempty"` // Annotations are used for image verification. // Every specified key-value pair must exist and match in the verified payload. // The payload may contain other key-value pairs. // +optional Annotations map[string]string `json:"annotations,omitempty"` } // Notary defines attestor configuration for Notary based signatures type Notary struct { // Certs define the cert chain for Notary signature verification Certs string `json:"certs"` // TSACerts define the cert chain for verifying timestamps of notary signature // +optional TSACerts string `json:"tsaCerts"` } // TUF defines the configuration to fetch sigstore root type TUF struct { // Root defines the path or data of the trusted root // +optional Root TUFRoot `json:"root,omitempty"` // Mirror is the base URL of Sigstore TUF repository // +optional Mirror string `json:"mirror,omitempty"` } // TUFRoot defines the path or data of the trusted root type TUFRoot struct { // Path is the URL or File location of the TUF root // +optional Path string `json:"path,omitempty"` // Data is the base64 encoded TUF root // +optional Data string `json:"data,omitempty"` } // Source specifies the location of the signature / attestations. type Source struct { // Repository defines the location from where to pull the signature / attestations. // +optional Repository string `json:"repository,omitempty"` // SignaturePullSecrets is an optional list of references to secrets in the // same namespace as the deploying resource for pulling any of the signatures // used by this Source. // +optional SignaturePullSecrets []corev1.LocalObjectReference `json:"PullSecrets,omitempty"` // TagPrefix is an optional prefix that signature and attestations have. // This is the 'tag based discovery' and in the future once references are // fully supported that should likely be the preferred way to handle these. // +optional TagPrefix string `json:"tagPrefix,omitempty"` } // CTLog sets the configuration to verify the authority against a Rekor instance. type CTLog struct { // URL sets the url to the rekor instance (by default the public rekor.sigstore.dev) // +optional URL string `json:"url,omitempty"` // RekorPubKey is an optional PEM-encoded public key to use for a custom Rekor. // If set, this will be used to validate transparency log signatures from a custom Rekor. // +optional RekorPubKey string `json:"rekorPubKey,omitempty"` // CTLogPubKey, if set, is used to validate SCTs against a custom source. // +optional CTLogPubKey string `json:"ctLogPubKey,omitempty"` // TSACertChain, if set, is the PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must // contain the root CA certificate. Optionally may contain intermediate CA certificates, and // may contain the leaf TSA certificate if not present in the timestamurce. // +optional TSACertChain string `json:"tsaCertChain,omitempty"` // InsecureIgnoreTlog skips transparency log verification. // +optional InsecureIgnoreTlog bool `json:"insecureIgnoreTlog,omitempty"` // IgnoreSCT defines whether to use the Signed Certificate Timestamp (SCT) log to check for a certificate // timestamp. Default is false. Set to true if this was opted out during signing. // +optional InsecureIgnoreSCT bool `json:"insecureIgnoreSCT,omitempty"` } // This references a public verification key stored in // a secret in the kyverno namespace. // A Key must specify only one of SecretRef, Data or KMS type Key struct { // SecretRef sets a reference to a secret with the key. // +optional SecretRef *corev1.SecretReference `json:"secretRef,omitempty"` // Data contains the inline public key // +optional Data string `json:"data,omitempty"` // KMS contains the KMS url of the public key // Supported formats differ based on the KMS system used. // +optional KMS string `json:"kms,omitempty"` // HashAlgorithm specifues signature algorithm for public keys. Supported values are // sha224, sha256, sha384 and sha512. Defaults to sha256. // +optional HashAlgorithm string `json:"hashAlgorithm,omitempty"` } // Keyless contains location of the validating certificate and the identities // against which to verify. type Keyless struct { // Identities sets a list of identities. Identities []Identity `json:"identities"` // Roots is an optional set of PEM encoded trusted root certificates. // If not provided, the system roots are used. // +kubebuilder:validation:Optional Roots string `json:"roots,omitempty"` } // Certificate defines the configuration for local signature verification type Certificate struct { // Certificate is the to the public certificate for local signature verification. // +optional Certificate string `json:"cert,omitempty"` // CertificateChain is the list of CA certificates in PEM format which will be needed // when building the certificate chain for the signing certificate. Must start with the // parent intermediate CA certificate of the signing certificate and end with the root certificate // +optional CertificateChain string `json:"certChain,omitempty"` } // Identity may contain the issuer and/or the subject found in the transparency // log. // Issuer/Subject uses a strict match, while IssuerRegExp and SubjectRegExp // apply a regexp for matching. type Identity struct { // Issuer defines the issuer for this identity. // +optional Issuer string `json:"issuer,omitempty"` // Subject defines the subject for this identity. // +optional Subject string `json:"subject,omitempty"` // IssuerRegExp specifies a regular expression to match the issuer for this identity. // +optional IssuerRegExp string `json:"issuerRegExp,omitempty"` // SubjectRegExp specifies a regular expression to match the subject for this identity. // +optional SubjectRegExp string `json:"subjectRegExp,omitempty"` } // Attestation defines the identification details of the metadata that has to be verified type Attestation struct { // Name is the name for this attestation. It is used to refer to the attestation in verification Name string `json:"name"` // InToto defines the details of attestation attached using intoto format // +optional InToto *InToto `json:"intoto,omitempty"` // Referrer defines the details of attestation attached using OCI 1.1 format // +optional Referrer *Referrer `json:"referrer,omitempty"` } func (a Attestation) GetKey() string { return a.Name } func (a Attestation) IsInToto() bool { return a.InToto != nil } func (a Attestation) IsReferrer() bool { return a.Referrer != nil } type InToto struct { // Type defines the type of attestation contained within the statement. Type string `json:"type"` } type Referrer struct { // Type defines the type of attestation attached to the image. Type string `json:"type"` } // EvaluationMode returns the evaluation mode of the policy. func (s ImageVerificationPolicySpec) EvaluationMode() EvaluationMode { if s.EvaluationConfiguration == nil || s.EvaluationConfiguration.Mode == "" { return EvaluationModeKubernetes } return s.EvaluationConfiguration.Mode }