
5초, 128 MB, 0.5코어, PID 및 출력 제한이 적용된 강화된 jail에서 신뢰할 수 없는 Python, JavaScript, Bash를 실행합니다.
신뢰할 수 없는 코드의 커널 강제 실행 — 모든 페이로드는 cgroups v2, 네임스페이스, seccomp-BPF 및 읽기 전용 rootfs에 의해 격리되며, 모든 실행은 PostgreSQL에 감사 기록됩니다.
python · javascript · bash → 5초, 128 MB, 0.5코어, 32-PID, 512 KB 출력 격리 환경
신뢰하지 않는 코드를 위한 보안 강화 실행 서비스입니다. 호출자는 작은 REST API를 통해 소스를 제출하고, 데몬은 각 페이로드를 일시적 커널 격리 환경에서 실행합니다. 이 환경에서는 애플리케이션 로직이 아닌 Linux 커널 자체가 격리를 보장합니다:
pivot_root),lo 비활성화 + seccomp가 socket 차단),PID · MOUNT · NET · IPC · UTS · USER), 엄격한 seccomp-BPF 허용 목록, cgroups v2 리소스 격리, 그리고 16 MB tmpfs 스크래치 공간을 갖춘 읽기 전용 pivot_root 파일시스템.5초 벽시계 타임아웃, 128 MB 메모리 상한(스왑 비활성화, 즉시 OOM 킬), 0.5 CPU 코어, pids.max = 32 포크 폭탄 면역, 그리고 stdout/stderr에 대한 512 KB 출력 상한.execution_runs)에 비동기로 기록됩니다.flowchart LR
CLI["👤 API Client<br/><i>tenant / CI job</i>"]
subgraph CP["🔐 Trusted Control Plane — Go daemon"]
API["Ingestion Daemon<br/><i>net/http :8080</i>"]
POOL["Warm Runner Pool<br/><i>16 slots · 3 s cap</i>"]
RUN["Process Runner<br/><i>watchdog + pipes</i>"]
ISO["Kernel Isolation<br/><i>cgroups v2 · namespaces · seccomp-BPF</i>"]
end
subgraph JAIL["⛓️ Ephemeral Kernel Jail — untrusted"]
INIT["sandbox_init<br/><i>exec trampoline</i>"]
GUEST["💀 Untrusted Code<br/><i>python · node · bash</i>"]
end
PG[("🗄️ PostgreSQL 16<br/><i>execution_runs</i>")]
RD[("📮 Redis 7 Queue<br/><i>sandbox:jobs</i>")]
CLI -->|"POST /api/v1/execute"| API
API -->|"validate + acquire"| POOL
POOL -->|"spawn + limits"| RUN
RUN -.->|"apply jail"| ISO
ISO ==>|"seccomp · pivot_root"| GUEST
RUN -->|"fork / exec"| INIT
INIT -->|"syscall.Exec"| GUEST
RUN -.->|"stdout / stderr ≤ 512 KB"| POOL
API -.->|"async audit INSERT"| PG
RD -.->|"BRPop drain · worker module"| POOL
style JAIL fill:#2a1215,stroke:#f87171,stroke-width:2px
style CP fill:#0d1b2a,stroke:#38bdf8,stroke-width:1px
| 구성 요소 | 역할 | 소스 |
|---|---|---|
| 수집 데몬 | REST 인터페이스: POST /api/v1/execute + /health; 요청 검증; 비동기 감사 로그 생성 | cmd/server/main.go |
| 웜 러너 풀 | 세마포어로 제한된 실행 슬롯(16개), 3초 획득 타임아웃 → 503 스타일 고갈 백프레셔 | internal/runner/pool.go |
| 프로세스 러너 | 언어 명령 구성, cgroup 연결, 절단형 라이터를 통한 파이프 스트리밍, 5초 워치독 적용, cgroup 메트릭 수집, 결과 분류 | internal/runner/process.go |
| 커널 격리 | cgroups v2 제한기(memory.max, cpu.max, pids.max), 네임스페이스 플래그(CLONE_NEWPID/NET/NS/IPC/UTS/USER), seccomp-BPF 필터 컴파일러 | internal/isolation/ |
| sandbox_init | 자식 트램폴린: 읽기 전용 pivot_root + tmpfs /dev//tmp, 네트워크 분리(lo down), seccomp 적용 후 대상의 syscall.Exec 실행 | cmd/sandbox_init/main.go |
| 비신뢰 코드 | 게스트 페이로드. 호스트 장치 제거, /proc 숨김(hidepid=2), 허용 목록에 없는 모든 시스템 콜 차단 | runtimes/ rootfs 이미지 |
| PostgreSQL 16 | 보안 위반을 포함한 모든 실행의 영구 감사 로그 | migrations/001_init_sandbox_schema.sql |
| Redis 7 | sandbox:jobs 큐 + 풀로 배출하는 BRPop 워커 (모듈은 구현되었으나 아직 데몬에 연결되지 않음 — 알려진 제한 사항 참조) | internal/queue/redis_consumer.go |
| 경계 | 분리 대상 | 적용 방식 |
|---|---|---|
| 클라이언트 ↔ 데몬 | 제어 평면으로부터의 비인증 페이로드 제출 | 스키마 검증(tenant_id, language, code) |
| 제어 평면 ↔ 격리 환경 | 신뢰된 Go 코드 대 적대적 페이로드 | 전용 네임스페이스로 fork; pivot_root; exec 시 상속되는 seccomp |
| 격리 환경 ↔ 커널 제한 | 게스트 리소스 소비 대 호스트 용량 | cgroups v2 memory.max=128MB, swap.max=0, cpu.max="50000 100000", pids.max=32 |
| 격리 환경 ↔ 네트워크 | 모든 외부 송신 또는 호스트 측 도달 가능성 | 전용 NET 네임스페이스, lo down, socket 시스템 콜 차단 |
🔍 이 다이어그램의 대화형 테마 지원 버전(PNG/SVG 내보내기 포함)은
docs/archify/의 증거 기반 후보들로부터 작성됩니다.
sequenceDiagram
autonumber
participant C as Client
participant A as REST API
participant P as Warm Pool
participant R as Process Runner
participant I as sandbox_init
participant G as Untrusted Code
participant DB as PostgreSQL
C->>A: POST /api/v1/execute
A->>P: acquire slot · 16 max, 3 s timeout
P->>R: Run(req, limits)
R->>I: fork under CLONE_NEWPID·NET·NS·IPC·UTS·USER
R->>R: create cgroup scope → attach PID<br/>128 MB · 0.5 CPU · 32 PIDs
I->>I: pivot_root RO rootfs · lo down · seccomp-BPF
I->>G: syscall.Exec target interpreter
G-->>R: stdout / stderr via pipes (512 KB cap)
R->>R: watchdog: SIGKILL at 5 s → exit 137
R-->>P: result + cgroup metrics (peak mem, CPU)
P-->>A: ExecutionResult
A-->>C: 200 JSON
A-)DB: async audit INSERT (non-blocking)
단계별:
tenant_id, language, code, 선택적 stdin/limits)을 POST합니다. 형식이 잘못되었거나 지원되지 않는 페이로드는 400/422로 즉시 실패합니다.sandbox_init이 읽기 전용 rootfs로 전환하고, 할당량이 설정된 tmpfs를 마운트하며, lo를 해제하고, seccomp-BPF 허용 목록을 설치한 다음 인터프리터를 exec합니다 — 페이로드는 부모에 의해 절대 권한 있게 실행되지 않습니다.memory.max를 초과시킵니다(깔끔한 OOM 킬). fork 폭탄은 pids.max = 32에서 거부됩니다. socket/ptrace/mount 호출은 커널에 의해 종료됩니다(SIGSYS/SIGKILL → SECURITY_VIOLATION).execution_runs에 삽입되므로, 영속화가 응답 지연을 차단하거나 지연 시간에 영향을 줄 수 없습니다.| 계층 | 기술 | 역할 |
|---|---|---|
| 언어 | Go 1.27 (github.com/kavaryx/sandboxed-runner) | 데몬, 러너, 격리, 테스트 |
| 커널 격리 | cgroups v2 · namespaces · seccomp-BPF (libseccomp-golang) · pivot_root | 억제 적용 |
| 런타임 이미지 | 최소 Python / Node rootfs (Docker로 빌드, runtimes/*.tar) | 읽기 전용 게스트 파일 시스템 |
| API | net/http 표준 라이브러리 (REST, JSON) | 수신 + 상태 확인 |
| 큐 | Redis 7 (go-redis/v9) — sandbox:jobs에 대한 LPUSH/BRPop | 비동기 작업 파이프라인 (모듈) |
| 영속화 | PostgreSQL 16 (lib/pq) | 내구성 있는 실행 감사 |
| 도구 | Docker Compose · Makefile · go test | 개발, 빌드, 보안 테스트 |