kyverno / kyverno

Cloud Native Policy Management
https://kyverno.io
Apache License 2.0
5.61k stars 856 forks source link

[Bug] Mutation issue in multiple policies scenario #10247

Open baechul opened 4 months ago

baechul commented 4 months ago

Kyverno Version

1.11.4

Description

This issue was actually caught in the latest 1.11.5 of 1.11. group (I haven't tried 1.11.4). And I verified it works in 1.12.0. Hence it appears it was fixed in 1.12. Due to other issues on 1.12, I have to use a version from 1.11.. It would be nice to backport the fix to 1.11 latest.

Here are 2 policies. Their match blocks are exclusive with the given resources. 1) policy1.yaml

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: abc
spec:
  background: false
  rules:  
  - name: abcrule
    match:
      all:
      - resources:
          kinds:
            - VirtualService
          operations:
          - UPDATE                 
          selector:
            matchLabels:
              hello: notExists.  # no resources have this label
    mutate:
      patchStrategicMerge:
        metadata:
          labels:
            newlabel: here

Since no resources have the "hello: notExists", this rule won't be applied to the given resources.

2) policy2.yaml

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: mutate-test3
  namespace: default
spec:
  background: false
  rules:
  - name: rule1
    match:
      all:
      - resources:
          kinds:
            - VirtualService
          operations:
          - UPDATE
          - CREATE 
    preconditions:
      all:
      - key: "{{ request.object.metadata.name }}"
        operator: Equals
        value: trigger
    mutate:
      targets:
      - apiVersion: networking.istio.io/v1beta1
        kind: VirtualService
        name: target
      foreach:
      - list: request.object.spec.http[]. # per route rule of the trigger resource
        patchesJson6902: |-
          - op: add                                     # mutate the target virtual service by adding rules from the trigger resource
            path: '/spec/http/{{elementIndex}}'
            value: {{ element }}

This policy mutates an existing virtual service upon a triggering virtual service. Per route rule of the trigger resource, it mutates the target virtual service by adding rules from the trigger resource.

3) target.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: target
  labels:
    hello: world
spec:
  http:
  - match:
    - uri:
        exact: /
    route:
    - destination:
        host: target
        port:
          number: 80

4) trigger.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: trigger
  labels:
    hello: world
spec:
  http:
  - match:
    - uri:
        prefix: /abc
    route:
    - destination:
        host: other
        port:
          number: 8080
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: other
        port:
          number: 80

Steps to Reproduce:

  1. kubectl create -f policy1.yaml
  2. kubectl create -f policy2.yaml
  3. kubectl create -f target.yaml
  4. kubectl create -f trigger.yaml
  5. kubectl get vs target -o yam

I expected this output:

spec:
  http:
  - match:
    - uri:
        prefix: /abc
    route:
    - destination:
        host: other
        port:
          number: 8080
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: other
        port:
          number: 80
  - match:
    - uri:
        exact: /
    route:
    - destination:
        host: target
        port:

But I got this:

spec:
  http:
  - match:
    - uri:
        prefix: /abc
    route:
    - destination:
        host: other
        port:
          number: 8080
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: other
        port:
          number: 80
  - match:
    - uri:
        prefix: /abc
    route:
    - destination:
        host: other
        port:
          number: 8080
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: other
        port:
          number: 80
  - match:
    - uri:
        exact: /
    route:
    - destination:
        host: target
        port:
          number: 80

The same rules were copied twice. In 1.12.0, it worked and copied only once.

In 1.11.5, If I delete the policy1 and repeat the same scenario, it works. Hence it appears somehow policy1 affected policy2 rule execution in 1.11.5. BTW this happens when policy1 has mutation rule. If policy1 has a validate or generate rule, it works as expected in 1.11.5.

Slack discussion

No response

Troubleshooting

realshuting commented 4 months ago

Could you please find the commit that fixed this issue?