← Back to list

Seccomp Filters Optimization Strategies on Linux

Seccomp filters are a powerful kernel-level security mechanism on Linux, allowing administrators to restrict the system calls that a…

Linux Guide · 2025-11-25 15:47 · 7 claps · 9.9 min read
#seccomp #filters #optimize #linux #security
Open on Medium ↗
Wiki topics: 🔓 · Open Source

Seccomp Filters Optimization Strategies on Linux

Seccomp filters are a powerful kernel-level security mechanism on Linux, allowing administrators to restrict the system calls that a process can make, reducing the attack surface and enhancing overall system security. This article delves into advanced strategies for optimizing seccomp filters, focusing on performance, maintainability, and adaptability in complex production environments, providing a guide for senior Linux engineers and cloud infrastructure specialists to enhance application security.

🏁 Introduction

Seccomp, short for secure computing mode, offers a way to sandbox processes by filtering system calls. Optimizing seccomp filters involves balancing security with application functionality, ensuring that necessary system calls are allowed while dangerous ones are blocked. By using advanced techniques, we can improve the efficiency and maintainability of these filters, minimizing the performance impact and operational overhead in dynamic environments.

🧠 Core Concepts

At its heart, seccomp operates by inspecting system calls made by a process and comparing them against a predefined policy. The policy specifies which system calls are allowed, and what action should be taken when a disallowed system call is encountered. Understanding the architecture and limitations of seccomp is a critical design consideration. Seccomp comes in two primary modes: strict mode and filter mode. Strict mode is the simplest, allowing only read, write, _exit, and sigreturn system calls. Filter mode, enabled through the seccomp_init and seccomp_rule_add family of functions in libseccomp, provides a more granular control over which system calls are permitted.

Seccomp filter policies are typically defined using the Berkeley Packet Filter or BPF syntax. BPF is a powerful, low-level language originally designed for network packet filtering, but it has been extended to allow inspection and filtering of system calls. The BPF bytecode is executed by the kernel, providing a highly efficient and secure mechanism for filtering system calls. When designing seccomp filters, it’s crucial to minimize the complexity of the BPF rules to avoid performance bottlenecks and maintain readability. Moreover, observability through metrics and monitoring becomes increasingly important.

1️⃣ Choosing the Right Mode for Seccomp Filters Optimization on Linux

Selecting between strict mode and filter mode depends on the application’s requirements. Strict mode is suitable for highly sandboxed environments where minimal functionality is required, offering the greatest security. Filter mode provides the flexibility to tailor the policy to the specific needs of the application, allowing for a more fine-grained control over system call access. Choosing the correct mode can significantly impact both security and application performance.

2️⃣ Understanding BPF Syntax for Seccomp Filters Optimization on Linux

BPF programs used for seccomp filters consist of a series of instructions that operate on a virtual machine within the kernel. Understanding the registers, memory model, and instruction set of the BPF virtual machine is essential for writing effective and efficient filters. BPF allows for complex conditional logic, enabling filters to make decisions based on the system call number, arguments, and even the process’s memory space.

3️⃣ Leveraging libseccomp for Seccomp Filters Optimization on Linux

libseccomp is a userspace library that simplifies the creation and management of seccomp filters. It provides a higher-level API for defining filter policies, abstracting away the complexity of BPF bytecode generation. libseccomp also offers features such as rule merging, allowing for modular filter policies that can be easily combined and reused. Using libseccomp promotes maintainability and reduces the risk of errors in BPF code.

⚙️ Comprehensive Code Examples

1️⃣ Basic Seccomp Filter Example for the ‘open’ System Call

This example demonstrates how to use libseccomp to create a seccomp filter that allows only the open system call with specific flags.

💡 Use Case: Restricting file access to read-only operations or specific directories.

⚠️ Risk Assessment: Incorrectly configured filters can block legitimate application functionality or create security loopholes.

🚀 Operational Value: Minimizes the attack surface by limiting the application’s ability to perform arbitrary file operations.

#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main() {
  scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
  if (!ctx) {
    perror("seccomp_init failed");
    return 1;
  }

  int rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_load(ctx);
  if (rc < 0) {
    perror("seccomp_load failed");
    seccomp_release(ctx);
    return 1;
  }

  seccomp_release(ctx);
  printf("Seccomp filter loaded successfully\n");
  return 0;
}

