Lock Down Container Syscalls With Seccomp Profiles
Scenario
The security team is rolling out seccomp in the beacon namespace. Seccomp limits which Linux system calls (syscalls) a container can make.
| Deployment | State |
|---|---|
beacon-api | Runs with seccomp explicitly turned off. |
beacon-worker | Has no seccomp settings. 2 replicas, one on each worker node. |
beacon-sync | Uses a custom profile, beacon-strict.json, and keeps crashing. |
Work on dev-machine as the default laborant user. The beacon Pods run only on the worker nodes, node-01 and node-02, because the control plane node cplane-01 is tainted. From dev-machine, copy files to a worker node with scp and log in with ssh. laborant can use sudo on the nodes.
Task
- Make the
beacon-apiDeployment in thebeaconnamespace run with the seccomp profile that the container runtime provides. - Apply the profile
/home/laborant/seccomp/no-chmod.jsonto thebeacon-workerDeployment in thebeaconnamespace at container level, on itsworkercontainer, not at Pod level. Both replicas must run with it, and the profile must be available on both worker nodes. - Fix the
beacon-syncDeployment in thebeaconnamespace so that it runs:- Keep it on the
beacon-strict.jsonprofile. - Unblock only
mkdirandmkdirat. Every other syscall in the profile must stay blocked. - Update the profile on both worker nodes.
- Keep it on the
Do not change the container images or commands, and do not run any container as privileged.
Hint 1 | Where seccomp is set in a Pod
Seccomp is part of the securityContext, at Pod level or container level:
securityContext:
seccompProfile:
type: <type>
localhostProfile: <path> # used by one of the types only
kubectl explain pod.spec.securityContext.seccompProfile
Hint 2 | Checking whether a container is filtered
The kernel reports the seccomp mode of every process:
kubectl exec <pod> -n <namespace> -- grep Seccomp /proc/1/status
0 means no filter, 2 means a filter is active.
Hint 3 | Where custom profiles live
In this cluster, custom profiles are kept on each node in:
/var/lib/kubelet/seccomp/profiles/
localhostProfile is relative to the kubelet's seccomp directory, /var/lib/kubelet/seccomp/, so a file at /var/lib/kubelet/seccomp/profiles/app.json is referenced as profiles/app.json. The directory belongs to root. When the kubelet cannot load a profile, the reason is in the Pod's events:
kubectl describe pod <pod> -n <namespace>
scp <file> <node>:<path>
ssh <node>
Hint 4 | Reading a profile
A profile is JSON: a defaultAction for every syscall, and a list of syscalls rules that override it. A program's error message names the operation that failed, and that operation is carried out by one or more syscalls:
kubectl logs <pod> -n <namespace>
The kubelet reads a profile when a container starts, so a container that is already running keeps the filter it started with.