diff --git a/Makefile b/Makefile index 028e30fe31..d811dac53a 100644 --- a/Makefile +++ b/Makefile @@ -617,6 +617,7 @@ codegen-cli-crds: codegen-crds-cli @cp config/crds/kyverno/kyverno.io_policyexceptions.yaml cmd/cli/kubectl-kyverno/data/crds @cp config/crds/policies.kyverno.io/policies.kyverno.io_celpolicyexceptions.yaml cmd/cli/kubectl-kyverno/data/crds @cp config/crds/policies.kyverno.io/policies.kyverno.io_validatingpolicies.yaml cmd/cli/kubectl-kyverno/data/crds + @cp config/crds/policies.kyverno.io/policies.kyverno.io_imageverificationpolicies.yaml cmd/cli/kubectl-kyverno/data/crds @cp cmd/cli/kubectl-kyverno/config/crds/* cmd/cli/kubectl-kyverno/data/crds .PHONY: codegen-docs-all @@ -671,6 +672,7 @@ codegen-helm-crds: codegen-crds-all $(call generate_crd,kyverno.io_updaterequests.yaml,kyverno,kyverno.io,kyverno,updaterequests) $(call generate_crd,policies.kyverno.io_celpolicyexceptions.yaml,policies.kyverno.io,policies.kyverno.io,policies,celpolicyexceptions) $(call generate_crd,policies.kyverno.io_validatingpolicies.yaml,policies.kyverno.io,policies.kyverno.io,policies,validatingpolicies) + $(call generate_crd,policies.kyverno.io_imageverificationpolicies.yaml,policies.kyverno.io,policies.kyverno.io,policies,imageverificationpolicies) $(call generate_crd,reports.kyverno.io_clusterephemeralreports.yaml,reports,reports.kyverno.io,reports,clusterephemeralreports) $(call generate_crd,reports.kyverno.io_ephemeralreports.yaml,reports,reports.kyverno.io,reports,ephemeralreports) $(call generate_crd,wgpolicyk8s.io_clusterpolicyreports.yaml,policyreport,wgpolicyk8s.io,wgpolicyk8s,clusterpolicyreports) diff --git a/api/kyverno/constants.go b/api/kyverno/constants.go index a5b764bd5d..9d0cc5bca2 100644 --- a/api/kyverno/constants.go +++ b/api/kyverno/constants.go @@ -11,6 +11,7 @@ const ( // Well known annotations AnnotationAutogenControllers = "pod-policies.kyverno.io/autogen-controllers" AnnotationImageVerify = "kyverno.io/verify-images" + AnnotationImageVerifyOutcomes = "kyverno.io/image-verification-outcomes" AnnotationPolicyCategory = "policies.kyverno.io/category" AnnotationPolicyScored = "policies.kyverno.io/scored" AnnotationPolicySeverity = "policies.kyverno.io/severity" diff --git a/api/policies.kyverno.io/v1alpha1/imageverification_policy.go b/api/policies.kyverno.io/v1alpha1/imageverification_policy.go index 0d083c9971..489b729323 100644 --- a/api/policies.kyverno.io/v1alpha1/imageverification_policy.go +++ b/api/policies.kyverno.io/v1alpha1/imageverification_policy.go @@ -1,6 +1,9 @@ package v1alpha1 import ( + "strings" + + "github.com/kyverno/kyverno/api/kyverno" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -21,6 +24,20 @@ type ImageVerificationPolicy struct { Status PolicyStatus `json:"status,omitempty"` } +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{} diff --git a/charts/kyverno/README.md b/charts/kyverno/README.md index d68bf4b55a..516b32da5f 100644 --- a/charts/kyverno/README.md +++ b/charts/kyverno/README.md @@ -260,6 +260,7 @@ The chart values are organised per component. | crds.groups.kyverno | object | `{"celpolicyexceptions":true,"cleanuppolicies":true,"clustercleanuppolicies":true,"clusterpolicies":true,"globalcontextentries":true,"policies":true,"policyexceptions":true,"updaterequests":true,"validatingpolicies":true}` | Install CRDs in group `kyverno.io` | | crds.groups.policies.validatingpolicies | bool | `true` | | | crds.groups.policies.celpolicyexceptions | bool | `true` | | +| crds.groups.policies.imageverificationpolicies | bool | `true` | | | crds.groups.reports | object | `{"clusterephemeralreports":true,"ephemeralreports":true}` | Install CRDs in group `reports.kyverno.io` | | crds.groups.wgpolicyk8s | object | `{"clusterpolicyreports":true,"policyreports":true}` | Install CRDs in group `wgpolicyk8s.io` | | crds.annotations | object | `{}` | Additional CRDs annotations | diff --git a/charts/kyverno/charts/crds/templates/policies.kyverno.io/policies.kyverno.io_imageverificationpolicies.yaml b/charts/kyverno/charts/crds/templates/policies.kyverno.io/policies.kyverno.io_imageverificationpolicies.yaml new file mode 100644 index 0000000000..5b16d70e61 --- /dev/null +++ b/charts/kyverno/charts/crds/templates/policies.kyverno.io/policies.kyverno.io_imageverificationpolicies.yaml @@ -0,0 +1,1583 @@ +{{- if .Values.groups.policies.imageverificationpolicies }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + {{- include "kyverno.crds.labels" . | nindent 4 }} + annotations: + {{- with .Values.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + controller-gen.kubebuilder.io/version: v0.16.1 + name: imageverificationpolicies.policies.kyverno.io +spec: + group: policies.kyverno.io + names: + categories: + - kyverno + kind: ImageVerificationPolicy + listKind: ImageVerificationPolicyList + plural: imageverificationpolicies + shortNames: + - ivpol + singular: imageverificationpolicy + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ImageVerificationPolicySpec is the specification of the desired + behavior of the ImageVerificationPolicy. + properties: + attestations: + description: Attestations provides a list of image metadata to verify + items: + description: Attestation defines the identification details of the metadata + that has to be verified + properties: + intoto: + description: InToto defines the details of attestation attached + using intoto format + properties: + type: + description: Type defines the type of attestation contained + within the statement. + type: string + required: + - type + type: object + name: + description: Name is the name for this attestation. It is used + to refer to the attestation in verification + type: string + referrer: + description: Referrer defines the details of attestation attached + using OCI 1.1 format + properties: + type: + description: Type defines the type of attestation attached + to the image. + type: string + required: + - type + type: object + required: + - name + type: object + type: array + attestors: + description: Attestors provides a list of trusted authorities. + items: + description: Attestor is an identity that confirms or verifies the + authenticity of an image or an attestation + properties: + cosign: + description: Cosign defines attestor configuration for Cosign + based signatures + properties: + annotations: + additionalProperties: + type: string + description: |- + 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. + type: object + certificate: + description: Certificate defines the configuration for local + signature verification + properties: + cert: + description: Certificate is the to the public certificate + for local signature verification. + type: string + certChain: + description: |- + 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 + type: string + type: object + ctlog: + description: CTLog sets the configuration to verify the + authority against a Rekor instance. + properties: + ctLogPubKey: + description: CTLogPubKey, if set, is used to validate + SCTs against a custom source. + type: string + insecureIgnoreSCT: + description: |- + 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. + type: boolean + insecureIgnoreTlog: + description: InsecureIgnoreTlog skips transparency log + verification. + type: boolean + rekorPubKey: + description: |- + 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. + type: string + tsaCertChain: + description: |- + 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. + type: string + url: + description: URL sets the url to the rekor instance + (by default the public rekor.sigstore.dev) + type: string + type: object + key: + description: Key defines the type of key to validate the + image. + properties: + data: + description: Data contains the inline public key + type: string + hashAlgorithm: + description: |- + HashAlgorithm specifues signature algorithm for public keys. Supported values are + sha224, sha256, sha384 and sha512. Defaults to sha256. + type: string + kms: + description: |- + KMS contains the KMS url of the public key + Supported formats differ based on the KMS system used. + type: string + secretRef: + description: SecretRef sets a reference to a secret + with the key. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within + which the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + type: object + keyless: + description: Keyless sets the configuration to verify the + authority against a Fulcio instance. + properties: + identities: + description: Identities sets a list of identities. + items: + description: |- + 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. + properties: + issuer: + description: Issuer defines the issuer for this + identity. + type: string + issuerRegExp: + description: IssuerRegExp specifies a regular + expression to match the issuer for this identity. + type: string + subject: + description: Subject defines the subject for this + identity. + type: string + subjectRegExp: + description: SubjectRegExp specifies a regular + expression to match the subject for this identity. + type: string + type: object + type: array + roots: + description: |- + Roots is an optional set of PEM encoded trusted root certificates. + If not provided, the system roots are used. + type: string + required: + - identities + type: object + source: + description: Sources sets the configuration to specify the + sources from where to consume the signature and attestations. + properties: + PullSecrets: + description: |- + 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. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + repository: + description: Repository defines the location from where + to pull the signature / attestations. + type: string + tagPrefix: + description: |- + 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. + type: string + type: object + tuf: + description: TUF defines the configuration to fetch sigstore + root + properties: + mirror: + description: Mirror is the base URL of Sigstore TUF + repository + type: string + root: + description: Root defines the path or data of the trusted + root + properties: + data: + description: Data is the base64 encoded TUF root + type: string + path: + description: Path is the URL or File location of + the TUF root + type: string + type: object + type: object + type: object + name: + description: Name is the name for this attestor. It is used + to refer to the attestor in verification + type: string + notary: + description: Notary defines attestor configuration for Notary + based signatures + properties: + certs: + description: Certs define the cert chain for Notary signature + verification + type: string + tsaCerts: + description: TSACerts define the cert chain for verifying + timestamps of notary signature + type: string + required: + - certs + type: object + required: + - name + type: object + type: array + credentials: + description: Credentials provides credentials that will be used for + authentication with registry. + properties: + allowInsecureRegistry: + description: AllowInsecureRegistry allows insecure access to a + registry. + type: boolean + providers: + description: |- + 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. + items: + description: CredentialsProvidersType provides the list of credential + providers required. + enum: + - default + - amazon + - azure + - google + - github + type: string + type: array + secrets: + description: |- + Secrets specifies a list of secrets that are provided for credentials. + Secrets must live in the Kyverno namespace. + items: + type: string + type: array + type: object + evaluation: + description: EvaluationConfiguration defines the configuration for + the policy evaluation. + properties: + admission: + description: Admission controls policy evaluation during admission. + properties: + enabled: + default: true + description: |- + Enabled controls if rules are applied during admission. + Optional. Default value is "true". + type: boolean + type: object + background: + description: Background controls policy evaluation during background + scan. + properties: + enabled: + default: true + description: |- + Enabled controls if rules are applied to existing resources during a background scan. + Optional. Default value is "true". The value must be set to "false" if the policy rule + uses variables that are only available in the admission review request (e.g. user name). + type: boolean + type: object + mode: + description: |- + Mode is the mode of policy evaluation. + Allowed values are "Kubernetes" or "JSON". + Optional. Default value is "Kubernetes". + type: string + type: object + failurePolicy: + description: |- + 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. + type: string + imageRules: + description: |- + 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 + items: + description: ImageRule defines a Glob or a CEL expression for matching + images + properties: + cel: + description: Cel defines CEL Expressions for matching images + type: string + glob: + description: Glob defines a globbing pattern for matching images + type: string + type: object + type: array + images: + description: Images is a list of CEL expression to extract images + from the resource + items: + properties: + expression: + description: Expression defines CEL expression to extact images + from the resource. + type: string + name: + description: Name is the name for this imageList. It is used + to refer to the images in verification block as images.<name> + type: string + required: + - expression + - name + type: object + type: array + matchConditions: + description: |- + 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. + items: + description: MatchCondition represents a condition which must by + fulfilled for a request to be sent to a webhook. + properties: + expression: + description: |- + Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. + CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables: + + 'object' - The object from the incoming request. The value is null for DELETE requests. + 'oldObject' - The existing object. The value is null for CREATE requests. + 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request. + See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the + request resource. + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Required. + type: string + name: + description: |- + Name is an identifier for this match condition, used for strategic merging of MatchConditions, + as well as providing an identifier for logging purposes. A good name should be descriptive of + the associated expression. + Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and + must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or + '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an + optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + + Required. + type: string + required: + - expression + - name + type: object + type: array + matchConstraints: + description: MatchConstraints specifies what resources this policy + is designed to validate. + properties: + excludeResourceRules: + description: |- + ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + items: + description: NamedRuleWithOperations is a tuple of Operations + and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation for + a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of + names that the rule applies to. An empty set means that + everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + matchPolicy: + description: |- + matchPolicy defines how the "MatchResources" list is used to match incoming requests. + Allowed values are "Exact" or "Equivalent". + + - Exact: match a request only if it exactly matches a specified rule. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy. + + - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy. + + Defaults to "Equivalent" + type: string + namespaceSelector: + description: |- + NamespaceSelector decides whether to run the admission control policy on an object based + on whether the namespace for that object matches the selector. If the + object itself is a namespace, the matching is performed on + object.metadata.labels. If the object is another cluster scoped resource, + it never skips the policy. + + For example, to run the webhook on any objects whose namespace is not + associated with "runlevel" of "0" or "1"; you will set the selector as + follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "runlevel", + "operator": "NotIn", + "values": [ + "0", + "1" + ] + } + ] + } + + If instead you want to only run the policy on any objects whose + namespace is associated with the "environment" of "prod" or "staging"; + you will set the selector as follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "environment", + "operator": "In", + "values": [ + "prod", + "staging" + ] + } + ] + } + + See + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + for more examples of label selectors. + + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + objectSelector: + description: |- + ObjectSelector decides whether to run the validation based on if the + object has matching labels. objectSelector is evaluated against both + the oldObject and newObject that would be sent to the cel validation, and + is considered to match if either object matches the selector. A null + object (oldObject in the case of create, or newObject in the case of + delete) or an object that cannot have labels (like a + DeploymentRollback or a PodProxyOptions object) is not considered to + match. + Use the object selector only if the webhook is opt-in, because end + users may skip the admission webhook by setting the labels. + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + resourceRules: + description: |- + ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. + The policy cares about an operation if it matches _any_ Rule. + items: + description: NamedRuleWithOperations is a tuple of Operations + and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation for + a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of + names that the rule applies to. An empty set means that + everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + mutateDigest: + default: true + description: |- + MutateDigest enables replacement of image tags with digests. + Defaults to true. + type: boolean + required: + default: true + description: Required validates that images are verified i.e. have + matched passed a signature or attestation check. + type: boolean + validationActions: + description: |- + ValidationAction specifies the action to be taken when the matched resource violates the policy. + Required. + items: + description: ValidationAction specifies a policy enforcement action. + type: string + type: array + x-kubernetes-list-type: set + variables: + description: |- + Variables contain definitions of variables that can be used in composition of other expressions. + Each variable is defined as a named CEL expression. + items: + description: Variable is the definition of a variable that is used + for composition. A variable is defined as a named expression. + properties: + expression: + description: |- + Expression is the expression that will be evaluated as the value of the variable. + The CEL expression has access to the same identifiers as the CEL expressions in Validation. + type: string + name: + description: |- + Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. + The variable can be accessed in other expressions through `variables` + For example, if name is "foo", the variable will be available as `variables.foo` + type: string + required: + - expression + - name + type: object + x-kubernetes-map-type: atomic + type: array + verifications: + description: Verifications contain CEL expressions which is used to + apply the image verification checks. + items: + description: Validation specifies the CEL expression which is used + to apply the validation. + properties: + expression: + description: "Expression represents the expression which will + be evaluated by CEL.\nref: https://github.com/google/cel-spec\nCEL + expressions have access to the contents of the API request/response, + organized into CEL variables as well as some other useful + variables:\n\n- 'object' - The object from the incoming request. + The value is null for DELETE requests.\n- 'oldObject' - The + existing object. The value is null for CREATE requests.\n- + 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).\n- + 'params' - Parameter resource referred to by the policy binding + being evaluated. Only populated if the policy has a ParamKind.\n- + 'namespaceObject' - The namespace object that the incoming + object belongs to. The value is null for cluster-scoped resources.\n- + 'variables' - Map of composited variables, from its name to + its lazily evaluated value.\n For example, a variable named + 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' + - A CEL Authorizer. May be used to perform authorization checks + for the principal (user or service account) of the request.\n + \ See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the\n request resource.\n\nThe + `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` + are always accessible from the root of the\nobject. No other + metadata properties are accessible.\n\nOnly property names + of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.\nAccessible + property names are escaped according to the following rules + when accessed in the expression:\n- '__' escapes to '__underscores__'\n- + '.' escapes to '__dot__'\n- '-' escapes to '__dash__'\n- '/' + escapes to '__slash__'\n- Property names that exactly match + a CEL RESERVED keyword escape to '__{keyword}__'. The keywords + are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", + \"const\", \"continue\", \"else\", \"for\", \"function\", + \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", + \"return\".\nExamples:\n - Expression accessing a property + named \"namespace\": {\"Expression\": \"object.__namespace__ + > 0\"}\n - Expression accessing a property named \"x-prop\": + {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression + accessing a property named \"redact__d\": {\"Expression\": + \"object.redact__underscores__d > 0\"}\n\nEquality on arrays + with list type of 'set' or 'map' ignores element order, i.e. + [1, 2] == [2, 1].\nConcatenation on arrays with x-kubernetes-list-type + use the semantics of the list type:\n - 'set': `X + Y` performs + a union where the array positions of all elements in `X` are + preserved and\n non-intersecting elements in `Y` are appended, + retaining their partial order.\n - 'map': `X + Y` performs + a merge where the array positions of all keys in `X` are preserved + but the values\n are overwritten by values in `Y` when + the key sets of `X` and `Y` intersect. Elements in `Y` with\n + \ non-intersecting keys are appended, retaining their partial + order.\nRequired." + type: string + message: + description: |- + Message represents the message displayed when validation fails. The message is required if the Expression contains + line breaks. The message must not contain line breaks. + If unset, the message is "failed rule: {Rule}". + e.g. "must be a URL with the host matching spec.host" + If the Expression contains line breaks. Message is required. + The message must not contain line breaks. + If unset, the message is "failed Expression: {Expression}". + type: string + messageExpression: + description: |- + messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. + Since messageExpression is used as a failure message, it must evaluate to a string. + If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. + If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced + as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string + that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and + the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. + messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. + Example: + "object.x must be less than max ("+string(params.max)+")" + type: string + reason: + description: |- + Reason represents a machine-readable description of why this validation failed. + If this is the first validation in the list to fail, this reason, as well as the + corresponding HTTP response code, are used in the + HTTP response to the client. + The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge". + If not set, StatusReasonInvalid is used in the response to the client. + type: string + required: + - expression + type: object + type: array + x-kubernetes-list-type: atomic + verifyDigest: + default: true + description: VerifyDigest validates that images have a digest. + type: boolean + webhookConfiguration: + description: WebhookConfiguration defines the configuration for the + webhook. + properties: + timeoutSeconds: + description: |- + TimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. + After the configured time expires, the admission request may fail, or may simply ignore the policy results, + based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds. + format: int32 + type: integer + type: object + required: + - attestors + - verifications + type: object + status: + description: Status contains policy runtime data. + properties: + autogen: + description: AutogenStatus contains autogen status information. + properties: + rules: + description: Rules is a list of Rule instances. It contains auto + generated rules added for pod controllers + items: + properties: + auditAnnotations: + items: + description: AuditAnnotation describes how to produce + an audit annotation for an API request. + properties: + key: + description: |- + key specifies the audit annotation key. The audit annotation keys of + a ValidatingAdmissionPolicy must be unique. The key must be a qualified + name ([A-Za-z0-9][-A-Za-z0-9_.]*) no more than 63 bytes in length. + + The key is combined with the resource name of the + ValidatingAdmissionPolicy to construct an audit annotation key: + "{ValidatingAdmissionPolicy name}/{key}". + + If an admission webhook uses the same resource name as this ValidatingAdmissionPolicy + and the same audit annotation key, the annotation key will be identical. + In this case, the first annotation written with the key will be included + in the audit event and all subsequent annotations with the same key + will be discarded. + + Required. + type: string + valueExpression: + description: |- + valueExpression represents the expression which is evaluated by CEL to + produce an audit annotation value. The expression must evaluate to either + a string or null value. If the expression evaluates to a string, the + audit annotation is included with the string value. If the expression + evaluates to null or empty string the audit annotation will be omitted. + The valueExpression may be no longer than 5kb in length. + If the result of the valueExpression is more than 10kb in length, it + will be truncated to 10kb. + + If multiple ValidatingAdmissionPolicyBinding resources match an + API request, then the valueExpression will be evaluated for + each binding. All unique values produced by the valueExpressions + will be joined together in a comma-separated list. + + Required. + type: string + required: + - key + - valueExpression + type: object + type: array + matchConditions: + items: + description: MatchCondition represents a condition which + must by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: |- + Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. + CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables: + + 'object' - The object from the incoming request. The value is null for DELETE requests. + 'oldObject' - The existing object. The value is null for CREATE requests. + 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request. + See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the + request resource. + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Required. + type: string + name: + description: |- + Name is an identifier for this match condition, used for strategic merging of MatchConditions, + as well as providing an identifier for logging purposes. A good name should be descriptive of + the associated expression. + Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and + must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or + '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an + optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + + Required. + type: string + required: + - expression + - name + type: object + type: array + matchConstraints: + description: |- + MatchResources decides whether to run the admission control policy on an object based + on whether it meets the match criteria. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + properties: + excludeResourceRules: + description: |- + ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + items: + description: NamedRuleWithOperations is a tuple of + Operations and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation + for a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An + empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + matchPolicy: + description: |- + matchPolicy defines how the "MatchResources" list is used to match incoming requests. + Allowed values are "Exact" or "Equivalent". + + - Exact: match a request only if it exactly matches a specified rule. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy. + + - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy. + + Defaults to "Equivalent" + type: string + namespaceSelector: + description: |- + NamespaceSelector decides whether to run the admission control policy on an object based + on whether the namespace for that object matches the selector. If the + object itself is a namespace, the matching is performed on + object.metadata.labels. If the object is another cluster scoped resource, + it never skips the policy. + + For example, to run the webhook on any objects whose namespace is not + associated with "runlevel" of "0" or "1"; you will set the selector as + follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "runlevel", + "operator": "NotIn", + "values": [ + "0", + "1" + ] + } + ] + } + + If instead you want to only run the policy on any objects whose + namespace is associated with the "environment" of "prod" or "staging"; + you will set the selector as follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "environment", + "operator": "In", + "values": [ + "prod", + "staging" + ] + } + ] + } + + See + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + for more examples of label selectors. + + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + objectSelector: + description: |- + ObjectSelector decides whether to run the validation based on if the + object has matching labels. objectSelector is evaluated against both + the oldObject and newObject that would be sent to the cel validation, and + is considered to match if either object matches the selector. A null + object (oldObject in the case of create, or newObject in the case of + delete) or an object that cannot have labels (like a + DeploymentRollback or a PodProxyOptions object) is not considered to + match. + Use the object selector only if the webhook is opt-in, because end + users may skip the admission webhook by setting the labels. + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + resourceRules: + description: |- + ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. + The policy cares about an operation if it matches _any_ Rule. + items: + description: NamedRuleWithOperations is a tuple of + Operations and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation + for a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An + empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + validations: + items: + description: Validation specifies the CEL expression which + is used to apply the validation. + properties: + expression: + description: "Expression represents the expression + which will be evaluated by CEL.\nref: https://github.com/google/cel-spec\nCEL + expressions have access to the contents of the API + request/response, organized into CEL variables as + well as some other useful variables:\n\n- 'object' + - The object from the incoming request. The value + is null for DELETE requests.\n- 'oldObject' - The + existing object. The value is null for CREATE requests.\n- + 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).\n- + 'params' - Parameter resource referred to by the + policy binding being evaluated. Only populated if + the policy has a ParamKind.\n- 'namespaceObject' + - The namespace object that the incoming object + belongs to. The value is null for cluster-scoped + resources.\n- 'variables' - Map of composited variables, + from its name to its lazily evaluated value.\n For + example, a variable named 'foo' can be accessed + as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. + May be used to perform authorization checks for + the principal (user or service account) of the request.\n + \ See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- + 'authorizer.requestResource' - A CEL ResourceCheck + constructed from the 'authorizer' and configured + with the\n request resource.\n\nThe `apiVersion`, + `kind`, `metadata.name` and `metadata.generateName` + are always accessible from the root of the\nobject. + No other metadata properties are accessible.\n\nOnly + property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` + are accessible.\nAccessible property names are escaped + according to the following rules when accessed in + the expression:\n- '__' escapes to '__underscores__'\n- + '.' escapes to '__dot__'\n- '-' escapes to '__dash__'\n- + '/' escapes to '__slash__'\n- Property names that + exactly match a CEL RESERVED keyword escape to '__{keyword}__'. + The keywords are:\n\t \"true\", \"false\", \"null\", + \"in\", \"as\", \"break\", \"const\", \"continue\", + \"else\", \"for\", \"function\", \"if\",\n\t \"import\", + \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n + \ - Expression accessing a property named \"namespace\": + {\"Expression\": \"object.__namespace__ > 0\"}\n + \ - Expression accessing a property named \"x-prop\": + {\"Expression\": \"object.x__dash__prop > 0\"}\n + \ - Expression accessing a property named \"redact__d\": + {\"Expression\": \"object.redact__underscores__d + > 0\"}\n\nEquality on arrays with list type of 'set' + or 'map' ignores element order, i.e. [1, 2] == [2, + 1].\nConcatenation on arrays with x-kubernetes-list-type + use the semantics of the list type:\n - 'set': + `X + Y` performs a union where the array positions + of all elements in `X` are preserved and\n non-intersecting + elements in `Y` are appended, retaining their partial + order.\n - 'map': `X + Y` performs a merge where + the array positions of all keys in `X` are preserved + but the values\n are overwritten by values in + `Y` when the key sets of `X` and `Y` intersect. + Elements in `Y` with\n non-intersecting keys + are appended, retaining their partial order.\nRequired." + type: string + message: + description: |- + Message represents the message displayed when validation fails. The message is required if the Expression contains + line breaks. The message must not contain line breaks. + If unset, the message is "failed rule: {Rule}". + e.g. "must be a URL with the host matching spec.host" + If the Expression contains line breaks. Message is required. + The message must not contain line breaks. + If unset, the message is "failed Expression: {Expression}". + type: string + messageExpression: + description: |- + messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. + Since messageExpression is used as a failure message, it must evaluate to a string. + If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. + If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced + as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string + that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and + the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. + messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. + Example: + "object.x must be less than max ("+string(params.max)+")" + type: string + reason: + description: |- + Reason represents a machine-readable description of why this validation failed. + If this is the first validation in the list to fail, this reason, as well as the + corresponding HTTP response code, are used in the + HTTP response to the client. + The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge". + If not set, StatusReasonInvalid is used in the response to the client. + type: string + required: + - expression + type: object + type: array + variables: + items: + description: Variable is the definition of a variable + that is used for composition. A variable is defined + as a named expression. + properties: + expression: + description: |- + Expression is the expression that will be evaluated as the value of the variable. + The CEL expression has access to the same identifiers as the CEL expressions in Validation. + type: string + name: + description: |- + Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. + The variable can be accessed in other expressions through `variables` + For example, if name is "foo", the variable will be available as `variables.foo` + type: string + required: + - expression + - name + type: object + x-kubernetes-map-type: atomic + type: array + type: object + type: array + type: object + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + generated: + description: Generated indicates whether a ValidatingAdmissionPolicy/MutatingAdmissionPolicy + is generated from the policy or not + type: boolean + message: + description: |- + Message is a human readable message indicating details about the generation of ValidatingAdmissionPolicy/MutatingAdmissionPolicy + It is an empty string when ValidatingAdmissionPolicy/MutatingAdmissionPolicy is successfully generated. + type: string + ready: + description: |- + The ready of a policy is a high-level summary of where the policy is in its lifecycle. + The conditions array, the reason and message fields contain more detail about the policy's status. + type: boolean + required: + - message + type: object + required: + - spec + type: object + served: true + storage: true +{{- end }} diff --git a/charts/kyverno/templates/admission-controller/clusterrole.yaml b/charts/kyverno/templates/admission-controller/clusterrole.yaml index 49d89b0f10..0ef622271e 100644 --- a/charts/kyverno/templates/admission-controller/clusterrole.yaml +++ b/charts/kyverno/templates/admission-controller/clusterrole.yaml @@ -89,6 +89,8 @@ rules: - validatingpolicies - validatingpolicies/status - celpolicyexceptions + - imageverificationpolicies + - imageverificationpolicies/status verbs: - create - delete diff --git a/charts/kyverno/templates/reports-controller/clusterrole.yaml b/charts/kyverno/templates/reports-controller/clusterrole.yaml index 568581931a..3652cc3a82 100644 --- a/charts/kyverno/templates/reports-controller/clusterrole.yaml +++ b/charts/kyverno/templates/reports-controller/clusterrole.yaml @@ -57,6 +57,8 @@ rules: resources: - validatingpolicies - validatingpolicies/status + - imageverificationpolicies + - imageverificationpolicies/status verbs: - create - delete diff --git a/charts/kyverno/values.yaml b/charts/kyverno/values.yaml index 6c497b1509..e39ce48489 100644 --- a/charts/kyverno/values.yaml +++ b/charts/kyverno/values.yaml @@ -95,6 +95,7 @@ crds: policies: validatingpolicies: true celpolicyexceptions: true + imageverificationpolicies: true # -- Install CRDs in group `reports.kyverno.io` reports: diff --git a/cmd/cli/kubectl-kyverno/data/crds/policies.kyverno.io_imageverificationpolicies.yaml b/cmd/cli/kubectl-kyverno/data/crds/policies.kyverno.io_imageverificationpolicies.yaml new file mode 100644 index 0000000000..f3daf37960 --- /dev/null +++ b/cmd/cli/kubectl-kyverno/data/crds/policies.kyverno.io_imageverificationpolicies.yaml @@ -0,0 +1,1576 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + name: imageverificationpolicies.policies.kyverno.io +spec: + group: policies.kyverno.io + names: + categories: + - kyverno + kind: ImageVerificationPolicy + listKind: ImageVerificationPolicyList + plural: imageverificationpolicies + shortNames: + - ivpol + singular: imageverificationpolicy + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ImageVerificationPolicySpec is the specification of the desired + behavior of the ImageVerificationPolicy. + properties: + attestations: + description: Attestations provides a list of image metadata to verify + items: + description: Attestation defines the identification details of the metadata + that has to be verified + properties: + intoto: + description: InToto defines the details of attestation attached + using intoto format + properties: + type: + description: Type defines the type of attestation contained + within the statement. + type: string + required: + - type + type: object + name: + description: Name is the name for this attestation. It is used + to refer to the attestation in verification + type: string + referrer: + description: Referrer defines the details of attestation attached + using OCI 1.1 format + properties: + type: + description: Type defines the type of attestation attached + to the image. + type: string + required: + - type + type: object + required: + - name + type: object + type: array + attestors: + description: Attestors provides a list of trusted authorities. + items: + description: Attestor is an identity that confirms or verifies the + authenticity of an image or an attestation + properties: + cosign: + description: Cosign defines attestor configuration for Cosign + based signatures + properties: + annotations: + additionalProperties: + type: string + description: |- + 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. + type: object + certificate: + description: Certificate defines the configuration for local + signature verification + properties: + cert: + description: Certificate is the to the public certificate + for local signature verification. + type: string + certChain: + description: |- + 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 + type: string + type: object + ctlog: + description: CTLog sets the configuration to verify the + authority against a Rekor instance. + properties: + ctLogPubKey: + description: CTLogPubKey, if set, is used to validate + SCTs against a custom source. + type: string + insecureIgnoreSCT: + description: |- + 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. + type: boolean + insecureIgnoreTlog: + description: InsecureIgnoreTlog skips transparency log + verification. + type: boolean + rekorPubKey: + description: |- + 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. + type: string + tsaCertChain: + description: |- + 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. + type: string + url: + description: URL sets the url to the rekor instance + (by default the public rekor.sigstore.dev) + type: string + type: object + key: + description: Key defines the type of key to validate the + image. + properties: + data: + description: Data contains the inline public key + type: string + hashAlgorithm: + description: |- + HashAlgorithm specifues signature algorithm for public keys. Supported values are + sha224, sha256, sha384 and sha512. Defaults to sha256. + type: string + kms: + description: |- + KMS contains the KMS url of the public key + Supported formats differ based on the KMS system used. + type: string + secretRef: + description: SecretRef sets a reference to a secret + with the key. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within + which the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + type: object + keyless: + description: Keyless sets the configuration to verify the + authority against a Fulcio instance. + properties: + identities: + description: Identities sets a list of identities. + items: + description: |- + 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. + properties: + issuer: + description: Issuer defines the issuer for this + identity. + type: string + issuerRegExp: + description: IssuerRegExp specifies a regular + expression to match the issuer for this identity. + type: string + subject: + description: Subject defines the subject for this + identity. + type: string + subjectRegExp: + description: SubjectRegExp specifies a regular + expression to match the subject for this identity. + type: string + type: object + type: array + roots: + description: |- + Roots is an optional set of PEM encoded trusted root certificates. + If not provided, the system roots are used. + type: string + required: + - identities + type: object + source: + description: Sources sets the configuration to specify the + sources from where to consume the signature and attestations. + properties: + PullSecrets: + description: |- + 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. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + repository: + description: Repository defines the location from where + to pull the signature / attestations. + type: string + tagPrefix: + description: |- + 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. + type: string + type: object + tuf: + description: TUF defines the configuration to fetch sigstore + root + properties: + mirror: + description: Mirror is the base URL of Sigstore TUF + repository + type: string + root: + description: Root defines the path or data of the trusted + root + properties: + data: + description: Data is the base64 encoded TUF root + type: string + path: + description: Path is the URL or File location of + the TUF root + type: string + type: object + type: object + type: object + name: + description: Name is the name for this attestor. It is used + to refer to the attestor in verification + type: string + notary: + description: Notary defines attestor configuration for Notary + based signatures + properties: + certs: + description: Certs define the cert chain for Notary signature + verification + type: string + tsaCerts: + description: TSACerts define the cert chain for verifying + timestamps of notary signature + type: string + required: + - certs + type: object + required: + - name + type: object + type: array + credentials: + description: Credentials provides credentials that will be used for + authentication with registry. + properties: + allowInsecureRegistry: + description: AllowInsecureRegistry allows insecure access to a + registry. + type: boolean + providers: + description: |- + 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. + items: + description: CredentialsProvidersType provides the list of credential + providers required. + enum: + - default + - amazon + - azure + - google + - github + type: string + type: array + secrets: + description: |- + Secrets specifies a list of secrets that are provided for credentials. + Secrets must live in the Kyverno namespace. + items: + type: string + type: array + type: object + evaluation: + description: EvaluationConfiguration defines the configuration for + the policy evaluation. + properties: + admission: + description: Admission controls policy evaluation during admission. + properties: + enabled: + default: true + description: |- + Enabled controls if rules are applied during admission. + Optional. Default value is "true". + type: boolean + type: object + background: + description: Background controls policy evaluation during background + scan. + properties: + enabled: + default: true + description: |- + Enabled controls if rules are applied to existing resources during a background scan. + Optional. Default value is "true". The value must be set to "false" if the policy rule + uses variables that are only available in the admission review request (e.g. user name). + type: boolean + type: object + mode: + description: |- + Mode is the mode of policy evaluation. + Allowed values are "Kubernetes" or "JSON". + Optional. Default value is "Kubernetes". + type: string + type: object + failurePolicy: + description: |- + 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. + type: string + imageRules: + description: |- + 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 + items: + description: ImageRule defines a Glob or a CEL expression for matching + images + properties: + cel: + description: Cel defines CEL Expressions for matching images + type: string + glob: + description: Glob defines a globbing pattern for matching images + type: string + type: object + type: array + images: + description: Images is a list of CEL expression to extract images + from the resource + items: + properties: + expression: + description: Expression defines CEL expression to extact images + from the resource. + type: string + name: + description: Name is the name for this imageList. It is used + to refer to the images in verification block as images.<name> + type: string + required: + - expression + - name + type: object + type: array + matchConditions: + description: |- + 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. + items: + description: MatchCondition represents a condition which must by + fulfilled for a request to be sent to a webhook. + properties: + expression: + description: |- + Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. + CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables: + + 'object' - The object from the incoming request. The value is null for DELETE requests. + 'oldObject' - The existing object. The value is null for CREATE requests. + 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request. + See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the + request resource. + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Required. + type: string + name: + description: |- + Name is an identifier for this match condition, used for strategic merging of MatchConditions, + as well as providing an identifier for logging purposes. A good name should be descriptive of + the associated expression. + Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and + must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or + '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an + optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + + Required. + type: string + required: + - expression + - name + type: object + type: array + matchConstraints: + description: MatchConstraints specifies what resources this policy + is designed to validate. + properties: + excludeResourceRules: + description: |- + ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + items: + description: NamedRuleWithOperations is a tuple of Operations + and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation for + a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of + names that the rule applies to. An empty set means that + everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + matchPolicy: + description: |- + matchPolicy defines how the "MatchResources" list is used to match incoming requests. + Allowed values are "Exact" or "Equivalent". + + - Exact: match a request only if it exactly matches a specified rule. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy. + + - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy. + + Defaults to "Equivalent" + type: string + namespaceSelector: + description: |- + NamespaceSelector decides whether to run the admission control policy on an object based + on whether the namespace for that object matches the selector. If the + object itself is a namespace, the matching is performed on + object.metadata.labels. If the object is another cluster scoped resource, + it never skips the policy. + + For example, to run the webhook on any objects whose namespace is not + associated with "runlevel" of "0" or "1"; you will set the selector as + follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "runlevel", + "operator": "NotIn", + "values": [ + "0", + "1" + ] + } + ] + } + + If instead you want to only run the policy on any objects whose + namespace is associated with the "environment" of "prod" or "staging"; + you will set the selector as follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "environment", + "operator": "In", + "values": [ + "prod", + "staging" + ] + } + ] + } + + See + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + for more examples of label selectors. + + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + objectSelector: + description: |- + ObjectSelector decides whether to run the validation based on if the + object has matching labels. objectSelector is evaluated against both + the oldObject and newObject that would be sent to the cel validation, and + is considered to match if either object matches the selector. A null + object (oldObject in the case of create, or newObject in the case of + delete) or an object that cannot have labels (like a + DeploymentRollback or a PodProxyOptions object) is not considered to + match. + Use the object selector only if the webhook is opt-in, because end + users may skip the admission webhook by setting the labels. + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + resourceRules: + description: |- + ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. + The policy cares about an operation if it matches _any_ Rule. + items: + description: NamedRuleWithOperations is a tuple of Operations + and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation for + a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of + names that the rule applies to. An empty set means that + everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + mutateDigest: + default: true + description: |- + MutateDigest enables replacement of image tags with digests. + Defaults to true. + type: boolean + required: + default: true + description: Required validates that images are verified i.e. have + matched passed a signature or attestation check. + type: boolean + validationActions: + description: |- + ValidationAction specifies the action to be taken when the matched resource violates the policy. + Required. + items: + description: ValidationAction specifies a policy enforcement action. + type: string + type: array + x-kubernetes-list-type: set + variables: + description: |- + Variables contain definitions of variables that can be used in composition of other expressions. + Each variable is defined as a named CEL expression. + items: + description: Variable is the definition of a variable that is used + for composition. A variable is defined as a named expression. + properties: + expression: + description: |- + Expression is the expression that will be evaluated as the value of the variable. + The CEL expression has access to the same identifiers as the CEL expressions in Validation. + type: string + name: + description: |- + Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. + The variable can be accessed in other expressions through `variables` + For example, if name is "foo", the variable will be available as `variables.foo` + type: string + required: + - expression + - name + type: object + x-kubernetes-map-type: atomic + type: array + verifications: + description: Verifications contain CEL expressions which is used to + apply the image verification checks. + items: + description: Validation specifies the CEL expression which is used + to apply the validation. + properties: + expression: + description: "Expression represents the expression which will + be evaluated by CEL.\nref: https://github.com/google/cel-spec\nCEL + expressions have access to the contents of the API request/response, + organized into CEL variables as well as some other useful + variables:\n\n- 'object' - The object from the incoming request. + The value is null for DELETE requests.\n- 'oldObject' - The + existing object. The value is null for CREATE requests.\n- + 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).\n- + 'params' - Parameter resource referred to by the policy binding + being evaluated. Only populated if the policy has a ParamKind.\n- + 'namespaceObject' - The namespace object that the incoming + object belongs to. The value is null for cluster-scoped resources.\n- + 'variables' - Map of composited variables, from its name to + its lazily evaluated value.\n For example, a variable named + 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' + - A CEL Authorizer. May be used to perform authorization checks + for the principal (user or service account) of the request.\n + \ See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the\n request resource.\n\nThe + `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` + are always accessible from the root of the\nobject. No other + metadata properties are accessible.\n\nOnly property names + of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.\nAccessible + property names are escaped according to the following rules + when accessed in the expression:\n- '__' escapes to '__underscores__'\n- + '.' escapes to '__dot__'\n- '-' escapes to '__dash__'\n- '/' + escapes to '__slash__'\n- Property names that exactly match + a CEL RESERVED keyword escape to '__{keyword}__'. The keywords + are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", + \"const\", \"continue\", \"else\", \"for\", \"function\", + \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", + \"return\".\nExamples:\n - Expression accessing a property + named \"namespace\": {\"Expression\": \"object.__namespace__ + > 0\"}\n - Expression accessing a property named \"x-prop\": + {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression + accessing a property named \"redact__d\": {\"Expression\": + \"object.redact__underscores__d > 0\"}\n\nEquality on arrays + with list type of 'set' or 'map' ignores element order, i.e. + [1, 2] == [2, 1].\nConcatenation on arrays with x-kubernetes-list-type + use the semantics of the list type:\n - 'set': `X + Y` performs + a union where the array positions of all elements in `X` are + preserved and\n non-intersecting elements in `Y` are appended, + retaining their partial order.\n - 'map': `X + Y` performs + a merge where the array positions of all keys in `X` are preserved + but the values\n are overwritten by values in `Y` when + the key sets of `X` and `Y` intersect. Elements in `Y` with\n + \ non-intersecting keys are appended, retaining their partial + order.\nRequired." + type: string + message: + description: |- + Message represents the message displayed when validation fails. The message is required if the Expression contains + line breaks. The message must not contain line breaks. + If unset, the message is "failed rule: {Rule}". + e.g. "must be a URL with the host matching spec.host" + If the Expression contains line breaks. Message is required. + The message must not contain line breaks. + If unset, the message is "failed Expression: {Expression}". + type: string + messageExpression: + description: |- + messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. + Since messageExpression is used as a failure message, it must evaluate to a string. + If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. + If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced + as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string + that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and + the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. + messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. + Example: + "object.x must be less than max ("+string(params.max)+")" + type: string + reason: + description: |- + Reason represents a machine-readable description of why this validation failed. + If this is the first validation in the list to fail, this reason, as well as the + corresponding HTTP response code, are used in the + HTTP response to the client. + The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge". + If not set, StatusReasonInvalid is used in the response to the client. + type: string + required: + - expression + type: object + type: array + x-kubernetes-list-type: atomic + verifyDigest: + default: true + description: VerifyDigest validates that images have a digest. + type: boolean + webhookConfiguration: + description: WebhookConfiguration defines the configuration for the + webhook. + properties: + timeoutSeconds: + description: |- + TimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. + After the configured time expires, the admission request may fail, or may simply ignore the policy results, + based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds. + format: int32 + type: integer + type: object + required: + - attestors + - verifications + type: object + status: + description: Status contains policy runtime data. + properties: + autogen: + description: AutogenStatus contains autogen status information. + properties: + rules: + description: Rules is a list of Rule instances. It contains auto + generated rules added for pod controllers + items: + properties: + auditAnnotations: + items: + description: AuditAnnotation describes how to produce + an audit annotation for an API request. + properties: + key: + description: |- + key specifies the audit annotation key. The audit annotation keys of + a ValidatingAdmissionPolicy must be unique. The key must be a qualified + name ([A-Za-z0-9][-A-Za-z0-9_.]*) no more than 63 bytes in length. + + The key is combined with the resource name of the + ValidatingAdmissionPolicy to construct an audit annotation key: + "{ValidatingAdmissionPolicy name}/{key}". + + If an admission webhook uses the same resource name as this ValidatingAdmissionPolicy + and the same audit annotation key, the annotation key will be identical. + In this case, the first annotation written with the key will be included + in the audit event and all subsequent annotations with the same key + will be discarded. + + Required. + type: string + valueExpression: + description: |- + valueExpression represents the expression which is evaluated by CEL to + produce an audit annotation value. The expression must evaluate to either + a string or null value. If the expression evaluates to a string, the + audit annotation is included with the string value. If the expression + evaluates to null or empty string the audit annotation will be omitted. + The valueExpression may be no longer than 5kb in length. + If the result of the valueExpression is more than 10kb in length, it + will be truncated to 10kb. + + If multiple ValidatingAdmissionPolicyBinding resources match an + API request, then the valueExpression will be evaluated for + each binding. All unique values produced by the valueExpressions + will be joined together in a comma-separated list. + + Required. + type: string + required: + - key + - valueExpression + type: object + type: array + matchConditions: + items: + description: MatchCondition represents a condition which + must by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: |- + Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. + CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables: + + 'object' - The object from the incoming request. The value is null for DELETE requests. + 'oldObject' - The existing object. The value is null for CREATE requests. + 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request. + See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the + request resource. + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Required. + type: string + name: + description: |- + Name is an identifier for this match condition, used for strategic merging of MatchConditions, + as well as providing an identifier for logging purposes. A good name should be descriptive of + the associated expression. + Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and + must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or + '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an + optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + + Required. + type: string + required: + - expression + - name + type: object + type: array + matchConstraints: + description: |- + MatchResources decides whether to run the admission control policy on an object based + on whether it meets the match criteria. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + properties: + excludeResourceRules: + description: |- + ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + items: + description: NamedRuleWithOperations is a tuple of + Operations and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation + for a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An + empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + matchPolicy: + description: |- + matchPolicy defines how the "MatchResources" list is used to match incoming requests. + Allowed values are "Exact" or "Equivalent". + + - Exact: match a request only if it exactly matches a specified rule. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy. + + - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy. + + Defaults to "Equivalent" + type: string + namespaceSelector: + description: |- + NamespaceSelector decides whether to run the admission control policy on an object based + on whether the namespace for that object matches the selector. If the + object itself is a namespace, the matching is performed on + object.metadata.labels. If the object is another cluster scoped resource, + it never skips the policy. + + For example, to run the webhook on any objects whose namespace is not + associated with "runlevel" of "0" or "1"; you will set the selector as + follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "runlevel", + "operator": "NotIn", + "values": [ + "0", + "1" + ] + } + ] + } + + If instead you want to only run the policy on any objects whose + namespace is associated with the "environment" of "prod" or "staging"; + you will set the selector as follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "environment", + "operator": "In", + "values": [ + "prod", + "staging" + ] + } + ] + } + + See + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + for more examples of label selectors. + + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + objectSelector: + description: |- + ObjectSelector decides whether to run the validation based on if the + object has matching labels. objectSelector is evaluated against both + the oldObject and newObject that would be sent to the cel validation, and + is considered to match if either object matches the selector. A null + object (oldObject in the case of create, or newObject in the case of + delete) or an object that cannot have labels (like a + DeploymentRollback or a PodProxyOptions object) is not considered to + match. + Use the object selector only if the webhook is opt-in, because end + users may skip the admission webhook by setting the labels. + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + resourceRules: + description: |- + ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. + The policy cares about an operation if it matches _any_ Rule. + items: + description: NamedRuleWithOperations is a tuple of + Operations and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation + for a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An + empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + validations: + items: + description: Validation specifies the CEL expression which + is used to apply the validation. + properties: + expression: + description: "Expression represents the expression + which will be evaluated by CEL.\nref: https://github.com/google/cel-spec\nCEL + expressions have access to the contents of the API + request/response, organized into CEL variables as + well as some other useful variables:\n\n- 'object' + - The object from the incoming request. The value + is null for DELETE requests.\n- 'oldObject' - The + existing object. The value is null for CREATE requests.\n- + 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).\n- + 'params' - Parameter resource referred to by the + policy binding being evaluated. Only populated if + the policy has a ParamKind.\n- 'namespaceObject' + - The namespace object that the incoming object + belongs to. The value is null for cluster-scoped + resources.\n- 'variables' - Map of composited variables, + from its name to its lazily evaluated value.\n For + example, a variable named 'foo' can be accessed + as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. + May be used to perform authorization checks for + the principal (user or service account) of the request.\n + \ See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- + 'authorizer.requestResource' - A CEL ResourceCheck + constructed from the 'authorizer' and configured + with the\n request resource.\n\nThe `apiVersion`, + `kind`, `metadata.name` and `metadata.generateName` + are always accessible from the root of the\nobject. + No other metadata properties are accessible.\n\nOnly + property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` + are accessible.\nAccessible property names are escaped + according to the following rules when accessed in + the expression:\n- '__' escapes to '__underscores__'\n- + '.' escapes to '__dot__'\n- '-' escapes to '__dash__'\n- + '/' escapes to '__slash__'\n- Property names that + exactly match a CEL RESERVED keyword escape to '__{keyword}__'. + The keywords are:\n\t \"true\", \"false\", \"null\", + \"in\", \"as\", \"break\", \"const\", \"continue\", + \"else\", \"for\", \"function\", \"if\",\n\t \"import\", + \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n + \ - Expression accessing a property named \"namespace\": + {\"Expression\": \"object.__namespace__ > 0\"}\n + \ - Expression accessing a property named \"x-prop\": + {\"Expression\": \"object.x__dash__prop > 0\"}\n + \ - Expression accessing a property named \"redact__d\": + {\"Expression\": \"object.redact__underscores__d + > 0\"}\n\nEquality on arrays with list type of 'set' + or 'map' ignores element order, i.e. [1, 2] == [2, + 1].\nConcatenation on arrays with x-kubernetes-list-type + use the semantics of the list type:\n - 'set': + `X + Y` performs a union where the array positions + of all elements in `X` are preserved and\n non-intersecting + elements in `Y` are appended, retaining their partial + order.\n - 'map': `X + Y` performs a merge where + the array positions of all keys in `X` are preserved + but the values\n are overwritten by values in + `Y` when the key sets of `X` and `Y` intersect. + Elements in `Y` with\n non-intersecting keys + are appended, retaining their partial order.\nRequired." + type: string + message: + description: |- + Message represents the message displayed when validation fails. The message is required if the Expression contains + line breaks. The message must not contain line breaks. + If unset, the message is "failed rule: {Rule}". + e.g. "must be a URL with the host matching spec.host" + If the Expression contains line breaks. Message is required. + The message must not contain line breaks. + If unset, the message is "failed Expression: {Expression}". + type: string + messageExpression: + description: |- + messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. + Since messageExpression is used as a failure message, it must evaluate to a string. + If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. + If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced + as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string + that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and + the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. + messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. + Example: + "object.x must be less than max ("+string(params.max)+")" + type: string + reason: + description: |- + Reason represents a machine-readable description of why this validation failed. + If this is the first validation in the list to fail, this reason, as well as the + corresponding HTTP response code, are used in the + HTTP response to the client. + The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge". + If not set, StatusReasonInvalid is used in the response to the client. + type: string + required: + - expression + type: object + type: array + variables: + items: + description: Variable is the definition of a variable + that is used for composition. A variable is defined + as a named expression. + properties: + expression: + description: |- + Expression is the expression that will be evaluated as the value of the variable. + The CEL expression has access to the same identifiers as the CEL expressions in Validation. + type: string + name: + description: |- + Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. + The variable can be accessed in other expressions through `variables` + For example, if name is "foo", the variable will be available as `variables.foo` + type: string + required: + - expression + - name + type: object + x-kubernetes-map-type: atomic + type: array + type: object + type: array + type: object + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + generated: + description: Generated indicates whether a ValidatingAdmissionPolicy/MutatingAdmissionPolicy + is generated from the policy or not + type: boolean + message: + description: |- + Message is a human readable message indicating details about the generation of ValidatingAdmissionPolicy/MutatingAdmissionPolicy + It is an empty string when ValidatingAdmissionPolicy/MutatingAdmissionPolicy is successfully generated. + type: string + ready: + description: |- + The ready of a policy is a high-level summary of where the policy is in its lifecycle. + The conditions array, the reason and message fields contain more detail about the policy's status. + type: boolean + required: + - message + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index 992b1ecccc..bf790bf0b6 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -644,7 +644,7 @@ func main() { os.Exit(1) } celEngine = celengine.NewEngine( - provider, + provider.CompiledValidationPolicies, func(name string) *corev1.Namespace { ns, err := setup.KubeClient.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { diff --git a/config/install-latest-testing.yaml b/config/install-latest-testing.yaml index aec1d2970a..8a9f5681c1 100644 --- a/config/install-latest-testing.yaml +++ b/config/install-latest-testing.yaml @@ -48595,6 +48595,1589 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + labels: + app.kubernetes.io/component: crds + app.kubernetes.io/instance: kyverno + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/part-of: kyverno-crds + app.kubernetes.io/version: v0.0.0 + helm.sh/chart: crds-v0.0.0 + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: imageverificationpolicies.policies.kyverno.io +spec: + group: policies.kyverno.io + names: + categories: + - kyverno + kind: ImageVerificationPolicy + listKind: ImageVerificationPolicyList + plural: imageverificationpolicies + shortNames: + - ivpol + singular: imageverificationpolicy + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ImageVerificationPolicySpec is the specification of the desired + behavior of the ImageVerificationPolicy. + properties: + attestations: + description: Attestations provides a list of image metadata to verify + items: + description: Attestation defines the identification details of the metadata + that has to be verified + properties: + intoto: + description: InToto defines the details of attestation attached + using intoto format + properties: + type: + description: Type defines the type of attestation contained + within the statement. + type: string + required: + - type + type: object + name: + description: Name is the name for this attestation. It is used + to refer to the attestation in verification + type: string + referrer: + description: Referrer defines the details of attestation attached + using OCI 1.1 format + properties: + type: + description: Type defines the type of attestation attached + to the image. + type: string + required: + - type + type: object + required: + - name + type: object + type: array + attestors: + description: Attestors provides a list of trusted authorities. + items: + description: Attestor is an identity that confirms or verifies the + authenticity of an image or an attestation + properties: + cosign: + description: Cosign defines attestor configuration for Cosign + based signatures + properties: + annotations: + additionalProperties: + type: string + description: |- + 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. + type: object + certificate: + description: Certificate defines the configuration for local + signature verification + properties: + cert: + description: Certificate is the to the public certificate + for local signature verification. + type: string + certChain: + description: |- + 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 + type: string + type: object + ctlog: + description: CTLog sets the configuration to verify the + authority against a Rekor instance. + properties: + ctLogPubKey: + description: CTLogPubKey, if set, is used to validate + SCTs against a custom source. + type: string + insecureIgnoreSCT: + description: |- + 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. + type: boolean + insecureIgnoreTlog: + description: InsecureIgnoreTlog skips transparency log + verification. + type: boolean + rekorPubKey: + description: |- + 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. + type: string + tsaCertChain: + description: |- + 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. + type: string + url: + description: URL sets the url to the rekor instance + (by default the public rekor.sigstore.dev) + type: string + type: object + key: + description: Key defines the type of key to validate the + image. + properties: + data: + description: Data contains the inline public key + type: string + hashAlgorithm: + description: |- + HashAlgorithm specifues signature algorithm for public keys. Supported values are + sha224, sha256, sha384 and sha512. Defaults to sha256. + type: string + kms: + description: |- + KMS contains the KMS url of the public key + Supported formats differ based on the KMS system used. + type: string + secretRef: + description: SecretRef sets a reference to a secret + with the key. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within + which the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + type: object + keyless: + description: Keyless sets the configuration to verify the + authority against a Fulcio instance. + properties: + identities: + description: Identities sets a list of identities. + items: + description: |- + 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. + properties: + issuer: + description: Issuer defines the issuer for this + identity. + type: string + issuerRegExp: + description: IssuerRegExp specifies a regular + expression to match the issuer for this identity. + type: string + subject: + description: Subject defines the subject for this + identity. + type: string + subjectRegExp: + description: SubjectRegExp specifies a regular + expression to match the subject for this identity. + type: string + type: object + type: array + roots: + description: |- + Roots is an optional set of PEM encoded trusted root certificates. + If not provided, the system roots are used. + type: string + required: + - identities + type: object + source: + description: Sources sets the configuration to specify the + sources from where to consume the signature and attestations. + properties: + PullSecrets: + description: |- + 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. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + repository: + description: Repository defines the location from where + to pull the signature / attestations. + type: string + tagPrefix: + description: |- + 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. + type: string + type: object + tuf: + description: TUF defines the configuration to fetch sigstore + root + properties: + mirror: + description: Mirror is the base URL of Sigstore TUF + repository + type: string + root: + description: Root defines the path or data of the trusted + root + properties: + data: + description: Data is the base64 encoded TUF root + type: string + path: + description: Path is the URL or File location of + the TUF root + type: string + type: object + type: object + type: object + name: + description: Name is the name for this attestor. It is used + to refer to the attestor in verification + type: string + notary: + description: Notary defines attestor configuration for Notary + based signatures + properties: + certs: + description: Certs define the cert chain for Notary signature + verification + type: string + tsaCerts: + description: TSACerts define the cert chain for verifying + timestamps of notary signature + type: string + required: + - certs + type: object + required: + - name + type: object + type: array + credentials: + description: Credentials provides credentials that will be used for + authentication with registry. + properties: + allowInsecureRegistry: + description: AllowInsecureRegistry allows insecure access to a + registry. + type: boolean + providers: + description: |- + 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. + items: + description: CredentialsProvidersType provides the list of credential + providers required. + enum: + - default + - amazon + - azure + - google + - github + type: string + type: array + secrets: + description: |- + Secrets specifies a list of secrets that are provided for credentials. + Secrets must live in the Kyverno namespace. + items: + type: string + type: array + type: object + evaluation: + description: EvaluationConfiguration defines the configuration for + the policy evaluation. + properties: + admission: + description: Admission controls policy evaluation during admission. + properties: + enabled: + default: true + description: |- + Enabled controls if rules are applied during admission. + Optional. Default value is "true". + type: boolean + type: object + background: + description: Background controls policy evaluation during background + scan. + properties: + enabled: + default: true + description: |- + Enabled controls if rules are applied to existing resources during a background scan. + Optional. Default value is "true". The value must be set to "false" if the policy rule + uses variables that are only available in the admission review request (e.g. user name). + type: boolean + type: object + mode: + description: |- + Mode is the mode of policy evaluation. + Allowed values are "Kubernetes" or "JSON". + Optional. Default value is "Kubernetes". + type: string + type: object + failurePolicy: + description: |- + 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. + type: string + imageRules: + description: |- + 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 + items: + description: ImageRule defines a Glob or a CEL expression for matching + images + properties: + cel: + description: Cel defines CEL Expressions for matching images + type: string + glob: + description: Glob defines a globbing pattern for matching images + type: string + type: object + type: array + images: + description: Images is a list of CEL expression to extract images + from the resource + items: + properties: + expression: + description: Expression defines CEL expression to extact images + from the resource. + type: string + name: + description: Name is the name for this imageList. It is used + to refer to the images in verification block as images.<name> + type: string + required: + - expression + - name + type: object + type: array + matchConditions: + description: |- + 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. + items: + description: MatchCondition represents a condition which must by + fulfilled for a request to be sent to a webhook. + properties: + expression: + description: |- + Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. + CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables: + + 'object' - The object from the incoming request. The value is null for DELETE requests. + 'oldObject' - The existing object. The value is null for CREATE requests. + 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request. + See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the + request resource. + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Required. + type: string + name: + description: |- + Name is an identifier for this match condition, used for strategic merging of MatchConditions, + as well as providing an identifier for logging purposes. A good name should be descriptive of + the associated expression. + Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and + must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or + '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an + optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + + Required. + type: string + required: + - expression + - name + type: object + type: array + matchConstraints: + description: MatchConstraints specifies what resources this policy + is designed to validate. + properties: + excludeResourceRules: + description: |- + ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + items: + description: NamedRuleWithOperations is a tuple of Operations + and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation for + a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of + names that the rule applies to. An empty set means that + everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + matchPolicy: + description: |- + matchPolicy defines how the "MatchResources" list is used to match incoming requests. + Allowed values are "Exact" or "Equivalent". + + - Exact: match a request only if it exactly matches a specified rule. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy. + + - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy. + + Defaults to "Equivalent" + type: string + namespaceSelector: + description: |- + NamespaceSelector decides whether to run the admission control policy on an object based + on whether the namespace for that object matches the selector. If the + object itself is a namespace, the matching is performed on + object.metadata.labels. If the object is another cluster scoped resource, + it never skips the policy. + + For example, to run the webhook on any objects whose namespace is not + associated with "runlevel" of "0" or "1"; you will set the selector as + follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "runlevel", + "operator": "NotIn", + "values": [ + "0", + "1" + ] + } + ] + } + + If instead you want to only run the policy on any objects whose + namespace is associated with the "environment" of "prod" or "staging"; + you will set the selector as follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "environment", + "operator": "In", + "values": [ + "prod", + "staging" + ] + } + ] + } + + See + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + for more examples of label selectors. + + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + objectSelector: + description: |- + ObjectSelector decides whether to run the validation based on if the + object has matching labels. objectSelector is evaluated against both + the oldObject and newObject that would be sent to the cel validation, and + is considered to match if either object matches the selector. A null + object (oldObject in the case of create, or newObject in the case of + delete) or an object that cannot have labels (like a + DeploymentRollback or a PodProxyOptions object) is not considered to + match. + Use the object selector only if the webhook is opt-in, because end + users may skip the admission webhook by setting the labels. + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + resourceRules: + description: |- + ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. + The policy cares about an operation if it matches _any_ Rule. + items: + description: NamedRuleWithOperations is a tuple of Operations + and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation for + a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of + names that the rule applies to. An empty set means that + everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + mutateDigest: + default: true + description: |- + MutateDigest enables replacement of image tags with digests. + Defaults to true. + type: boolean + required: + default: true + description: Required validates that images are verified i.e. have + matched passed a signature or attestation check. + type: boolean + validationActions: + description: |- + ValidationAction specifies the action to be taken when the matched resource violates the policy. + Required. + items: + description: ValidationAction specifies a policy enforcement action. + type: string + type: array + x-kubernetes-list-type: set + variables: + description: |- + Variables contain definitions of variables that can be used in composition of other expressions. + Each variable is defined as a named CEL expression. + items: + description: Variable is the definition of a variable that is used + for composition. A variable is defined as a named expression. + properties: + expression: + description: |- + Expression is the expression that will be evaluated as the value of the variable. + The CEL expression has access to the same identifiers as the CEL expressions in Validation. + type: string + name: + description: |- + Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. + The variable can be accessed in other expressions through `variables` + For example, if name is "foo", the variable will be available as `variables.foo` + type: string + required: + - expression + - name + type: object + x-kubernetes-map-type: atomic + type: array + verifications: + description: Verifications contain CEL expressions which is used to + apply the image verification checks. + items: + description: Validation specifies the CEL expression which is used + to apply the validation. + properties: + expression: + description: "Expression represents the expression which will + be evaluated by CEL.\nref: https://github.com/google/cel-spec\nCEL + expressions have access to the contents of the API request/response, + organized into CEL variables as well as some other useful + variables:\n\n- 'object' - The object from the incoming request. + The value is null for DELETE requests.\n- 'oldObject' - The + existing object. The value is null for CREATE requests.\n- + 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).\n- + 'params' - Parameter resource referred to by the policy binding + being evaluated. Only populated if the policy has a ParamKind.\n- + 'namespaceObject' - The namespace object that the incoming + object belongs to. The value is null for cluster-scoped resources.\n- + 'variables' - Map of composited variables, from its name to + its lazily evaluated value.\n For example, a variable named + 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' + - A CEL Authorizer. May be used to perform authorization checks + for the principal (user or service account) of the request.\n + \ See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the\n request resource.\n\nThe + `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` + are always accessible from the root of the\nobject. No other + metadata properties are accessible.\n\nOnly property names + of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.\nAccessible + property names are escaped according to the following rules + when accessed in the expression:\n- '__' escapes to '__underscores__'\n- + '.' escapes to '__dot__'\n- '-' escapes to '__dash__'\n- '/' + escapes to '__slash__'\n- Property names that exactly match + a CEL RESERVED keyword escape to '__{keyword}__'. The keywords + are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", + \"const\", \"continue\", \"else\", \"for\", \"function\", + \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", + \"return\".\nExamples:\n - Expression accessing a property + named \"namespace\": {\"Expression\": \"object.__namespace__ + > 0\"}\n - Expression accessing a property named \"x-prop\": + {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression + accessing a property named \"redact__d\": {\"Expression\": + \"object.redact__underscores__d > 0\"}\n\nEquality on arrays + with list type of 'set' or 'map' ignores element order, i.e. + [1, 2] == [2, 1].\nConcatenation on arrays with x-kubernetes-list-type + use the semantics of the list type:\n - 'set': `X + Y` performs + a union where the array positions of all elements in `X` are + preserved and\n non-intersecting elements in `Y` are appended, + retaining their partial order.\n - 'map': `X + Y` performs + a merge where the array positions of all keys in `X` are preserved + but the values\n are overwritten by values in `Y` when + the key sets of `X` and `Y` intersect. Elements in `Y` with\n + \ non-intersecting keys are appended, retaining their partial + order.\nRequired." + type: string + message: + description: |- + Message represents the message displayed when validation fails. The message is required if the Expression contains + line breaks. The message must not contain line breaks. + If unset, the message is "failed rule: {Rule}". + e.g. "must be a URL with the host matching spec.host" + If the Expression contains line breaks. Message is required. + The message must not contain line breaks. + If unset, the message is "failed Expression: {Expression}". + type: string + messageExpression: + description: |- + messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. + Since messageExpression is used as a failure message, it must evaluate to a string. + If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. + If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced + as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string + that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and + the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. + messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. + Example: + "object.x must be less than max ("+string(params.max)+")" + type: string + reason: + description: |- + Reason represents a machine-readable description of why this validation failed. + If this is the first validation in the list to fail, this reason, as well as the + corresponding HTTP response code, are used in the + HTTP response to the client. + The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge". + If not set, StatusReasonInvalid is used in the response to the client. + type: string + required: + - expression + type: object + type: array + x-kubernetes-list-type: atomic + verifyDigest: + default: true + description: VerifyDigest validates that images have a digest. + type: boolean + webhookConfiguration: + description: WebhookConfiguration defines the configuration for the + webhook. + properties: + timeoutSeconds: + description: |- + TimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. + After the configured time expires, the admission request may fail, or may simply ignore the policy results, + based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds. + format: int32 + type: integer + type: object + required: + - attestors + - verifications + type: object + status: + description: Status contains policy runtime data. + properties: + autogen: + description: AutogenStatus contains autogen status information. + properties: + rules: + description: Rules is a list of Rule instances. It contains auto + generated rules added for pod controllers + items: + properties: + auditAnnotations: + items: + description: AuditAnnotation describes how to produce + an audit annotation for an API request. + properties: + key: + description: |- + key specifies the audit annotation key. The audit annotation keys of + a ValidatingAdmissionPolicy must be unique. The key must be a qualified + name ([A-Za-z0-9][-A-Za-z0-9_.]*) no more than 63 bytes in length. + + The key is combined with the resource name of the + ValidatingAdmissionPolicy to construct an audit annotation key: + "{ValidatingAdmissionPolicy name}/{key}". + + If an admission webhook uses the same resource name as this ValidatingAdmissionPolicy + and the same audit annotation key, the annotation key will be identical. + In this case, the first annotation written with the key will be included + in the audit event and all subsequent annotations with the same key + will be discarded. + + Required. + type: string + valueExpression: + description: |- + valueExpression represents the expression which is evaluated by CEL to + produce an audit annotation value. The expression must evaluate to either + a string or null value. If the expression evaluates to a string, the + audit annotation is included with the string value. If the expression + evaluates to null or empty string the audit annotation will be omitted. + The valueExpression may be no longer than 5kb in length. + If the result of the valueExpression is more than 10kb in length, it + will be truncated to 10kb. + + If multiple ValidatingAdmissionPolicyBinding resources match an + API request, then the valueExpression will be evaluated for + each binding. All unique values produced by the valueExpressions + will be joined together in a comma-separated list. + + Required. + type: string + required: + - key + - valueExpression + type: object + type: array + matchConditions: + items: + description: MatchCondition represents a condition which + must by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: |- + Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. + CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables: + + 'object' - The object from the incoming request. The value is null for DELETE requests. + 'oldObject' - The existing object. The value is null for CREATE requests. + 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request. + See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the + request resource. + Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + + Required. + type: string + name: + description: |- + Name is an identifier for this match condition, used for strategic merging of MatchConditions, + as well as providing an identifier for logging purposes. A good name should be descriptive of + the associated expression. + Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and + must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or + '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an + optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + + Required. + type: string + required: + - expression + - name + type: object + type: array + matchConstraints: + description: |- + MatchResources decides whether to run the admission control policy on an object based + on whether it meets the match criteria. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + properties: + excludeResourceRules: + description: |- + ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about. + The exclude rules take precedence over include rules (if a resource matches both, it is excluded) + items: + description: NamedRuleWithOperations is a tuple of + Operations and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation + for a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An + empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + matchPolicy: + description: |- + matchPolicy defines how the "MatchResources" list is used to match incoming requests. + Allowed values are "Exact" or "Equivalent". + + - Exact: match a request only if it exactly matches a specified rule. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy. + + - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy. + + Defaults to "Equivalent" + type: string + namespaceSelector: + description: |- + NamespaceSelector decides whether to run the admission control policy on an object based + on whether the namespace for that object matches the selector. If the + object itself is a namespace, the matching is performed on + object.metadata.labels. If the object is another cluster scoped resource, + it never skips the policy. + + For example, to run the webhook on any objects whose namespace is not + associated with "runlevel" of "0" or "1"; you will set the selector as + follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "runlevel", + "operator": "NotIn", + "values": [ + "0", + "1" + ] + } + ] + } + + If instead you want to only run the policy on any objects whose + namespace is associated with the "environment" of "prod" or "staging"; + you will set the selector as follows: + "namespaceSelector": { + "matchExpressions": [ + { + "key": "environment", + "operator": "In", + "values": [ + "prod", + "staging" + ] + } + ] + } + + See + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + for more examples of label selectors. + + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + objectSelector: + description: |- + ObjectSelector decides whether to run the validation based on if the + object has matching labels. objectSelector is evaluated against both + the oldObject and newObject that would be sent to the cel validation, and + is considered to match if either object matches the selector. A null + object (oldObject in the case of create, or newObject in the case of + delete) or an object that cannot have labels (like a + DeploymentRollback or a PodProxyOptions object) is not considered to + match. + Use the object selector only if the webhook is opt-in, because end + users may skip the admission webhook by setting the labels. + Default to the empty LabelSelector, which matches everything. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + resourceRules: + description: |- + ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches. + The policy cares about an operation if it matches _any_ Rule. + items: + description: NamedRuleWithOperations is a tuple of + Operations and Resources with ResourceNames. + properties: + apiGroups: + description: |- + APIGroups is the API groups the resources belong to. '*' is all groups. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + apiVersions: + description: |- + APIVersions is the API versions the resources belong to. '*' is all versions. + If '*' is present, the length of the slice must be one. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + operations: + description: |- + Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * + for all of those operations and any future admission operations that are added. + If '*' is present, the length of the slice must be one. + Required. + items: + description: OperationType specifies an operation + for a request. + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white + list of names that the rule applies to. An + empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Resources is a list of resources this rule applies to. + + For example: + 'pods' means pods. + 'pods/log' means the log subresource of pods. + '*' means all resources, but not subresources. + 'pods/*' means all subresources of pods. + '*/scale' means all scale subresources. + '*/*' means all resources and their subresources. + + If wildcard is present, the validation rule will ensure resources do not + overlap with each other. + + Depending on the enclosing object, subresources might not be allowed. + Required. + items: + type: string + type: array + x-kubernetes-list-type: atomic + scope: + description: |- + scope specifies the scope of this rule. + Valid values are "Cluster", "Namespaced", and "*" + "Cluster" means that only cluster-scoped resources will match this rule. + Namespace API objects are cluster-scoped. + "Namespaced" means that only namespaced resources will match this rule. + "*" means that there are no scope restrictions. + Subresources match the scope of their parent resource. + Default is "*". + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + validations: + items: + description: Validation specifies the CEL expression which + is used to apply the validation. + properties: + expression: + description: "Expression represents the expression + which will be evaluated by CEL.\nref: https://github.com/google/cel-spec\nCEL + expressions have access to the contents of the API + request/response, organized into CEL variables as + well as some other useful variables:\n\n- 'object' + - The object from the incoming request. The value + is null for DELETE requests.\n- 'oldObject' - The + existing object. The value is null for CREATE requests.\n- + 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).\n- + 'params' - Parameter resource referred to by the + policy binding being evaluated. Only populated if + the policy has a ParamKind.\n- 'namespaceObject' + - The namespace object that the incoming object + belongs to. The value is null for cluster-scoped + resources.\n- 'variables' - Map of composited variables, + from its name to its lazily evaluated value.\n For + example, a variable named 'foo' can be accessed + as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. + May be used to perform authorization checks for + the principal (user or service account) of the request.\n + \ See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- + 'authorizer.requestResource' - A CEL ResourceCheck + constructed from the 'authorizer' and configured + with the\n request resource.\n\nThe `apiVersion`, + `kind`, `metadata.name` and `metadata.generateName` + are always accessible from the root of the\nobject. + No other metadata properties are accessible.\n\nOnly + property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` + are accessible.\nAccessible property names are escaped + according to the following rules when accessed in + the expression:\n- '__' escapes to '__underscores__'\n- + '.' escapes to '__dot__'\n- '-' escapes to '__dash__'\n- + '/' escapes to '__slash__'\n- Property names that + exactly match a CEL RESERVED keyword escape to '__{keyword}__'. + The keywords are:\n\t \"true\", \"false\", \"null\", + \"in\", \"as\", \"break\", \"const\", \"continue\", + \"else\", \"for\", \"function\", \"if\",\n\t \"import\", + \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n + \ - Expression accessing a property named \"namespace\": + {\"Expression\": \"object.__namespace__ > 0\"}\n + \ - Expression accessing a property named \"x-prop\": + {\"Expression\": \"object.x__dash__prop > 0\"}\n + \ - Expression accessing a property named \"redact__d\": + {\"Expression\": \"object.redact__underscores__d + > 0\"}\n\nEquality on arrays with list type of 'set' + or 'map' ignores element order, i.e. [1, 2] == [2, + 1].\nConcatenation on arrays with x-kubernetes-list-type + use the semantics of the list type:\n - 'set': + `X + Y` performs a union where the array positions + of all elements in `X` are preserved and\n non-intersecting + elements in `Y` are appended, retaining their partial + order.\n - 'map': `X + Y` performs a merge where + the array positions of all keys in `X` are preserved + but the values\n are overwritten by values in + `Y` when the key sets of `X` and `Y` intersect. + Elements in `Y` with\n non-intersecting keys + are appended, retaining their partial order.\nRequired." + type: string + message: + description: |- + Message represents the message displayed when validation fails. The message is required if the Expression contains + line breaks. The message must not contain line breaks. + If unset, the message is "failed rule: {Rule}". + e.g. "must be a URL with the host matching spec.host" + If the Expression contains line breaks. Message is required. + The message must not contain line breaks. + If unset, the message is "failed Expression: {Expression}". + type: string + messageExpression: + description: |- + messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. + Since messageExpression is used as a failure message, it must evaluate to a string. + If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. + If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced + as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string + that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and + the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. + messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. + Example: + "object.x must be less than max ("+string(params.max)+")" + type: string + reason: + description: |- + Reason represents a machine-readable description of why this validation failed. + If this is the first validation in the list to fail, this reason, as well as the + corresponding HTTP response code, are used in the + HTTP response to the client. + The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge". + If not set, StatusReasonInvalid is used in the response to the client. + type: string + required: + - expression + type: object + type: array + variables: + items: + description: Variable is the definition of a variable + that is used for composition. A variable is defined + as a named expression. + properties: + expression: + description: |- + Expression is the expression that will be evaluated as the value of the variable. + The CEL expression has access to the same identifiers as the CEL expressions in Validation. + type: string + name: + description: |- + Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. + The variable can be accessed in other expressions through `variables` + For example, if name is "foo", the variable will be available as `variables.foo` + type: string + required: + - expression + - name + type: object + x-kubernetes-map-type: atomic + type: array + type: object + type: array + type: object + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + generated: + description: Generated indicates whether a ValidatingAdmissionPolicy/MutatingAdmissionPolicy + is generated from the policy or not + type: boolean + message: + description: |- + Message is a human readable message indicating details about the generation of ValidatingAdmissionPolicy/MutatingAdmissionPolicy + It is an empty string when ValidatingAdmissionPolicy/MutatingAdmissionPolicy is successfully generated. + type: string + ready: + description: |- + The ready of a policy is a high-level summary of where the policy is in its lifecycle. + The conditions array, the reason and message fields contain more detail about the policy's status. + type: boolean + required: + - message + type: object + required: + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: labels: app.kubernetes.io/component: crds @@ -51445,6 +53028,8 @@ rules: - validatingpolicies - validatingpolicies/status - celpolicyexceptions + - imageverificationpolicies + - imageverificationpolicies/status verbs: - create - delete @@ -52000,6 +53585,8 @@ rules: resources: - validatingpolicies - validatingpolicies/status + - imageverificationpolicies + - imageverificationpolicies/status verbs: - create - delete diff --git a/pkg/cel/autogen/ivpol.go b/pkg/cel/autogen/ivpol.go index 30219bb6ce..cc79611ac1 100644 --- a/pkg/cel/autogen/ivpol.go +++ b/pkg/cel/autogen/ivpol.go @@ -47,6 +47,11 @@ func autogenIvPols(ivpol *policiesv1alpha1.ImageVerificationPolicy, controllerSe } var err error policy := ivpol.DeepCopy() + if controllers == "cronjobs" { + policy.Name = "autogen-cronjobs-" + policy.Name + } else { + policy.Name = "autogen-" + policy.Name + } operations := ivpol.Spec.MatchConstraints.ResourceRules[0].Operations // create a resource rule for pod controllers policy.Spec.MatchConstraints = createMatchConstraints(controllers, operations) diff --git a/pkg/cel/autogen/ivpol_test.go b/pkg/cel/autogen/ivpol_test.go index 4fe9a2898a..b32254b52b 100644 --- a/pkg/cel/autogen/ivpol_test.go +++ b/pkg/cel/autogen/ivpol_test.go @@ -44,7 +44,7 @@ var ( Images: []policiesv1alpha1.Image{ { Name: "containers", - Expression: "request.object.spec.containers.map(e, e.image)", + Expression: "object.spec.containers.map(e, e.image)", }, }, Attestors: []policiesv1alpha1.Attestor{ @@ -109,20 +109,21 @@ func Test_AutogenImageVerify(t *testing.T) { cronimg := []policiesv1alpha1.Image{ { Name: "containers", - Expression: "request.object.spec.jobTemplate.spec.template.spec.containers.map(e, e.image)", + Expression: "object.spec.jobTemplate.spec.template.spec.containers.map(e, e.image)", }, } podctrlimg := []policiesv1alpha1.Image{ { Name: "containers", - Expression: "request.object.spec.template.spec.containers.map(e, e.image)", + Expression: "object.spec.template.spec.containers.map(e, e.image)", }, } autogenerated, err := GetAutogenRulesImageVerify(ivpol) assert.NoError(t, err) assert.Equal(t, len(autogenerated), 1) + assert.Equal(t, autogenerated[0].Name, "autogen-cronjobs-test") assert.Equal(t, autogenerated[0].Spec.MatchConstraints.ResourceRules, cronRule) assert.Equal(t, len(autogenerated[0].Spec.Images), 1) assert.Equal(t, autogenerated[0].Spec.Images, cronimg) @@ -133,6 +134,7 @@ func Test_AutogenImageVerify(t *testing.T) { assert.NoError(t, err) assert.Equal(t, len(autogenerated), 2) assert.Equal(t, autogenerated[0].Spec.MatchConstraints.ResourceRules, cronRule) + assert.Equal(t, autogenerated[1].Name, "autogen-test") assert.Equal(t, autogenerated[1].Spec.MatchConstraints.ResourceRules, podctrl) assert.Equal(t, len(autogenerated[1].Spec.Images), 1) assert.Equal(t, autogenerated[1].Spec.Images, podctrlimg) diff --git a/pkg/cel/engine/imageverifyengine.go b/pkg/cel/engine/imageverifyengine.go new file mode 100644 index 0000000000..a8810463b8 --- /dev/null +++ b/pkg/cel/engine/imageverifyengine.go @@ -0,0 +1,301 @@ +package engine + +import ( + "context" + "encoding/json" + "fmt" + "reflect" + + "github.com/go-logr/logr" + "github.com/kyverno/kyverno/api/kyverno" + contextlib "github.com/kyverno/kyverno/pkg/cel/libs/context" + "github.com/kyverno/kyverno/pkg/cel/matching" + engineapi "github.com/kyverno/kyverno/pkg/engine/api" + eval "github.com/kyverno/kyverno/pkg/imageverification/evaluator" + "github.com/kyverno/kyverno/pkg/imageverification/imagedataloader" + admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" + "golang.org/x/exp/maps" + "gomodules.xyz/jsonpatch/v2" + admissionv1 "k8s.io/api/admission/v1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/admission" + k8scorev1 "k8s.io/client-go/kubernetes/typed/core/v1" +) + +type ImageVerifyEngine interface { + HandleMutating(context.Context, EngineRequest) (eval.ImageVerifyEngineResponse, []jsonpatch.JsonPatchOperation, error) + HandleValidating(ctx context.Context, request EngineRequest) (eval.ImageVerifyEngineResponse, error) +} + +type ivengine struct { + logger logr.Logger + provider ImageVerifyPolProviderFunc + nsResolver NamespaceResolver + matcher matching.Matcher + lister k8scorev1.SecretInterface + registryOpts []imagedataloader.Option +} + +func NewImageVerifyEngine(logger logr.Logger, provider ImageVerifyPolProviderFunc, nsResolver NamespaceResolver, matcher matching.Matcher, lister k8scorev1.SecretInterface, registryOpts []imagedataloader.Option) ImageVerifyEngine { + return &ivengine{ + logger: logger, + provider: provider, + nsResolver: nsResolver, + matcher: matcher, + lister: lister, + registryOpts: registryOpts, + } +} + +func (e *ivengine) HandleValidating(ctx context.Context, request EngineRequest) (eval.ImageVerifyEngineResponse, error) { + var response eval.ImageVerifyEngineResponse + // fetch compiled policies + policies, err := e.provider.ImageVerificationPolicies(ctx) + if err != nil { + return response, err + } + // load objects + object, oldObject, err := admissionutils.ExtractResources(nil, request.request) + if err != nil { + return response, err + } + response.Resource = &object + if response.Resource.Object == nil { + response.Resource = &oldObject + } + // default dry run + dryRun := false + if request.request.DryRun != nil { + dryRun = *request.request.DryRun + } + // create admission attributes + attr := admission.NewAttributesRecord( + &object, + &oldObject, + schema.GroupVersionKind(request.request.Kind), + request.request.Namespace, + request.request.Name, + schema.GroupVersionResource(request.request.Resource), + request.request.SubResource, + admission.Operation(request.request.Operation), + nil, + dryRun, + // TODO + nil, + ) + // resolve namespace + var namespace runtime.Object + if ns := request.request.Namespace; ns != "" { + namespace = e.nsResolver(ns) + } + // evaluate policies + responses, err := e.handleValidation(policies, attr, namespace) + if err != nil { + return response, err + } + response.Policies = append(response.Policies, responses...) + return response, nil +} + +func (e *ivengine) HandleMutating(ctx context.Context, request EngineRequest) (eval.ImageVerifyEngineResponse, []jsonpatch.JsonPatchOperation, error) { + var response eval.ImageVerifyEngineResponse + // fetch compiled policies + policies, err := e.provider.ImageVerificationPolicies(ctx) + if err != nil { + return response, nil, err + } + // load objects + object, oldObject, err := admissionutils.ExtractResources(nil, request.request) + if err != nil { + return response, nil, err + } + response.Resource = &object + if response.Resource.Object == nil { + response.Resource = &oldObject + } + // default dry run + dryRun := false + if request.request.DryRun != nil { + dryRun = *request.request.DryRun + } + // create admission attributes + attr := admission.NewAttributesRecord( + &object, + &oldObject, + schema.GroupVersionKind(request.request.Kind), + request.request.Namespace, + request.request.Name, + schema.GroupVersionResource(request.request.Resource), + request.request.SubResource, + admission.Operation(request.request.Operation), + nil, + dryRun, + // TODO + nil, + ) + // resolve namespace + var namespace runtime.Object + if ns := request.request.Namespace; ns != "" { + namespace = e.nsResolver(ns) + } + // evaluate policies + responses, patches, err := e.handleMutation(ctx, policies, attr, &request.request, namespace, request.context) + if err != nil { + return response, nil, err + } + response.Policies = append(response.Policies, responses...) + return response, patches, nil +} + +func (e *ivengine) matchPolicy(policy CompiledImageVerificationPolicy, attr admission.Attributes, namespace runtime.Object) (bool, error) { + match := func(constraints *admissionregistrationv1.MatchResources) (bool, error) { + criteria := matchCriteria{constraints: constraints} + matches, err := e.matcher.Match(&criteria, attr, namespace) + if err != nil { + return false, err + } + return matches, nil + } + + // match against main policy constraints + matches, err := match(policy.Policy.Spec.MatchConstraints) + if err != nil { + return false, err + } + if matches { + return true, nil + } + + return false, nil +} + +func (e *ivengine) handleMutation(ctx context.Context, policies []CompiledImageVerificationPolicy, attr admission.Attributes, request *admissionv1.AdmissionRequest, namespace runtime.Object, context contextlib.ContextInterface) ([]eval.ImageVerifyPolicyResponse, []jsonpatch.JsonPatchOperation, error) { + results := make(map[string]eval.ImageVerifyPolicyResponse, len(policies)) + filteredPolicies := make([]CompiledImageVerificationPolicy, 0) + if e.matcher != nil { + for _, pol := range policies { + matches, err := e.matchPolicy(pol, attr, namespace) + response := eval.ImageVerifyPolicyResponse{ + Policy: pol.Policy, + Actions: pol.Actions, + } + if err != nil { + response.Result = *engineapi.RuleError("match", engineapi.ImageVerify, "failed to execute matching", err, nil) + results[pol.Policy.GetName()] = response + } else if matches { + filteredPolicies = append(filteredPolicies, pol) + } + } + } + + ictx, err := imagedataloader.NewImageContext(e.lister, e.registryOpts...) + if err != nil { + return nil, nil, err + } + + c := eval.NewCompiler(ictx, e.lister, request.RequestResource) + for _, ivpol := range filteredPolicies { + response := eval.ImageVerifyPolicyResponse{ + Policy: ivpol.Policy, + Actions: ivpol.Actions, + } + + if p, errList := c.Compile(e.logger, ivpol.Policy); errList != nil { + response.Result = *engineapi.RuleError("evaluation", engineapi.ImageVerify, "failed to compile policy", err, nil) + } else { + result, err := p.Evaluate(ctx, ictx, attr, request, namespace, true) + if err != nil { + response.Result = *engineapi.RuleError("evaluation", engineapi.ImageVerify, "failed to evaluate policy", err, nil) + } else { + ruleName := ivpol.Policy.GetName() + if result.Error != nil { + response.Result = *engineapi.RuleError(ruleName, engineapi.ImageVerify, "error", err, nil) + } else if result.Result { + response.Result = *engineapi.RulePass(ruleName, engineapi.ImageVerify, "success", nil) + } else { + response.Result = *engineapi.RuleFail(ruleName, engineapi.ImageVerify, result.Message, nil) + } + } + } + results[ivpol.Policy.GetName()] = response + } + + ann, err := objectAnnotations(attr) + if err != nil { + return nil, nil, err + } + patches, err := eval.MakeImageVerifyOutcomePatch(len(ann) != 0, e.logger, results) + if err != nil { + return nil, nil, err + } + + return maps.Values(results), patches, nil +} + +func objectAnnotations(attr admission.Attributes) (map[string]string, error) { + obj := attr.GetObject() + if obj == nil || reflect.ValueOf(obj).IsNil() { + return nil, nil + } + + ret, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) + if err != nil { + return nil, err + } + + u := &unstructured.Unstructured{Object: ret} + return u.GetAnnotations(), nil +} + +func (e *ivengine) handleValidation(policies []CompiledImageVerificationPolicy, attr admission.Attributes, namespace runtime.Object) ([]eval.ImageVerifyPolicyResponse, error) { + responses := make(map[string]eval.ImageVerifyPolicyResponse) + annotations, err := objectAnnotations(attr) + if err != nil { + return nil, err + } + if len(annotations) == 0 { + return nil, fmt.Errorf("annotations not present on object, image verification failed") + } + + filteredPolicies := make([]CompiledImageVerificationPolicy, 0) + if e.matcher != nil { + for _, pol := range policies { + matches, err := e.matchPolicy(pol, attr, namespace) + response := eval.ImageVerifyPolicyResponse{ + Policy: pol.Policy, + Actions: pol.Actions, + } + if err != nil { + response.Result = *engineapi.RuleError("match", engineapi.ImageVerify, "failed to execute matching", err, nil) + responses[pol.Policy.GetName()] = response + } else if matches { + filteredPolicies = append(filteredPolicies, pol) + } + } + } + + if data, found := annotations[kyverno.AnnotationImageVerifyOutcomes]; !found { + return nil, fmt.Errorf("%s annotation not present", kyverno.AnnotationImageVerifyOutcomes) + } else { + var outcomes map[string]eval.ImageVerificationOutcome + if err := json.Unmarshal([]byte(data), &outcomes); err != nil { + return nil, err + } + + for _, pol := range filteredPolicies { + resp := eval.ImageVerifyPolicyResponse{ + Policy: pol.Policy, + Actions: pol.Actions, + } + if o, found := outcomes[pol.Policy.GetName()]; !found { + resp.Result = *engineapi.RuleFail(pol.Policy.GetName(), engineapi.ImageVerify, "policy not evaluated", nil) + } else { + resp.Result = *engineapi.NewRuleResponse(o.Name, engineapi.ImageVerify, o.Message, o.Status, o.Properties) + } + } + } + return maps.Values(responses), nil +} diff --git a/pkg/cel/engine/imageverifyengine_test.go b/pkg/cel/engine/imageverifyengine_test.go new file mode 100644 index 0000000000..823a4f64b8 --- /dev/null +++ b/pkg/cel/engine/imageverifyengine_test.go @@ -0,0 +1,181 @@ +package engine + +import ( + "context" + "encoding/json" + "testing" + + "github.com/go-logr/logr" + policiesv1alpha1 "github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1" + contextlib "github.com/kyverno/kyverno/pkg/cel/libs/context" + "github.com/kyverno/kyverno/pkg/cel/matching" + engineapi "github.com/kyverno/kyverno/pkg/engine/api" + eval "github.com/kyverno/kyverno/pkg/imageverification/evaluator" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/admission/v1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apiruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" +) + +var ( + signedImage = "ghcr.io/kyverno/test-verify-image:signed" + unsignedImage = "ghcr.io/kyverno/test-verify-image:unsigned" + + ivpol = &policiesv1alpha1.ImageVerificationPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ivpol-notary", + }, + Spec: policiesv1alpha1.ImageVerificationPolicySpec{ + MatchConstraints: &admissionregistrationv1.MatchResources{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ + { + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Create, + admissionregistrationv1.Update, + }, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{""}, + APIVersions: []string{"v1"}, + Resources: []string{"pods"}, + }, + }, + }, + }, + }, + EvaluationConfiguration: &policiesv1alpha1.EvaluationConfiguration{ + Mode: policiesv1alpha1.EvaluationModeKubernetes, + }, + ImageRules: []policiesv1alpha1.ImageRule{ + { + Glob: "ghcr.io/*", + }, + }, + Images: []policiesv1alpha1.Image{}, + Attestors: []policiesv1alpha1.Attestor{ + { + Name: "notary", + Notary: &policiesv1alpha1.Notary{ + Certs: `-----BEGIN CERTIFICATE----- +MIIDTTCCAjWgAwIBAgIJAPI+zAzn4s0xMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG +Tm90YXJ5MQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMDUyMjIxMTUxOFoXDTMzMDUxOTIx +MTUxOFowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0 +dGxlMQ8wDQYDVQQKDAZOb3RhcnkxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDNhTwv+QMk7jEHufFfIFlBjn2NiJaYPgL4eBS+ +b+o37ve5Zn9nzRppV6kGsa161r9s2KkLXmJrojNy6vo9a6g6RtZ3F6xKiWLUmbAL +hVTCfYw/2n7xNlVMjyyUpE+7e193PF8HfQrfDFxe2JnX5LHtGe+X9vdvo2l41R6m +Iia04DvpMdG4+da2tKPzXIuLUz/FDb6IODO3+qsqQLwEKmmUee+KX+3yw8I6G1y0 +Vp0mnHfsfutlHeG8gazCDlzEsuD4QJ9BKeRf2Vrb0ywqNLkGCbcCWF2H5Q80Iq/f +ETVO9z88R7WheVdEjUB8UrY7ZMLdADM14IPhY2Y+tLaSzEVZAgMBAAGjMjAwMAkG +A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0G +CSqGSIb3DQEBCwUAA4IBAQBX7x4Ucre8AIUmXZ5PUK/zUBVOrZZzR1YE8w86J4X9 +kYeTtlijf9i2LTZMfGuG0dEVFN4ae3CCpBst+ilhIndnoxTyzP+sNy4RCRQ2Y/k8 +Zq235KIh7uucq96PL0qsF9s2RpTKXxyOGdtp9+HO0Ty5txJE2txtLDUIVPK5WNDF +ByCEQNhtHgN6V20b8KU2oLBZ9vyB8V010dQz0NRTDLhkcvJig00535/LUylECYAJ +5/jn6XKt6UYCQJbVNzBg/YPGc1RF4xdsGVDBben/JXpeGEmkdmXPILTKd9tZ5TC0 +uOKpF5rWAruB5PCIrquamOejpXV9aQA/K2JQDuc0mcKz +-----END CERTIFICATE-----`, + }, + }, + }, + Attestations: []policiesv1alpha1.Attestation{ + { + Name: "sbom", + Referrer: &policiesv1alpha1.Referrer{ + Type: "sbom/cyclone-dx", + }, + }, + }, + Verifications: []admissionregistrationv1.Validation{ + { + Expression: "images.containers.map(image, verifyImageSignatures(image, [attestors.notary])).all(e, e > 0)", + Message: "failed to verify image with notary cert", + }, + { + Expression: "images.containers.map(image, verifyAttestationSignatures(image, attestations.sbom ,[attestors.notary])).all(e, e > 0)", + Message: "failed to verify attestation with notary cert", + }, + { + Expression: "images.containers.map(image, payload(image, attestations.sbom).bomFormat == 'CycloneDX').all(e, e)", + Message: "sbom is not a cyclone dx sbom", + }, + }, + }, + } + + ivfunc = func(ctx context.Context) ([]CompiledImageVerificationPolicy, error) { + return []CompiledImageVerificationPolicy{ + { + Policy: ivpol, + Actions: sets.Set[admissionregistrationv1.ValidationAction]{admissionregistrationv1.Deny: sets.Empty{}}, + }, + }, nil + } + + nsResolver = func(_ string) *corev1.Namespace { + return &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + } + } + + pod = `{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "test-pod", + "namespace": "" + }, + "spec": { + "containers": [ + { + "name": "nginx", + "image": "ghcr.io/kyverno/test-verify-image:signed" + } + ] + } + } +` +) + +func Test_ImageVerifyEngine(t *testing.T) { + engine := NewImageVerifyEngine(logr.Discard(), ivfunc, nsResolver, matching.NewMatcher(), nil, nil) + engineRequest := EngineRequest{ + request: v1.AdmissionRequest{ + Operation: v1.Create, + Kind: metav1.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, + Resource: metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}, + Object: apiruntime.RawExtension{ + Raw: []byte(pod), + }, + RequestResource: &metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}, + }, + context: &contextlib.MockCtx{}, + } + + resp, patches, err := engine.HandleMutating(context.Background(), engineRequest) + assert.NoError(t, err) + assert.Equal(t, len(resp.Policies), 1) + + response := resp.Policies[0] + assert.Equal(t, response.Result.Name(), "ivpol-notary") + assert.Equal(t, response.Result.Status(), engineapi.RuleStatusPass) + + assert.Equal(t, len(patches), 2) + outcomePatch := patches[1] + data, ok := outcomePatch.Value.(string) + assert.True(t, ok) + + var outcomes map[string]eval.ImageVerificationOutcome + err = json.Unmarshal([]byte(data), &outcomes) + assert.NoError(t, err) + + v, ok := outcomes["ivpol-notary"] + assert.True(t, ok) + assert.Equal(t, v.Status, engineapi.RuleStatusPass) +} diff --git a/pkg/cel/engine/provider.go b/pkg/cel/engine/provider.go index e48079330d..9d4762881b 100644 --- a/pkg/cel/engine/provider.go +++ b/pkg/cel/engine/provider.go @@ -6,12 +6,14 @@ import ( "sync" policiesv1alpha1 "github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1" + "github.com/kyverno/kyverno/pkg/cel/autogen" "github.com/kyverno/kyverno/pkg/cel/policy" policiesv1alpha1listers "github.com/kyverno/kyverno/pkg/client/listers/policies.kyverno.io/v1alpha1" "golang.org/x/exp/maps" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/util/workqueue" ctrl "sigs.k8s.io/controller-runtime" @@ -21,29 +23,46 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -type CompiledPolicy struct { +type CompiledValidatingPolicy struct { Actions sets.Set[admissionregistrationv1.ValidationAction] Policy policiesv1alpha1.ValidatingPolicy CompiledPolicy policy.CompiledPolicy } -type Provider interface { - CompiledPolicies(context.Context) ([]CompiledPolicy, error) +type CompiledImageVerificationPolicy struct { + Policy *policiesv1alpha1.ImageVerificationPolicy + Actions sets.Set[admissionregistrationv1.ValidationAction] } -type ProviderFunc func(context.Context) ([]CompiledPolicy, error) +type Provider interface { + CompiledValidationPolicies(context.Context) ([]CompiledValidatingPolicy, error) + ImageVerificationPolicies(context.Context) ([]CompiledImageVerificationPolicy, error) +} -func (f ProviderFunc) CompiledPolicies(ctx context.Context) ([]CompiledPolicy, error) { +type reconcilers struct { + *policyReconciler + *ivpolpolicyReconciler +} + +type VPolProviderFunc func(context.Context) ([]CompiledValidatingPolicy, error) + +func (f VPolProviderFunc) CompiledValidationPolicies(ctx context.Context) ([]CompiledValidatingPolicy, error) { return f(ctx) } -func NewProvider(compiler policy.Compiler, policies []policiesv1alpha1.ValidatingPolicy, exceptions []*policiesv1alpha1.CELPolicyException) (ProviderFunc, error) { - compiled := make([]CompiledPolicy, 0, len(policies)) - for _, vp := range policies { +type ImageVerifyPolProviderFunc func(context.Context) ([]CompiledImageVerificationPolicy, error) + +func (f ImageVerifyPolProviderFunc) ImageVerificationPolicies(ctx context.Context) ([]CompiledImageVerificationPolicy, error) { + return f(ctx) +} + +func NewProvider(compiler policy.Compiler, vpolicies []policiesv1alpha1.ValidatingPolicy, exceptions []*policiesv1alpha1.CELPolicyException) (VPolProviderFunc, error) { + compiled := make([]CompiledValidatingPolicy, 0, len(vpolicies)) + for _, vp := range vpolicies { var matchedExceptions []policiesv1alpha1.CELPolicyException for _, polex := range exceptions { for _, ref := range polex.Spec.PolicyRefs { - if ref.Name == vp.GetName() { + if ref.Name == vp.GetName() && ref.Kind == vp.GetKind() { matchedExceptions = append(matchedExceptions, *polex) } } @@ -56,13 +75,13 @@ func NewProvider(compiler policy.Compiler, policies []policiesv1alpha1.Validatin if len(actions) == 0 { actions.Insert(admissionregistrationv1.Deny) } - compiled = append(compiled, CompiledPolicy{ + compiled = append(compiled, CompiledValidatingPolicy{ Actions: actions, Policy: vp, CompiledPolicy: policy, }) } - provider := func(context.Context) ([]CompiledPolicy, error) { + provider := func(context.Context) ([]CompiledValidatingPolicy, error) { return compiled, nil } return provider, nil @@ -73,65 +92,76 @@ func NewKubeProvider( mgr ctrl.Manager, polexLister policiesv1alpha1listers.CELPolicyExceptionLister, ) (Provider, error) { + exceptionHandlerFuncs := &handler.Funcs{ + CreateFunc: func( + ctx context.Context, + tce event.TypedCreateEvent[client.Object], + trli workqueue.TypedRateLimitingInterface[reconcile.Request], + ) { + polex := tce.Object.(*policiesv1alpha1.CELPolicyException) + for _, ref := range polex.Spec.PolicyRefs { + trli.Add(reconcile.Request{ + NamespacedName: client.ObjectKey{ + Name: ref.Name, + }, + }) + } + }, + UpdateFunc: func( + ctx context.Context, + tue event.TypedUpdateEvent[client.Object], + trli workqueue.TypedRateLimitingInterface[reconcile.Request], + ) { + polex := tue.ObjectNew.(*policiesv1alpha1.CELPolicyException) + for _, ref := range polex.Spec.PolicyRefs { + trli.Add(reconcile.Request{ + NamespacedName: client.ObjectKey{ + Name: ref.Name, + }, + }) + } + }, + DeleteFunc: func( + ctx context.Context, + tde event.TypedDeleteEvent[client.Object], + trli workqueue.TypedRateLimitingInterface[reconcile.Request], + ) { + polex := tde.Object.(*policiesv1alpha1.CELPolicyException) + for _, ref := range polex.Spec.PolicyRefs { + trli.Add(reconcile.Request{ + NamespacedName: client.ObjectKey{ + Name: ref.Name, + }, + }) + } + }, + } r := newPolicyReconciler(compiler, mgr.GetClient(), polexLister) err := ctrl.NewControllerManagedBy(mgr). For(&policiesv1alpha1.ValidatingPolicy{}). - Watches(&policiesv1alpha1.CELPolicyException{}, &handler.Funcs{ - CreateFunc: func( - ctx context.Context, - tce event.TypedCreateEvent[client.Object], - trli workqueue.TypedRateLimitingInterface[reconcile.Request], - ) { - polex := tce.Object.(*policiesv1alpha1.CELPolicyException) - for _, ref := range polex.Spec.PolicyRefs { - trli.Add(reconcile.Request{ - NamespacedName: client.ObjectKey{ - Name: ref.Name, - }, - }) - } - }, - UpdateFunc: func( - ctx context.Context, - tue event.TypedUpdateEvent[client.Object], - trli workqueue.TypedRateLimitingInterface[reconcile.Request], - ) { - polex := tue.ObjectNew.(*policiesv1alpha1.CELPolicyException) - for _, ref := range polex.Spec.PolicyRefs { - trli.Add(reconcile.Request{ - NamespacedName: client.ObjectKey{ - Name: ref.Name, - }, - }) - } - }, - DeleteFunc: func( - ctx context.Context, - tde event.TypedDeleteEvent[client.Object], - trli workqueue.TypedRateLimitingInterface[reconcile.Request], - ) { - polex := tde.Object.(*policiesv1alpha1.CELPolicyException) - for _, ref := range polex.Spec.PolicyRefs { - trli.Add(reconcile.Request{ - NamespacedName: client.ObjectKey{ - Name: ref.Name, - }, - }) - } - }, - }). + Watches(&policiesv1alpha1.CELPolicyException{}, exceptionHandlerFuncs). Complete(r) if err != nil { return nil, fmt.Errorf("failed to construct manager: %w", err) } - return r, nil + + ivpolr := newivPolicyReconciler(mgr.GetClient(), polexLister) + err = ctrl.NewControllerManagedBy(mgr). + For(&policiesv1alpha1.ImageVerificationPolicy{}). + Watches(&policiesv1alpha1.CELPolicyException{}, exceptionHandlerFuncs). + Complete(ivpolr) + if err != nil { + return nil, fmt.Errorf("failed to construct manager: %w", err) + } + + return reconcilers{r, ivpolr}, nil } type policyReconciler struct { client client.Client compiler policy.Compiler lock *sync.RWMutex - policies map[string]CompiledPolicy + policies map[string]CompiledValidatingPolicy polexLister policiesv1alpha1listers.CELPolicyExceptionLister } @@ -144,7 +174,7 @@ func newPolicyReconciler( client: client, compiler: compiler, lock: &sync.RWMutex{}, - policies: map[string]CompiledPolicy{}, + policies: map[string]CompiledValidatingPolicy{}, polexLister: polexLister, } } @@ -169,7 +199,7 @@ func (r *policyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } // get exceptions that match the policy - exceptions, err := r.ListExceptions(policy.GetName()) + exceptions, err := listExceptions(r.polexLister, policy.GetName()) if err != nil { return ctrl.Result{}, err } @@ -185,7 +215,7 @@ func (r *policyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if len(actions) == 0 { actions.Insert(admissionregistrationv1.Deny) } - r.policies[req.NamespacedName.String()] = CompiledPolicy{ + r.policies[req.NamespacedName.String()] = CompiledValidatingPolicy{ Actions: actions, Policy: policy, CompiledPolicy: compiled, @@ -193,14 +223,14 @@ func (r *policyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } -func (r *policyReconciler) CompiledPolicies(ctx context.Context) ([]CompiledPolicy, error) { +func (r *policyReconciler) CompiledValidationPolicies(ctx context.Context) ([]CompiledValidatingPolicy, error) { r.lock.RLock() defer r.lock.RUnlock() return maps.Values(r.policies), nil } -func (r *policyReconciler) ListExceptions(policyName string) ([]policiesv1alpha1.CELPolicyException, error) { - polexList, err := r.polexLister.List(labels.Everything()) +func listExceptions(polexLister policiesv1alpha1listers.CELPolicyExceptionLister, policyName string) ([]policiesv1alpha1.CELPolicyException, error) { + polexList, err := polexLister.List(labels.Everything()) if err != nil { return nil, err } @@ -214,3 +244,75 @@ func (r *policyReconciler) ListExceptions(policyName string) ([]policiesv1alpha1 } return exceptions, nil } + +type ivpolpolicyReconciler struct { + client client.Client + lock *sync.RWMutex + policies map[string]CompiledImageVerificationPolicy + polexLister policiesv1alpha1listers.CELPolicyExceptionLister +} + +func newivPolicyReconciler( + client client.Client, + polexLister policiesv1alpha1listers.CELPolicyExceptionLister, +) *ivpolpolicyReconciler { + return &ivpolpolicyReconciler{ + client: client, + lock: &sync.RWMutex{}, + policies: map[string]CompiledImageVerificationPolicy{}, + polexLister: polexLister, + } +} + +func (r *ivpolpolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + var policy policiesv1alpha1.ImageVerificationPolicy + err := r.client.Get(ctx, req.NamespacedName, &policy) + if errors.IsNotFound(err) { + r.lock.Lock() + defer r.lock.Unlock() + delete(r.policies, req.NamespacedName.String()) + return ctrl.Result{}, nil + } + if err != nil { + return ctrl.Result{}, err + } + + // todo: exception support + // get exceptions that match the policy + // exceptions, err := listExceptions(r.polexLister, policy.GetName()) + // if err != nil { + // return ctrl.Result{}, err + // } + + autogeneratedIvPols, err := autogen.GetAutogenRulesImageVerify(&policy) + if err != nil { + return ctrl.Result{}, err + } + r.lock.Lock() + defer r.lock.Unlock() + actions := sets.New(policy.Spec.ValidationAction...) + if len(actions) == 0 { + actions.Insert(admissionregistrationv1.Deny) + } + r.policies[req.NamespacedName.String()] = CompiledImageVerificationPolicy{ + Policy: &policy, + Actions: actions, + } + for _, p := range autogeneratedIvPols { + namespacedName := types.NamespacedName{ + Namespace: p.Namespace, + Name: p.Name, + } + r.policies[namespacedName.String()] = CompiledImageVerificationPolicy{ + Policy: p, + Actions: actions, + } + } + return ctrl.Result{}, nil +} + +func (r *ivpolpolicyReconciler) ImageVerificationPolicies(ctx context.Context) ([]CompiledImageVerificationPolicy, error) { + r.lock.RLock() + defer r.lock.RUnlock() + return maps.Values(r.policies), nil +} diff --git a/pkg/cel/engine/engine.go b/pkg/cel/engine/vpolengine.go similarity index 92% rename from pkg/cel/engine/engine.go rename to pkg/cel/engine/vpolengine.go index 7f788c980b..6c3848ed40 100644 --- a/pkg/cel/engine/engine.go +++ b/pkg/cel/engine/vpolengine.go @@ -84,10 +84,10 @@ func (r *EngineRequest) AdmissionRequest() admissionv1.AdmissionRequest { type EngineResponse struct { Resource *unstructured.Unstructured - Policies []PolicyResponse + Policies []ValidatingPolicyResponse } -type PolicyResponse struct { +type ValidatingPolicyResponse struct { Actions sets.Set[admissionregistrationv1.ValidationAction] Policy policiesv1alpha1.ValidatingPolicy Rules []engineapi.RuleResponse @@ -100,12 +100,12 @@ type Engine interface { type NamespaceResolver = func(string) *corev1.Namespace type engine struct { - provider Provider + provider VPolProviderFunc nsResolver NamespaceResolver matcher matching.Matcher } -func NewEngine(provider Provider, nsResolver NamespaceResolver, matcher matching.Matcher) Engine { +func NewEngine(provider VPolProviderFunc, nsResolver NamespaceResolver, matcher matching.Matcher) Engine { return &engine{ provider: provider, nsResolver: nsResolver, @@ -116,7 +116,7 @@ func NewEngine(provider Provider, nsResolver NamespaceResolver, matcher matching func (e *engine) Handle(ctx context.Context, request EngineRequest) (EngineResponse, error) { var response EngineResponse // fetch compiled policies - policies, err := e.provider.CompiledPolicies(ctx) + policies, err := e.provider.CompiledValidationPolicies(ctx) if err != nil { return response, err } @@ -170,7 +170,7 @@ func (e *engine) Handle(ctx context.Context, request EngineRequest) (EngineRespo return response, nil } -func (e *engine) matchPolicy(policy CompiledPolicy, attr admission.Attributes, namespace runtime.Object) (bool, int, error) { +func (e *engine) matchPolicy(policy CompiledValidatingPolicy, attr admission.Attributes, namespace runtime.Object) (bool, int, error) { match := func(constraints *admissionregistrationv1.MatchResources) (bool, error) { criteria := matchCriteria{constraints: constraints} matches, err := e.matcher.Match(&criteria, attr, namespace) @@ -205,8 +205,8 @@ func (e *engine) matchPolicy(policy CompiledPolicy, attr admission.Attributes, n return false, -1, nil } -func (e *engine) handlePolicy(ctx context.Context, policy CompiledPolicy, jsonPayload interface{}, attr admission.Attributes, request *admissionv1.AdmissionRequest, namespace runtime.Object, context contextlib.ContextInterface) PolicyResponse { - response := PolicyResponse{ +func (e *engine) handlePolicy(ctx context.Context, policy CompiledValidatingPolicy, jsonPayload interface{}, attr admission.Attributes, request *admissionv1.AdmissionRequest, namespace runtime.Object, context contextlib.ContextInterface) ValidatingPolicyResponse { + response := ValidatingPolicyResponse{ Actions: policy.Actions, Policy: policy.Policy, } diff --git a/pkg/cel/libs/context/impl_test.go b/pkg/cel/libs/context/impl_test.go index 3fd28ffd1d..ae9dc44bec 100644 --- a/pkg/cel/libs/context/impl_test.go +++ b/pkg/cel/libs/context/impl_test.go @@ -13,39 +13,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -type ctx struct { - GetConfigMapFunc func(string, string) (*unstructured.Unstructured, error) - GetGlobalReferenceFunc func(string, string) (any, error) - GetImageDataFunc func(string) (*imagedataloader.ImageData, error) - ParseImageReferenceFunc func(string) (imagedataloader.ImageReference, error) - ListResourcesFunc func(string, string, string) (*unstructured.UnstructuredList, error) - GetResourcesFunc func(string, string, string, string) (*unstructured.Unstructured, error) -} - -func (mock *ctx) GetConfigMap(ns string, n string) (*unstructured.Unstructured, error) { - return mock.GetConfigMapFunc(ns, n) -} - -func (mock *ctx) GetGlobalReference(n, p string) (any, error) { - return mock.GetGlobalReferenceFunc(n, p) -} - -func (mock *ctx) GetImageData(n string) (*imagedataloader.ImageData, error) { - return mock.GetImageDataFunc(n) -} - -func (mock *ctx) ParseImageReference(n string) (imagedataloader.ImageReference, error) { - return mock.ParseImageReferenceFunc(n) -} - -func (mock *ctx) ListResources(apiVersion, resource, namespace string) (*unstructured.UnstructuredList, error) { - return mock.ListResourcesFunc(apiVersion, resource, namespace) -} - -func (mock *ctx) GetResource(apiVersion, resource, namespace, name string) (*unstructured.Unstructured, error) { - return mock.GetResourcesFunc(apiVersion, resource, namespace, name) -} - func Test_impl_get_configmap_string_string(t *testing.T) { opts := Lib() base, err := cel.NewEnv(opts) @@ -65,46 +32,19 @@ func Test_impl_get_configmap_string_string(t *testing.T) { assert.NotNil(t, prog) called := false data := map[string]any{ - "context": Context{&ctx{ + "context": Context{&MockCtx{ GetConfigMapFunc: func(string, string) (*unstructured.Unstructured, error) { called = true return &unstructured.Unstructured{}, nil }, - }}, - } + }, + }} out, _, err := prog.Eval(data) assert.NoError(t, err) assert.NotNil(t, out) assert.True(t, called) } -type mockGctxStore struct { - data map[string]store.Entry -} - -func (m *mockGctxStore) Get(name string) (store.Entry, bool) { - entry, ok := m.data[name] - return entry, ok -} - -func (m *mockGctxStore) Set(name string, data store.Entry) { - if m.data == nil { - m.data = make(map[string]store.Entry) - } - m.data[name] = data -} - -type mockEntry struct { - data any - err error -} - -func (m *mockEntry) Get(_ string) (any, error) { - return m.data, m.err -} - -func (m *mockEntry) Stop() {} - func Test_impl_get_globalreference_string_string(t *testing.T) { opts := Lib() base, err := cel.NewEnv(opts) @@ -161,7 +101,7 @@ func Test_impl_get_globalreference_string_string(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mockStore := &mockGctxStore{data: tt.gctxStoreData} data := map[string]any{ - "context": Context{&ctx{ + "context": Context{&MockCtx{ GetGlobalReferenceFunc: func(name string, path string) (any, error) { ent, ok := mockStore.Get(name) if !ok { @@ -213,14 +153,14 @@ func Test_impl_get_imagedata_string(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, prog) data := map[string]any{ - "context": Context{&ctx{ + "context": Context{&MockCtx{ GetImageDataFunc: func(image string) (*imagedataloader.ImageData, error) { idl, err := imagedataloader.New(nil) assert.NoError(t, err) return idl.FetchImageData(context.TODO(), image) }, - }}, - } + }, + }} out, _, err := prog.Eval(data) assert.NoError(t, err) img := out.Value().(*imagedataloader.ImageData) @@ -249,13 +189,14 @@ func Test_impl_parse_image_ref_string(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, prog) data := map[string]any{ - "context": Context{&ctx{ + "context": Context{&MockCtx{ ParseImageReferenceFunc: func(image string) (imagedataloader.ImageReference, error) { idl, err := imagedataloader.New(nil) assert.NoError(t, err) return idl.ParseImageReference(image) }, - }}, + }, + }, } out, _, err := prog.Eval(data) assert.NoError(t, err) @@ -283,7 +224,7 @@ func Test_impl_get_resource_string_string_string_string(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, prog) data := map[string]any{ - "context": Context{&ctx{ + "context": Context{&MockCtx{ GetResourcesFunc: func(apiVersion, resource, namespace, name string) (*unstructured.Unstructured, error) { return &unstructured.Unstructured{ Object: map[string]any{ @@ -296,7 +237,8 @@ func Test_impl_get_resource_string_string_string_string(t *testing.T) { }, }, nil }, - }}, + }, + }, } out, _, err := prog.Eval(data) assert.NoError(t, err) @@ -323,7 +265,7 @@ func Test_impl_list_resources_string_string_string(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, prog) data := map[string]any{ - "context": Context{&ctx{ + "context": Context{&MockCtx{ ListResourcesFunc: func(apiVersion, resource, namespace string) (*unstructured.UnstructuredList, error) { return &unstructured.UnstructuredList{ Items: []unstructured.Unstructured{ @@ -340,8 +282,8 @@ func Test_impl_list_resources_string_string_string(t *testing.T) { }, }, nil }, - }}, - } + }, + }} out, _, err := prog.Eval(data) assert.NoError(t, err) object := out.Value().(map[string]any) diff --git a/pkg/cel/libs/context/mock.go b/pkg/cel/libs/context/mock.go new file mode 100644 index 0000000000..490c80c80c --- /dev/null +++ b/pkg/cel/libs/context/mock.go @@ -0,0 +1,68 @@ +package context + +import ( + "github.com/kyverno/kyverno/pkg/globalcontext/store" + "github.com/kyverno/kyverno/pkg/imageverification/imagedataloader" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// MOCK FOR TESTING +type MockCtx struct { + GetConfigMapFunc func(string, string) (*unstructured.Unstructured, error) + GetGlobalReferenceFunc func(string, string) (any, error) + GetImageDataFunc func(string) (*imagedataloader.ImageData, error) + ParseImageReferenceFunc func(string) (imagedataloader.ImageReference, error) + ListResourcesFunc func(string, string, string) (*unstructured.UnstructuredList, error) + GetResourcesFunc func(string, string, string, string) (*unstructured.Unstructured, error) +} + +func (mock *MockCtx) GetConfigMap(ns string, n string) (*unstructured.Unstructured, error) { + return mock.GetConfigMapFunc(ns, n) +} + +func (mock *MockCtx) GetGlobalReference(n, p string) (any, error) { + return mock.GetGlobalReferenceFunc(n, p) +} + +func (mock *MockCtx) GetImageData(n string) (*imagedataloader.ImageData, error) { + return mock.GetImageDataFunc(n) +} + +func (mock *MockCtx) ParseImageReference(n string) (imagedataloader.ImageReference, error) { + return mock.ParseImageReferenceFunc(n) +} + +func (mock *MockCtx) ListResources(apiVersion, resource, namespace string) (*unstructured.UnstructuredList, error) { + return mock.ListResourcesFunc(apiVersion, resource, namespace) +} + +func (mock *MockCtx) GetResource(apiVersion, resource, namespace, name string) (*unstructured.Unstructured, error) { + return mock.GetResourcesFunc(apiVersion, resource, namespace, name) +} + +type mockGctxStore struct { + data map[string]store.Entry +} + +func (m *mockGctxStore) Get(name string) (store.Entry, bool) { + entry, ok := m.data[name] + return entry, ok +} + +func (m *mockGctxStore) Set(name string, data store.Entry) { + if m.data == nil { + m.data = make(map[string]store.Entry) + } + m.data[name] = data +} + +type mockEntry struct { + data any + err error +} + +func (m *mockEntry) Get(_ string) (any, error) { + return m.data, m.err +} + +func (m *mockEntry) Stop() {} diff --git a/pkg/imageverification/evaluator/eval.go b/pkg/imageverification/evaluator/eval.go index 6c23492d1a..3337391e95 100644 --- a/pkg/imageverification/evaluator/eval.go +++ b/pkg/imageverification/evaluator/eval.go @@ -14,13 +14,12 @@ import ( k8scorev1 "k8s.io/client-go/kubernetes/typed/core/v1" ) -func Evaluate(ctx context.Context, logger logr.Logger, ivpols []*v1alpha1.ImageVerificationPolicy, request interface{}, admissionAttr admission.Attributes, namespace runtime.Object, lister k8scorev1.SecretInterface, registryOpts ...imagedataloader.Option) ([]*EvaluationResult, error) { +func Evaluate(ctx context.Context, logger logr.Logger, ivpols []*v1alpha1.ImageVerificationPolicy, request interface{}, admissionAttr admission.Attributes, namespace runtime.Object, lister k8scorev1.SecretInterface, registryOpts ...imagedataloader.Option) (map[string]*EvaluationResult, error) { ictx, err := imagedataloader.NewImageContext(lister, registryOpts...) if err != nil { return nil, err } - // TODO: use environmentconfig, add support for other controllers (autogen) isAdmissionRequest := false var gvr *metav1.GroupVersionResource if r, ok := request.(*admissionv1.AdmissionRequest); ok { @@ -31,7 +30,7 @@ func Evaluate(ctx context.Context, logger logr.Logger, ivpols []*v1alpha1.ImageV policies := filterPolicies(ivpols, isAdmissionRequest) c := NewCompiler(ictx, lister, gvr) - results := make([]*EvaluationResult, 0) + results := make(map[string]*EvaluationResult, len(policies)) for _, ivpol := range policies { p, errList := c.Compile(logger, ivpol) if errList != nil { @@ -42,7 +41,7 @@ func Evaluate(ctx context.Context, logger logr.Logger, ivpols []*v1alpha1.ImageV if err != nil { return nil, err } - results = append(results, result) + results[ivpol.Name] = result } return results, nil } diff --git a/pkg/imageverification/evaluator/eval_test.go b/pkg/imageverification/evaluator/eval_test.go index 09cf4e1646..09e5dbb510 100644 --- a/pkg/imageverification/evaluator/eval_test.go +++ b/pkg/imageverification/evaluator/eval_test.go @@ -95,11 +95,11 @@ func Test_Eval(t *testing.T) { result, err := Evaluate(context.Background(), logr.Discard(), []*policiesv1alpha1.ImageVerificationPolicy{ivpol}, obj(signedImage), nil, nil, nil) assert.NoError(t, err) assert.True(t, len(result) == 1) - assert.True(t, result[0].Result) + assert.True(t, result[ivpol.Name].Result) result, err = Evaluate(context.Background(), logr.Discard(), []*policiesv1alpha1.ImageVerificationPolicy{ivpol}, obj(unsignedImage), nil, nil, nil) assert.NoError(t, err) assert.True(t, len(result) == 1) - assert.False(t, result[0].Result) - assert.Equal(t, result[0].Message, "failed to verify image with notary cert") + assert.False(t, result[ivpol.Name].Result) + assert.Equal(t, result[ivpol.Name].Message, "failed to verify image with notary cert") } diff --git a/pkg/imageverification/evaluator/validate.go b/pkg/imageverification/evaluator/validate.go new file mode 100644 index 0000000000..8a419fb51e --- /dev/null +++ b/pkg/imageverification/evaluator/validate.go @@ -0,0 +1,86 @@ +package eval + +import ( + "encoding/json" + "strings" + + "github.com/go-logr/logr" + "github.com/kyverno/kyverno/api/kyverno" + policiesv1alpha1 "github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1" + engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "gomodules.xyz/jsonpatch/v2" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/sets" +) + +type ImageVerificationOutcome struct { + // Name is the rule name specified in policy + Name string `json:"name,omitempty"` + // RuleType is the rule type (Mutation,Generation,Validation) for Kyverno Policy + RuleType engineapi.RuleType `json:"ruleType,omitempty"` + // Message is the message response from the rule application + Message string `json:"message,omitempty"` + // Status rule status + Status engineapi.RuleStatus `json:"status,omitempty"` + // EmitWarning enable passing rule message as warning to api server warning header + EmitWarning bool `json:"emitWarning,omitempty"` + // Properties are the additional properties from the rule that will be added to the policy report result + Properties map[string]string `json:"properties,omitempty"` +} + +type ImageVerifyEngineResponse struct { + Resource *unstructured.Unstructured + Policies []ImageVerifyPolicyResponse +} + +type ImageVerifyPolicyResponse struct { + Policy *policiesv1alpha1.ImageVerificationPolicy + Actions sets.Set[admissionregistrationv1.ValidationAction] + Result engineapi.RuleResponse +} + +func outcomeFromPolicyResponse(responses map[string]ImageVerifyPolicyResponse) map[string]ImageVerificationOutcome { + outcomes := make(map[string]ImageVerificationOutcome) + for pol, resp := range responses { + outcomes[pol] = ImageVerificationOutcome{ + Name: resp.Result.Name(), + RuleType: resp.Result.RuleType(), + Message: resp.Result.Message(), + Status: resp.Result.Status(), + EmitWarning: resp.Result.EmitWarning(), + Properties: resp.Result.Properties(), + } + } + return outcomes +} + +func MakeImageVerifyOutcomePatch(hasAnnotations bool, log logr.Logger, responses map[string]ImageVerifyPolicyResponse) ([]jsonpatch.JsonPatchOperation, error) { + patches := make([]jsonpatch.JsonPatchOperation, 0) + annotationKey := "/metadata/annotations/" + strings.ReplaceAll(kyverno.AnnotationImageVerifyOutcomes, "/", "~1") + if !hasAnnotations { + patch := jsonpatch.JsonPatchOperation{ + Operation: "add", + Path: "/metadata/annotations", + Value: map[string]string{}, + } + log.V(4).Info("adding annotation patch", "patch", patch) + patches = append(patches, patch) + } + + outcomes := outcomeFromPolicyResponse(responses) + data, err := json.Marshal(outcomes) + if err != nil { + return nil, err + } + + patch := jsonpatch.JsonPatchOperation{ + Operation: "add", + Path: annotationKey, + Value: string(data), + } + + log.V(4).Info("adding image verification patch", "patch", patch) + patches = append(patches, patch) + return patches, nil +} diff --git a/pkg/imageverification/variables/images.go b/pkg/imageverification/variables/images.go index b3a65deab2..1b61a5a82d 100644 --- a/pkg/imageverification/variables/images.go +++ b/pkg/imageverification/variables/images.go @@ -12,43 +12,43 @@ var ( podImageExtractors = []v1alpha1.Image{ { Name: "containers", - Expression: "request.object.spec.containers.map(e, e.image)", + Expression: "has(object.spec.containers) ? object.spec.containers.map(e, e.image) : []", }, { Name: "initContainers", - Expression: "request.object.spec.initContainers.map(e, e.image)", + Expression: "has(object.spec.initContainers) ? object.spec.initContainers.map(e, e.image) : []", }, { Name: "ephemeralContainers", - Expression: "request.object.spec.ephemeralContainers.map(e, e.image)", + Expression: "has(object.spec.ephemeralContainers) ? object.spec.ephemeralContainers.map(e, e.image) : []", }, } podControllerImageExtractors = []v1alpha1.Image{ { Name: "containers", - Expression: "request.object.spec.template.spec.containers.map(e, e.image)", + Expression: "has(object.spec.template.spec.containers) ? object.spec.template.spec.containers.map(e, e.image) : []", }, { Name: "initContainers", - Expression: "request.object.spec.template.spec.initContainers.map(e, e.image)", + Expression: "has(object.spec.template.spec.initContainers) ? object.spec.template.spec.initContainers.map(e, e.image) : []", }, { Name: "ephemeralContainers", - Expression: "request.object.spec.template.spec.ephemeralContainers.map(e, e.image)", + Expression: "has(object.spec.template.spec.ephemeralContainers) ? object.spec.template.spec.ephemeralContainers.map(e, e.image) : []", }, } cronJobImageExtractors = []v1alpha1.Image{ { Name: "containers", - Expression: "request.object.spec.jobTemplate.spec.template.spec.containers.map(e, e.image)", + Expression: "has(object.spec.jobTemplate.spec.template.spec.containers) ? object.spec.jobTemplate.spec.template.spec.containers.map(e, e.image) : []", }, { Name: "initContainers", - Expression: "request.object.spec.jobTemplate.spec.template.spec.initContainers.map(e, e.image)", + Expression: "has(object.spec.jobTemplate.spec.template.spec.initContainers) ? object.spec.jobTemplate.spec.template.spec.initContainers.map(e, e.image) : []", }, { Name: "ephemeralContainers", - Expression: "request.object.spec.jobTemplate.spec.template.spec.ephemeralContainers.map(e, e.image)", + Expression: "has(object.spec.jobTemplate.spec.template.spec.ephemeralContainers) ? object.spec.jobTemplate.spec.template.spec.ephemeralContainers.map(e, e.image) : []", }, } ) diff --git a/pkg/imageverification/variables/images_test.go b/pkg/imageverification/variables/images_test.go index 1fbd3c76de..453db87470 100644 --- a/pkg/imageverification/variables/images_test.go +++ b/pkg/imageverification/variables/images_test.go @@ -62,31 +62,31 @@ func Test_Match(t *testing.T) { "nginx:latest", "alpine:latest", }, - "object": map[string]any{ - "spec": map[string]any{ - "containers": []map[string]string{ - { - "image": "kyverno/image-one", - }, - { - "image": "kyverno/image-two", - }, + }, + "object": map[string]any{ + "spec": map[string]any{ + "containers": []map[string]string{ + { + "image": "kyverno/image-one", }, - "initContainers": []map[string]string{ - { - "image": "kyverno/init-image-one", - }, - { - "image": "kyverno/init-image-two", - }, + { + "image": "kyverno/image-two", }, - "ephemeralContainers": []map[string]string{ - { - "image": "kyverno/ephr-image-one", - }, - { - "image": "kyverno/ephr-image-two", - }, + }, + "initContainers": []map[string]string{ + { + "image": "kyverno/init-image-one", + }, + { + "image": "kyverno/init-image-two", + }, + }, + "ephemeralContainers": []map[string]string{ + { + "image": "kyverno/ephr-image-one", + }, + { + "image": "kyverno/ephr-image-two", }, }, }, @@ -132,7 +132,7 @@ func Test_Match(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c, errList := CompileImageExtractors(field.NewPath("spec", "images"), tt.imageExtractor, tt.gvr, []cel.EnvOption{cel.Variable(policy.RequestKey, types.DynType)}) + c, errList := CompileImageExtractors(field.NewPath("spec", "images"), tt.imageExtractor, tt.gvr, []cel.EnvOption{cel.Variable(policy.RequestKey, types.DynType), cel.Variable(policy.ObjectKey, types.DynType)}) assert.Nil(t, errList) images, err := ExtractImages(c, tt.request) if tt.wantErr { diff --git a/pkg/webhooks/resource/ivpol/handler.go b/pkg/webhooks/resource/ivpol/handler.go new file mode 100644 index 0000000000..8aa5d4748e --- /dev/null +++ b/pkg/webhooks/resource/ivpol/handler.go @@ -0,0 +1,58 @@ +package ivpol + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + celengine "github.com/kyverno/kyverno/pkg/cel/engine" + celpolicy "github.com/kyverno/kyverno/pkg/cel/policy" + engineapi "github.com/kyverno/kyverno/pkg/engine/api" + "github.com/kyverno/kyverno/pkg/engine/mutate/patch" + eval "github.com/kyverno/kyverno/pkg/imageverification/evaluator" + admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" + jsonutils "github.com/kyverno/kyverno/pkg/utils/json" + "github.com/kyverno/kyverno/pkg/webhooks/handlers" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" +) + +type handler struct { + context celpolicy.Context + engine celengine.ImageVerifyEngine +} + +func New( + engine celengine.ImageVerifyEngine, + context celpolicy.Context, +) *handler { + return &handler{ + context: context, + engine: engine, + } +} + +func (h *handler) Mutate(ctx context.Context, logger logr.Logger, admissionRequest handlers.AdmissionRequest, failurePolicy string, startTime time.Time) handlers.AdmissionResponse { + request := celengine.RequestFromAdmission(h.context, admissionRequest.AdmissionRequest) + response, patches, err := h.engine.HandleMutating(ctx, request) + if err != nil { + return admissionutils.Response(admissionRequest.UID, err) + } + rawPatches := jsonutils.JoinPatches(patch.ConvertPatches(patches...)...) + return h.mutationResponse(request, response, rawPatches) +} + +func (h *handler) mutationResponse(request celengine.EngineRequest, response eval.ImageVerifyEngineResponse, rawPatches []byte) handlers.AdmissionResponse { + var warnings []string + for _, policy := range response.Policies { + if policy.Actions.Has(admissionregistrationv1.Warn) { + switch policy.Result.Status() { + case engineapi.RuleStatusFail: + warnings = append(warnings, fmt.Sprintf("Policy %s failed: %s", policy.Policy.GetName(), policy.Result.Message())) + case engineapi.RuleStatusError: + warnings = append(warnings, fmt.Sprintf("Policy %s error: %s", policy.Policy.GetName(), policy.Result.Message())) + } + } + } + return admissionutils.MutationResponse(request.AdmissionRequest().UID, rawPatches, warnings...) +}