All Policies

Restrict Secrets by Label

Secrets often contain sensitive information and their access should be carefully controlled. Although Kubernetes RBAC can be effective at restricting them in several ways, it lacks the ability to use labels on referenced entities. This policy ensures that only Secrets not labeled with `status=protected` can be consumed by Pods.

Policy Definition

/other/restrict-secrets-by-label/restrict-secrets-by-label.yaml

  1apiVersion: kyverno.io/v1
  2kind: ClusterPolicy
  3metadata:
  4  name: restrict-secrets-by-label
  5  annotations:
  6    policies.kyverno.io/title: Restrict Secrets by Label
  7    policies.kyverno.io/category: Other
  8    policies.kyverno.io/severity: medium
  9    policies.kyverno.io/minversion: 1.6.0
 10    kyverno.io/kyverno-version: 1.6.0
 11    kyverno.io/kubernetes-version: "1.23"
 12    policies.kyverno.io/subject: Pod, Secret
 13    policies.kyverno.io/description: >-
 14      Secrets often contain sensitive information and their access should be carefully controlled.
 15      Although Kubernetes RBAC can be effective at restricting them in several ways,
 16      it lacks the ability to use labels on referenced entities. This policy ensures
 17      that only Secrets not labeled with `status=protected` can be consumed by Pods.
 18spec:
 19  background: false
 20  validationFailureAction: Audit
 21  rules:
 22  - name: secrets-lookup-from-env
 23    match:
 24      any:
 25      - resources:
 26          kinds:
 27          - Pod
 28    preconditions:
 29      all:
 30      # check if any secrets are in env statements
 31      - key: "{{ request.object.spec.[containers, initContainers, ephemeralContainers][].env[].valueFrom.secretKeyRef || '' | length(@) }}"
 32        operator: GreaterThanOrEquals
 33        value: 1
 34      - key: "{{request.operation || 'BACKGROUND'}}"
 35        operator: NotEquals
 36        value: DELETE
 37    validate:
 38      message: These Secrets are restricted.
 39      foreach:
 40      # get every secret, then do an API lookup on each one checking for the `status` label.
 41      # deny the request if any of those secrets have `status=protected`.
 42      - list: "request.object.spec.[containers, initContainers, ephemeralContainers][].env[].valueFrom.secretKeyRef"
 43        context:
 44        - name: status
 45          apiCall:
 46            jmesPath: "metadata.labels.status || ''"
 47            urlPath: "/api/v1/namespaces/{{request.namespace}}/secrets/{{element.name}}"
 48        deny:
 49          conditions:
 50            any:
 51            - key: "{{ status }}"
 52              operator: Equals
 53              value: protected
 54  - name: secrets-lookup-from-envfrom
 55    match:
 56      any:
 57      - resources:
 58          kinds:
 59          - Pod
 60    preconditions:
 61      all:
 62      # check if any secrets are in envfrom statements
 63      - key: "{{ request.object.spec.[containers, initContainers, ephemeralContainers][].envFrom[].secretRef || '' | length(@) }}"
 64        operator: GreaterThanOrEquals
 65        value: 1
 66      - key: "{{request.operation || 'BACKGROUND'}}"
 67        operator: NotEquals
 68        value: DELETE
 69    validate:
 70      message: These Secrets are restricted.
 71      foreach:
 72      # get every secret, then do an API lookup on each one checking for the `status` label.
 73      # deny the request if any of those secrets have `status=protected`.
 74      - list: "request.object.spec.[containers, initContainers, ephemeralContainers][].envFrom[].secretRef"
 75        context:
 76        - name: status
 77          apiCall:
 78            jmesPath: "metadata.labels.status || ''"
 79            urlPath: "/api/v1/namespaces/{{request.namespace}}/secrets/{{element.name}}"
 80        deny:
 81          conditions:
 82            any:
 83            - key: "{{ status }}"
 84              operator: AnyIn
 85              value: protected
 86  - name: secrets-lookup-from-volumes
 87    match:
 88      any:
 89      - resources:
 90          kinds:
 91          - Pod
 92    preconditions:
 93      all:
 94      # check if any secrets are in volume statements
 95      - key: "{{ request.object.spec.volumes[].secret || '' | length(@) }}"
 96        operator: GreaterThanOrEquals
 97        value: 1
 98      - key: "{{request.operation || 'BACKGROUND'}}"
 99        operator: NotEquals
100        value: DELETE
101    validate:
102      message: These Secrets are restricted.
103      foreach:
104      # get every secret, then do an API lookup on each one checking for the `status` label.
105      # deny the request if any of those secrets have `status=protected`.
106      - list: "request.object.spec.volumes[].secret"
107        context:
108        - name: status
109          apiCall:
110            jmesPath: "metadata.labels.status || ''"
111            urlPath: "/api/v1/namespaces/{{request.namespace}}/secrets/{{element.secretName}}"
112        deny:
113          conditions:
114            any:
115            - key: "{{ status }}"
116              operator: Equals
117              value: protected