Skip to content

[Bug] context vars for request.object are evaluated in DELETE operations, even if excluded by preconditions and block deletes #4374

@zswanson

Description

@zswanson

Kyverno Version

1.7.2

Description

A policy with preconditions that limits to operations UPDATE and CREATE, was still partially evaluated in a DELETE operation. Context Variables were evaluated by the policy during the DELETE admission, and returned null because request.object is null in a DELETE, as per the Kyverno documentation. The context variable evaluating to null caused an error condition, and blocked deleting the resource. Weirdly enough, the policy still runs the preconditions and acknowledges that it should have skipped the resource because of the operation.

We tested this behavior in 1.7.2 and also in 1.8-dev-latest off main.

Suggestion - since some parts of the admission request object can be null based on the operation, and because Preconditions aren't restricted from using either request.object or context variables, it seems that filtering admission by operation should be supported directly in the match statement.

12:38:59 ❯ k delete ingress flaggertest-ing-canary
Error from server: admission webhook "validate.kyverno.svc-fail" denied the request:

resource Ingress/flaggertest-ml476k/flaggertest-ing-canary was blocked due to the following policies

ingress-unique-host:
  unique-ingress-against-other-ingress-class: 'failed to load context: unable to add
    context entry for variable requestIngressClass since it evaluated to nil'
  unique-ingress-host-against-services: preconditions not met

In the policy below the evaluation of request.object in the context variables was not strictly required and was mostly a way of making the final validation expression easier to read. I was able to work around this issue for now by rewriting the validation jmespath expression and cleaning up the context variables.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: ingress-unique-host
spec:
  validationFailureAction: enforce
  failurePolicy: Fail

  rules:
    - name: unique-ingress-against-other-ingress-class
      match: # match any ingress resource
        all:
          - resources:
              kinds:
                - Ingress

      context:
        - name: requestIngressClass
          variable:
            jmesPath: request.object.metadata.annotations."kubernetes.io/ingress.class"
        # Create a list of ingresses, excluding the ingress we are currently reviewing
        - name: ingresses
          apiCall:
            urlPath: '/apis/networking.k8s.io/v1/ingresses'
            jmesPath: items[?metadata.name != '{{ request.object.metadata.name }}']

      preconditions:
        all:
          - key: "{{ request.operation }}"
            operator: AnyIn
            value:
              - CREATE
              - UPDATE

      validate:
        message: >
          Ingress must have a unique hostname across different ingress classes
        deny:
          conditions:
            any:
              # select ingresses with ingress class that does NOT match the class of the request object
              # compare the request object hosts against the selected set of hosts - if they match, deny
              - key: '{{ request.object.spec.rules[].host }}'
                operator: AnyIn
                value: "{{ingresses[?metadata.annotations.\"kubernetes.io/ingress.class\" != '{{ request.object.metadata.annotations.\"kubernetes.io/ingress.class\" }}'].spec.rules[].host }}"

Slack discussion

https://kubernetes.slack.com/archives/CLGR9BJU9/p1661187029836889

Troubleshooting

  • I have read and followed the documentation AND the troubleshooting guide.
  • I have searched other issues in this repository and mine is not recorded.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions