Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

seccomp: Restrict Syscalls for Security

By Kokil Thapa | Last reviewed: September 2026

Most production compromises do not stop at a single bug. Attackers chain syscalls like ptrace, mount, or unshare to move from a PHP flaw to host access. seccomp breaks that chain at the kernel. It uses BPF filters to allow only the syscalls your process needs. In my experience hardening web servers and VPS hosts in Nepal, seccomp delivers more real protection than many application-layer add-ons, with almost no runtime cost.

What is seccomp and how does it restrict syscalls for security?

Seccomp (Secure Computing Mode) is a Linux kernel feature. Every thread can run under a syscall policy. When code calls a blocked syscall, the kernel returns an error or sends SIGSYS before dangerous kernel work runs.

Two modes matter in production. SECCOMP_MODE_STRICT allows only read, write, _exit, and sigreturn. It is too tight for PHP or Node.js. SECCOMP_MODE_FILTER loads a BPF program that evaluates each syscall number and optional arguments. That is what Docker, containerd, and systemd use today.

The filter sits between userspace and the kernel. Application code cannot disable it without a separate privilege escalation. Memory corruption, deserialisation bugs, or shell uploads still run attacker code. They cannot call mount to escape a container or bpf to install a new filter. This is defence in depth, not a replacement for patches, WAF rules, or input validation.

PHP-FPM PoolLaravel requestseccomp BPFSyscall whitelistLinux KernelAllow or SIGSYSAllowed syscallsread, write, openat, futexBlocked syscallsptrace, mount, kexec_loadseccomp runs on every syscallPolicy survives in-process memory corruption
How seccomp restricts syscalls: a BPF filter evaluates each call before the kernel executes it

On Ubuntu 22.04 and 24.04 servers running PHP 8.3 or 8.4, seccomp pairs well with namespaces, dropped capabilities, and read-only root filesystems. I apply it to Laravel queue workers, PHP-FPM pools, and sidecar containers on the same hosts I manage through Linux system administration engagements. The official kernel documentation at docs.kernel.org seccomp remains the authoritative reference for filter semantics.

How do you build and test a seccomp profile without breaking production?

Guessing syscall lists causes outages. Tracing real workloads produces a baseline you can tighten over time. The workflow below is what I use before rolling seccomp to client-facing services.

Step 1: Capture syscalls with strace

Run representative traffic on staging first. Cover logins, file uploads, queue jobs, failed payments, and cache misses. A single happy-path trace is never enough.

sudo strace -f -e trace=%syscall -o /tmp/syscalls.log \
  php artisan queue:work --once

grep -oP '(?<=^[0-9]+ )[a-z_0-9]+' /tmp/syscalls.log \
  | sort -u > /tmp/allowed_syscalls.txt

wc -l /tmp/allowed_syscalls.txt

Typical Laravel workers on PHP 8.4 need roughly 40–80 unique syscalls. Symfony and WordPress counts differ once image processing or shell commands enter the path. Compare staging traces after every major PHP extension or kernel upgrade.

Step 2: Start from Docker's default profile

Do not build JSON from scratch on day one. Docker ships a maintained default profile that already blocks many dangerous calls. Trim or extend it after you know your app's needs. The profile lives in the Moby repository and is documented in Docker's seccomp guide.

curl -fsSL \
  https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json \
  -o seccomp-default.json

jq '.syscalls += [{
  "names": ["io_uring_enter"],
  "action": "SCMP_ACT_ALLOW"
}]' seccomp-default.json > seccomp-laravel-worker.json

Validate JSON before deploy. A syntax error silently falls back to Docker defaults or rejects the container start, depending on runtime settings.

Step 3: Watch for blocked syscalls in audit logs

When a legitimate call gets denied, you need the syscall name, not a vague 500 error. Linux audit rules make that visible.

echo '-a always,exit -F arch=b64 -S all -F success=0 -k seccomp-deny' \
  | sudo tee /etc/audit/rules.d/seccomp.rules

sudo augenrules --load
sudo ausearch -k seccomp-deny --interpret | tail -20

Map numeric IDs with ausyscall --dump or scmp_sys_resolver from libseccomp. Add the missing name to your allow list, redeploy to staging, and repeat. This loop is faster than disabling seccomp after an incident.