This C code initializes a seccomp context with a default action to kill the process if a disallowed system call is made. It then adds a rule to allow the open system call. The seccomp_load function loads the filter into the kernel, and the program concludes by releasing the seccomp context. This example can be compiled with: gcc -o seccomp_example seccomp_example.c -lseccomp.

2️⃣ Allowing ‘read’ and ‘write’ System Calls using Seccomp on Linux

This example creates a seccomp filter that allows both the read and write system calls, enabling basic input and output operations.

💡 Use Case: Essential for applications that need to read data from and write data to files or network sockets.

⚠️ Risk Assessment: Allowing unrestricted read and write can still pose security risks if the application is vulnerable to exploits that leverage these calls.

🚀 Operational Value: Provides the fundamental capabilities needed for most applications to interact with the operating system.

#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main() {
  scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
  if (!ctx) {
    perror("seccomp_init failed");
    return 1;
  }

  int rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed for read");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed for write");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_load(ctx);
  if (rc < 0) {
    perror("seccomp_load failed");
    seccomp_release(ctx);
    return 1;
  }

  seccomp_release(ctx);
  printf("Seccomp filter loaded successfully with read and write\n");
  return 0;
}

This code extends the basic example by adding rules for both read and write system calls. These system calls are crucial for many applications, and this filter allows them while still providing a degree of security by restricting other system calls. This code should be compiled the same as above, with gcc -o seccomp_read_write seccomp_read_write.c -lseccomp.

3️⃣ Implementing a Seccomp Filter to Restrict Network System Calls

This example restricts network-related system calls, useful for isolating applications that shouldn’t initiate network connections.

💡 Use Case: Isolating processes that handle sensitive data and should not communicate over the network.

⚠️ Risk Assessment: Overly restrictive network filters can prevent necessary communication, leading to application failures.

🚀 Operational Value: Reduces the risk of data exfiltration and unauthorized network access.

#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/seccomp.h>
#include <sys/syscall.h>

int main() {
  scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
  if (!ctx) {
    perror("seccomp_init failed");
    return 1;
  }

  // Blacklist network-related syscalls
  int rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(socket), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed for socket");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(connect), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed for connect");
    seccomp_release(ctx);
    return 1;
  }
   rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(bind), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed for bind");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_load(ctx);
  if (rc < 0) {
    perror("seccomp_load failed");
    seccomp_release(ctx);
    return 1;
  }

  seccomp_release(ctx);
  printf("Seccomp filter loaded successfully, blocking network syscalls\n");
  return 0;
}

This code snippet demonstrates how to block specific network-related system calls such as socket, connect and bind, preventing the application from creating or connecting to network sockets. Instead of killing the process (SCMPACTKILL), it returns an EACCES error to the application. The compilation command is the same as above: gcc -o seccomp_network_block seccomp_network_block.c -lseccomp.

4️⃣ Creating a Seccomp Filter that Logs Blocked System Calls

This example enhances seccomp by logging blocked system calls, improving observability and debugging capabilities.

💡 Use Case: Monitoring application behavior and identifying unexpected system call usage.

⚠️ Risk Assessment: Excessive logging can impact performance and create storage issues if not managed properly.

🚀 Operational Value: Facilitates incident response and security auditing by providing detailed information about blocked system calls.

#define _GNU_SOURCE
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <signal.h>

void signal_handler(int sig) {
  if (sig == SIGSYS) {
    long syscall_nr = syscall(SYS_seccomp, SECCOMP_GET_DATA);
    fprintf(stderr, "Seccomp: Illegal syscall %ld blocked\n", syscall_nr);
    exit(1);
  }
}

int main() {
  signal(SIGSYS, signal_handler);

  scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_TRAP);
  if (!ctx) {
    perror("seccomp_init failed");
    return 1;
  }

  int rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed for read");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
  if (rc < 0) {
    perror("seccomp_rule_add failed for write");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_load(ctx);
  if (rc < 0) {
    perror("seccomp_load failed");
    seccomp_release(ctx);
    return 1;
  }

  seccomp_release(ctx);
  printf("Seccomp filter loaded successfully with logging.\n");

  syscall(SYS_getpid); // This will be blocked and logged
  return 0;
}

This example uses SCMP_ACT_TRAP to trigger a signal when a blocked system call is encountered. A signal handler is then used to log the system call number. This approach provides detailed insights into which system calls are being blocked. Compile this example with gcc -o seccomp_logging seccomp_logging.c -lseccomp. Note that this requires glibc extensions and needs to be compiled with the _GNU_SOURCE flag.

