Policy: validate-namespace-metadata

Disallow auxiliary labels and annotations
Category

Namespace Ownership

Minimum Kyverno version

v1

Subject

APPUiO Organizations

Policy types

validate

Implementation

component/namespace-policies.jsonnet

This policy will:

  • Check annotations and labels on new and modified namespaces against a whitelist.

If the namespace has an annotation or label which isn’t whitelisted and the requester doesn’t have a cluster role which allows them to bypass the policy, the request is denied.

The list of allowed namespace annotations and labels is configured with component parameter allowedNamespaceAnnotations and component parameter allowedNamespaceLabels respectively.

Requesters which match an entry of component parameter bypassNamespaceRestrictions are allowed to bypass the policy.

Policy Definition

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  annotations:
    pod-policies.kyverno.io/autogen-controllers: none
    policies.kyverno.io/category: Namespace Ownership
    policies.kyverno.io/description: |
      This policy will:

      - Check annotations and labels on new and modified namespaces against a whitelist.

      If the namespace has an annotation or label which isn't whitelisted and the requester doesn't have a cluster role which allows them to bypass the policy, the request is denied.

      The list of allowed namespace annotations and labels is configured with xref:references/parameters#_allowednamespaceannotations[component parameter `allowedNamespaceAnnotations`] and xref:references/parameters#_allowednamespacelabels[component parameter `allowedNamespaceLabels`] respectively.

      Requesters which match an entry of xref:references/parameters#_bypassnamespacerestrictions[component parameter `bypassNamespaceRestrictions`] are allowed to bypass the policy.
    policies.kyverno.io/jsonnet: component/namespace-policies.jsonnet
    policies.kyverno.io/minversion: v1
    policies.kyverno.io/subject: APPUiO Organizations
    policies.kyverno.io/title: Disallow auxiliary labels and annotations
  labels:
    app.kubernetes.io/component: appuio-cloud
    app.kubernetes.io/managed-by: commodore
    app.kubernetes.io/name: appuio-cloud
    name: validate-namespace-metadata
  name: validate-namespace-metadata