1. Tracestrace stagingAll code paths2. BaseDocker defaultJSON profile3. TestAudit SIGSYSFix allow list4. Roll outWorkers firstWeb pools nextExercise error paths during tracingUploads, retries, SMTP, Redis timeouts, payment callbacksNever ship untested profilesSilent job failures are commonKeep one unrestricted fallbackInstant rollback path
Safe seccomp rollout: trace broadly, extend Docker defaults, audit denials, deploy incrementally

How do you apply seccomp on Docker, Kubernetes, and systemd?

Configuration shape depends on where the process runs. The same JSON profile can often serve Docker Compose and Kubernetes with small packaging changes.

Docker and Docker Compose

services:
  laravel-worker:
    image: php:8.4-cli
    security_opt:
      - seccomp:/opt/seccomp/laravel-worker.json
    volumes:
      - ./seccomp:/opt/seccomp:ro

Confirm the filter is active inside the container:

docker exec laravel-worker grep Seccomp /proc/self/status
# Seccomp: 2 means SECCOMP_MODE_FILTER is active

Pair seccomp with read-only mounts and CPU and memory limits so a compromised worker cannot exhaust the host. Image scanning with Trivy catches CVEs; seccomp limits what exploited code can do afterward.

systemd for bare-metal PHP-FPM

Many Nepali VPS setups still run PHP-FPM directly on Ubuntu without containers. systemd exposes declarative syscall groups that are easier to maintain than hand-written BPF for simple pools.

# /etc/systemd/system/php8.4-fpm.service.d/seccomp.conf
[Service]
SystemCallFilter=@system-service @file-system @network-io
SystemCallFilter=~@privileged @resources @mount @debug
SystemCallErrorNumber=EPERM

Reload and inspect exposure:

sudo systemctl daemon-reload
sudo systemctl restart php8.4-fpm
systemd-analyze security php8.4-fpm.service

This approach fits Laravel on Ubuntu with Nginx and mirrors patterns from Ubuntu server hardening guides. For deeper audits, cross-check against CIS benchmark controls.

RuntimeConfig locationProfile formatVerify active
Dockersecurity_opt: seccomp:JSON (Moby schema)/proc/self/status Seccomp: 2
KubernetesPod Security Standard restrictedJSON via runtime defaultkubectl describe pod
systemdSystemCallFilter= drop-inNamed syscall setssystemd-analyze security
containerdOCI runtime seccomp blockJSON (OCI spec)crictl inspect

Which syscalls should PHP and Laravel workloads allow or block?

There is no universal list. Extensions, libc versions, and kernel lines all shift requirements. Still, patterns repeat across projects I maintain, including legal-tech portals like Mijar Law Associates and e-commerce stacks on shared EC2 hosts.

Commonly required syscalls

  • File I/O: openat, read, write, close, lseek, fstat, statx, access, getdents64
  • Memory: mmap, munmap, mprotect, brk, madvise
  • Threads: clone, clone3, futex, set_robust_list
  • Network: socket, connect, accept, sendto, recvfrom, poll, epoll_wait
  • Time and signals: clock_gettime, gettimeofday, rt_sigaction, rt_sigreturn

Queue workers that shell out to wkhtmltopdf or ffmpeg need execve and extra pipe syscalls. Redis or MySQL clients may pull in getsockopt and recvmsg. Image libraries sometimes need memfd_create. Trace, do not assume.

Syscalls worth blocking everywhere

  • ptrace — debug other processes, steal secrets
  • mount, umount2, pivot_root — filesystem escape
  • kexec_load, reboot — kernel or host control
  • init_module, finit_module — load kernel modules
  • unshare, setns — namespace breakout helper
  • bpf — load new BPF programs, including filters
  • userfaultfd, perf_event_open — exploit and side-channel primitives

Blocking these aligns with 2026 cybersecurity practice that assumes breach, then contains blast radius. Application fixes still matter. See OWASP Top 10 hardening for Laravel for the layer above seccomp.

No seccomp filterPHP RCE achievedptrace dumps secretsmount breaks containerHost compromiseseccomp activeSame PHP RCE achievedptrace returns SIGSYSmount returns SIGSYSChain ends in sandboxseccomp breaks post-exploitation chainsRCE alone is not enough when syscalls are restrictedLayer 1: WAF + validationStop easy exploitsLayer 2: seccomp filterBlock kernel abuseLayer 3: namespacesLimit visibility
seccomp attack surface reduction: the same PHP exploit fails to escalate when dangerous syscalls are denied

What performance impact and operational pitfalls should you expect?

