gVisor를 이용한 신뢰할 수 없는 에이전트 코드 샌드박싱은 콜드 스타트당 약 200ms가 소요됩니다. 시스템 콜(syscall)을
요약
신뢰할 수 없는 코드를 실행하는 AI 에이전트와 CI 환경을 위해 gVisor의 오버헤드를 해결하는 새로운 컨테이너 런타임 zviz를 소개합니다. zviz는 시스템 콜 에뮬레이션 대신 선택적 거부(selective denial) 방식을 사용하여 네이티브 속도에 가까운 성능을 제공합니다.
핵심 포인트
- gVisor는 높은 보안성을 제공하지만 시스템 콜 오버헤드와 콜드 스타트 문제가 있음
- zviz는 Zig로 작성된 OCI 호환 런타임으로 에뮬레이션 없이 시스템 콜을 필터링함
- zviz의 핵심 메커니즘은 ALLOW, DENY, BROKER의 세 가지 결정 방식임
- 에뮬레이션 방식은 호환성에 유리하고, 거부 방식은 속도와 엄격함에 유리함
당신은 직접 작성하지 않은 코드를 실행하고 있습니다. 그것은 LLM의 출력을 실행하는 AI 에이전트일 수도 있고, 아무도 감사하지 않은 종속성들을 대상으로 npm install을 실행하는 CI 작업일 수도 있으며, 셸(shell) 액세스가 필요하다고 주장하는 플러그인일 수도 있습니다. 일반적인 컨테이너는 이 상황에서 당신을 위해 거의 아무것도 해주지 못합니다. 그것은 네임스페이스(namespaces)와 cgroups일 뿐이며, 전체 커널 공격 표면(attack surface)은 여전히 그곳에 그대로 노출되어 있습니다. 모든 runc 탈출 CVE(Common Vulnerabilities and Exposures)가 이를 상기시켜 줍니다.
일반적인 무거운 해답은 gVisor입니다. gVisor는 워크로드 앞에 사용자 공간 커널(userspace kernel)을 배치하고 시스템 콜(syscall)을 에뮬레이션(emulation)합니다. 이는 효과가 있습니다. 하지만 시스템 콜(syscall) 오버헤드가 약 5~250배 발생하고, 콜드 스타트(cold start)에 약 200ms가 소요되며, 컨테이너당 약 50MB의 메모리를 사용합니다. 처리량이 높은 API나 서버리스 함수(serverless function)의 경우, 이러한 오버헤드는 전체 예산(budget)을 차지할 정도입니다.
zviz는 다른 경로를 택합니다. 이는 Zig로 작성된 OCI 호환 컨테이너 런타임(container runtime)으로, 에뮬레이션(emulation) 대신 선택적 거부(selective denial)를 사용합니다. 대부분의 시스템 콜(syscall)은 네이티브 속도로 호스트 커널(host kernel)에 도달합니다. 위험한 소수의 시스템 콜(syscall)은 커널 코드가 실행되기 전에 차단됩니다. 하나는 인자 필터링(argument-filtered)을 통해 인라인(inline)으로 처리됩니다. 사용자 공간 커널(userspace kernel)도, 에뮬레이션(emulation)도, 데몬(daemon)도 없습니다.
핵심 아이디어: 허용(allow), 거부(deny), 중개(broker)
gVisor의 모델은 모든 시스템 콜(syscall)이 이를 에뮬레이션하는 Sentry 프로세스를 통과하는 방식입니다. zviz의 모델은 시스템 콜(syscall)당 세 가지 결정 중 하나를 내리는 필터입니다:
gVisor: App -> Sentry (약 300개의 syscall 에뮬레이션) -> Host kernel (~70개의 syscall)
zviz: App -> BPF filter -> ALLOW (네이티브 속도) / DENY (EPERM) / BROKER (중개됨)
허용된 시스템 콜(syscall)은 커널에 직접 도달하므로 네이티브 속도로 실행됩니다. 위험한 시스템 콜(syscall)은 EPERM과 함께 즉시 거부되므로, 익스플로잇(exploit) 코드가 안전하게 에뮬레이션되는 대신 그 자리에서 실패합니다. 아주 적은 수의 시스템 콜(syscall)은 검사를 위해 사용자 공간 브로커(userspace broker)로 라우팅됩니다. socket 시스템 콜(syscall)은 인자(arguments)에 대해 인라인(inline)으로 필터링됩니다.
이러한 철학적 차이는 호환성 측면에서 중요합니다. gVisor는 위험한 시스템 콜(syscall)을 안전하게 에뮬레이션(emulation)합니다. zviz는 이를 거부(refuse)합니다. 둘 다 격리(isolate)하지만, 실패 모드(failure modes)는 정반대입니다. 에뮬레이션(emulation)은 호환성을 유지하고, 거부(denial)는 엄격함과 속도를 유지합니다.
작동 원리
강제 적용(enforcement)은 특정 순서에 따라 적용되는 5개의 계층으로 구성되며, 이 순서가 매우 흥미로운 부분입니다:
| 계층 | 메커니즘 | 목적 |
|---|---|---|
| 1 | 네임스페이스 (Namespaces: user, pid, mount, ipc, uts) | 리소스 격리 |
| ... |
seccomp이 로드되기 전에 Capabilities가 제거되고, seccomp이 적용되기 전에 Landlock이 적용됩니다. 따라서 보안 설정 시스템 콜(syscall)이 자신이 설치하려는 바로 그 필터에 의해 차단되지 않도록 합니다. 이 순서가 잘못되면 런타임(runtime)이 무장하는 과정에서 스스로를 차단하게 됩니다. 기본 프로필은 41개의 모든 Linux capabilities를 제거하고, Landlock 규칙 세트를 적용하며, /proc, /sys, /dev를 개별적으로 마운트(mount)하고, 새로운 user, PID, mount, IPC, UTS 네임스페이스의 PID 1로 워크로드(workload)를 실행합니다.
전체 seccomp 정책은 124개의 BPF 명령어로 구성됩니다. 이는 수동으로 감사(audit)할 수 있을 만큼 충분히 작으며, 이는 진정한 보안 특성입니다. 신뢰할 수 없는 코드와 커널(kernel) 사이에 존재하는 정확한 필터를 직접 읽을 수 있습니다.
실행 방법
Linux 5.13 이상(Landlock이 도입된 버전)에서 Zig 0.15.0+를 사용하여 빌드한 다음, 어떤 OCI 번들(bundle)이든 실행할 수 있습니다. README의 예제는 Redis 이미지를 추출하여 실행합니다:
git clone https://github.com/Skelf-Research/zviz.git
cd zviz && zig build -Doptimize=ReleaseSafe
...
--verbose 플래그는 차단된 모든 시스템 콜(syscall)을 로그로 남기며, 이는 에이전트 워크로드가 예상치 못한 제한에 걸렸을 때 정확히 필요한 기능입니다. 일반적인 사례를 위한 내장 프로필이 준비되어 있습니다: ci-runner (균형 잡힌 기본값), web-server (네트워크 허용), batch-job (네트워크 없음, 8G 메모리), hostile-tenant (최대 제한), 그리고 development (ptrace를 허용하며, 명시적으로 프로덕션용이 아님).
zviz는 가상 파일 시스템(pseudo-filesystems)을 자동으로 마운트하므로 별도로 선언할 필요가 없습니다: /proc은 nosuid, nodev, noexec 옵션이 적용된 procfs로, /sys는 읽기 전용 sysfs로, /dev는 표준 장치 노드(device nodes)가 바인드 마운트(bind-mounted)된 프라이빗 tmpfs로 마운트됩니다.
수치
README는 실제 탈출 기법(escape techniques)을 대상으로 테스트한 다음 결과들을 보고합니다:
| 지표 | zviz | gVisor |
|---|---|---|
| 탈출 테스트 차단 (Escape tests blocked) | 19/19 (100%) | 11/19 (58%)* |
| ... |
시스템 콜(Syscall) 지연 시간(latency)은 선택적 차단(selective denial)이 빛을 발하는 부분입니다. clock_gettime의 경우 zviz는 20ns인 반면 gVisor는 4,982ns로, 249배의 격차가 발생합니다. 이는 zviz가 커널에 직접 접근하도록 허용하는 반면, gVisor는 Sentry를 통해 라우팅하기 때문입니다. read는 20.7배, getpid는 4.1배 더 빠릅니다. 탈출 수치에 붙은 별표(*)는 중요하며, README는 이에 대해 솔직하게 설명하고 있습니다. gVisor는 ptrace나 mount와 같은 일부 시스템 콜(syscall)을 "허용"하지만 이를 안전하게 에뮬레이션(emulate)합니다. 이는 단순한 성능 손실이 아니라, 해당 작업들에 대해 동일한 보안 결과를 제공하는 다른 철학을 가진 것입니다.
적합하지 않은 경우
README에는 "언제 gVisor를 대신 사용해야 하는가"라는 섹션이 통째로 포함되어 있는데, 이는 올바른 직관입니다.
워크로드에 ptrace가 필요한 경우, zviz는 이를 차단합니다. strace, 디버거(debugger), 그리고 다른 프로세스를 추적하는 모든 것은 실행되지 않습니다. gVisor는 이를 안전하게 에뮬레이션하므로, 디버깅 비중이 높은 워크로드에서는 gVisor가 승리합니다. Docker-in-Docker를 위해 mount나 unshare가 필요하거나, 자체적인 내부 네임스페이스(namespace)를 생성하는 Bazel 또는 Nix 빌드를 실행하는 경우, zviz는 해당 작업에 필요한 시스템 콜(syscall)을 거부합니다. 중첩 컨테이너화(Nested containerization)는 gVisor의 역할입니다.
1.8%의 정책 격차는 의도적인 선택입니다. zviz는 네트워크 이그레스(network egress)를 기본적으로 차단(deny)하는 반면, gVisor는 허용(allow)합니다. 이것이 더 엄격하지만, 이는 외부 네트워크를 예상하는 워크로드가 허용 프로필을 선택하기 전까지는 실패(fail closed)함을 의미합니다. Ubuntu 24.04+ 버전에서는 추가적인 설정 단계가 필요한데, 커널의 apparmor_restrict_unprivileged_userns sysctl이 pivot_root에 필요한 바인드 마운트(bind mount)를 차단하기 때문입니다. 제공된 AppArmor 프로필을 설치하지 않으면, zviz는 더 취약한 chdir 전용 파일 시스템 격리 방식으로 후퇴합니다. 또한 이는 Linux 전용이며, 커널 5.13 이상, cgroups v2가 필요합니다. macOS나 Windows용 버전은 없습니다.
README의 솔직한 요약은 다음과 같습니다: 중첩 컨테이너나 프로세스 추적(process tracing)이 필요하다면 gVisor를 사용하십시오. 그렇지 않다면 zviz가 더 빠르고 엄격합니다.
시사점 (Takeaways)
- 선택적 차단(Selective denial)이 에뮬레이션(emulation)보다 속도 면에서 우세한 이유는 허용된 시스템 콜(syscall)이 네이티브 속도로 커널에 도달하기 때문입니다. 이것이
clock_gettime에서 나타나는 249배의 격차입니다. - 차단 방식은 익스플로잇(exploit)에 대해서도 더 안전하게 작동합니다. 악성 코드는 안전하게 에뮬레이션된 성공 응답을 받는 대신 즉시 EPERM(권한 없음) 오류를 받게 됩니다.
- 레이어 순서(Layer ordering)는 매우 중요합니다. Capabilities와 Landlock은 seccomp보다 먼저 적용되어야 런타임(runtime)이 자체 설정 과정을 차단하는 일이 발생하지 않습니다.
- 비용은 호환성입니다. ptrace, 중첩 컨테이너(nested containers), Bazel/Nix 내부 샌드박싱(sandboxing)을 사용할 수 없습니다. 이것은 약 8ms의 콜드 스타트(cold start)와 실제로 읽을 수 있는 124개의 명령어 필터를 얻기 위한 대가입니다.
신뢰할 수 없거나 에이전트가 생성한 코드를 실행하며, 워크로드에 ptrace나 중첩 컨테이너가 필요하지 않다면, 현재 사용 중인 샌드박스와 zviz를 벤치마크해 볼 가치가 있습니다. 코드, 위협 모델(threat model), 그리고 비교 문서는 여기에서 확인할 수 있습니다: https://github.com/Skelf-Research/zviz
어떤 차단된 시스템 콜이 여러분이 처음으로 투입하는 실제 에이전트 워크로드를 중단시키는지 궁금합니다. --verbose 옵션과 함께 실행하고 트레이스(trace)와 함께 이슈(issue)를 제기해 주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기