5️⃣ Applying Argument-Based Filtering to Seccomp

This advanced example demonstrates argument-based filtering, restricting system calls based on their arguments.

💡 Use Case: Fine-grained control over system calls, allowing specific operations while blocking others based on input.

⚠️ Risk Assessment: Complex argument filters can be difficult to maintain and may introduce performance overhead.

🚀 Operational Value: Enhanced security by limiting the scope of allowed system calls.

#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>

int main() {
  scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
  if (!ctx) {
    perror("seccomp_init failed");
    return 1;
  }

  // Allow open only with O_RDONLY flag
  int rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
                             SCMP_A0(SCMP_CMP_EQ, O_RDONLY));
  if (rc < 0) {
    perror("seccomp_rule_add failed for open");
    seccomp_release(ctx);
    return 1;
  }

  rc = seccomp_load(ctx);
  if (rc < 0) {
    perror("seccomp_load failed");
    seccomp_release(ctx);
    return 1;
  }

  seccomp_release(ctx);
  printf("Seccomp filter loaded successfully, allowing open with O_RDONLY.\n");
  int fd = open("test.txt", O_RDONLY); // This will succeed
  if (fd < 0) {
      perror("Open failed (expected if not O_RDONLY)");
  } else {
      close(fd);
  }
  fd = open("test.txt", O_WRONLY);  // This will fail
  if (fd < 0) {
      perror("Open failed as expected");
  } else {
      close(fd); //This should not be reached
  }
  return 0;
}

This example demonstrates argument-based filtering by only allowing the open system call when the O_RDONLY flag is used. This restricts the application to read-only file access. Compile with gcc -o seccomp_arg_filter seccomp_arg_filter.c -lseccomp.

6️⃣ Implementing a Seccomp Filter Using BPF Directly

This example demonstrates how to create a seccomp filter using raw BPF instructions, providing maximum flexibility and control.

💡 Use Case: Highly specialized filtering requirements that cannot be easily expressed using libseccomp.

⚠️ Risk Assessment: Writing raw BPF code is complex and error-prone, requiring a deep understanding of the BPF architecture.

🚀 Operational Value: Unparalleled control over system call filtering, enabling highly customized security policies.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <stddef.h>

int main() {
    struct sock_filter filter[] = {
        /* Validate architecture */
        { BPF_LD | BPF_W | BPF_ABS, 0, 0, offsetof(struct seccomp_data, arch) },
        { BPF_JMP | BPF_JEQ | BPF_K, 0, 5, AUDIT_ARCH_X86_64 },
        /* Load syscall number */
        { BPF_LD | BPF_W | BPF_ABS, 0, 0, offsetof(struct seccomp_data, nr) },
        /* Check syscall number (example: write) */
        { BPF_JMP | BPF_JEQ | BPF_K, 0, 1, SYS_write },
        { BPF_RET | BPF_K, 0, 0, SECCOMP_RET_ALLOW },
        /* Default action: kill */
        { BPF_RET | BPF_K, 0, 0, SECCOMP_RET_KILL },
    };

    struct sock_fprog prog = {
        .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
        .filter = filter,
    };

    if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog) == -1) {
        perror("seccomp");
        return 1;
    }

    printf("Seccomp filter loaded successfully, allowing only write syscall.\n");
    write(1, "Hello, seccomp!\n", 15); // This will succeed
    syscall(SYS_getpid); // This will be blocked and kill the process
    return 0;
}

This code demonstrates setting a seccomp filter using raw BPF instructions. It allows only the write system call. Compiling this code requires including <linux/seccomp.h> and <linux/filter.h>. Compile with: gcc -o seccomp_bpf seccomp_bpf.c. Note that this example requires a good understanding of BPF and the seccomp data structure.

7️⃣ Using Seccomp with Docker for Container Security

This example shows how to use seccomp profiles with Docker to enhance container security.

💡 Use Case: Securing containerized applications by limiting the system calls available to the container.

⚠️ Risk Assessment: Incorrectly configured seccomp profiles can break container functionality or introduce security vulnerabilities.

🚀 Operational Value: Provides an additional layer of security for containerized applications, reducing the attack surface.

To use seccomp with Docker, you can create a JSON file that defines the allowed and disallowed system calls.

