1
0
Fork 0
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:
Vyom Yadav 2023-03-29 21:24:42 +05:30 committed by GitHub
parent c7192912fa
commit c01b5cc381
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 196 additions and 6 deletions

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Pod
metadata:
labels:
foo: empty
name: nginx-pod
namespace: test-ns

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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