Introduction

Linux seccomp (secure computing mode) restricts the system calls a process can make. Docker applies a default seccomp profile that blocks approximately 44 syscalls out of 300+ available on modern kernels. When an application inside a container attempts a blocked syscall, it receives EPERM (Operation not permitted), which manifests as cryptic failures that are difficult to diagnose without understanding the seccomp filter.

Symptoms

  • Container application crashes with Operation not permitted on specific operations
  • dmesg shows audit: type=1326 seccomp auid=0 uid=0 syscall=XYZ
  • Docker logs show fatal: seccomp: operation not permitted
  • Go applications failing with clone3: function not implemented
  • Node.js fsync or rseq syscall blocked on newer kernels

Common Causes

  • Default Docker seccomp profile does not include newer syscalls (clone3, rseq, statx)
  • Application compiled against newer glibc that uses recently added syscalls
  • Custom seccomp profile too restrictive for the workload
  • Kernel updated with new syscalls not yet in Docker's default profile
  • Container running with --security-opt seccomp=default.json which is outdated

Step-by-Step Fix

  1. 1.Identify the blocked syscall from audit logs:
  2. 2.```bash
  3. 3.dmesg | grep seccomp | tail -10
  4. 4.# Look for the syscall number, e.g., syscall=330
  5. 5.# Convert to name:
  6. 6.ausyscall 330
  7. 7.`
  8. 8.Check container seccomp profile:
  9. 9.```bash
  10. 10.docker inspect <container> | grep -A 5 SeccompProfile
  11. 11.`
  12. 12.Test with unconfined seccomp to confirm the issue:
  13. 13.```bash
  14. 14.docker run --security-opt seccomp=unconfined <image>
  15. 15.# If the application works, seccomp is the cause
  16. 16.`
  17. 17.Create a custom seccomp profile allowing the needed syscall:
  18. 18.```bash
  19. 19.# Start with the default profile
  20. 20.curl -s https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json > custom-seccomp.json
  21. 21.# Add the blocked syscall to the allowed list
  22. 22.python3 -c "
  23. 23.import json
  24. 24.with open('custom-seccomp.json') as f:
  25. 25.profile = json.load(f)
  26. 26.for action in profile['syscalls']:
  27. 27.if action['action'] == 'SCMP_ACT_ALLOW':
  28. 28.action['names'].append('clone3')
  29. 29.break
  30. 30.with open('custom-seccomp.json', 'w') as f:
  31. 31.json.dump(profile, f, indent=2)
  32. 32."
  33. 33.`
  34. 34.Run container with custom profile:
  35. 35.```bash
  36. 36.docker run --security-opt seccomp=./custom-seccomp.json <image>
  37. 37.`
  38. 38.Alternative: Use a less restrictive built-in profile:
  39. 39.```bash
  40. 40.# Docker's unconfined removes all seccomp restrictions
  41. 41.docker run --security-opt seccomp=unconfined <image>
  42. 42.# Or disable entirely (not recommended for production)
  43. 43.docker run --privileged <image>
  44. 44.`

Prevention

  • Keep Docker updated to get the latest default seccomp profile with new syscall support
  • Audit application syscall requirements during development, not production
  • Use strace -c on the application outside containers to identify required syscalls
  • Maintain a curated seccomp profile per application type
  • Monitor dmesg for seccomp denials in production containers