How to set filesystem permissions on Volumes for non-root containers

3 min read | by Jordi Prats

As a best practice we should try run containers with the minimum privileges they require: If we want to run a container with a non-root user we need to specify the user we want to use with securityContext.runAsUser (unless the container is not already using a non-privileged user).

By doing so when working with Volumes we might get a Permission denied while accessing the container

We can reproduce this problem by running the following StatefulSet that will use a PersistentVolume for /test:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sts-test-volume-runasuser
spec:
  serviceName: default
  replicas: 1
  selector:
    matchLabels:
      component: test-volume
  volumeClaimTemplates:
  - metadata:
      name: test-volume
      labels:
        component: test-volume
    spec:
      accessModes: [ "ReadWriteOnce" ]
      volumeMode: Filesystem
      resources:
        requests:
          storage: 10Gi
  template:
    metadata:
      labels:
        component: test-volume
    spec:
      securityContext:
        runAsUser: 1000
      containers:
      - name: test-container
        image: "alpine:latest"
        command:
        - sleep
        - 24h
        volumeMounts:
          - mountPath: /test
            name: test-volume

You can find all the yaml files on the pet2cattle/kubernetes-volume-fsgroup repository on GitHub.

If we try to create a file on the Volume we are going to get a Permission denied error:

$ kubectl exec -it sts-test-volume-runasuser-0 -n test -- touch /test/permissions
touch: /test/permissions: Permission denied
command terminated with exit code 1

Because the Volume is mounted as root

$ kubectl exec -it sts-test-volume-runasuser-0 -n test -- ls -ld /test
drwxr-xr-x    3 root     root          4096 Feb 18 04:48 /test

To be able to access the volume we'll need to use the securityContext.fsGroup. By using it, all processes of the container are also part of the supplementary group we set (So it doesn't need to be the same as runAsGroup)

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sts-test-volume-runasuser-fsgroup
spec:
  serviceName: default
  replicas: 1
  selector:
    matchLabels:
      component: test-volume
  volumeClaimTemplates:
  - metadata:
      name: test-volume
      labels:
        component: test-volume
    spec:
      accessModes: [ "ReadWriteOnce" ]
      volumeMode: Filesystem
      resources:
        requests:
          storage: 10Gi
  template:
    metadata:
      labels:
        component: test-volume
    spec:
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
      containers:
      - name: test-container
        image: "alpine:latest"
        command:
        - sleep
        - 24h
        volumeMounts:
          - mountPath: /test
            name: test-volume

Now we are going to be allowed to create a file on the Volume:

$ kubectl exec -it sts-test-volume-runasuser-fsgroup-0 -n test -- touch /test/permissions
$ kubectl exec -it sts-test-volume-runasuser-fsgroup-0 -n test -- ls -l /test/permissions
-rw-r--r--    1 1000     1000             0 Feb 18 04:50 /test/permissions

We can check how the /test Volume group's is the to securityContext.fsGroup:

$ kubectl exec -it sts-test-volume-runasuser-fsgroup-0 -n test -- ls -ld /test
drwxrwsr-x    3 root     1000          4096 Feb 18 04:50 /test

Posted on 18/02/2022