CKS: Edit Security Context

I have an exiting Deployment in a Kubernetes cluster with the following sepecifications:

  • Deployment name => nginx-deployment
  • Image => nginx:1.14.2
  • Replicas => 3

The tasks is to Modify Deployment frontend-deploy as follows:
• Start the container with a user ID of 1000
• Do not allow processes to gain privileges beyond those of their parents (disable allowPrivilegeEscalation).
• Load the container’s root filesystem as read-only (read-only permissions to the root file).

NOTE:

I have used the following YAML declaration file, but I keep getting errors

apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.14.2
name: nginx
securityContext:
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
resources: {}
status: {}

First, I literally cannot tell from your YAML – indentation is key here, and you’ve pasted your YAML in without putting it into

A code window
  That preserves the correct
     indentation.

Use the </> button to create the right markup (it’s based on Markdown) so I can tell whether your securityContext is like this:

containers:
- image: nginx:1.14.2
   name: nginx
   securityContext:
     runAsUser: 1000
     allowPrivilegeEscalation: false
     readOnlyRootFilesystem: true
  resources: {}

which would at least potentially be correct.


Task: Modify Deployment nginx-deployment the sec-ns namespace as follows
• Start the container with a user ID of 1000
• Do not allow processes to gain privileges beyond those of their parents (disable allowPrivilegeEscalation).
• Load the container’s root filesystem as read-only (read-only permissions to the root file).

.

  • This is the code block I made use of. I placed the security context under the container section.
  • The ( namespace: sec-ns ) and user with ID: 1000 already exist. However, I still keep getting errors.
  • Can you please check if there is anything I am missing out?
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-deployment
  name: nginx-deployment
  namespace: sec-ns
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-deployment
    spec:
      containers:
      - image: nginx:1.14.2
        name: nginx
        securityContext:
          runAsUser: 1000
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
        resources: {}
status: {}

Moreover, I also tried placing the SecurityContext under the specs sections just to see if it would work, but in both cases, it still didn’t work.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: sec-ns
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      securityContext:                    
        runAsUser: 1000                 
        allowPrivilegeEscalation: false   
        readOnlyRootFilesystem: true      
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

The allowed fields under securityContext change depending upon whether you’re in a container or directly under spec, so I"m not surprised the second example didn’t load.

When you say the first example “didn’t work” what do you mean? You mean that that the grader did not verify it? Or that there was some error printed somehow?

You might want to look at the log of the nginx container, BTW. If you set the root filesystem to read only, if something needs to write something, there will be trouble. You might need to create a temporary, writeable directory so that the container runs correctly.

So, when the Security Context was placed within the container:

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-deployment
  name: nginx-deployment
  namespace: sec-ns
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-deployment
    spec:
      containers:
      - image: nginx:1.14.2
        name: nginx
        securityContext:
          runAsUser: 1001
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
        resources: {}
status: {}

The deployment object was created, and the associated pods where also created. However, the STATUS of the pods were in a CrashLoopBackOff state.

controlplane ~ ➜  k -n sec-ns get pods
NAME                                READY   STATUS             RESTARTS      AGE
nginx-deployment-7d46769d7c-ww2ww   0/1     CrashLoopBackOff   3 (31s ago)   76s
nginx-deployment-7d46769d7c-b5p9b   0/1     CrashLoopBackOff   3 (24s ago)   76s
nginx-deployment-7d46769d7c-5zwnx   0/1     CrashLoopBackOff   3 (26s ago)   76s

And when I described the events in one of the pods:

controlplane ~ ➜  k -n sec-ns describe pods nginx-deployment-7d46769d7c-ww2ww 
Name:             nginx-deployment-7d46769d7c-ww2ww
Namespace:        sec-ns
Priority:         0
Service Account:  default
Node:             controlplane/192.5.2.6
Start Time:       Fri, 03 May 2024 03:19:15 +0000
Labels:           app=nginx-deployment
                  pod-template-hash=7d46769d7c
Annotations:      <none>
Status:           Running
IP:               10.42.0.9
IPs:
  IP:           10.42.0.9
