This is the second part of our series about running a workload without root on kubernetes. Check the first part here where we try to fix it from the start when building container images.

From image to runtime

This is about the second part of the journey to runtime:

Code to execution

Once we have decided the image that we want to run, we will have to define our deployment, which in kubernetes looks something like:

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-deployment

labels:

app: nginx

spec:

replicas: 3

selector:

matchLabels:

app: nginx

template:

metadata:

labels:

app: nginx

spec:

containers:

- name: nginx

image: nginx:1.15.12

ports:

- containerPort: 80

Using this template, we can specify the behavior of our workload. In essence we have declared that we want to create 3 instances of the image nginx:1.15.12 that will listen on port 80.

Without even trying to apply this manifest, we already know that some problems will arise if root is not allowed in kubernetes as we are trying to use port 80 which is a privileged port.

$> kubectl apply -f nginx.yaml

deployment.apps/nginx-deployment created

Is it working?

$> kubectl get pods

NAME READY STATUS RESTARTS AGE

nginx-deployment–2ccht 0/1 CreateContainerConfigError 0 10s

nginx-deployment-p9b2r 0/1 CreateContainerConfigError 0 11s

nginx-deployment-xn47q 0/1 CreateContainerConfigError 0 10s $> kubectl describe pod nginx-deployment-5b49cc9d49–2ccht

Error: container has runAsNonRoot and image will run as root

Can we fix it?

If we are creating the image, you already know how to fit it, otherwise take a look at the first part.

The first way to avoid this problem is to keep digging dockerhub and find a different (always use maintained and trustful sources) image that meets our requirements:

Instead of using the Docker Official Image with 10.000.000 downloads

You can find another image made by Nginx itself, with 100.000 downloads :(

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-deployment

labels:

app: nginx

spec:

replicas: 3

selector:

matchLabels:

app: nginx

template:

metadata:

labels:

app: nginx

spec:

containers:

- name: nginx

image: nginxinc/nginx-unprivileged:1.15.12

ports:

- containerPort: 8080

If we change the image and the port: voila!

$> kubectl get pods

nginx-deployment-dlkvn 1/1 Running 0 9s

nginx-deployment-tpdq8 1/1 Running 0 26s

nginx-deployment-zvkrp 1/1 Running 0 18s

What if there is no alternative image?

For example, we would like to get some analytics for our web page. Among all options, we choose Fathom because we respect our users privacy.

And they have a Docker Image :) but looking at their Dockerfile they are using root :(

There’s still hope as they are using port 8080 and Fathom is written in Go so it’s a simple binary which will make our life easier.

Let’s fix it using securityContext:

apiVersion: apps/v1

kind: Deployment

metadata:

name: fathom

spec:

replicas: 1

selector:

matchLabels:

app: fathom

template:

metadata:

labels:

app: fathom

spec:

securityContext:

runAsUser: 10001

containers:

- name: fathom

image: usefathom/fathom:latest

ports:

- containerPort: 8080

This annotations allows us to overwrite de user that will be executing the process, and once we apply this change we can check that everything is working perfectly (as non root):

$> kubectl logs fathom-59cdcc4768-vb6gx

level=info msg=”Fathom version 1.2.1+36-ge6d9f17“

level=info msg=”Configuration file: /etc/fathom/fathom.env”

level=info msg=”Connected to sqlite3 database: /db/fathom.sqlite”

level=info msg=”Applied 27 database migrations!”

level=info msg=”Server is now listening on :8080"

We removed non-relevant parts of the fathom deployment from this example, but you can find the full (non-persistent deployment) here.

In the next post we will talk about persistent storage for our non-root deployment.

If you want to play with this and more, you can use our platform k8spin.cloud ;)