Example seccomp-profile.json:

{
  "defaultAction": "SCMP_ACT_KILL",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_X86",
    "SCMP_ARCH_X32"
  ],
  "syscalls": [
    {
      "names": [
        "read",
        "write",
        "_exit",
        "close",
        "fstat",
        "lstat",
        "poll",
        "lseek",
        "mmap",
        "mprotect",
        "munmap",
        "brk",
        "rt_sigaction",
        "rt_sigprocmask",
        "rt_sigreturn",
        "ioctl",
        "pread64",
        "pwrite64",
        "readv",
        "writev",
        "access",
        "pipe",
        "dup2",
        "set_robust_list",
        "gettid",
        "getpid",
        "getuid",
        "geteuid",
        "getgid",
        "getegid",
        "fcntl",
        "futex",
        "clock_gettime",
        "exit_group"
      ],
      "action": "SCMP_ACT_ALLOW",
      "args": []
    }
  ]
}

To run a container with this seccomp profile:

docker run --security-opt seccomp:seccomp-profile.json your_image

This command tells Docker to use the seccomp-profile.json file to filter system calls made by the container. Docker’s seccomp integration makes it easy to apply seccomp profiles to containers without modifying the application code.

8️⃣ Building a Custom Tool for Seccomp Profile Generation

This example shows the creation of a basic tool that helps to generate seccomp profiles based on observed syscall usage.

💡 Use Case: Automating the creation of seccomp profiles tailored to specific applications.

⚠️ Risk Assessment: The tool must accurately capture syscall usage to avoid creating overly restrictive or permissive profiles.

🚀 Operational Value: Simplifies seccomp profile management, reducing the manual effort required to create and maintain secure profiles.

#!/usr/bin/env python3

import subprocess
import json

def get_syscalls(pid):
    """
    Uses `strace` to collect syscalls for a given PID.
    """
    try:
        result = subprocess.run(['strace', '-c', '-p', str(pid)],
                                capture_output=True, text=True, check=True)
        output_lines = result.stderr.splitlines()
        syscalls = []
        for line in output_lines:
            if "% time" in line and "syscalls" not in line:
                parts = line.split()
                syscall = parts[3]
                syscalls.append(syscall)
        return syscalls
    except subprocess.CalledProcessError as e:
        print(f"Error running strace: {e}")
        return None

def generate_seccomp_profile(syscalls):
    """
    Generates a seccomp profile in JSON format from a list of syscalls.
    """
    profile = {
        "defaultAction": "SCMP_ACT_KILL",
        "architectures": [
            "SCMP_ARCH_X86_64",
            "SCMP_ARCH_X86",
            "SCMP_ARCH_X32"
        ],
        "syscalls": [
            {
                "names": syscalls,
                "action": "SCMP_ACT_ALLOW",
                "args": []
            }
        ]
    }
    return json.dumps(profile, indent=2)

if __name__ == "__main__":
    pid = input("Enter the PID of the process to trace: ")
    syscalls = get_syscalls(pid)

    if syscalls:
        profile = generate_seccomp_profile(syscalls)
        print(profile)
    else:
        print("Failed to collect syscalls.")

This Python script uses strace to monitor a process and collect the syscalls it makes. It then generates a seccomp profile allowing only those syscalls, setting the default action to kill for all others. Before running this script, ensure strace is installed. Run the script by executing python3 seccomp_profile_generator.py.

🧩 Conclusion

Optimizing seccomp filters is essential for enhancing the security of Linux systems. By understanding the core concepts of seccomp, BPF, and libseccomp, and by leveraging advanced techniques such as argument-based filtering and logging, administrators can create highly effective and maintainable security policies. Remember that continuous monitoring and adaptation are crucial for maintaining the effectiveness of seccomp filters over time. Properly designed seccomp filters significantly reduce the attack surface and improve the overall security posture of applications running on Linux.


메타데이터
post_id
fbda31875a4e
slug
seccomp-filters-optimization-strategies-on-linux-fbda31875a4e
url
https://medium.com/@linuxgd/seccomp-filters-optimization-strategies-on-linux-fbda31875a4e
canonical_url
https://medium.com/@linuxgd/seccomp-filters-optimization-strategies-on-linux-fbda31875a4e
author_url
https://medium.com/@linuxgd
status
ok
fetched_at
2026-08-24 06:47:05