Docker용 Seccomp 보안 프로필

Docker용 Seccomp 보안 프로필

보안 컴퓨팅 모드(seccomp)는 Linux 커널 기능으로, 컨테이너 안에서 실행할 수 있는 작업을 제한하는 데 사용해요. seccomp() 시스템 콜이 호출 프로세스의 seccomp 상태를 다루기 때문에, 이 기능을 통해 애플리케이션의 접근 권한을 세밀하게 제어할 수 있답니다. 다만 Docker가 seccomp로 빌드되어 있고 커널에 CONFIG_SECCOMP가 활성화되어 있어야 사용할 수 있어요.

커널이 seccomp를 지원하는지 확인하려면 다음 명령을 실행해 보세요.

$ grep CONFIG_SECCOMP= /boot/config-$(uname -r)
CONFIG_SECCOMP=y

출처: 공식문서

컨테이너에 프로필 전달하기

기본 seccomp 프로필은 seccomp로 컨테이너를 실행할 때 합리적인 기본값을 제공해요. 300개가 넘는 시스템 콜 중 약 44개를 비활성화하죠. 보호 수준은 적당하면서도 다양한 애플리케이션과 호환되도록 설계되었어요.

실질적으로 이 프로필은 허용 목록(allowlist) 방식이에요. 기본적으로 시스템 콜 접근을 거부하고, 특정 시스템 콜만 허용하죠. 프로필은 defaultActionSCMP_ACT_ERRNO로 정의하고, 특정 시스템 콜에 대해서만 그 동작을 재정의하는 방식으로 동작해요. SCMP_ACT_ERRNOPermission Denied 오류를 발생시키는 효과가 있어요. 그리고 actionSCMP_ACT_ALLOW로 재정의된 시스템 콜 목록은 완전히 허용돼요. 마지막으로 personality 같은 개별 시스템 콜에는 특정 인자를 가진 변형만 허용하는 세부 규칙도 있어요.

seccomp는 Docker 컨테이너를 최소 권한으로 실행하는 데 아주 중요한 역할을 해요. 기본 seccomp 프로필을 변경하는 것은 권장하지 않아요.

컨테이너를 실행할 때 --security-opt 옵션으로 재정의하지 않으면 기본 프로필을 사용해요. 예를 들어, 다음처럼 정책을 명시적으로 지정할 수 있어요.

$ docker run --rm \
 -it \
 --security-opt seccomp=/path/to/seccomp/profile.json \
 hello-world

Docker CLI는 docker를 실행하는 작업 디렉터리를 기준으로 상대 프로필 경로를 해석해요. CLI가 프로필을 읽어서 그 내용을 데몬에게 보내기 때문에, 클라이언트와 데몬이 별도 호스트에서 실행되는 경우 프로필은 클라이언트 호스트에 있어야 해요.

기본 프로필에서 차단되는 주요 시스템 콜

Docker의 기본 seccomp 프로필은 허용되는 호출을 지정하는 허용 목록이에요. 아래 표는 허용 목록에 없어서 실제로 차단되는 주요 시스템 콜(전부는 아니에요)을 정리한 거예요. 각 시스템 콜이 허용 목록 대신 차단된 이유도 함께 설명할게요.

| Syscall | Description |

