보안 강화: rootless·seccomp·AppArmor
컨테이너는 기본적으로 얼마나 안전한가
"컨테이너는 격리돼 있으니 안전하다"는 믿음은 절반만 맞다. 기본 설정의 Docker는 컨테이너 프로세스가 host root와 동일한 UID 0으로 실행된다. 컨테이너 탈출 취약점이 발생하면 공격자는 호스트에서 root 권한을 얻는다.
VM과 달리 컨테이너는 커널을 공유한다. 따라서 컨테이너 보안은 커널 공격 표면을 최소화하는 것이 핵심이다. Docker는 이를 위해 여러 레이어의 방어 기제를 제공한다.
Rootless Mode: 데몬 자체를 비-root로
기본 모드의 문제점
일반 Docker 설치에서 dockerd는 root로 실행된다. /var/run/docker.sock 에 쓸 수 있는 사용자는 사실상 호스트 root와 동등한 권한을 가진다.
# docker.sock에 접근할 수 있으면 호스트 root가 된다
docker run -v /:/host --privileged alpine chroot /hostRootless Mode 내부 동작
Rootless Mode는 dockerd와 컨테이너 전체를 사용자 네임스페이스(user namespace) 안에서 실행한다. 핵심 메커니즘은 다음과 같다.
UID/GID 매핑: /etc/subuid와 /etc/subgid에 정의된 종속 UID 범위를 사용한다. 컨테이너 내부에서 root(UID 0)로 보이는 프로세스가 호스트에서는 일반 사용자 UID(예: 100000)에 매핑된다.
# /etc/subuid 예시
alice:100000:65536
# alice 계정의 컨테이너 root → 호스트 UID 100000네트워크: 일반 사용자는 네트워크 네임스페이스를 직접 구성할 수 없어서 slirp4netns 또는 pasta 가 userspace에서 TCP/IP 스택을 에뮬레이트한다. NAT 없이 완전한 격리를 제공하지만, 커널 네트워크 스택보다 약간 느리다.
newuidmap / newgidmap: 단, 여러 UID를 user namespace에 매핑하려면 setuid 비트가 설정된 이 두 바이너리가 필요하다. Rootless라도 이 두 파일은 예외적으로 setuid로 실행된다.
Rootless 설치 및 활성화
# 설치 (Ubuntu/Debian)
sudo apt-get install -y uidmap dbus-user-session
dockerd-rootless-setuptool.sh install
# 환경 변수 설정 (~/.bashrc에 추가)
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
# 자동 시작 (systemd --user)
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)Rootless의 제약사항
| 제약 | 이유 |
|---|---|
| 특권 포트(< 1024) 바인딩 불가 | 일반 사용자 권한 |
--privileged 사용 불가 | user namespace 내에서 의미 없음 |
| AppArmor/Overlay2 일부 제한 | 커널 버전에 따라 다름 |
| 성능 (slirp4netns 네트워크 오버헤드) | userspace 네트워크 스택 |
Rootless는 개발 환경과 멀티 테넌트 CI 환경에 특히 적합하다.
Linux Capabilities: root를 쪼개다
UNIX 전통에서 프로세스는 "root이거나 아니거나" 두 가지였다. Linux 2.2부터 Capabilities가 root 권한을 38개(Linux 6.x 기준 42개)의 독립적인 단위로 분리한다.
Docker 기본 Capability 세트
Docker는 기본적으로 컨테이너에 약 14개의 Capability를 부여한다. 나머지는 모두 제거된 상태다.
| 기본 부여 Capability | 의미 |
|---|---|
CAP_CHOWN | 파일 소유자 변경 |
CAP_NET_BIND_SERVICE | 특권 포트(< 1024) 바인딩 |
CAP_NET_RAW | 원시 소켓 생성 (ping 등) |
CAP_SETUID / CAP_SETGID | UID/GID 변경 |
CAP_MKNOD | 디바이스 파일 생성 |
| ... |
최소 권한 원칙 적용
# 모든 Capability 제거 후 필요한 것만 추가
docker run \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--cap-add CHOWN \
--security-opt no-new-privileges \
nginx:alpine
# 위험한 Capability (절대 부여하지 말 것)
# CAP_SYS_ADMIN — 호스트 마운트, 네임스페이스 조작 등 거의 모든 것
# CAP_SYS_PTRACE — 다른 프로세스 메모리 접근
# CAP_SYS_MODULE — 커널 모듈 로드
# CAP_NET_ADMIN — 네트워크 인터페이스, 라우팅 테이블 변경--security-opt no-new-privileges는 컨테이너 프로세스가 execve()를 통해 더 높은 권한을 얻는 것을 금지한다. setuid 바이너리(예: sudo, passwd)가 컨테이너 안에 있어도 작동하지 않는다.
Seccomp: 시스템 콜 방화벽
Seccomp이란
Seccomp(Secure Computing Mode) 는 Linux 커널이 제공하는 시스템 콜 필터링 기능이다. BPF 프로그램으로 구현되어 각 시스템 콜 호출 시 허용 여부를 결정한다. 컨테이너 탈출 공격의 대부분은 커널 취약점을 시스템 콜로 트리거하므로, seccomp은 공격 표면을 대폭 줄인다.
Docker 기본 프로파일
Docker는 기본적으로 약 44개의 시스템 콜을 차단한다. 차단되는 주요 콜은 다음과 같다.
| 차단된 시스템 콜 | 이유 |
|---|---|
ptrace | 프로세스 메모리 조작, 디버거 연결 |
kexec_load | 새 커널 부팅 |
mount | 임의 마운트 |
pivot_root | 루트 파일시스템 전환 |
reboot | 시스템 재부팅 |
clone (일부 플래그) | 새 네임스페이스 생성 |
# 기본 seccomp 프로파일 확인 (JSON 형식)
# https://github.com/moby/moby/blob/master/profiles/seccomp/default.json
# seccomp 비활성화 (절대 프로덕션에서 사용하지 말 것)
docker run --security-opt seccomp=unconfined myapp
# 커스텀 프로파일 적용
docker run --security-opt seccomp=my-profile.json myapp커스텀 Seccomp 프로파일 작성
특수한 애플리케이션(예: eBPF 사용 앱, 고급 네트워킹 도구)은 기본 프로파일로는 동작하지 않을 수 있다. 이때 커스텀 프로파일을 작성한다.
{
"defaultAction": "SCMP_ACT_ERRNO",
"syscalls": [
{
"names": ["read", "write", "open", "close", "stat", "fstat",
"mmap", "mprotect", "brk", "exit_group", "futex",
"clone", "execve", "wait4", "socket", "connect",
"accept", "sendto", "recvfrom", "epoll_wait"],
"action": "SCMP_ACT_ALLOW"
}
]
}defaultAction: SCMP_ACT_ERRNO는 명시되지 않은 모든 시스템 콜을 거부한다. 이 방식은 매우 강력하지만 애플리케이션마다 허용 목록을 세밀하게 조정해야 한다.
실전 팁:
strace -f -c myapp으로 실제 사용하는 시스템 콜 목록을 먼저 파악한 뒤 프로파일을 작성한다.
AppArmor: 파일·네트워크 접근 제어
Seccomp vs AppArmor
두 기술은 다른 계층에서 작동한다.
| 항목 | Seccomp | AppArmor |
|---|---|---|
| 제어 대상 | 시스템 콜 종류 | 파일·네트워크·리소스 접근 경로 |
| 정의 방식 | syscall 번호 + BPF 필터 | 텍스트 프로파일 (경로 패턴) |
| 우선 순위 | 시스템 콜 진입 전 | 시스템 콜 내부에서 |
| 기본 동작 | 44개 차단 | docker-default 프로파일 |
Docker 기본 AppArmor 프로파일
Docker는 컨테이너마다 자동으로 docker-default AppArmor 프로파일을 적용한다 (비활성화하지 않는 한).
# 현재 컨테이너에 적용된 프로파일 확인
docker inspect --format='{{.HostConfig.SecurityOpt}}' mycontainer
# AppArmor 상태 확인
aa-status커스텀 AppArmor 프로파일
#include <tunables/global>
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
# 필요한 파일 경로만 허용
/etc/nginx/** r,
/var/log/nginx/** w,
/usr/share/nginx/html/** r,
/var/run/nginx.pid rw,
/tmp/** rw,
# 네트워크 허용
network inet tcp,
network inet udp,
# 다른 모든 파일 접근 거부 (암묵적)
}# 프로파일 로드
apparmor_parser -r -W /etc/apparmor.d/docker-nginx
# 프로파일 적용
docker run --security-opt apparmor=docker-nginx nginx:alpine추가 강화 기법
읽기 전용 루트 파일시스템
# 루트 FS를 읽기 전용으로, 쓰기 필요 경로만 tmpfs
docker run \
--read-only \
--tmpfs /tmp:rw,size=64m \
--tmpfs /run:rw,noexec,nosuid \
myapp파일시스템 변조(웹 쉘 업로드, 바이너리 교체)를 원천 차단한다.
리소스 제한
# CPU와 메모리 제한으로 DoS 방어
docker run \
--cpus="0.5" \
--memory="256m" \
--memory-swap="256m" \
--pids-limit=100 \
myapp--pids-limit은 fork 폭탄 공격을 방어한다.
비-root 사용자로 실행
Dockerfile 수준에서 비-root 사용자를 설정하는 것이 가장 기본적인 방어다.
FROM node:20-alpine
# 전용 사용자 생성
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -s /bin/sh -D appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["node", "server.js"]# 실행 시 오버라이드도 가능
docker run --user 1001:1001 myapp보안 강화 체크리스트
Docker Compose에서 보안 옵션 적용
services:
api:
image: myapi:latest
user: "1001:1001"
read_only: true
tmpfs:
- /tmp:size=32m
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
- seccomp:./seccomp-profile.json
- apparmor:docker-myapi
pids_limit: 50
deploy:
resources:
limits:
cpus: "0.5"
memory: 256M심층 방어의 핵심: 레이어 중첩
단일 기술만으로는 충분하지 않다. Rootless + Capability 최소화 + Seccomp + AppArmor + 읽기 전용 FS를 중첩하면, 공격자는 여러 독립적인 방어 레이어를 모두 우회해야 한다. 각 레이어가 완벽하지 않아도 중첩 자체가 공격 비용을 크게 높인다.
Open question: 2025년 Qualys는 Ubuntu의 unprivileged namespace 제한을 우회하는 세 가지 방법을 공개했다. Rootless 모드도 BuildKit을 컨테이너 안에서 실행할 때는
seccomp=unconfined와apparmor=unconfined가 필요해 보안 수준이 낮아질 수 있다. 이 제약의 해소 방안은 커뮤니티에서 활발히 논의 중이다.
References
- Seccomp security profiles for Docker — Docker Docs
- AppArmor security profiles for Docker — Docker Docs
- Rootless mode — Docker Docs
- Isolate containers with a user namespace — Docker Docs
- Docker Security — OWASP Cheat Sheet Series
- Docker Security 2025: Hardening Containers — OnlineHashCrack Guide
- Rootless Docker and Its Hidden Security Trade-Offs — Ken Muse
- Docker Security Hardening: Rootless, Seccomp, AppArmor on VPS — Virtua Cloud
- Docker Security Hardening: Implementing Rootless Containers and Seccomp Profiles 2026
- Hardening Docker and Kubernetes with seccomp — Martin Heinz
- Understanding Seccomp and How It Compares to AppArmor — Medium
- Container Escape: All You Need is Cap — Cybereason Blog