Logo

Kubernetes Security Context and Policies

The previous articles about Docker security have discussed many security features that can be applied at container's runtime. Those features, such as privileges management, kernel capabilities, mandatory access control, among many others, can be enforced in Kubernetes environments through security contexts and policies.

A security context can be applied for a Pod or container.

 

The features that a security context allows to define are:

  • Discretionary Access Control: Permission to access an object, like a file, is based on user ID (UID) and group ID (GID).
  • SELinux: Objects are assigned security labels.
  • Privileges: Run as privileged or not (Docker --privileged flag).
  • Linux Capabilities: Give a process some privileges, but not all the privileges of the root user.
  • AppArmor: Use program profiles to restrict the capabilities of individual programs.
  • Seccomp: Filter a process's system calls.
  • AllowPrivilegeEscalation: Controls whether a process can gain more privileges than its parent process.
  • readOnlyRootFilesystem: Mounts the container's root filesystem as read-only.

To specify these security settings for a Pod, the securityContext field must be included in the Pod Spec. For the container level case, then the field must be located within the container’s array definition.

Analyzing an example:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-example
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
  containers:
  - name: ubuntu-container
    image: ubuntu
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      allowPrivilegeEscalation: false

As noted, securityContext is declared twice. The first is at the Pod level, the directives "runAsUser" and "runAsGroup" apply to all containers in the Pod definition file. The second securityContext declaration is inside the container definition, so "allowPrivilegeEscalation" will apply only for that container.

The image above shows that the UID and GID of the container are the ones specified in the security context.

Looking at another example:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-example-2
spec:
  containers:
  - name: nginx-container
    image: nginx
    ports:
      - containerPort: 80
    securityContext:
      capabilities:
        drop:
          - all
        add:
          - NET_BIND_SERVICE

 

In this case, the security context is used to drop all the kernel capabilities at container's runtime and add only the one needed for this scenario. The definition of capabilities can only be applied at container level.

An excellent practice is to define a Pod Security Policy for the cluster. This is a cluster-level resource, that defines a set of conditions related to security that a pod must run with, in order to be accepted into the system.

Pod Security Policies are enforced by enabling the admission controller, if implemented this acts an optional Kubernetes admission controller.

Below is an example of a restrictive policy that requires users to run as an unprivileged user, blocks possible escalations to root, and requires use of several security mechanisms.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'runtime/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
  privileged: false
  # Required to prevent escalations to root.
  allowPrivilegeEscalation: false
  # This is redundant, but we can provide it for defense in depth.
  requiredDropCapabilities:
    - ALL
  # Allow core volume types.
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    # Assume that persistentVolumes set up by the cluster admin are safe to use.
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    # Require the container to run without root privileges.
    rule: 'MustRunAsNonRoot'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  readOnlyRootFilesystem: false

 

Create the security policy by running:

$ kubectl create -f pod-policy.yaml

 

In order to use the Pod Security Policy created, the requesting user or target pod's service account must be authorized to use that policy. This can be achieved easily by using an RBAC Cluster Role.

The following Cluster Role definition allows to use the security policies specified.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: security-policies-authorization
rules:
- apiGroups: ['policy']
  resources: ['podsecuritypolicies']
  verbs: ['use']
  resourceNames:
  - <list of policies to authorize>

 

Then the Cluster Role is bound to the authorized users or service accounts.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cr-binding-security-policies
roleRef:
  kind: ClusterRole
  name: security-policies-authorization
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: <authorized service account name>

 

From now on, all Pods created using the service account configured in the previous Cluster Role Binding, will have the Pod Security Policy enforced.

Do you want to learn more?

https://dreamlab.net/en/education/trainings-schedule/

 

__________________

References

Sheila A. Berta
Head of Research at Dreamlab Technologies

Kubernetes Security Context and Policies

All blog posts