| acct | 회계(accounting) 시스템 콜이에요. 컨테이너가 자신의 리소스 제한이나 프로세스 회계를 비활성화할 수 있어서 차단돼요. CAP_SYS_PACCT로도 제한돼요. | | add_key | 커널 키링(keyring)은 네임스페이스로 격리되지 않기 때문에, 컨테이너가 이를 사용하지 못하도록 차단해요. | | bpf | 커널에 지속적인 BPF 프로그램을 로드하지 못하도록 차단해요. 이미 CAP_SYS_ADMIN으로도 제한돼요. | | clock_adjtime | 시간/날짜는 네임스페이스로 격리되지 않아요. CAP_SYS_TIME으로도 제한돼요. | | clock_settime | 시간/날짜는 네임스페이스로 격리되지 않아요. CAP_SYS_TIME으로도 제한돼요. | | clone | 새 네임스페이스를 복제하지 못하도록 차단해요. CLONE_NEWUSER를 제외한 CLONE_* 플래그는 CAP_SYS_ADMIN으로도 제한돼요. | | create_module | 커널 모듈 조작 및 관련 기능을 차단해요. 더 이상 사용되지 않는 syscall이에요. CAP_SYS_MODULE로도 제한돼요. | | delete_module | 커널 모듈 조작 및 관련 기능을 차단해요. CAP_SYS_MODULE로도 제한돼요. | | finit_module | 커널 모듈 조작 및 관련 기능을 차단해요. CAP_SYS_MODULE로도 제한돼요. | | get_kernel_syms | 내보내진 커널 및 모듈 심볼을 가져오지 못하도록 차단해요. 더 이상 사용되지 않는 syscall이에요. | | get_mempolicy | 커널 메모리와 NUMA 설정을 수정하는 syscall이에요. 이미 CAP_SYS_NICE로 제한돼요. | | init_module | 커널 모듈 조작 및 관련 기능을 차단해요. CAP_SYS_MODULE로도 제한돼요. | | ioperm | 컨테이너가 커널 I/O 권한 수준을 수정하지 못하도록 차단해요. 이미 CAP_SYS_RAWIO로 제한돼요. | | iopl | 컨테이너가 커널 I/O 권한 수준을 수정하지 못하도록 차단해요. 이미 CAP_SYS_RAWIO로 제한돼요. | | io_uring_enter | 컨테이너 탈출에 악용될 수 있는 보안 취약점 때문에 차단돼요. moby/moby#46762를 참고하세요. | | io_uring_register | 컨테이너 탈출에 악용될 수 있는 보안 취약점 때문에 차단돼요. moby/moby#46762를 참고하세요. | | io_uring_setup | 컨테이너 탈출에 악용될 수 있는 보안 취약점 때문에 차단돼요. moby/moby#46762를 참고하세요. | | kcmp | 프로세스 검사 기능을 제한해요. CAP_SYS_PTRACE를 드롭하여 이미 차단돼요. | | kexec_file_load | kexec_load와 같은 일을 하지만 인자가 조금 다른 형제 syscall이에요. CAP_SYS_BOOT로도 제한돼요. | | kexec_load | 나중에 실행할 새 커널을 로드하지 못하도록 차단해요. CAP_SYS_BOOT로도 제한돼요. | | keyctl | 커널 키링은 네임스페이스로 격리되지 않기 때문에, 컨테이너가 이를 사용하지 못하도록 차단해요. | | lookup_dcookie | 추적/프로파일링 syscall로, 호스트의 많은 정보를 누출할 수 있어요. CAP_SYS_ADMIN으로도 제한돼요. | | mbind | 커널 메모리와 NUMA 설정을 수정하는 syscall이에요. 이미 CAP_SYS_NICE로 제한돼요. | | mount | 마운트를 차단해요. 이미 CAP_SYS_ADMIN으로 제한돼요. | | move_pages | 커널 메모리와 NUMA 설정을 수정하는 syscall이에요. | | nfsservctl | 커널 NFS 데몬과의 상호작용을 차단해요. Linux 3.1부터 더 이상 사용되지 않아요. | | open_by_handle_at | 과거 컨테이너 탈출의 원인이었어요. CAP_DAC_READ_SEARCH로도 제한돼요. | | perf_event_open | 추적/프로파일링 syscall로, 호스트의 많은 정보를 누출할 수 있어요. | | personality | 컨테이너가 BSD 에뮬레이션을 활성화하지 못하도록 차단해요. 본질적으로 위험하지는 않지만 테스트가 부족하고 커널 취약점 가능성이 많아요. | | pivot_root | pivot_root를 차단해요. 권한이 필요한 작업이에요. | | process_vm_readv | 프로세스 검사 기능을 제한해요. CAP_SYS_PTRACE를 드롭하여 이미 차단돼요. | | process_vm_writev | 프로세스 검사 기능을 제한해요. CAP_SYS_PTRACE를 드롭하여 이미 차단돼요. | | ptrace | 추적/프로파일링 syscall이에요. Linux 커널 4.8 이전 버전에서는 seccomp 우회를 피하기 위해 차단됐어요. 임의 프로세스를 추적/프로파일링하는 것은 호스트의 많은 정보를 누출할 수 있기 때문에 CAP_SYS_PTRACE를 드롭하여 이미 차단돼요. | | query_module | 커널 모듈 조작 및 관련 기능을 차단해요. 더 이상 사용되지 않는 syscall이에요. | | quotactl | 쿼터(quota) syscall로, 컨테이너가 자신의 리소스 제한이나 프로세스 회계를 비활성화할 수 있어요. CAP_SYS_ADMIN으로도 제한돼요. | | reboot | 컨테이너가 호스트를 재부팅하지 못하도록 차단해요. CAP_SYS_BOOT로도 제한돼요. | | request_key | 커널 키링은 네임스페이스로 격리되지 않기 때문에, 컨테이너가 이를 사용하지 못하도록 차단해요. | | set_mempolicy | 커널 메모리와 NUMA 설정을 수정하는 syscall이에요. 이미 CAP_SYS_NICE로 제한돼요. | | setns | 스레드를 네임스페이스에 연결하지 못하도록 차단해요. CAP_SYS_ADMIN으로도 제한돼요. | | settimeofday | 시간/날짜는 네임스페이스로 격리되지 않아요. CAP_SYS_TIME으로도 제한돼요. | | stime | 시간/날짜는 네임스페이스로 격리되지 않아요. CAP_SYS_TIME으로도 제한돼요. | | socket | AF_ALG에 대해 차단돼요. 커널 암호화 API를 통한 컨테이너 내 권한 상승을 막기 위해서예요 (CVE-2026-31431). AF_VSOCK에 대해서도 차단돼요. seccomp 인자 필터링은 socketcall(2)를 다루지 않아요. 기본 AppArmor 프로필과 SELinux 정책 모듈은 socket(2)socketcall(2)를 통해 두 주소 패밀리를 모두 차단해요. moby/moby#52494moby/moby#53551을 참고하세요. | | swapon | 파일/디바이스에 대한 스왑 시작/중지를 차단해요. CAP_SYS_ADMIN으로도 제한돼요. | | swapoff | 파일/디바이스에 대한 스왑 시작/중지를 차단해요. CAP_SYS_ADMIN으로도 제한돼요. | | sysfs | 더 이상 사용되지 않는 syscall이에요. | | _sysctl | 더 이상 사용되지 않아요. /proc/sys로 대체됐어요. | | umount | 권한이 필요한 작업이에요. CAP_SYS_ADMIN으로도 제한돼요. | | umount2 | 권한이 필요한 작업이에요. CAP_SYS_ADMIN으로도 제한돼요. | | unshare | 프로세스를 위한 새 네임스페이스 복제를 차단해요. unshare --user를 제외하고 CAP_SYS_ADMIN으로도 제한돼요. | | uselib | 공유 라이브러리와 관련된 오래된 syscall로, 오랫동안 사용되지 않았어요. | | userfaultfd | 사용자 공간 페이지 폴트 처리를 담당하며, 주로 프로세스 마이그레이션에 필요해요. | | ustat | 더 이상 사용되지 않는 syscall이에요. | | vm86 | 커널 내 x86 리얼 모드 가상 머신이에요. CAP_SYS_ADMIN으로도 제한돼요. | | vm86old | 커널 내 x86 리얼 모드 가상 머신이에요. CAP_SYS_ADMIN으로도 제한돼요. |

중요

AF_VSOCK을 차단하는 SELinux 규칙은 SELinux userspace 3.6 이상, container-selinux 정책, 그리고 Docker 데몬에 SELinux가 활성화되어 있어야 해요. RHEL 8은 더 오래된 SELinux userspace를 제공하므로 기본 패키지로는 이 규칙을 적용할 수 없어요. 영향을 받는 호스트에서는 AppArmor를 사용하거나 socketcall을 차단하는 커스텀 seccomp 프로필을 사용하세요. socketcall을 차단하면 이를 사용하는 32비트 프로그램의 네트워킹이 깨질 수 있어요.

기본 seccomp 프로필 없이 실행하기

unconfined를 전달하면 기본 seccomp 프로필 없이 컨테이너를 실행할 수 있어요.

$ docker run --rm -it --security-opt seccomp=unconfined debian:latest \
 unshare --map-root-user --user sh -c whoami

더 알아보기