1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-21 07:12:42 +00:00

feat: enable mutating webhook for ivpol (#12423)

* feat: enable mutating webhook for ivpol

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: unit tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: add objects to payload

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: add chainsaw test

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: add update codegen

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: propagate policy response to admission reponse

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: update chainsaw tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2025-03-17 20:31:37 +08:00 committed by GitHub
parent 1982ebca47
commit 4f9b07070a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 525 additions and 376 deletions

View file

@ -18,7 +18,6 @@ import (
// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="READY",type=string,JSONPath=`.status.ready`
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ImageVerificationPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
@ -29,7 +28,7 @@ type ImageVerificationPolicy struct {
}
type IvpolStatus struct {
ConditionStatus *ConditionStatus `json:",inline"`
ConditionStatus `json:"conditionStatus,inline"`
// +optional
Autogen IvpolAutogenStatus `json:"autogen,omitempty"`
@ -114,10 +113,6 @@ func (s ImageVerificationPolicySpec) BackgroundEnabled() bool {
}
func (status *IvpolStatus) SetReadyByCondition(c PolicyConditionType, s metav1.ConditionStatus, message string) {
if status.ConditionStatus == nil {
status.ConditionStatus = &ConditionStatus{}
}
reason := "Succeeded"
if s != metav1.ConditionTrue {
reason = "Failed"
@ -133,10 +128,7 @@ func (status *IvpolStatus) SetReadyByCondition(c PolicyConditionType, s metav1.C
}
func (status *IvpolStatus) GetConditionStatus() *ConditionStatus {
if status.ConditionStatus != nil {
return status.ConditionStatus
}
return &ConditionStatus{}
return &status.ConditionStatus
}
// +kubebuilder:object:root=true

View file

@ -44,7 +44,7 @@ func (status *ConditionStatus) SetReadyByCondition(c PolicyConditionType, s meta
meta.SetStatusCondition(&status.Conditions, newCondition)
}
func (status *ConditionStatus) IsReady() bool {
func (status ConditionStatus) IsReady() bool {
if status.Ready != nil {
return *status.Ready
}

View file

@ -21,7 +21,7 @@ func TestPolicyStatus_IsReady(t *testing.T) {
}, {
name: "true",
status: VpolStatus{
ConditionStatus: &ConditionStatus{
ConditionStatus: ConditionStatus{
Ready: ptr.To(true),
},
},
@ -29,7 +29,7 @@ func TestPolicyStatus_IsReady(t *testing.T) {
}, {
name: "false",
status: VpolStatus{
ConditionStatus: &ConditionStatus{
ConditionStatus: ConditionStatus{
Ready: ptr.To(false),
},
},

View file

@ -24,7 +24,7 @@ type ValidatingPolicy struct {
}
type VpolStatus struct {
ConditionStatus *ConditionStatus `json:",inline"`
ConditionStatus `json:"conditionStatus,inline"`
// +optional
Autogen AutogenStatus `json:"autogen"`
@ -87,10 +87,7 @@ func (s *ValidatingPolicy) GetKind() string {
}
func (status *VpolStatus) GetConditionStatus() *ConditionStatus {
if status.ConditionStatus != nil {
return status.ConditionStatus
}
return &ConditionStatus{}
return &status.ConditionStatus
}
// +kubebuilder:object:root=true

View file

@ -714,11 +714,7 @@ func (in *IvpolAutogenStatus) DeepCopy() *IvpolAutogenStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IvpolStatus) DeepCopyInto(out *IvpolStatus) {
*out = *in
if in.ConditionStatus != nil {
in, out := &in.ConditionStatus, &out.ConditionStatus
*out = new(ConditionStatus)
(*in).DeepCopyInto(*out)
}
in.ConditionStatus.DeepCopyInto(&out.ConditionStatus)
in.Autogen.DeepCopyInto(&out.Autogen)
return
}
@ -1009,11 +1005,7 @@ func (in *ValidatingPolicySpec) DeepCopy() *ValidatingPolicySpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VpolStatus) DeepCopyInto(out *VpolStatus) {
*out = *in
if in.ConditionStatus != nil {
in, out := &in.ConditionStatus, &out.ConditionStatus
*out = new(ConditionStatus)
(*in).DeepCopyInto(*out)
}
in.ConditionStatus.DeepCopyInto(&out.ConditionStatus)
in.Autogen.DeepCopyInto(&out.Autogen)
return
}

View file

@ -731,10 +731,11 @@ func main() {
Validation: webhooks.HandlerFunc(policyHandlers.Validate),
},
webhooks.ResourceHandlers{
Mutation: webhooks.HandlerFunc(resourceHandlers.Mutate),
Validation: webhooks.HandlerFunc(resourceHandlers.Validate),
ValidatingPolicies: webhooks.HandlerFunc(voplHandlers.Validate),
ImageVerificationPolicies: webhooks.HandlerFunc(ivpolHandlers.Validate),
Mutation: webhooks.HandlerFunc(resourceHandlers.Mutate),
ImageVerificationPoliciesMutation: webhooks.HandlerFunc(ivpolHandlers.Mutate),
Validation: webhooks.HandlerFunc(resourceHandlers.Validate),
ValidatingPolicies: webhooks.HandlerFunc(voplHandlers.Validate),
ImageVerificationPolicies: webhooks.HandlerFunc(ivpolHandlers.Validate),
},
webhooks.ExceptionHandlers{
Validation: webhooks.HandlerFunc(exceptionHandlers.Validate),

View file

@ -10176,8 +10176,6 @@ Resource Types:
<ul><li>
<a href="#policies.kyverno.io/v1alpha1.CELPolicyException">CELPolicyException</a>
</li><li>
<a href="#policies.kyverno.io/v1alpha1.ImageVerificationPolicy">ImageVerificationPolicy</a>
</li><li>
<a href="#policies.kyverno.io/v1alpha1.ValidatingPolicy">ValidatingPolicy</a>
</li></ul>
<hr />
@ -10272,307 +10270,6 @@ CELPolicyExceptionSpec
</tbody>
</table>
<hr />
<h3 id="policies.kyverno.io/v1alpha1.ImageVerificationPolicy">ImageVerificationPolicy
</h3>
<p>
</p>
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>apiVersion</code><br/>
string</td>
<td>
<code>
policies.kyverno.io/v1alpha1
</code>
</td>
</tr>
<tr>
<td>
<code>kind</code><br/>
string
</td>
<td><code>ImageVerificationPolicy</code></td>
</tr>
<tr>
<td>
<code>metadata</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta">
Kubernetes meta/v1.ObjectMeta
</a>
</em>
</td>
<td>
Refer to the Kubernetes API documentation for the fields of the
<code>metadata</code> field.
</td>
</tr>
<tr>
<td>
<code>spec</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.ImageVerificationPolicySpec">
ImageVerificationPolicySpec
</a>
</em>
</td>
<td>
<br/>
<br/>
<table class="table table-striped">
<tr>
<td>
<code>matchConstraints</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#matchresources-v1-admissionregistration">
Kubernetes admissionregistration/v1.MatchResources
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>MatchConstraints specifies what resources this policy is designed to validate.</p>
</td>
</tr>
<tr>
<td>
<code>failurePolicy</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#failurepolicytype-v1-admissionregistration">
Kubernetes admissionregistration/v1.FailurePolicyType
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>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.</p>
</td>
</tr>
<tr>
<td>
<code>validationActions</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#validationaction-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.ValidationAction
</a>
</em>
</td>
<td>
<p>ValidationAction specifies the action to be taken when the matched resource violates the policy.
Required.</p>
</td>
</tr>
<tr>
<td>
<code>matchConditions</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#matchcondition-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.MatchCondition
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>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.</p>
</td>
</tr>
<tr>
<td>
<code>variables</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#variable-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.Variable
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Variables contain definitions of variables that can be used in composition of other expressions.
Each variable is defined as a named CEL expression.</p>
</td>
</tr>
<tr>
<td>
<code>imageRules</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.ImageRule">
[]ImageRule
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>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</p>
</td>
</tr>
<tr>
<td>
<code>mutateDigest</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>MutateDigest enables replacement of image tags with digests.
Defaults to true.</p>
</td>
</tr>
<tr>
<td>
<code>verifyDigest</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>VerifyDigest validates that images have a digest.</p>
</td>
</tr>
<tr>
<td>
<code>required</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>Required validates that images are verified i.e. have matched passed a signature or attestation check.</p>
</td>
</tr>
<tr>
<td>
<code>credentials</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Credentials">
Credentials
</a>
</em>
</td>
<td>
<p>Credentials provides credentials that will be used for authentication with registry.</p>
</td>
</tr>
<tr>
<td>
<code>images</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Image">
[]Image
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Images is a list of CEL expression to extract images from the resource</p>
</td>
</tr>
<tr>
<td>
<code>attestors</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Attestor">
[]Attestor
</a>
</em>
</td>
<td>
<p>Attestors provides a list of trusted authorities.</p>
</td>
</tr>
<tr>
<td>
<code>attestations</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Attestation">
[]Attestation
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Attestations provides a list of image metadata to verify</p>
</td>
</tr>
<tr>
<td>
<code>verifications</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#validation-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.Validation
</a>
</em>
</td>
<td>
<p>Verifications contain CEL expressions which is used to apply the image verification checks.</p>
</td>
</tr>
<tr>
<td>
<code>webhookConfiguration</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.WebhookConfiguration">
WebhookConfiguration
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>WebhookConfiguration defines the configuration for the webhook.</p>
</td>
</tr>
<tr>
<td>
<code>evaluation</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.EvaluationConfiguration">
EvaluationConfiguration
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>EvaluationConfiguration defines the configuration for the policy evaluation.</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<code>status</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.IvpolStatus">
IvpolStatus
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Status contains policy runtime data.</p>
</td>
</tr>
</tbody>
</table>
<hr />
<h3 id="policies.kyverno.io/v1alpha1.ValidatingPolicy">ValidatingPolicy
</h3>
<p>
@ -11782,6 +11479,290 @@ string
</tbody>
</table>
<hr />
<h3 id="policies.kyverno.io/v1alpha1.ImageVerificationPolicy">ImageVerificationPolicy
</h3>
<p>
</p>
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>metadata</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta">
Kubernetes meta/v1.ObjectMeta
</a>
</em>
</td>
<td>
Refer to the Kubernetes API documentation for the fields of the
<code>metadata</code> field.
</td>
</tr>
<tr>
<td>
<code>spec</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.ImageVerificationPolicySpec">
ImageVerificationPolicySpec
</a>
</em>
</td>
<td>
<br/>
<br/>
<table class="table table-striped">
<tr>
<td>
<code>matchConstraints</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#matchresources-v1-admissionregistration">
Kubernetes admissionregistration/v1.MatchResources
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>MatchConstraints specifies what resources this policy is designed to validate.</p>
</td>
</tr>
<tr>
<td>
<code>failurePolicy</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#failurepolicytype-v1-admissionregistration">
Kubernetes admissionregistration/v1.FailurePolicyType
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>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.</p>
</td>
</tr>
<tr>
<td>
<code>validationActions</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#validationaction-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.ValidationAction
</a>
</em>
</td>
<td>
<p>ValidationAction specifies the action to be taken when the matched resource violates the policy.
Required.</p>
</td>
</tr>
<tr>
<td>
<code>matchConditions</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#matchcondition-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.MatchCondition
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>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.</p>
</td>
</tr>
<tr>
<td>
<code>variables</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#variable-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.Variable
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Variables contain definitions of variables that can be used in composition of other expressions.
Each variable is defined as a named CEL expression.</p>
</td>
</tr>
<tr>
<td>
<code>imageRules</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.ImageRule">
[]ImageRule
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>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</p>
</td>
</tr>
<tr>
<td>
<code>mutateDigest</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>MutateDigest enables replacement of image tags with digests.
Defaults to true.</p>
</td>
</tr>
<tr>
<td>
<code>verifyDigest</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>VerifyDigest validates that images have a digest.</p>
</td>
</tr>
<tr>
<td>
<code>required</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>Required validates that images are verified i.e. have matched passed a signature or attestation check.</p>
</td>
</tr>
<tr>
<td>
<code>credentials</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Credentials">
Credentials
</a>
</em>
</td>
<td>
<p>Credentials provides credentials that will be used for authentication with registry.</p>
</td>
</tr>
<tr>
<td>
<code>images</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Image">
[]Image
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Images is a list of CEL expression to extract images from the resource</p>
</td>
</tr>
<tr>
<td>
<code>attestors</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Attestor">
[]Attestor
</a>
</em>
</td>
<td>
<p>Attestors provides a list of trusted authorities.</p>
</td>
</tr>
<tr>
<td>
<code>attestations</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.Attestation">
[]Attestation
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Attestations provides a list of image metadata to verify</p>
</td>
</tr>
<tr>
<td>
<code>verifications</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#validation-v1-admissionregistration">
[]Kubernetes admissionregistration/v1.Validation
</a>
</em>
</td>
<td>
<p>Verifications contain CEL expressions which is used to apply the image verification checks.</p>
</td>
</tr>
<tr>
<td>
<code>webhookConfiguration</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.WebhookConfiguration">
WebhookConfiguration
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>WebhookConfiguration defines the configuration for the webhook.</p>
</td>
</tr>
<tr>
<td>
<code>evaluation</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.EvaluationConfiguration">
EvaluationConfiguration
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>EvaluationConfiguration defines the configuration for the policy evaluation.</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<code>status</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.IvpolStatus">
IvpolStatus
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Status contains policy runtime data.</p>
</td>
</tr>
</tbody>
</table>
<hr />
<h3 id="policies.kyverno.io/v1alpha1.ImageVerificationPolicySpec">ImageVerificationPolicySpec
</h3>
<p>
@ -12378,7 +12359,7 @@ EvaluationConfiguration
<tbody>
<tr>
<td>
<code>ConditionStatus</code><br/>
<code>conditionStatus</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.ConditionStatus">
ConditionStatus
@ -12387,7 +12368,7 @@ ConditionStatus
</td>
<td>
<p>
(Members of <code>ConditionStatus</code> are embedded into this type.)
(Members of <code>conditionStatus</code> are embedded into this type.)
</p>
</td>
</tr>
@ -13006,7 +12987,7 @@ EvaluationConfiguration
<tbody>
<tr>
<td>
<code>ConditionStatus</code><br/>
<code>conditionStatus</code><br/>
<em>
<a href="#policies.kyverno.io/v1alpha1.ConditionStatus">
ConditionStatus
@ -13015,7 +12996,7 @@ ConditionStatus
</td>
<td>
<p>
(Members of <code>ConditionStatus</code> are embedded into this type.)
(Members of <code>conditionStatus</code> are embedded into this type.)
</p>
</td>
</tr>

View file

@ -120,14 +120,6 @@ func convertMatchConditions(conditions []admissionregistrationv1.MatchCondition,
m.Expression = expression + m.Expression
matchConditions = append(matchConditions, m)
}
// if bytes, err := json.Marshal(matchConditions); err != nil {
// return nil, err
// } else {
// bytes = updateFields(bytes, resource)
// if err := json.Unmarshal(bytes, &matchConditions); err != nil {
// return nil, err
// }
// }
return matchConditions, nil
}

View file

@ -292,6 +292,7 @@ func (e *ivengine) handleValidation(policies []CompiledImageVerificationPolicy,
} else {
resp.Result = *engineapi.NewRuleResponse(o.Name, engineapi.ImageVerify, o.Message, o.Status, o.Properties)
}
responses[pol.Policy.GetName()] = resp
}
}
return maps.Values(responses), nil

View file

@ -54,8 +54,10 @@ const (
VerifyMutatingWebhookName = "monitor-webhooks.kyverno.svc"
// ValidatingPolicyWebhookName defines default webhook name for validatingpolicies
ValidatingPolicyWebhookName = "vpol.validate.kyverno.svc"
// ImageVerificationPolicyWebhookName defines default webhook name for imageverificationpolicies
ImageVerificationPolicyWebhookName = "ivpol.validate.kyverno.svc"
// ImageVerificationPolicyWebhookName defines default validating webhook name for imageverificationpolicies
ImageVerificationPolicyValidateWebhookName = "ivpol.validate.kyverno.svc"
// ImageVerificationPolicyWebhookName defines default mutating webhook name for imageverificationpolicies
ImageVerificationPolicyMutateWebhookName = "ivpol.mutate.kyverno.svc"
)
// paths

View file

@ -38,7 +38,7 @@ func (c controller) updateIvpolStatus(ctx context.Context, ivpol *policiesv1alph
if conditionStatus.Ready == nil || conditionStatus.IsReady() != ready {
conditionStatus.Ready = &ready
}
status.ConditionStatus = conditionStatus
status.ConditionStatus = *conditionStatus
return nil
}

View file

@ -33,7 +33,7 @@ func (c controller) updateVpolStatus(ctx context.Context, vpol *policiesv1alpha1
if conditionStatus.Ready == nil || conditionStatus.IsReady() != ready {
conditionStatus.Ready = &ready
}
status.ConditionStatus = conditionStatus
status.ConditionStatus = *conditionStatus
return nil
}

View file

@ -811,17 +811,68 @@ func (c *controller) buildDefaultResourceMutatingWebhookConfiguration(_ context.
}
func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
result := admissionregistrationv1.MutatingWebhookConfiguration{
result := &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.MutatingWebhook{},
}
var errs []error
if err := c.buildForPoliciesMutation(ctx, cfg, caBundle, result); err != nil {
errs = append(errs, fmt.Errorf("failed to build webhook rules for policies: %v", err))
}
if err := c.buildForJSONPoliciesMutation(cfg, caBundle, result); err != nil {
errs = append(errs, fmt.Errorf("failed to build webhook rules for imageverificationpolicies: %v", err))
}
return result, multierr.Combine(errs...)
}
func (c *controller) buildForJSONPoliciesMutation(cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.MutatingWebhookConfiguration) error {
if !c.watchdogCheck() {
return nil
}
ivpols, err := c.getImageVerificationPolicy()
if err != nil {
return err
}
validate := buildWebhookRules(cfg,
c.server,
config.ImageVerificationPolicyMutateWebhookName,
config.PolicyServicePath+config.ImageVerificationPolicyServicePath+config.MutatingWebhookServicePath,
c.servicePort,
caBundle,
ivpols)
mutate := make([]admissionregistrationv1.MutatingWebhook, 0, len(validate))
for _, w := range validate {
mutate = append(mutate, admissionregistrationv1.MutatingWebhook{
Name: w.Name,
ClientConfig: w.ClientConfig,
FailurePolicy: w.FailurePolicy,
SideEffects: w.SideEffects,
AdmissionReviewVersions: w.AdmissionReviewVersions,
NamespaceSelector: w.NamespaceSelector,
ObjectSelector: w.ObjectSelector,
Rules: w.Rules,
MatchConditions: w.MatchConditions,
TimeoutSeconds: w.TimeoutSeconds,
})
}
result.Webhooks = append(result.Webhooks, mutate...)
// todo(shuting): record policy state?
return nil
}
func (c *controller) buildForPoliciesMutation(ctx context.Context, cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.MutatingWebhookConfiguration) error {
if c.watchdogCheck() {
webhookCfg := cfg.GetWebhook()
ignoreWebhook := newWebhook(c.defaultTimeout, ignore, cfg.GetMatchConditions())
failWebhook := newWebhook(c.defaultTimeout, fail, cfg.GetMatchConditions())
policies, err := c.getAllPolicies()
if err != nil {
return nil, err
return err
}
var fineGrainedIgnoreList, fineGrainedFailList []*webhook
c.recordKyvernoPolicyState(config.MutatingWebhookConfigurationName, policies...)
@ -856,7 +907,7 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte
} else {
c.recordKyvernoPolicyState(config.MutatingWebhookConfigurationName)
}
return &result, nil
return nil
}
func (c *controller) buildResourceMutatingWebhookRules(caBundle []byte, webhookCfg config.WebhookConfig, sideEffects *admissionregistrationv1.SideEffectClass, webhooks []*webhook) []admissionregistrationv1.MutatingWebhook {
@ -954,18 +1005,18 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con
}
var errs []error
if err := c.buildForPolicies(ctx, cfg, caBundle, webhookConfig); err != nil {
if err := c.buildForPoliciesValidation(ctx, cfg, caBundle, webhookConfig); err != nil {
errs = append(errs, fmt.Errorf("failed to build webhook rules for policies: %v", err))
}
if err := c.buildForValidatingPolicies(cfg, caBundle, webhookConfig); err != nil {
if err := c.buildForJSONPoliciesValidation(cfg, caBundle, webhookConfig); err != nil {
errs = append(errs, fmt.Errorf("failed to build webhook rules for validatingpolicies: %v", err))
}
return webhookConfig, multierr.Combine(errs...)
}
func (c *controller) buildForValidatingPolicies(cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.ValidatingWebhookConfiguration) error {
func (c *controller) buildForJSONPoliciesValidation(cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.ValidatingWebhookConfiguration) error {
if !c.watchdogCheck() {
return nil
}
@ -977,7 +1028,7 @@ func (c *controller) buildForValidatingPolicies(cfg config.Configuration, caBund
result.Webhooks = append(result.Webhooks, buildWebhookRules(cfg,
c.server,
config.ValidatingPolicyWebhookName,
config.PolicyServicePath+config.ValidatingPolicyServicePath,
config.PolicyServicePath+config.ValidatingPolicyServicePath+config.ValidatingWebhookServicePath,
c.servicePort,
caBundle,
pols)...)
@ -988,8 +1039,8 @@ func (c *controller) buildForValidatingPolicies(cfg config.Configuration, caBund
}
result.Webhooks = append(result.Webhooks, buildWebhookRules(cfg,
c.server,
config.ImageVerificationPolicyWebhookName,
config.PolicyServicePath+config.ImageVerificationPolicyServicePath,
config.ImageVerificationPolicyValidateWebhookName,
config.PolicyServicePath+config.ImageVerificationPolicyServicePath+config.ValidatingWebhookServicePath,
c.servicePort,
caBundle,
ivpols)...)
@ -998,7 +1049,7 @@ func (c *controller) buildForValidatingPolicies(cfg config.Configuration, caBund
return nil
}
func (c *controller) buildForPolicies(ctx context.Context, cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.ValidatingWebhookConfiguration) error {
func (c *controller) buildForPoliciesValidation(ctx context.Context, cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.ValidatingWebhookConfiguration) error {
if c.watchdogCheck() {
webhookCfg := cfg.GetWebhook()
ignoreWebhook := newWebhook(c.defaultTimeout, ignore, cfg.GetMatchConditions())

View file

@ -139,7 +139,7 @@ func TestBuildWebhookRules_ValidatingPolicy(t *testing.T) {
expectedWebhooks: []admissionregistrationv1.ValidatingWebhook{
{
Name: config.ValidatingPolicyWebhookName + "-ignore-finegrained-test-fine-grained-ignore",
ClientConfig: newClientConfig("", 0, nil, "/policies/vpol/ignore"+config.FineGrainedWebhookPath+"/test-fine-grained-ignore"),
ClientConfig: newClientConfig("", 0, nil, "/policies/vpol/validate/ignore"+config.FineGrainedWebhookPath+"/test-fine-grained-ignore"),
Rules: []admissionregistrationv1.RuleWithOperations{
{
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
@ -197,7 +197,7 @@ func TestBuildWebhookRules_ValidatingPolicy(t *testing.T) {
expectedWebhooks: []admissionregistrationv1.ValidatingWebhook{
{
Name: config.ValidatingPolicyWebhookName + "-fail-finegrained-test-fine-grained-fail",
ClientConfig: newClientConfig("", 0, nil, "/policies/vpol/fail"+config.FineGrainedWebhookPath+"/test-fine-grained-fail"),
ClientConfig: newClientConfig("", 0, nil, "/policies/vpol/validate/fail"+config.FineGrainedWebhookPath+"/test-fine-grained-fail"),
Rules: []admissionregistrationv1.RuleWithOperations{
{
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
@ -230,7 +230,7 @@ func TestBuildWebhookRules_ValidatingPolicy(t *testing.T) {
vpols = append(vpols, engineapi.NewValidatingPolicy(vpol))
}
webhooks := buildWebhookRules(config.NewDefaultConfiguration(false), "", config.ValidatingPolicyWebhookName,
config.PolicyServicePath+config.ValidatingPolicyServicePath, 0, nil, vpols)
config.PolicyServicePath+config.ValidatingPolicyServicePath+config.ValidatingWebhookServicePath, 0, nil, vpols)
assert.Equal(t, len(tt.expectedWebhooks), len(webhooks))
for i, expect := range tt.expectedWebhooks {
assert.Equal(t, expect.Name, webhooks[i].Name)
@ -286,7 +286,7 @@ func TestBuildWebhookRules_ImageVerificationPolicy(t *testing.T) {
},
expectedWebhooks: []admissionregistrationv1.ValidatingWebhook{
{
Name: config.ImageVerificationPolicyWebhookName + "-ignore",
Name: config.ImageVerificationPolicyValidateWebhookName + "-ignore",
Rules: []admissionregistrationv1.RuleWithOperations{
{
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
@ -367,8 +367,8 @@ func TestBuildWebhookRules_ImageVerificationPolicy(t *testing.T) {
},
expectedWebhooks: []admissionregistrationv1.ValidatingWebhook{
{
Name: config.ImageVerificationPolicyWebhookName + "-ignore-finegrained-ivpol-sample",
ClientConfig: newClientConfig("", 0, nil, "/policies/ivpol/ignore"+config.FineGrainedWebhookPath+"/ivpol-sample"),
Name: config.ImageVerificationPolicyValidateWebhookName + "-ignore-finegrained-ivpol-sample",
ClientConfig: newClientConfig("", 0, nil, "/policies/ivpol/validate/ignore"+config.FineGrainedWebhookPath+"/ivpol-sample"),
Rules: []admissionregistrationv1.RuleWithOperations{
{
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
@ -433,8 +433,8 @@ func TestBuildWebhookRules_ImageVerificationPolicy(t *testing.T) {
for _, ivpol := range tt.ivpols {
ivpols = append(ivpols, engineapi.NewImageVerificationPolicy(ivpol))
}
webhooks := buildWebhookRules(config.NewDefaultConfiguration(false), "", config.ImageVerificationPolicyWebhookName,
config.PolicyServicePath+config.ImageVerificationPolicyServicePath, 0, nil, ivpols)
webhooks := buildWebhookRules(config.NewDefaultConfiguration(false), "", config.ImageVerificationPolicyValidateWebhookName,
config.PolicyServicePath+config.ImageVerificationPolicyServicePath+config.ValidatingWebhookServicePath, 0, nil, ivpols)
assert.Equal(t, len(tt.expectedWebhooks), len(webhooks), tt.name)
for i, expect := range tt.expectedWebhooks {
assert.Equal(t, expect.Name, webhooks[i].Name)

View file

@ -148,8 +148,18 @@ func (p *compiledPolicy) match(
if err != nil {
return false, fmt.Errorf("failed to prepare request variable for evaluation: %w", err)
}
objectVal, err := objectToResolveVal(attr.GetObject())
if err != nil {
return false, fmt.Errorf("failed to prepare object variable for evaluation: %w", err)
}
oldObjectVal, err := objectToResolveVal(attr.GetOldObject())
if err != nil {
return false, fmt.Errorf("failed to prepare oldObject variable for evaluation: %w", err)
}
data[NamespaceObjectKey] = namespaceVal
data[RequestKey] = requestVal.Object
data[ObjectKey] = objectVal
data[OldObjectKey] = oldObjectVal
} else {
data[ObjectKey] = request
}

View file

@ -88,6 +88,23 @@ func NewServer(
WithAdmission(resourceLogger.WithName("mutate"))
},
)
registerWebhookHandlersWithAll(
mux,
"IVPOL-MUTATE",
config.PolicyServicePath+config.ImageVerificationPolicyServicePath+config.MutatingWebhookServicePath,
resourceHandlers.ImageVerificationPoliciesMutation,
func(handler handlers.AdmissionHandler) handlers.HttpHandler {
return handler.
WithFilter(configuration).
WithProtection(toggle.FromContext(ctx).ProtectManagedResources()).
WithDump(debugModeOpts.DumpPayload).
WithTopLevelGVK(discovery).
WithRoles(rbLister, crbLister).
WithOperationFilter(admissionv1.Create, admissionv1.Update, admissionv1.Connect).
WithMetrics(resourceLogger, metricsConfig.Config(), metrics.WebhookMutating).
WithAdmission(resourceLogger.WithName("mutate"))
},
)
registerWebhookHandlersWithAll(
mux,
"VALIDATE",
@ -107,7 +124,7 @@ func NewServer(
registerWebhookHandlers(
mux,
"VPOL",
config.PolicyServicePath+config.ValidatingPolicyServicePath,
config.PolicyServicePath+config.ValidatingPolicyServicePath+config.ValidatingWebhookServicePath,
resourceHandlers.ValidatingPolicies,
func(handler handlers.AdmissionHandler) handlers.HttpHandler {
return handler.
@ -122,8 +139,8 @@ func NewServer(
)
registerWebhookHandlers(
mux,
"IVPOL",
config.PolicyServicePath+config.ImageVerificationPolicyServicePath,
"IVPOL-VALIDATE",
config.PolicyServicePath+config.ImageVerificationPolicyServicePath+config.ValidatingWebhookServicePath,
resourceHandlers.ImageVerificationPolicies,
func(handler handlers.AdmissionHandler) handlers.HttpHandler {
return handler.

View file

@ -54,6 +54,8 @@ type ResourceHandlers struct {
Validation Handler
// ValidatingPolicies evaluates validating policies against kube resources
ValidatingPolicies Handler
// ImageVerificationPolicies evaluates imageverificationpolicies against kube resources
// ImageVerificationPolicies evaluates imageverificationpolicies mutation phase against kube resources
ImageVerificationPoliciesMutation Handler
// ImageVerificationPolicies evaluates imageverificationpolicies validation phase against kube resources
ImageVerificationPolicies Handler
}

View file

@ -0,0 +1,11 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod-bad
namespace: default
labels:
prod: "true"
spec:
containers:
- name: nginx
image: 'ghcr.io/kyverno/test-verify-image:unsigned'

View file

@ -0,0 +1,29 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: audit
spec:
steps:
- name: create policy
try:
- create:
file: policy.yaml
- sleep:
duration: 10s
- name: create bad pod
try:
- create:
file: bad-pod.yaml
expect:
- check:
($error): >-
admission webhook "ivpol.validate.kyverno.svc-ignore-finegrained-ivpol-sample" denied the request: Policy ivpol-sample failed: failed to verify image with notary cert
- name: create good pod
try:
- create:
file: good-pod.yaml
- sleep:
duration: 10s
- assert:
file: good-pod.yaml

View file

@ -0,0 +1,11 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod-good
namespace: default
labels:
prod: "true"
spec:
containers:
- name: nginx
image: 'ghcr.io/kyverno/test-verify-image:signed'

View file

@ -0,0 +1,60 @@
apiVersion: policies.kyverno.io/v1alpha1
kind: ImageVerificationPolicy
metadata:
name: ivpol-sample
spec:
webhookConfiguration:
timeoutSeconds: 20
failurePolicy: Ignore
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
matchConditions:
- name: "check-prod-label"
expression: >-
has(object.metadata.labels) && has(object.metadata.labels.prod) && object.metadata.labels.prod == 'true'
imageRules:
- glob: ghcr.io/*
attestors:
- name: notary
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:
- name: sbom
referrer:
type: sbom/cyclone-dx
verifications:
- 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 cer
- expression: >-
images.containers.map(image, payload(image, attestations.sbom).bomFormat == 'CycloneDX').all(e, e)
message: sbom is not a cyclone dx sbom

View file

@ -11,7 +11,7 @@ webhooks:
service:
name: kyverno-svc
namespace: kyverno
path: /policies/vpol/fail/finegrained/disallow-privilege-escalation
path: /policies/vpol/validate/fail/finegrained/disallow-privilege-escalation
port: 443
failurePolicy: Fail
matchConditions:

View file

@ -11,7 +11,7 @@ webhooks:
service:
name: kyverno-svc
namespace: kyverno
path: /policies/vpol/fail
path: /policies/vpol/validate/fail
port: 443
failurePolicy: Fail
matchPolicy: Equivalent

View file

@ -11,7 +11,7 @@ webhooks:
service:
name: kyverno-svc
namespace: kyverno
path: /policies/vpol/fail
path: /policies/vpol/validate/fail
port: 443
failurePolicy: Fail
matchPolicy: Equivalent