Runtime cost is tiny. BPF filters run in kernel space with fixed instruction counts. Typical web profiles add microseconds per syscall. PHP 8.4 Laravel 12 benchmarks under load rarely move more than one percent once filters are loaded. Memory overhead is shared across threads in the same process.

Operational cost is real. Every new PHP extension, glibc update, or image-processing library can introduce fresh syscalls. Without monitoring, you get mystery 502 responses or queue stalls. Treat profiles as infrastructure code.

  1. Store profiles in Git next to Docker Compose and systemd units.
  2. Re-run strace in CI on integration tests; flag unexpected new syscalls.
  3. Match kernel versions between staging and production. Ubuntu 22.04 and 24.04 differ on clone3 and statx behaviour.
  4. Document each allow-list exception with a comment citing the feature that needs it.
  5. Alert on audit spikes for the seccomp-deny key. Spikes may mean attack or broken deploy.

Agencies running many small sites can maintain base profiles per stack—Laravel, WordPress, WooCommerce—and overlay project-specific rules. That scales better than one-off heroics per server. Combine with rootless containers, UFW firewall rules, and Ubuntu security updates for a coherent baseline. When you need to validate regex allow lists for log parsers, a regex tester saves time wiring audit dashboards.

seccomp gotchas in productionIncomplete straceRare paths miss syscallsPay callbacks fail silentlyKernel drift22.04 vs 24.04 differRetest after dist-upgradeNew PHP extensionimagick adds memfd_createUpdate profile in same PRFix patternAudit log to nameAllow, test, documentRoll out to queue workers before public web poolsLower blast radius while you tune the allow list
Operational seccomp pitfalls: incomplete tracing and kernel drift cause most production denials

Kubernetes clusters should adopt the restricted Pod Security Standard, which enables the runtime default seccomp profile automatically. See Kubernetes pod security and network policies for how seccomp fits beside seccomp-adjacent controls. For local container experiments, start with Dockerizing a Laravel app before layering custom JSON.

Key Takeaways

  • seccomp restricts syscalls at the kernel with BPF filters attackers cannot turn off from userspace.
  • Trace real staging workloads with strace; never deploy a guessed allow list.
  • Extend Docker's default JSON profile, then tighten using audit logs for denied syscalls.
  • Apply via Docker security_opt, Kubernetes restricted PSS, or systemd SystemCallFilter.
  • Block ptrace, mount, bpf, and module-loading calls on every web-facing pool.
  • Version-control profiles, retest on kernel or PHP extension changes, and roll out workers first.

People Also Ask

Does seccomp slow down PHP or Laravel applications?

No meaningful slowdown appears in typical production loads. BPF evaluation adds microseconds per syscall. Once loaded, the filter is shared by all threads in the process. Benchmark before and after if you run extreme syscall-heavy workloads, but standard HTTP and queue traffic rarely notice a difference.

What is the difference between seccomp and AppArmor or SELinux?

seccomp filters syscalls only. AppArmor and SELinux enforce path, capability, and label policies on files and processes. They complement each other. A complete hardening stack uses seccomp plus MAC, dropped capabilities, and namespaces together rather than picking one tool.

Can seccomp block container escape exploits?

It blocks many escape steps that rely on forbidden syscalls such as mount or unshare. It does not fix kernel bugs by itself. Keep kernels patched, run rootless or non-root users, and combine seccomp with read-only root filesystems for practical escape resistance.

Should I use Docker's default seccomp profile or write a custom one?

Start with Docker's default profile for most services. It already denies numerous dangerous syscalls. Move to a custom JSON profile when audit logs show you can remove permissions safely or when compliance requires explicit documentation of every allowed call.

Put seccomp to work on your stack

Pick one non-critical service this week—a Laravel queue worker on staging, a sidecar, or a PHP-FPM pool behind low traffic. Apply Docker's default profile or a systemd SystemCallFilter, enable audit logging, and watch for denials for 48 hours. Expand once the allow list is stable. Sustainable seccomp practice beats a perfect profile that never ships. For hands-on help profiling syscalls on your VPS or container fleet, contact us or message me directly about your environment. If you prefer a full hardening review, see our support and maintenance services and related production Docker guides.

Frequently Asked Questions

Seccomp filters system calls at the kernel level, restricting processes to only allowed operations. For PHP-FPM or Nginx, this prevents exploited code from executing dangerous syscalls like execve or ptrace, limiting damage even if application-level defenses fail.

