Policy: organization-namespaces

Ensure that all namespaces created by users have a label appuio.io/organization which isn’t empty.
Category

Namespace Ownership

Minimum Kyverno version

v1

Subject

APPUiO Organizations

Policy types

mutate, validate

Implementation

component/namespace-policies.jsonnet

This policy will:

  • Check that each namespace created by a user without cluster-admin permissions has a label appuio.io/organization which isn’t empty.

  • Check that the creating user is in the organization it tries to create a namespace for.

The user’s organization membership is checked by:

  • Fetching all OpenShift groups

  • Reading the appuio.io/organization label of the request and finding a group with the same name

If a group matching the label value exists, the policy checks that the user which issued the request is a member of that group.

If the label appuio.io/organization is missing or empty or the user isn’t a member of the group, the request is denied.

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

Policy Definition

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  annotations:
    policies.kyverno.io/category: Namespace Ownership
    policies.kyverno.io/description: 'This policy will:


      - Check that each namespace created by a user without cluster-admin  permissions
      has a label appuio.io/organization which isn''t empty.

      - Check that the creating user is in the organization it tries to create a namespace
      for.


      The user''s organization membership is checked by:


      - Fetching all OpenShift groups

      - Reading the `appuio.io/organization` label of the request and finding a group
      with the same name


      If a group matching the label value exists, the policy checks that the user
      which issued the request is a member of that group.


      If the label `appuio.io/organization` is missing or empty or the user isn''t
      a member of the group, the request is denied.


      Users 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: Ensure that all namespaces created by users have a
      label `appuio.io/organization` which isn't empty.
  labels:
    app.kubernetes.io/component: appuio-cloud
    app.kubernetes.io/managed-by: commodore
    app.kubernetes.io/name: appuio-cloud
    name: organization-namespaces
  name: organization-namespaces
spec:
  background: false
  rules:
    - context:
        - apiCall:
            jmesPath: '@'
            urlPath: /apis/user.openshift.io/v1/users/{{request.userInfo.username}}
          name: ocpuser
      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: 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
      mutate:
        patchStrategicMerge:
          metadata:
            labels:
              +(appuio.io/organization): '{{ocpuser.metadata.annotations."appuio.io/default-organization"
                || ""}}'
      name: set-default-organization
      preconditions:
        all:
          - key: '{{serviceAccountName}}'
            operator: Equals
            value: ''
    - 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: 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: has-organization
      preconditions:
        all:
          - key: '{{serviceAccountName}}'
            operator: Equals
            value: ''
      validate:
        message: Namespace must have organization
        pattern:
          metadata:
            labels:
              appuio.io/organization: ?*
    - 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: 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: is-in-organization
      preconditions:
        all:
          - key: '{{serviceAccountName}}'
            operator: Equals
            value: ''
          - key: '{{request.object.metadata.labels."appuio.io/organization" || ""}}'
            operator: NotEquals
            value: ''
      validate:
        deny:
          conditions:
            any:
              - key: '{{request.object.metadata.labels."appuio.io/organization" ||
                  ""}}'
                operator: AnyNotIn
                value: '{{request.userInfo.groups}}'
        message: Creating namespace for {{request.object.metadata.labels."appuio.io/organization"}}
          but {{request.userInfo.username}} is not in organization
  validationFailureAction: enforce