Seccomp
Seccomp profiles for Kubernetes pods and containers, hosting profiles on nodes via NCI, and securely implementing syscall restrictions.
General information
Seccomp (Secure computing mode) is a Linux kernel security mechanism that limits system calls from user space to the kernel. In Kubernetes, Seccomp profiles are applied to pods and containers to reduce the attack surface: a process inside a container receives only those kernel capabilities that are allowed by the selected profile.
A Seccomp profile is a JSON file in a format compatible with the OCI runtime specification. It specifies the default action, lists of system calls, and, if necessary, conditions on system call arguments.
Basic profile actions:
- SCMP_ACT_ALLOW — allow the call (white list).
- SCMP_ACT_ERRNO - return an error to the process for a blocked call.
- SCMP_ACT_LOG — allow the call, but record it in the log (audit).
- SCMP_ACT_KILL_PROCESS — kill the process.
- SCMP_ACT_KILL_THREAD or SCMP_ACT_KILL — terminate the thread.
- SCMP_ACT_TRAP — send the
SIGSYSsignal to the process. - SCMP_ACT_TRACE — notify the tracing process.
For universal profiles, SCMP_ACT_LOG, SCMP_ACT_ERRNO and SCMP_ACT_ALLOW are usually used.
The defaultAction parameter determines what to do with system calls that are not included in any rules. For example:
SCMP_ACT_LOGis suitable for auditing: calls are executed but recorded in the log.SCMP_ACT_ERRNOis suitable for a hard profile: unauthorized calls are blocked.
Blocking and audit logs are looked at on the node where the Pod is running, in /var/log/messages or /var/log/syslog by the keyword SECCOMP or by process name.
How Kubernetes applies Seccomp
The profile is specified in securityContext.seccompProfile. It can be set:
- for the entire Pod:
spec.securityContext.seccompProfile; - for a separate container:
spec.containers[*].securityContext.seccompProfile; - for the init container:
spec.initContainers[*].securityContext.seccompProfile; - for an ephemeral container:
spec.ephemeralContainers[*].securityContext.seccompProfile.
The container level profile takes precedence over the Pod level profile. If a container does not set its seccompProfile, it inherits the Pod profile.
Available values for seccompProfile.type:
| Value | Behavior |
|---|---|
RuntimeDefault | The default profile provided by the container runtime is applied. Such profiles are designed for secure settings without losing basic functionality, but may differ between CRI-O, containerd and different runtime versions. |
Localhost | The local JSON profile from the node is applied. On Linux, profiles are located relative to the /var/lib/kubelet/seccomp directory. The presence of the file is checked by the container runtime when creating a container. If the file does not exist, the container is not created and receives the error CreateContainerError. |
Unconfined | The load starts without Seccomp restrictions. |
You cannot apply the Seccomp profile to a Pod or container with privileged: true: privileged containers always run as Unconfined.
In new manifests, use securityContext.seccompProfile fields. The Seccomp annotations used in older versions of Kubernetes are no longer needed for the current configuration.
Basic option: RuntimeDefault
For most loads, it’s worth checking the RuntimeDefault profile first. It includes the restrictions set by the container runtime and does not require a separate JSON file to be placed on the nodes.
apiVersion: v1
kind: Pod
metadata:
name: runtime-default-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: hashicorp/http-echo:1.0
args:
- "-text=hello"
securityContext:
allowPrivilegeEscalation: false
If seccompDefault is enabled in the kubelet configuration or the kubelet is launched with the --seccomp-default flag, a Pod without an explicit profile will receive RuntimeDefault. If this behavior is not enabled, a Pod without seccompProfile runs as Unconfined.
To implement RuntimeDefault by default, enable the setting step by step: first on some nodes, then check the launch and main application scenarios. Some workloads may require less stringent restrictions. For such cases, use one of the options: explicitly specify Unconfined, prepare a separate Localhost profile, or run the workload on nodes without seccompDefault.
You can check the actually applied profile using container runtime, for example through crictl inspect on the node.
Creating a local profile via NCI
For a custom profile, create a JSON file and place it on Linux nodes. In the Shturval platform, this is done using the NCI (NodeConfigItem) resource. More information about creating and using NCI can be found on the Node Configuration page.
You can create an NCI by importing a manifest or in the GUI: Administration → Node Configuration. When creating an object, select kubernetes.io/os: linux in the node selector, select Files and directories in the custom sections, create a directory and place the JSON profile file in it.
Below is an example NCI that creates a directory /var/lib/kubelet/seccomp/ and a file /var/lib/kubelet/seccomp/myprofile.json.
apiVersion: node.shturval.tech/v1beta2
kind: NodeConfigItem
metadata:
name: seccomp-profile
spec:
files:
- path: /var/lib/kubelet/seccomp/
type: directory
owner: root
group: root
mode: "0755"
- path: /var/lib/kubelet/seccomp/myprofile.json
type: file
owner: root
group: root
mode: "0644"
content: |-
{
"defaultAction": "SCMP_ACT_LOG",
"architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
"syscalls": [
{
"names": ["arch_prctl", "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp"],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["add_key", "keyctl", "ptrace"],
"action": "SCMP_ACT_ERRNO"
}
]
}
nodeconfigselector:
kubernetes.io/os: linux
priority: 100
After applying NCI, check that the profile appears on the required nodes:
kubectl get nci seccomp-profile -o yaml
kubectl get nodes --selector kubernetes.io/os=linux
If necessary, check the file directly on the node using OS administration tools. Kubernetes does not store the contents of the Localhost profile in the Pod: when creating a container, runtime looks for the file on the node where the Pod was assigned.
Running workloads with the Seccomp profile
To apply a profile,specify type: Localhost and the file name in localhostProfile. The value of localhostProfile is set relative to /var/lib/kubelet/seccomp.
If the file is located as /var/lib/kubelet/seccomp/myprofile.json, specify:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: myprofile.json
If the file is located in a subdirectory of /var/lib/kubelet/seccomp/profiles/myprofile.json, specify localhostProfile: profiles/myprofile.json.
Example Pod with a local profile at the container level:
apiVersion: v1
kind: Pod
metadata:
name: nettool
namespace: namespacename
labels:
run: nettool
spec:
containers:
- name: nettool
image: r.shturval.tech/network-multitool:alpine-extra
securityContext:
seccompProfile:
type: Localhost
localhostProfile: myprofile.json
Example Pod, where the general profile is set at the Pod level and the individual container overrides it:
apiVersion: v1
kind: Pod
metadata:
name: seccomp-priority
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: hashicorp/http-echo:1.0
args:
- "-text=hello"
- name: custom
image: hashicorp/http-echo:1.0
args:
- "-text=custom"
securityContext:
seccompProfile:
type: Localhost
localhostProfile: myprofile.json
In the GUI, when creating or editing a Pod or Deployment, the profile is configured in the Security context block at the Pod or container level. For a local profile, choose to use a custom profile and provide a file name.
Creating a profile based on audit results
For application containers, it’s safer to move from observing to constraining:
- Create an audit profile with
defaultAction: SCMP_ACT_LOG. - Run the workload with this profile.
- Execute basic user and service application scripts.
- Collect system calls from node logs.
- Create a profile with
defaultAction: SCMP_ACT_ERRNOand allow only necessary calls throughSCMP_ACT_ALLOW. - Check startup, readiness/liveness tests, network calls, file handling, graceful shutdown and application version update.
Minimum audit profile:
{
"defaultAction": "SCMP_ACT_LOG"
}
An example of a strict profile that by default blocks calls and allows only those listed:
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 38,
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"accept4",
"bind",
"brk",
"close",
"epoll_ctl",
"epoll_wait",
"exit",
"exit_group",
"futex",
"getpid",
"gettid",
"mmap",
"munmap",
"openat",
"read",
"rt_sigaction",
"rt_sigprocmask",
"socket",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
This approach requires testing on a specific application. Audit logs do not guarantee that a single test run will capture all the system calls that an application might make in rare scenarios.
Diagnostics
| Symptom | Possible reason | What to check |
|---|---|---|
Pod is created, but the container does not start with CreateContainerError | A non-existent profile file was specified for Localhost | Check localhostProfile, the presence of the file on the node and the path relative to /var/lib/kubelet/seccomp. |
The container goes to CrashLoopBackOff | The profile is blocking a system call required by the process | Look at Pod events and node logs, temporarily enable auditing via SCMP_ACT_LOG, expand the list of allowed calls. |
| The profile does not limit the container | The container was started with privileged: true or Unconfined was specified | Check securityContext.privileged and the actually applied profile via runtime tools. |
| Different results on different nodes | Profile files, container runtime or runtime versions differ on nodes | Check the NCI, file location on each target node, and runtime version. |
kubectl describe pod <pod-name> -n <namespace>
kubectl get pod <pod-name> -n <namespace> -o yaml
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name>
For local profiles, additionally check which node the Pod is assigned to:
kubectl get pod <pod-name> -n <namespace> -o wide
Recommendations
- Start with
RuntimeDefault. This is a basic security hardening without the accompanying JSON profile. - Use
Localhostfor critical and well-studied workloads. A custom profile is useful when you need to explicitly limit the set of system calls for a particular application. - Go from auditing to blocking. First collect system calls with
SCMP_ACT_LOG, then move on toSCMP_ACT_ERRNOandSCMP_ACT_ALLOW. - Set the profile at the Pod level if it is common to all containers. Override the profile at the container level only for exceptions.
- Don’t use
Unconfinedwithout reason. This mode disables Seccomp limits for the load. - Don’t rely on Seccomp for privileged containers. A container with
privileged: truealways works likeUnconfined. - Test the profile on the full application scenario. A profile that is too strict can block legitimate calls after the container has successfully started.
- Monitor the same profiles on nodes. For
Localhost, the file must be available on every node that the Pod can access. - Be aware of runtime differences. The
RuntimeDefaultprofile and support for individual Seccomp actions may differ between container runtimes and their versions.