{"id":627,"date":"2025-04-01T13:37:59","date_gmt":"2025-04-01T13:37:59","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=627"},"modified":"2025-04-01T13:37:59","modified_gmt":"2025-04-01T13:37:59","slug":"seccomp-in-kubernetes","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=627","title":{"rendered":"Seccomp in Kubernetes: What It Is and How to Use It"},"content":{"rendered":"<p>In this blog, you will learn what Seccomp is and explore practical examples of using custom Seccomp profiles with Kubernetes.<\/p>\n<p>By the end of this blog, you will know:<\/p>\n<ol>\n<li>What is seccomp<\/li>\n<li>What is a seccomp profile<\/li>\n<li>How to use seccomp with Docker<\/li>\n<li>How to use seccomp with Kubernetes pods<\/li>\n<\/ol>\n<h2 id=\"what-is-seccomp\">What is Seccomp?<\/h2>\n<p>System calls are how user-space programs interact with the Linux kernel.<\/p>\n<p>Seccomp (Secure Computing Model) is a security layer in the Linux kernel introduced in 2005 that <strong>restricts the system calls<\/strong> a process can make (pre-container era).<\/p>\n<p>It may sound similar to Linux capabilities. However, Seccomp is more flexible as it allows filtering individual system calls. (Usually, both are used)<\/p>\n<p>It essentially creates a sandbox that limits what actions a program can perform.<\/p>\n<p>A common example where Seccomp is used is in container runtimes (e.g., containerd, CRI-O, etc.).<\/p>\n<p>For example, <a href=\"https:\/\/devopscube.com\/what-is-docker\/\" rel=\"noreferrer\">Docker<\/a>, by default, applies a Seccomp filter to containers, which blocks a significant number of system calls not necessary for typical container operations.<\/p>\n<h2 id=\"how-does-seccomp-work\">How does Seccomp work?<\/h2>\n<p>Seccomp uses Linux\u2019s <strong>seccomp-bpf<\/strong> (Berkeley Packet Filter) mechanism that filters syscalls using predefined rules.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">BPF :&nbsp;<\/strong><\/b>Originally used for network packet filtering, BPF is now a general-purpose filtering mechanism in the Linux kernel.<\/div>\n<\/div>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/02\/image-62.png\" class=\"kg-image\" alt=\"Seccomp Workflow\" loading=\"lazy\" width=\"648\" height=\"605\"><\/figure>\n<ol>\n<li>The container makes syscalls directly to the kernel entry point<\/li>\n<li>The container runtime translates the seccomp profile JSON into a BPF program<\/li>\n<li>The runtime attaches this BPF program to the container&#8217;s process during container creation<\/li>\n<li>When the container makes syscalls, they get filtered by the seccomp BPF program<\/li>\n<li>Allowed syscalls proceed to kernel execution, while blocked ones return an error<\/li>\n<\/ol>\n<h2 id=\"seccomp-profile\">Seccomp Profile<\/h2>\n<p>To create a predefined&nbsp;<strong>seccomp<\/strong>&nbsp;rule, you need to define a&nbsp;<strong>seccomp profile<\/strong>&nbsp;in a JSON file.<\/p>\n<p>For example, here is a simple seccomp profile that allows&nbsp;<code>read<\/code>,&nbsp;<code>write<\/code>, and&nbsp;<code>exit<\/code>&nbsp;syscalls but blocks&nbsp;<code>chmod<\/code>&nbsp;syscall with a &#8220;Permission denied&#8221; error.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/02\/image-63.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"644\" height=\"527\"><\/figure>\n<p>Why Deny Everything by Default?<\/p>\n<p>It\u2019s safer to start by denying everything and explicitly allow only what is needed (whitelist approach). This reduces the risk of accidentally letting a dangerous syscall.<\/p>\n<h2 id=\"seccomp-contianers\">Seccomp &amp; Contianers<\/h2>\n<p>As you all know, the container is basically a sandboxed process, and seccomp plays a key role in that sandboxing.<\/p>\n<p>Container runtimes like containerd &amp; crio apply a default seccomp profile that blocks around 40+ system calls (e.g., mount, ptrace, reboot) unless explicitly disabled.<\/p>\n<p>Refer to this detailed&nbsp;<a href=\"https:\/\/github.com\/moby\/moby\/blob\/master\/profiles\/seccomp\/default.json?ref=devopscube.com\" rel=\"noreferrer\">seccomp profile&nbsp;used by Docker<\/a>.<\/p>\n<p>While default profiles provide basic security, you can also apply&nbsp;<strong>custom seccomp profiles<\/strong>&nbsp;based on your use case. <\/p>\n<p>Let\u2019s see how to do that.<\/p>\n<p>The following profile (<strong><code>block-mkdir.json<\/code><\/strong>) blocks the&nbsp;<code><strong>mkdirat<\/strong><\/code>&nbsp;syscall (the syscall used for the&nbsp;<code>mkdir<\/code>&nbsp;command).<\/p>\n<pre><code class=\"language-json\">{\n  \"defaultAction\": \"SCMP_ACT_ALLOW\",\n  \"syscalls\": [\n    {\n      \"names\": [\"mkdirat\"],\n      \"action\": \"SCMP_ACT_ERRNO\"\n    }\n  ]\n}<\/code><\/pre>\n<p>If I run a docker container using this seccomp profile, I will not be able to create a directory using the mkdir command. Here is an example<\/p>\n<pre><code class=\"language-bash\">$ docker run --rm -it --security-opt seccomp=block-mkdir.json busybox sh\n\n\/ # mkdir test\nmkdir: can't create directory 'test': Operation not permitted<\/code><\/pre>\n<h2 id=\"kubernetes-pod-seccomp\">Kubernetes Pod &amp; Seccomp<\/h2>\n<p>Now lets look at how to use Seccomp with Kubernetes.<\/p>\n<p>If you want to restrict or apply a specific profile to a container inside a Kubernetes pod, you can do so using&nbsp;<strong>securityContext<\/strong>.<\/p>\n<p>Kubernetes comes with a&nbsp;<strong><code>RuntimeDefault<\/code><\/strong>&nbsp;profile built into the cluster. It tells Kubernetes to use the default profile provided by the container runtime.<\/p>\n<p>For example,<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/02\/image-64.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"612\" height=\"473\"><\/figure>\n<p>You can implement&nbsp;<strong>custom seccomp profiles<\/strong>&nbsp;as well.<\/p>\n<p>For this, the profile should be present on all the worker nodes at the&nbsp;<code>\/var\/lib\/kubelet\/seccomp\/<\/code>&nbsp;location.<\/p>\n<p>Kubernetes does not provide any native mechanism to add seccomp profiles to the worker nodes. You need to add them to the nodes manually.<\/p>\n<p>For example, I have added the following profile to my worker nodes at&nbsp;<code>\/var\/lib\/kubelet\/seccomp\/block-mkdir.json<\/code>.<\/p>\n<p>This profile primarily blocks the&nbsp;<code>mkdir<\/code>&nbsp;syscall, similar to what we tried with Docker.<\/p>\n<pre><code class=\"language-json\">{\n  \"defaultAction\": \"SCMP_ACT_ALLOW\",\n  \"architectures\": [\n    \"SCMP_ARCH_X86_64\"\n  ],\n  \"syscalls\": [\n    {\n      \"names\": [\n        \"mkdir\",\n        \"mkdirat\"\n      ],\n      \"action\": \"SCMP_ACT_KILL\"\n    }\n  ]\n}<\/code><\/pre>\n<p>Now, you can implement this seccomp profile in a pod under the&nbsp;<strong>securityContext<\/strong>&nbsp;using&nbsp;<code>type: Localhost<\/code>&nbsp;and the profile path as shown below.<\/p>\n<pre><code class=\"language-YAML\">apiVersion: v1\nkind: Pod\nmetadata:\n  name: custom-seccomp-pod\nspec:\n  securityContext:\n    seccompProfile:\n      type: Localhost\n      localhostProfile: block-mkdir.json\n  containers:\n  - name: busybox\n    image: busybox\n    command: [ \"sh\", \"-c\", \"sleep 3600\" ]<\/code><\/pre>\n<p>This configuration applies the custom seccomp profile located at&nbsp;<code>\/var\/lib\/kubelet\/seccomp\/block-mkdir.json<\/code>&nbsp;to the container, blocking the&nbsp;<code>mkdir<\/code>&nbsp;syscall.<\/p>\n<p>Now, if you deploy the pod and try to create a directory from within the pod, you will get an error, as shown below.<\/p>\n<pre><code class=\"language-bash\">$ k exec -it custom-seccomp-pod -- sh\n\n\/ # mkdir logs\nBad system call (core dumped)\n\/ # <\/code><\/pre>\n<p><strong>&#8220;Bad system call (core dumped)&#8221;<\/strong>&nbsp;error happens because the applied seccomp profile blocks the&nbsp;<code>mkdir<\/code>&nbsp;syscall, preventing directory creation within the pod.<\/p>\n<blockquote><p><strong>Note<\/strong>: You can&#8217;t apply a seccomp profile to containers that run in Privileged mode. The Privileged flag essentially disables most security constraints, including seccomp filters.<\/p><\/blockquote>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Seccomp is a powerful security mechanism that helps restrict system calls in Linux-based containers, enhancing security in Docker and Kubernetes. <\/p>\n<p>Using default or custom seccomp profiles, you can customize syscall access and reduce security risks. <\/p>\n<p>Implementing seccomp effectively requires careful planning, but it significantly strengthens container security. <\/p>\n<p>If you have any questions, feel free to leave a comment!<\/p>\n<p>Want to Stay Ahead in DevOps &amp; Cloud? Join the Free Newsletter Below.<\/p>\n<p><!--kg-card-begin: html--><br \/>\n<iframe loading=\"lazy\" src=\"https:\/\/embeds.beehiiv.com\/2a495ef4-3de7-4600-8a0d-de5dc968b372\" data-test-id=\"beehiiv-embed\" width=\"100%\" height=\"320\" frameborder=\"0\" scrolling=\"no\" style=\"border-radius: 4px; border: 2px solid #e5e7eb; margin: 0; background-color: transparent;\"><\/iframe><br \/>\n<!--kg-card-end: html--><\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/seccomp-in-kubernetes\/\" target=\"_blank\" rel=\"noopener noreferrer\">Seccomp in Kubernetes: What It Is and How to Use It \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/seccomp-in-kubernetes\/<\/p>\n","protected":false},"author":1,"featured_media":628,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-627","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/627","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=627"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/627\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/628"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=627"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=627"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=627"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}