Use OPA Gatekeeper

To guide you in the creation of OPA Gatekeeper policies, as an example this topic illustrates how to generate a policy for restricting escalation to root privileges.

Note

Gatekeeper provides a library of commonly used policies, including replacements for familiar PodSecurityPolicies.

Important

For users who are new to Gatekeeper, Mirantis recommends performing a dry run on potential policies prior to production deployment. Such an approach, by only auditing violations, will prevent potential cluster disruption. To perform a dry run, set spec.enforcementAction to dryrun in the constraint.yaml detailed herein.

  1. Create a YAML file called template.yaml and place the following code in that file:

    apiVersion: templates.gatekeeper.sh/v1
    kind: ConstraintTemplate
    metadata:
      name: k8spspallowprivilegeescalationcontainer
      annotations:
        description: >-
          Controls restricting escalation to root privileges. Corresponds to the
          `allowPrivilegeEscalation` field in a PodSecurityPolicy. For more
          information, see
          https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privilege-escalation
    spec:
      crd:
        spec:
          names:
            kind: K8sPSPAllowPrivilegeEscalationContainer
          validation:
            openAPIV3Schema:
              type: object
              description: >-
                Controls restricting escalation to root privileges. Corresponds to the
                `allowPrivilegeEscalation` field in a PodSecurityPolicy. For more
                information, see
                https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privilege-escalation
              properties:
                exemptImages:
                  description: >-
                    Any container that uses an image that matches an entry in this list will be excluded
                    from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.
    
                    It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)
                    in order to avoid unexpectedly exempting images from an untrusted repository.
                  type: array
                  items:
                    type: string
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8spspallowprivilegeescalationcontainer
    
            import data.lib.exempt_container.is_exempt
    
            violation[{"msg": msg, "details": {}}] {
                c := input_containers[_]
                not is_exempt(c)
                input_allow_privilege_escalation(c)
                msg := sprintf("Privilege escalation container is not allowed: %v", [c.name])
            }
    
            input_allow_privilege_escalation(c) {
                not has_field(c, "securityContext")
            }
            input_allow_privilege_escalation(c) {
                not c.securityContext.allowPrivilegeEscalation == false
            }
            input_containers[c] {
                c := input.review.object.spec.containers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.initContainers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.ephemeralContainers[_]
            }
            # has_field returns whether an object has a field
            has_field(object, field) = true {
                object[field]
            }
          libs:
            - |
              package lib.exempt_container
    
              is_exempt(container) {
                  exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
                  img := container.image
                  exemption := exempt_images[_]
                  _matches_exemption(img, exemption)
              }
    
              _matches_exemption(img, exemption) {
                  not endswith(exemption, "*")
                  exemption == img
              }
    
              _matches_exemption(img, exemption) {
                  endswith(exemption, "*")
                  prefix := trim_suffix(exemption, "*")
                  startswith(img, prefix)
              }
    
  2. Create the constraint template:

    kubectl create -f template.yaml
    

    Expected output:

    constrainttemplate.templates.gatekeeper.sh/k8spspallowprivilegeescalationcontainer created
    
  3. Create a YAML file called constraint.yaml and place the following code in that file:

    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPAllowPrivilegeEscalationContainer
    metadata:
      name: psp-allow-privilege-escalation-container
    spec:
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
    
  4. Create the constraint:

    kubectl create -f constraint.yaml
    

    Expected output:

    k8spspallowprivilegeescalationcontainer.constraints.gatekeeper.sh/psp-allow-privilege-escalation-container created
    
  5. Create a YAML file called disallowed-pod.yaml and place the following code in that file:

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-privilege-escalation-disallowed
      labels:
        app: nginx-privilege-escalation
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          allowPrivilegeEscalation: true
    
  6. Create the Pod:

    kubectl create -f disallowed-pod.yaml
    

    Expected output:

    Error from server (Forbidden): error when creating "disallowed.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [psp-allow-privilege-escalation-container] Privilege escalation container is not allowed: nginx
    
  7. Create a YAML file called allowed-pod.yaml and place the following code in that file:

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-privilege-escalation-allowed
      labels:
        app: nginx-privilege-escalation
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          allowPrivilegeEscalation: false
    
  8. Create the Pod:

    kubectl create -f allowed-pod.yaml
    

    Expected output:

    pod/nginx-privilege-escalation-allowed created