Policy: validate-namespace-metadata
Disallow auxiliary labels and annotations
Category |
Namespace Ownership |
Minimum Kyverno version |
v1 |
Subject |
APPUiO Organizations |
Policy types |
|
Implementation |
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