spec:
  background: false
  rules:
    - exclude:
        any:
          - clusterRoles:
              - cluster-admin
              - cluster-image-registry-operator
              - cluster-node-tuning-operator
              - kyverno:generatecontroller
              - kyverno:policycontroller
              - multus-admission-controller-webhook
              - openshift-dns-operator
              - openshift-ingress-operator
              - syn-admin
              - syn-argocd-application-controller
              - syn-argocd-server
              - system:controller:generic-garbage-collector
              - system:controller:operator-lifecycle-manager
              - system:master
              - system:openshift:controller:namespace-security-allocation-controller
              - system:openshift:controller:podsecurity-admission-label-syncer-controller
          - subjects:
              - kind: ServiceAccount
                name: argocd-application-controller
                namespace: argocd
              - kind: ServiceAccount
                name: cluster-logging-operator
                namespace: openshift-logging
              - kind: ServiceAccount
                name: olm-operator-serviceaccount
                namespace: openshift-operator-lifecycle-manager
              - kind: ServiceAccount
                name: namespace-openshift-config-2c8343f13594d63-manager
                namespace: syn-resource-locker
              - kind: ServiceAccount
                name: namespace-default-d6a0af6dd07e8a3-manager
                namespace: syn-resource-locker
              - kind: ServiceAccount
                name: namespace-openshift-monitoring-c4273dc15ddfdf7-manager
                namespace: syn-resource-locker
      match:
        all:
          - resources:
              kinds:
                - Namespace
      name: validate-labels
      preconditions:
        all:
          - key: '{{request.operation}}'
            operator: In
            value:
              - CREATE
              - UPDATE
      validate:
        foreach:
          - deny:
              conditions:
                all:
                  - key: '{{request.object.metadata.labels."{{element.key}}" != request.oldObject.metadata.labels."{{element.key}}"}}'
                    operator: Equals
                    value: true
                  - key: '{{regex_match(`"^appuio.io/organization$"`, `"{{element.key}}"`)
                      || regex_match(`"^custom.appuio.io/.*$"`, `"{{element.key}}"`)
                      || regex_match(`"^kubernetes.io/metadata.name$"`, `"{{element.key}}"`)
                      || regex_match(`"^network-policies.syn.tools/no-defaults$"`,
                      `"{{element.key}}"`) || regex_match(`"^network-policies.syn.tools/purge-defaults$"`,
                      `"{{element.key}}"`) || regex_match(`"^test.appuio.io/.*$"`,
                      `"{{element.key}}"`) || regex_match(`"^compute.test.appuio.io/cpu$"`,
                      `"{{element.key}}"`)}}'
                    operator: Equals
                    value: false
            list: 'request.object&& merge(    not_null(request.object.metadata.labels,
              `{}`)   ,not_null(request.oldObject.metadata.labels, `{}`))  | map(&{key:
              @}, keys(@))'
        message: |-
          The following labels can be modified:
              appuio.io/organization, custom.appuio.io/*, kubernetes.io/metadata.name, network-policies.syn.tools/no-defaults, network-policies.syn.tools/purge-defaults, test.appuio.io/*, compute.test.appuio.io/cpu.
          labels given:
              {{request.object.metadata.labels}}.
          labels before modification:
              {{request.oldObject.metadata.labels}}.
    - exclude:
        any:
          - clusterRoles:
              - cluster-admin
              - cluster-image-registry-operator
              - cluster-node-tuning-operator
              - kyverno:generatecontroller
              - kyverno:policycontroller
              - multus-admission-controller-webhook
              - openshift-dns-operator
              - openshift-ingress-operator
              - syn-admin
              - syn-argocd-application-controller
              - syn-argocd-server
              - system:controller:generic-garbage-collector
              - system:controller:operator-lifecycle-manager
              - system:master
              - system:openshift:controller:namespace-security-allocation-controller
              - system:openshift:controller:podsecurity-admission-label-syncer-controller
          - subjects:
              - kind: ServiceAccount
                name: argocd-application-controller
                namespace: argocd
              - kind: ServiceAccount
                name: cluster-logging-operator
                namespace: openshift-logging
              - kind: ServiceAccount
                name: olm-operator-serviceaccount
                namespace: openshift-operator-lifecycle-manager
              - kind: ServiceAccount
                name: namespace-openshift-config-2c8343f13594d63-manager
                namespace: syn-resource-locker
              - kind: ServiceAccount
                name: namespace-default-d6a0af6dd07e8a3-manager
                namespace: syn-resource-locker
              - kind: ServiceAccount
                name: namespace-openshift-monitoring-c4273dc15ddfdf7-manager
                namespace: syn-resource-locker
      match:
        all:
          - resources:
              kinds:
                - Namespace
      name: validate-annotations
      preconditions:
        all:
          - key: '{{request.operation}}'
            operator: In
            value:
              - CREATE
              - UPDATE
      validate:
        foreach:
          - deny:
              conditions:
                all:
                  - key: '{{request.object.metadata.annotations."{{element.key}}"
                      != request.oldObject.metadata.annotations."{{element.key}}"}}'
                    operator: Equals
                    value: true
                  - key: '{{regex_match(`"^custom.appuio.io/.*$"`, `"{{element.key}}"`)
                      || regex_match(`"^appuio.io/default-node-selector$"`, `"{{element.key}}"`)
                      || regex_match(`"^kubectl.kubernetes.io/last-applied-configuration$"`,
                      `"{{element.key}}"`) || regex_match(`"^policies.kyverno.io/last-applied-patches$"`,
                      `"{{element.key}}"`) || regex_match(`"^appuio.io/active-deadline-seconds-override$"`,
                      `"{{element.key}}"`) || regex_match(`"^test.appuio.io/.*$"`,
                      `"{{element.key}}"`) || regex_match(`"^compute.test.appuio.io/cpu$"`,
                      `"{{element.key}}"`)}}'
                    operator: Equals
                    value: false
            list: 'request.object&& merge(    not_null(request.object.metadata.annotations,
              `{}`)   ,not_null(request.oldObject.metadata.annotations, `{}`))  |
              map(&{key: @}, keys(@))'
        message: |-
          The following annotations can be modified:
              custom.appuio.io/*, appuio.io/default-node-selector, kubectl.kubernetes.io/last-applied-configuration, policies.kyverno.io/last-applied-patches, appuio.io/active-deadline-seconds-override, test.appuio.io/*, compute.test.appuio.io/cpu.
          annotations given:
              {{request.object.metadata.annotations}}.
          annotations before modification:
              {{request.oldObject.metadata.annotations}}.
  validationFailureAction: enforce