Controlled By:  ReplicaSet/nginx-deployment-7d46769d7c
Containers:
  nginx:
    Container ID:   containerd://c9dd166d427528c900a8a7c11578ad11d322b3ed5e095ee59716301e968515ef
    Image:          nginx:1.14.2
    Image ID:       docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1
      Started:      Fri, 03 May 2024 03:25:02 +0000
      Finished:     Fri, 03 May 2024 03:25:02 +0000
    Ready:          False
    Restart Count:  6
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-rvz9k (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True 
  Initialized                 True 
  Ready                       False 
  ContainersReady             False 
  PodScheduled                True 
Volumes:
  kube-api-access-rvz9k:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason     Age                    From               Message
  ----     ------     ----                   ----               -------
  Normal   Scheduled  6m48s                  default-scheduler  Successfully assigned sec-ns/nginx-deployment-7d46769d7c-ww2ww to controlplane
  Normal   Pulling    6m48s                  kubelet            Pulling image "nginx:1.14.2"
  Normal   Pulled     6m44s                  kubelet            Successfully pulled image "nginx:1.14.2" in 3.626s (3.626s including waiting)
  Normal   Pulled     5m21s (x4 over 6m43s)  kubelet            Container image "nginx:1.14.2" already present on machine
  Normal   Created    5m21s (x5 over 6m44s)  kubelet            Created container nginx
  Normal   Started    5m21s (x5 over 6m44s)  kubelet            Started container nginx
  Warning  BackOff    100s (x26 over 6m41s)  kubelet            Back-off restarting failed container nginx in pod nginx-deployment-7d46769d7c-ww2ww_sec-ns(0b57761f-d1d6-4b8f-8521-33504045ef53)

The logs of one of the pods:

controlplane ~ ➜  k -n sec-ns logs nginx-deployment-7d46769d7c-ww2ww 
2024/05/03 03:25:02 [warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
2024/05/03 03:25:02 [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)

The warnings you can ignore. Looks like the process cannot write to /var/cache/nginx. You’ll need to make that write possible by using emptyDir or some similar trick, it would appear.

As suggested, I decided to try an emptyDir volume approach using the following code:

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-deployment
  name: nginx-deployment
  namespace: sec-ns
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-deployment
    spec:
      containers:
      - image: nginx:1.14.2
        name: nginx
        securityContext:
          runAsUser: 1001
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
        volumeMounts:
        - name: readonly-volume
          mountPath: /var/cache/nginx
          readOnly: true
      volumes:
      - name: readonly-volume
        emptyDir: {}

However, I still keep getting this errors from the logs:

controlplane ~ ➜  k -n sec-ns get pods 
NAME                               READY   STATUS   RESTARTS      AGE
nginx-deployment-c6fffd664-nrlq4   0/1     Error    2 (25s ago)   28s
nginx-deployment-c6fffd664-zfqvn   0/1     Error    2 (26s ago)   28s
nginx-deployment-c6fffd664-5l45n   0/1     Error    2 (25s ago)   28s

controlplane ~ ➜  k -n sec-ns logs nginx-deployment-c6fffd664-nrlq4 
2024/05/03 03:54:30 [warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
2024/05/03 03:54:30 [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)

You’re close; you don’t want that cache volume to be readOnly – nginx needs to write to it.

I am using this code block and everything seems to be working okay…

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-deployment
  name: nginx-deployment
  namespace: sec-ns
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-deployment
    spec:
      containers:
      - image: nginx:1.14.2
        name: nginx
        securityContext:
          runAsUser: 1001
          allowPrivilegeEscalation: false
        volumeMounts:
        - name: readonly-volume
          mountPath: /var/cache/nginx
        - name: nginx-root
          mountPath: /var/run/nginx.pid
          readOnly: true
      volumes:
      - name: readonly-volume
        emptyDir: {}
      - name: nginx-root
        emptyDir:
          medium: "Memory"

And here is the running deployments and pods

controlplane ~ ➜  k -n sec-ns get deployments.apps,pods
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           12s

NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-8466cbb4cd-x4k6d   1/1     Running   0          12s
pod/nginx-deployment-8466cbb4cd-bc8qv   1/1     Running   0          12s
pod/nginx-deployment-8466cbb4cd-zfwxt   1/1     Running   0          12s

controlplane ~ ➜  k -n sec-ns get deployments.apps,pods
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           15s

NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-8466cbb4cd-x4k6d   1/1     Running   0          15s
pod/nginx-deployment-8466cbb4cd-bc8qv   1/1     Running   0          15s
pod/nginx-deployment-8466cbb4cd-zfwxt   1/1     Running   0          15s

You’re definitely getting the idea. I think that the nginx.pid file needs to be writable, but in any case, you now know why it wasn’t starting. Bravo!

Following what you just said about allowing the the nginx.pid file to be writeable. However, the requirements for the question I was solving stated that

Task: Modify Deployment nginx-deployment the sec-ns namespace as follows
• Start the container with a user ID of 1001
• Do not allow processes to gain privileges beyond those of their parents (disable allowPrivilegeEscalation).
Load the container’s root filesystem as read-only (read-only permissions to the root file).

Please will my code block satisfy the above requirement if I set nginx.pid file to be writeable.

No, it’s consistent. The root file system is indeed read only. But you can mount writeable directories into the root file system so that the program works. As you’ve seen, nginx won’t load unless it can write its run-file and its cache. So you mount those. But everything else is read-only, as it should be.

For comparison, here’s my version of your final file:

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-deployment
  name: nginx-deployment
  namespace: sec-ns
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-deployment
    spec:
      volumes:
        - name: cache
          emptyDir: {}
        - name: run
          emptyDir: {}
      containers:
      - image: nginx:1.14.2
        name: nginx
        securityContext:
          runAsUser: 1000
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
        resources: {}
        volumeMounts:
          - name: cache
            mountPath: /var/cache/nginx
          - name: run
            mountPath: /var/run
status: {}

You can check it in the lab, and will see that it passes.

Thanks so much for for your explanation. The concepts is lots clearer to me now. I am super grateful. This is my first time of using the kodekloud community plaform. It’s just amazing.