Question 16: Docker Image Attack Surface

Problem Statement

Solve this question on: ssh cks7262

There is a Deployment image-verify in Namespace team-blue which runs image registry.killer.sh:5000/image-verify:v1. DevSecOps has asked you to improve this image by:

  • Changing the base image to alpine:3.12
  • Not installing curl
  • Updating nginx to use the version constraint >=1.18.0
  • Running the main process as user myuser

Do not add any new lines to the Dockerfile, just edit existing ones. The file is located at /opt/course/16/image/Dockerfile.

Tag your version as v2. You can build, tag and push using:

cd /opt/course/16/image
podman build -t registry.killer.sh:5000/image-verify:v2 .
podman run registry.killer.sh:5000/image-verify:v2 # to test your changes
podman push registry.killer.sh:5000/image-verify:v2

Make the Deployment use your updated image tag v2.

Ensure to run podman as user candidate and not root

Solution

Step 1: Examine Current Dockerfile

First, let's look at the current Dockerfile and make a backup:

➜ ssh cks7262

➜ candidate@cks7262:~# cd /opt/course/16/image

➜ candidate@cks7262:/opt/course/16/image$ cp Dockerfile Dockerfile.bak

➜ candidate@cks7262:/opt/course/16/image$ cat Dockerfile
FROM alpine:3.4
RUN apk update && apk add vim curl nginx=1.10.3-r0
RUN addgroup -S myuser && adduser -S myuser -G myuser
COPY ./run.sh run.sh
RUN ["chmod", "+x", "./run.sh"]
USER root
ENTRYPOINT ["/bin/sh", "./run.sh"]

Let's also check the run.sh script:

➜ candidate@cks7262:/opt/course/16/image$ cat run.sh
while true; do date; id; echo; sleep 1; done
Step 2: Update Dockerfile

Update the Dockerfile according to the requirements:

# /opt/course/16/image/Dockerfile
FROM alpine:3.12
RUN apk update && apk add vim nginx>=1.18.0
RUN addgroup -S myuser && adduser -S myuser -G myuser
COPY ./run.sh run.sh
RUN ["chmod", "+x", "./run.sh"]
USER myuser
ENTRYPOINT ["/bin/sh", "./run.sh"]

Changes made:

  • Changed base image to alpine:3.12
  • Removed curl from apk add
  • Updated nginx version constraint to >=1.18.0
  • Changed USER from root to myuser

Step 3: Build and Test the New Image

Build the new image with tag v2:

➜ candidate@cks7262:/opt/course/16/image$ podman build -t registry.killer.sh:5000/image-verify:v2 .
STEP 1/7: FROM alpine:3.12
Resolved "alpine" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
Trying to pull docker.io/library/alpine:3.12...
Getting image source signatures
Copying blob 1b7ca6aea1dd done   | 
Copying config 24c8ece58a done   | 
Writing manifest to image destination
STEP 2/7: RUN apk update && apk add vim nginx>=1.18.0
fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/community/x86_64/APKINDEX.tar.gz
v3.12.12-52-g800c17231ad [http://dl-cdn.alpinelinux.org/alpine/v3.12/main]
v3.12.12-52-g800c17231ad [http://dl-cdn.alpinelinux.org/alpine/v3.12/community]
OK: 12767 distinct packages available
--> 87781619777d
STEP 3/7: RUN addgroup -S myuser && adduser -S myuser -G myuser
--> ae553aeea607
STEP 4/7: COPY ./run.sh run.sh
--> 943d90848b52
STEP 5/7: RUN ["chmod", "+x", "./run.sh"]
--> 224656b3ddd8
STEP 6/7: USER myuser
--> 48de19088ba3
STEP 7/7: ENTRYPOINT ["/bin/sh", "./run.sh"]
COMMIT registry.killer.sh:5000/image-verify:v2
--> 09516fa460aa
Successfully tagged registry.killer.sh:5000/image-verify:v2

Test the new image locally:

➜ candidate@cks7262:/opt/course/16/image$ podman run registry.killer.sh:5000/image-verify:v2
Sun Sep  8 12:11:30 UTC 2024
uid=101(myuser) gid=102(myuser) groups=102(myuser)

Sun Sep  8 12:11:31 UTC 2024
uid=101(myuser) gid=102(myuser) groups=102(myuser)

Sun Sep  8 12:11:32 UTC 2024
uid=101(myuser) gid=102(myuser) groups=102(myuser)
Step 4: Push the New Image

Push the new image to the registry:

➜ candidate@cks7262:/opt/course/16/image$ podman push registry.killer.sh:5000/image-verify:v2
Getting image source signatures
Copying blob 6a1c1a1200d3 done   | 
Copying blob fd5841c2ff0f done   | 
Copying blob 761b8fb2b1d2 skipped: already exists  
Copying blob aed9d43cb02e done   | 
Copying blob 1ad27bdd166b done   | 
Copying config 09516fa460 done   | 
Writing manifest to image destination
Step 5: Update the Deployment

Update the Deployment to use the new image:

➜ candidate@cks7262:~# k -n team-blue edit deploy image-verify

Change the image tag from v1 to v2:

apiVersion: apps/v1
kind: Deployment
metadata:
...
spec:
...
  template:
...
    spec:
      containers:
      - image: registry.killer.sh:5000/image-verify:v2 # change
Step 6: Verify the Changes

Check the Pod logs to verify the process is running as myuser:

➜ candidate@cks7262:~# k -n team-blue logs -f -l id=image-verify
Sun Sep  8 12:13:12 UTC 2024
uid=101(myuser) gid=102(myuser) groups=102(myuser)

Sun Sep  8 12:13:13 UTC 2024
uid=101(myuser) gid=102(myuser) groups=102(myuser)

Sun Sep  8 12:13:14 UTC 2024
uid=101(myuser) gid=102(myuser) groups=102(myuser)

Verify that curl is not installed:

➜ candidate@cks7262:~# k -n team-blue exec image-verify-55fbcd4c9b-x2flc -- curl
error: Internal error occurred: Internal error occurred: error executing command in container: failed to exec in container: failed to start container process: exec: "curl": executable file not found in $PATH: unknown

Verify nginx version:

➜ candidate@cks7262:~# k -n team-blue exec image-verify-6cd88b645f-8d5cn -- nginx -v
nginx version: nginx/1.18.0
Summary of changes:
  • Updated base image to alpine:3.12
  • Removed curl from installed packages
  • Updated nginx to version >=1.18.0
  • Changed process to run as myuser instead of root
  • Built, tested, and pushed the new image as v2
  • Updated the Deployment to use the new image
Security Note: Running containers as non-root users and minimizing the attack surface by removing unnecessary packages are important security best practices in container security.
Back to Questions List