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
- ig
$ kubectl gadget run ghcr.io/inspektor-gadget/gadget/advise_seccomp:latest [flags]
$ sudo ig 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
- ig
$ kubectl gadget run advise_seccomp:latest --map-fetch-count=0 --map-fetch-interval=0 --podname default-pod
$ sudo ig run advise_seccomp:latest --map-fetch-count=0 --map-fetch-interval=0 --containername mycontainer
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.
- kubectl gadget
- ig
# 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>
$ docker run --name mycontainer --rm -d docker.io/library/nginx
e6bc6e02989054f4984c04b7213d304767faffa295b277e8f36a5b2422409e18
$ curl $(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer)
<!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
- ig
$ 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"
}
]
}
$ sudo ig run advise_seccomp:latest --map-fetch-count=0 --map-fetch-interval=0 --containername mycontainer
// mycontainer
{
"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",
"umask",
"uname",
"utimensat",
"vfork",
"wait4",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Now, let's configure our container to use that policy.
- kubectl gadget
- ig
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>
....
For Docker, we can follow this intructions in Seccomp security profiles for Docker.
Save the profile in a file (removing the first line with the container name), and run the container as:
$ docker stop mycontainer
mycontainer
$ docker run --name mycontainer --rm -d --security-opt seccomp=<PATH-TO-profile.json> docker.io/library/nginx
921e6b33c6660c27d2fc2ca27ef817a66b893c345226d517bad2ebeb90083a07
$ curl $(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer)
<!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>
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 gadget
- ig
$ kubectl exec -it default-pod -- bash
bash: initialize_job_control: getpgrp failed: No such file or directory
command terminated with exit code 1
$ docker exec -it mycontainer bash
bash: initialize_job_control: getpgrp failed: No such file or directory
To finish, let's clean up the environment:
- kubectl gadget
- ig
$ kubectl delete -f confined.yaml
$ docker stop mycontainer
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 likecrun
. - 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.