현대의 애플리케이션은 사용자 공간에서 실행되며, 다양한 시스템 콜(SYSCALL)을 통해 커널 자원에 접근합니다. 현재 리눅스에는 약 435개의 SYSCALL이 존재하지만, 모든 애플리케이션이 이 많은 SYSCALL을 모두 필요로 하지는 않습니다. 오히려 모든 SYSCALL에 접근 가능하게 하면 공격 표면이 증가해 보안 취약점이 생길 수 있습니다.
특히 2016년에 발견된 Dirty Cow 취약점은 ptrace SYSCALL을 통해 읽기 전용 파일에 쓰기 작업을 수행하고, 루트 접근 권한을 획득하며, 컨테이너 탈출을 할 수 있음을 보여줬습니다. 이를 방지하기 위해 SECCOMP(Secure Computing)를 활용하여 애플리케이션이 필요로 하는 SYSCALL만 사용하도록 제한하는 방법을 알아보겠습니다.
SECCOMP는 Secure Computing의 약자로, 애플리케이션을 특정 SYSCALL만 사용하도록 샌드박스화할 수 있는 리눅스 커널 수준의 기능입니다. SECCOMP는 2005년에 처음 도입되어 2.6.12 버전 이후의 리눅스 커널에 포함되었습니다.
호스트 커널이 SECCOMP를 지원하는지 확인하려면 부트 구성 파일에서 SECCOMP 키워드를 찾아야 합니다.
$ grep SECCOMP /boot/config-$(uname -r)
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_SECCOMP_FILTER=y
CONFIG_SECCOMP=y
출력 결과가 CONFIG SECCOMP=yes
라면 커널에서 SECCOMP를 지원하는 것입니다.
Docker는 기본적으로 컨테이너를 생성할 때 SECCOMP 필터를 사용하여 보안을 강화합니다. 이를 통해 불필요한 SYSCALL을 차단하고, 컨테이너의 보안성을 높입니다. 예를 들어, Docker의 VLC 이미지를 사용하여 간단한 컨테이너를 실행해보겠습니다.
$ docker run --rm -it --name vlc-container vlc
이 컨테이너는 예상대로 작동하며, Docker의 기본 SECCOMP 프로파일이 적용되어 있습니다.
컨테이너 내부의 프로세스 상태를 확인하여 SECCOMP 필드 값을 검사해보겠습니다.
$ cat /proc/1/status | grep Seccomp
Seccomp: 2
SECCOMP 필드 값이 2라면, 이 컨테이너에 SECCOMP가 적용된 것입니다.
SECCOMP는 세 가지 모드로 작동할 수 있습니다. 모드 0은 SECCOMP가 비활성화됨을 의미합니다. 모드 1에서는 SECCOMP가 엄격 모드로 적용되어 읽기, 쓰기, 종료 및 시그널 관련 SYSCALLS를 제외한 거의 모든 SYSCALLS를 차단합니다. 모드 2는 선택적으로 SYSCALLS를 필터링합니다.
SECCOMP 프로파일은 주로 세 가지 요소로 구성됩니다.
# whitelist.json
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"<syscall-1>",
"<syscall-2>",
"<syscall-3>"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
# blacklist.json
{
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"<syscall-1>",
"<syscall-2>",
"<syscall-3>"
],
"action": "SCMP_ACT_ERRNO"
}
]
}
기본 Docker SECCOMP 프로파일은 약 300개 이상의 SYSCALL 중 약 60개를 차단합니다. 여기에는 reboot
, clock_settime
, ptrace
등의 SYSCALL이 포함됩니다.
기본 SECCOMP 프로파일을 수정하여 필요에 따라 사용자 정의 프로파일을 사용할 수 있습니다. 예를 들어, mkdir
SYSCALL을 제거한 사용자 정의 프로파일을 만들어 보겠습니다.
custom.json
파일에 사용자 정의 SECCOMP 프로파일을 저장합니다.
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"arch_prctl",
"brk",
"capget",
"capset",
"close",
"execve",
"clone"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Docker 명령어에 --security-opt
옵션을 사용하여 사용자 정의 SECCOMP 프로파일을 적용합니다.
docker run --security-opt seccomp=custom.json ...
/ #
/ # mkdir test
mkdir: can't create directory 'test': Operation not permitted
이제 컨테이너 내에서는 mkdir
SYSCALL이 차단되어 디렉터리를 생성할 수 없습니다.
모든 SYSCALL을 사용할 수 있도록 SECCOMP를 완전히 비활성화할 수도 있지만, 이는 보안상 매우 위험합니다.
$ docker run --security-opt seccomp=unconfined ...
이 설정은 절대 사용하지 말아야 하며, 필요할 때마다 특정 SYSCALL을 허용하거나 제한하기 위해 항상 사용자 정의 프로파일을 사용해야 합니다.
SECCOMP를 활용하여 애플리케이션이 필요로 하는 SYSCALL만 사용하도록 제한함으로써 공격 표면을 줄이고, 보안을 강화할 수 있습니다. Docker와 같은 컨테이너 기술과 결합하여 더욱 안전한 애플리케이션 환경을 구축할 수 있습니다. SECCOMP의 세부 조정 방법을 익히고, 필요에 따라 사용자 정의 프로파일을 적용하여 보안성을 높이는 것이 중요합니다.