mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
fix: Don't check for subresource existence when it is the trigger. (#6544)
Signed-off-by: Vyom-Yadav <jackhammervyom@gmail.com> Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
parent
c7192912fa
commit
c01b5cc381
9 changed files with 196 additions and 6 deletions
|
@ -18,6 +18,7 @@ import (
|
|||
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
|
||||
"go.uber.org/multierr"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
|
@ -84,11 +85,46 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e
|
|||
continue
|
||||
}
|
||||
|
||||
trigger, err := common.GetResource(c.client, ur.Spec, c.log)
|
||||
if err != nil || trigger == nil {
|
||||
logger.WithName(rule.Name).Error(err, "failed to get trigger resource")
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
var trigger *unstructured.Unstructured
|
||||
admissionRequest := ur.Spec.Context.AdmissionRequestInfo.AdmissionRequest
|
||||
if admissionRequest == nil {
|
||||
trigger, err = common.GetResource(c.client, ur.Spec, c.log)
|
||||
if err != nil || trigger == nil {
|
||||
logger.WithName(rule.Name).Error(err, "failed to get trigger resource")
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if admissionRequest.Operation == admissionv1.Create {
|
||||
trigger, err = common.GetResource(c.client, ur.Spec, c.log)
|
||||
if err != nil || trigger == nil {
|
||||
if admissionRequest.SubResource == "" {
|
||||
logger.WithName(rule.Name).Error(err, "failed to get trigger resource")
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
} else {
|
||||
logger.WithName(rule.Name).Info("trigger resource not found for subresource, reverting to resource in AdmissionReviewRequest", "subresource", admissionRequest.SubResource)
|
||||
triggerBytes := admissionRequest.Object.Raw
|
||||
trigger = &unstructured.Unstructured{}
|
||||
if err := trigger.UnmarshalJSON(triggerBytes); err != nil {
|
||||
logger.WithName(rule.Name).Error(err, "failed to convert trigger resource")
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
triggerBytes := admissionRequest.Object.Raw
|
||||
if triggerBytes == nil {
|
||||
triggerBytes = admissionRequest.OldObject.Raw
|
||||
}
|
||||
trigger = &unstructured.Unstructured{}
|
||||
if err := trigger.UnmarshalJSON(triggerBytes); err != nil {
|
||||
logger.WithName(rule.Name).Error(err, "failed to convert trigger resource")
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger)
|
||||
|
@ -98,6 +134,16 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e
|
|||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if admissionRequest != nil {
|
||||
var gvk schema.GroupVersionKind
|
||||
gvk, err = c.client.Discovery().GetGVKFromGVR(schema.GroupVersionResource(admissionRequest.Resource))
|
||||
if err != nil {
|
||||
logger.WithName(rule.Name).Error(err, "failed to get GVK from GVR", "GVR", admissionRequest.Resource)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
policyContext = policyContext.WithResourceKind(gvk, admissionRequest.SubResource)
|
||||
}
|
||||
|
||||
er := c.engine.Mutate(context.TODO(), policyContext)
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
|
|
|
@ -96,7 +96,7 @@ var (
|
|||
// kyvernoConfigMapName is the Kyverno configmap name
|
||||
kyvernoConfigMapName = osutils.GetEnvWithFallback("INIT_CONFIG", "kyverno")
|
||||
// defaultExcludedUsernames are the usernames excluded by default when matching an incoming admission request
|
||||
defaultExcludedUsernames []string = []string{"system:kube-scheduler"}
|
||||
defaultExcludedUsernames []string
|
||||
// defaultExcludedGroups are the groups excluded by default when matching an incoming admission request
|
||||
defaultExcludedGroups []string = []string{"system:serviceaccounts:kube-system", "system:nodes"}
|
||||
// kyvernoDryRunNamespace is the namespace for DryRun option of YAML verification
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: mutate-pod-on-binding-request
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: test-ns
|
||||
status:
|
||||
phase: Active
|
|
@ -0,0 +1,66 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: test-ns
|
||||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: mutate-pod-on-binding-request
|
||||
spec:
|
||||
background: false
|
||||
rules:
|
||||
- name: mutate-pod-on-binding-request
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod/binding
|
||||
names:
|
||||
- nginx-pod
|
||||
preconditions:
|
||||
all:
|
||||
- key: "{{node}}"
|
||||
operator: NotEquals
|
||||
value: ""
|
||||
- key: "{{ request.operation }}"
|
||||
operator: AnyIn
|
||||
value:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
context:
|
||||
- name: node
|
||||
variable:
|
||||
jmesPath: request.object.target.name
|
||||
default: ''
|
||||
- name: foolabel
|
||||
apiCall:
|
||||
urlPath: "/api/v1/nodes/{{node}}"
|
||||
jmesPath: metadata.labels.foo || 'empty'
|
||||
mutate:
|
||||
targets:
|
||||
- apiVersion: v1
|
||||
kind: Pod
|
||||
name: "{{ request.name }}"
|
||||
namespace: "{{ request.namespace}}"
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
labels:
|
||||
foo: "{{ foolabel }}"
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: admission-controller
|
||||
app.kubernetes.io/instance: kyverno
|
||||
app.kubernetes.io/part-of: kyverno
|
||||
name: kyverno:modify-pods
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- update
|
||||
- patch
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
foo: empty
|
||||
name: nginx-pod
|
||||
namespace: test-ns
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
commands:
|
||||
- script: ./modify-resource-filters.sh removeBinding
|
||||
- command: kubectl run nginx-pod --image=nginx -n test-ns
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
commands:
|
||||
- command: kubectl delete pod nginx-pod -n test-ns --force --wait=true
|
||||
- command: kubectl delete -f 01-manifests.yaml --force --wait=true
|
||||
- script: ./modify-resource-filters.sh addBinding
|
|
@ -0,0 +1,22 @@
|
|||
## Description
|
||||
|
||||
This test validates that an incoming request to `Pod/binding` subresource can act as a trigger for mutation of an existing `Pod` object.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The `Pod` `nginx-pod` is labelled with `foo: empty` label.
|
||||
|
||||
## Steps
|
||||
|
||||
### Test Steps
|
||||
|
||||
1. Create a `ClusterPolicy` that matches on `Pod/binding` and mutates `Pod` object.
|
||||
2. Create `ClusterRole` for allowing modifications to `Pod` resource.
|
||||
3. Modify kyverno `resourceFilters` to allow mutating incoming requests to `Pod/binding` subresource.
|
||||
4. Modify kyverno `resourceFilters` to allow mutating incoming requests from `kube-system` namespace.
|
||||
5. Create a `Pod` object.
|
||||
6. Verify that the `Pod` object is labelled with `foo: empty` label.
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/6503
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 [addBinding|removeBinding]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$1" = "removeBinding" ]; then
|
||||
resource_filters=$(kubectl get ConfigMap kyverno -n kyverno -o json | jq .data.resourceFilters)
|
||||
resource_filters="${resource_filters//\[Binding,\*,\*\]/}"
|
||||
|
||||
kubectl patch ConfigMap kyverno -n kyverno --type='json' -p="[{\"op\": \"replace\", \"path\": \"/data/resourceFilters\", \"value\":""$resource_filters""}]"
|
||||
fi
|
||||
|
||||
if [ "$1" = "addBinding" ]; then
|
||||
resource_filters=$(kubectl get ConfigMap kyverno -n kyverno -o json | jq .data.resourceFilters)
|
||||
resource_filters="${resource_filters%?}"
|
||||
|
||||
resource_filters="${resource_filters}""[Binding,*,*]\""
|
||||
kubectl patch ConfigMap kyverno -n kyverno --type='json' -p="[{\"op\": \"replace\", \"path\": \"/data/resourceFilters\", \"value\":""$resource_filters""}]"
|
||||
fi
|
Loading…
Reference in a new issue