Seccomp operates at the syscall interface with minimal overhead, while AppArmor and SELinux enforce path-based or label-based access control at higher abstraction levels. Seccomp complements rather than replaces them; I often layer seccomp profiles inside AppArmor-confined PHP-FPM workers for defense in depth on Ubuntu 24.04 servers.

Start with libseccomp’s default allowlist plus read, write, openat, close, fstat, lseek, mmap, mprotect, brk, rt_sigaction, epoll_wait, accept4, sendto, recvfrom, and clone3. Block execve, ptrace, mount, reboot, and kexec_load. Test thoroughly in log-only mode before enforcing, as missing syscalls cause silent worker crashes.

Yes, especially if workers spawn subprocesses or use pcntl_fork. Artisan commands running via cron may need broader profiles than HTTP workers. In production Laravel deployments, I maintain separate seccomp profiles for PHP-FPM, queue:work processes, and scheduler daemons to avoid false positives during peak traffic or batch jobs.

Use strace -f -o trace.log to capture syscalls during representative load, then convert with tools like scmp_sys_resolver or oci-seccomp-bpf-hook. For PHP-FPM, run strace against multiple request types including file uploads, API calls, and cache misses. Always validate generated profiles in audit mode before enforcement to catch edge cases missed during tracing.

Negligible in practice. BPF filtering adds microseconds per syscall, typically under 1% overhead for PHP-FPM. The real cost is development time for profile tuning. On high-traffic WooCommerce sites I’ve secured, seccomp added no measurable latency while blocking exploit attempts targeting imagemagick and GD libraries.

Store JSON profiles in your repository under deploy/seccomp/. Add a task to copy profiles to /etc/seccomp/ and set permissions 644 root:root. Hook into deploy:update_code to symlink versioned profiles, then reload PHP-FPM via systemctl reload php8.4-fpm. Never store profiles in shared storage since they’re version-controlled security artifacts.

No, Docker applies its own default seccomp profile unless overridden. Pass custom profiles via --security-opt seccomp=/path/to/profile.json in docker run or compose files. For Laravel containerized deployments, I extend Docker’s default profile rather than replacing it, adding PHP-specific allowances while retaining container isolation guarantees.

Check dmesg or journalctl for SECCOMP audit messages showing blocked syscall numbers and PIDs. Use scmp_sys_resolver -r to translate to names. Enable audit logging with auditd rules for finer detail. Temporarily switch to SCMP_ACT_LOG instead of SCMP_ACT_ERRNO to observe violations without killing processes during staging validation.

Rarely. Shared hosts restrict kernel access and rarely expose seccomp configuration. Focus on application hardening instead. For VPS or dedicated servers running legal-tech portals or eCommerce platforms where you control the OS, seccomp provides meaningful attack surface reduction that justifies the initial profiling effort.

Enabling enforcement without sufficient logging, copying profiles between different PHP versions without revalidation, forgetting architecture-specific syscalls on ARM servers, and assuming dev environment parity with production. Always test on identical OS and PHP builds, start with log-only mode, and maintain rollback procedures for immediate profile disablement.

Database and Redis clients use socket, connect, sendto, and recvfrom syscalls that must be explicitly allowed. Connection pooling via persistent connections reduces syscall frequency. When securing Laravel apps connecting to MySQL 8.4 and Redis 7.x, verify TLS handshake syscalls like getrandom and futex aren’t blocked, as failures manifest as intermittent connection timeouts.

Partially. Seccomp blocks malicious post-install scripts attempting execve or network exfiltration during composer install. However, it cannot stop logic bombs executing within allowed PHP runtime behavior. Combine seccomp with composer audit, locked dependencies, and CI pipeline scanning. Run composer install only in build stages, never on production servers directly.

Initial profiling and testing takes 8–16 hours for experienced engineers, roughly NPR 40,000–80,000 (USD 300–600) at Nepal senior developer rates. Ongoing maintenance adds 2–4 hours per major PHP or framework upgrade. Budget-conscious clients should prioritize seccomp after basic hardening like firewall rules, updated packages, and least-privilege file permissions.

Skip seccomp for short-lived scripts, development environments, or systems where debugging time exceeds security benefit. For low-risk brochure sites with no user input or payments, standard OS hardening suffices. Reserve seccomp for production systems handling sensitive data, financial transactions, or legal documents where breach consequences justify operational complexity.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: