Skip to main content
Version: latest

advise_seccomp

The seccomp profile advisor gadget records syscalls that are issued in a specified container, and then uses this information to generate the corresponding seccomp profile.

Getting started

$ kubectl gadget run ghcr.io/inspektor-gadget/gadget/advise_seccomp:latest [flags]

Flags

No Flags.

Guide

We need to start the advise_seccomp gadget before running our workload, so it's able to capture all the syscalls the container uses.

$ kubectl gadget run advise_seccomp:latest --map-fetch-count=0 --map-fetch-interval=0 --podname default-pod

The --map-fetch-count=0 --map-fetch-interval=0 parameters indicate the policy will be generated once and only when the gadget is stopped.

Then, start our application and interact with it to be sure it generates all the syscalls it needs to work.

# unconfined.yaml
apiVersion: v1
kind: Pod
metadata:
name: default-pod
labels:
app: default-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: test-container
image: docker.io/library/nginx:latest
$ kubectl apply -f https://raw.githubusercontent.com/inspektor-gadget/inspektor-gadget/refs/heads/main/docs/gadgets/files/seccomp-unconfined.yaml

Use port-forward to access the server from the client machine

$ kubectl -n default port-forward default-pod 3000:80 &

$ curl localhost:3000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Now, go back and stop the gadget. It'll print to the terminal the seccomp policy for all container running on the system:

$ kubectl gadget run advise_seccomp:latest --map-fetch-count=0 --map-fetch-interval=0 --podname default-pod
^C
// test-container
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"accept4",
"access",
"arch_prctl",
"bind",
"brk",
"capget",
"capset",
"chdir",
"chown",
"clone",
"close",
"connect",
"dup2",
"epoll_create",
"epoll_ctl",
"epoll_pwait",
"epoll_wait",
"eventfd2",
"execve",
"exit_group",
"faccessat2",
"fadvise64",
"fchdir",
"fchown",
"fcntl",
"fgetxattr",
"fsetxattr",
"fstat",
"fstatfs",
"futex",
"getcwd",
"getdents64",
"getegid",
"geteuid",
"getgid",
"getpid",
"getppid",
"getrandom",
"gettid",
"getuid",
"io_setup",
"ioctl",
"listen",
"lseek",
"mkdir",
"mmap",
"mprotect",
"munmap",
"nanosleep",
"newfstatat",
"openat",
"pipe2",
"prctl",
"pread64",
"prlimit64",
"pwrite64",
"read",
"recvfrom",
"recvmsg",
"rename",
"rseq",
"rt_sigaction",
"rt_sigprocmask",
"rt_sigreturn",
"rt_sigsuspend",
"sched_getaffinity",
"sendfile",
"sendmsg",
"set_robust_list",
"set_tid_address",
"setgid",
"setgroups",
"setsockopt",
"setuid",
"sigaltstack",
"socket",
"socketpair",
"statfs",
"syscall_1f4",
"sysinfo",
"tgkill",
"umask",
"uname",
"utimensat",
"vfork",
"wait4",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}

Now, let's configure our container to use that policy.

For Kubernetes, we can follow Restrict a Container's Syscalls with seccomp.

Copy the profile to the node:

$ minikube cp profile.json /var/lib/kubelet/seccomp/profile.json

And then deploy a pod using that profile:

# confined.yaml
apiVersion: v1
kind: Pod
metadata:
name: default-pod
labels:
app: default-pod
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profile.json
containers:
- name: test-container
image: docker.io/library/nginx:latest
$ kubectl delete -f https://raw.githubusercontent.com/inspektor-gadget/inspektor-gadget/refs/heads/main/docs/gadgets/files/seccomp-unconfined.yaml
$ kubectl apply -f https://raw.githubusercontent.com/inspektor-gadget/inspektor-gadget/refs/heads/main/docs/gadgets/files/seccomp-confined.yaml

$ kubectl -n default port-forward default-pod 3000:80 &

$ curl localhost:3000
<!DOCTYPE html>
<html>
....

If you try to open a bash to the pod, you'll get an error as it tries to execute syscall not allowed by the profile.

$ kubectl exec -it default-pod -- bash
bash: initialize_job_control: getpgrp failed: No such file or directory
command terminated with exit code 1

To finish, let's clean up the environment:

$ kubectl delete -f confined.yaml

Limitations:

  • The gadget generates a profile for each container, if you're running multiple instances of a container (by using a ReplicaSet or DaemonSet), you'll need to combine the profiles manually.
  • The current implementation relies on the implementation of runc to detect when to start recording syscalls, hence it might not work well with other container runtimes like crun.
  • This approach requires the workload to execute all the syscalls it might use when running the gadget. Please be sure you run the application long enough so all possible code paths needed to work are captured.