KubernetesLXIII · Linux Security Controls in KubernetesLinux security controls
Custom seccomp profiles — Localhost and workload-specific rules
What you'll learn
- Author a custom seccomp profile in JSON
- Load the profile onto nodes via a DaemonSet or bootstrap script
- Use the `Localhost` profile type in a Pod
- Generate profiles using `seccomp-profile-auditor` or similar tools
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
A custom seccomp profile is a JSON file that
whitelists the syscalls a workload needs. The
Localhost profile type in Kubernetes references a
profile loaded onto the node at a well-known path.
This lesson covers the JSON profile format, the
authoring workflow, and the operational patterns.
The JSON profile format
A seccomp profile is a JSON document with three
sections: defaultAction, architectures, and
syscalls:
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["read", "write", "openat", "close", "fstat"],
"action": "SCMP_ACT_ALLOW"
}
]
}
The defaultAction is the action for syscalls not
listed: SCMP_ACT_ERRNO (return an error) or
SCMP_ACT_KILL (kill the process). architectures
limits the profile to specific CPU architectures.
syscalls is the whitelist.
flowchart LR
A[Process] --> B[Syscall]
B --> C{In whitelist?}
C -->|yes| D[SCMP_ACT_ALLOW]
C -->|no| E{defaultAction}
E -->|ERRNO| F[Return error]
E -->|KILL| G[SIGKILL]
The profile path
The kubelet loads profiles from
/var/lib/kubelet/seccomp/<profile-name>.json. The
profile name in the Pod spec maps to the file:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: legacy-app.json
# kubelet reads /var/lib/kubelet/seccomp/legacy-app.json
The profile must be present on every node where the Pod may run. The convention is a DaemonSet that ships the profile to every node:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: seccomp-profiles
namespace: kube-system
spec:
selector:
matchLabels:
name: seccomp-profiles
template:
metadata:
labels:
name: seccomp-profiles
spec:
containers:
- name: profiles
image: busybox
command: ["sleep", "infinity"]
volumeMounts:
- name: profiles
mountPath: /var/lib/kubelet/seccomp
readOnly: false
volumes:
- name: profiles
hostPath:
path: /var/lib/kubelet/seccomp
type: Directory
initContainers:
- name: install
image: myorg/seccomp-profiles:v1
command: ["cp", "-r", "/profiles/.", "/var/lib/kubelet/seccomp/"]
volumeMounts:
- name: profiles
mountPath: /var/lib/kubelet/seccomp
The DaemonSet copies the profiles to every node’s seccomp directory.
The authoring workflow
Three steps:
- Audit syscalls. Run the workload under
RuntimeDefault(orUnconfinedwith syscall tracing) to identify the syscalls the workload needs. - Author the profile. Create the JSON profile with the whitelist.
- Test. Run the workload under the custom profile; verify it starts and operates normally.
# 1. Audit syscalls
strace -f -e trace=syscall -p $(pidof myapp) 2>&1 | \
grep -oP '^[a-z_]+\(' | sort -u
# 2. Generate the profile from the trace
seccomp-profile-auditor \
--input trace.txt \
--output /var/lib/kubelet/seccomp/legacy-app.json
# 3. Test
kubectl apply -f pod-with-localhost-profile.yaml
A typical profile
A profile for a Java application that does network I/O:
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": [
"read", "write", "openat", "close", "fstat",
"lseek", "mmap", "mprotect", "munmap", "brk",
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
"ioctl", "select", "poll", "epoll_wait",
"socket", "connect", "accept", "bind", "listen",
"recvfrom", "sendto", "setsockopt", "getsockopt",
"clone", "execve", "exit_group", "exit",
"getpid", "getuid", "geteuid", "getgid", "getegid",
"setuid", "setgid", "setgroups",
"stat", "fstat", "lstat", "access", "readlink",
"gettimeofday", "clock_gettime",
"futex", "nanosleep"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
The profile allows ~50 syscalls (the common ones for a
network I/O application) and rejects all others via
SCMP_ACT_ERRNO.
Operational discipline
- Profile per workload. Each workload has its own profile; profiles are not shared unless the workloads are identical.
- Version-controlled profiles. Profiles are in Git; the DaemonSet is updated when a profile changes.
- Audit the profiles. A profile that allows
kexec_loadorrebootis a Critical finding. - Test before deploying. A workload under a custom profile is tested in a staging environment before production.
Production failure modes
- Profile not on every node. A Pod scheduled on a node without the profile is rejected. The fix is the DaemonSet.
- Profile too restrictive. The workload makes a
syscall not in the whitelist; the process is
killed. The fix is to add the syscall (or to use
RuntimeDefaultif the workload has no custom needs). - Profile too permissive. The profile allows
kexec_loadorreboot. The fix is to remove the syscall from the profile. - No audit of profiles. A profile is added but never reviewed. The fix is a CI check that compares the profile’s whitelist against the runtime’s safe baseline.
Cross-course references
- The Linux course covers the BPF filter mechanism that seccomp uses.
- The Observability course covers the audit log entries for seccomp violations.
Quiz
Knowledge check · 4 questions
Q1. What is the right `defaultAction` for a custom seccomp profile?
Q2. A `Localhost` seccomp profile is automatically distributed to every node by the kubelet.
Q3. Your custom seccomp profile is too restrictive. The workload is killed at startup with `bad syscall`. Walk the response.
The profile whitelists 30 syscalls. The workload needs 35. The 5 missing syscalls cause the workload to fail. The audit log shows `seccomp: blocked syscall` for each missing one.
Q4. What path does the kubelet read `Localhost` seccomp profiles from, and how do you ship a profile to every node?
Passing score: 75%. Answers are checked in this browser.
Production discipline
Custom seccomp profiles are the right primitive for
workloads with specific syscall needs. A defensible
seccomp programme uses RuntimeDefault for most
workloads, Localhost for the exceptions, and ships
profiles via a DaemonSet. The profiles are
version-controlled, audited for dangerous syscalls,
and tested before deployment. A cluster whose profiles
are all audited and shipped via a DaemonSet has a
syscall-surface security programme that is
auditable.