Codex CLI 샌드박스를 nsjail 구조로 이해하기: 격리 경계, AGENTS.md 설계, CI에서 codex exec로 자율 리팩토링 연결하기
시니어 백엔드·풀스택 개발자가 팀 파이프라인에 Codex CLI를 붙일 때 실제로 중요한 것은 두 가지로 좁혀집니다. 에이전트 프로세스를 어디까지 격리할 수 있는가, 그리고 그 격리 안에서 팀 컨벤션을 어떻게 안정적으로 전달할 것인가입니다. 이 글은 격리 경계의 구조를 nsjail의 개념 축을 렌즈 삼아 해체해 보고, AGENTS.md를 팀 표준 인터페이스로 굳히는 방법, 그리고 codex exec를 CI에 삽입할 때 반드시 챙겨야 할 옵션과 실패 패턴을 정리합니다.
Codex CLI 샌드박스가 실제로 막는 것
Codex CLI는 에이전트 프로세스를 OS 수준에서 격리한 상태로 실행합니다. Linux에서는 Bubblewrap(bwrap)이 기본 엔진으로, user/PID 네임스페이스와 seccomp 필터로 프로세스를 감쌉니다. 커널이 unprivileged user namespace를 지원하지 않는 환경에서는 Landlock LSM으로 폴백하고, macOS에서는 Apple Seatbelt(sandbox-exec)가 같은 역할을 합니다. 이 부분은 OpenAI Codex 공식 저장소와 Windows 샌드박스 구현 블로그에서 확인할 수 있습니다.
여기에 두 가지 선택적 방어층이 있습니다. Docker 또는 devcontainer로 컨테이너 격리를 쌓을 수 있고, 커스텀 설정으로 에이전트의 네트워크 egress를 제한할 수 있습니다. 참고로 격리 기본 제공은 Codex CLI만의 고유 속성은 아닙니다. Claude Code 등 다른 에이전트도 비슷한 기본 격리를 갖추고 있으므로, "기본 활성화된다"는 사실 자체가 도입 근거가 되고 도구 간 절대 우위를 뜻하지는 않습니다.
각 계층은 독립적으로 켜고 끌 수 있으므로, 조직의 위험 허용 수준에 맞춰 조합을 결정하면 됩니다. 민감한 저장소일수록 OS 격리 + 컨테이너 + egress 제한을 모두 적용하고, 개인 실험용에서는 OS 격리만으로 충분한 경우가 많습니다.
nsjail 구조로 격리 경계를 이해하기
먼저 명확히 해두자면, nsjail은 Codex CLI의 공식 지원 격리 메커니즘이 아닙니다. OpenAI 문서에 nsjail은 등장하지 않으며, 아래 예시 설정을 Codex가 직접 소비하지도 않습니다. 그럼에도 이 절을 두는 이유는, nsjail이 명시적으로 노출하는 축 — Linux 네임스페이스, seccomp, cgroups — 을 훑어 두면 Bubblewrap 기반 샌드박스가 실제로 어떤 속성을 보장하는지, 그리고 외부 래퍼(예: 자체 CI 러너의 프리 훅)로 에이전트를 감쌀 때 어떤 항목을 챙겨야 하는지를 체크리스트처럼 정리할 수 있기 때문입니다.
| nsjail이 명시하는 축 | Codex 운용에서 대응 지점 |
|---|---|
| 파일시스템 쓰기 범위 제한 | 워크스페이스만 read-write로 마운트, 홈·시스템 경로는 read-only |
| 네트워크 egress 제어 | Codex 설정 또는 외부 프록시로 허용 도메인만 통과 |
| CPU·메모리·프로세스 수 상한 (cgroups) | cgroup v2 상한, 그리고 --max-turns·토큰 상한으로 루프 비용 제어 |
| PID/net/uts 네임스페이스 격리 | Bubblewrap의 user/PID 네임스페이스로 동일 수준 대응 |
아래는 자체 러너에서 codex exec 프로세스를 nsjail 래퍼로 감쌀 때 참고할 수 있는 개념적 설정 예시입니다. 그대로 복사해 프로덕션에 넣는 용도가 아니라, 위 표의 네 축이 실제 설정에서 어떻게 표현되는지를 눈으로 확인하는 용도로 읽어 주세요.
name: "codex-sandbox-example"
mount {
src: "/workspace"
dst: "/workspace"
is_bind: true
rw: true
}
mount {
src: "/usr"
dst: "/usr"
is_bind: true
rw: false
}
clone_newnet: true
rlimit_as_type: HARD
rlimit_cpu_type: HARD
max_cpus: 2실제 팀 환경에서 바로 손 댈 수 있는 지점은 두 곳입니다. 하나는 Codex의 설정 파일에서 sandbox 모드와 네트워크 허용 여부를 지정하는 것이고, 다른 하나는 Docker/CI 이미지에서 워크스페이스 마운트와 egress 경로를 명시적으로 잠그는 것입니다.
~/.codex/config.toml에서 샌드박스 정책을 세팅하는 형태는 대략 다음과 같습니다. 정확한 키 이름과 사용 가능 값은 사용 중인 Codex CLI 버전의 codex --help와 공식 저장소에서 최신 스펙을 확인하는 것을 권합니다.
# ~/.codex/config.toml — 개념적 예시 (실제 키는 설치 버전 문서로 재확인)
sandbox_mode = "workspace-write"
[sandbox_workspace_write]
network_access = falseegress를 완전히 차단하지 않고 특정 도메인만 열어야 하는 경우에는, 컨테이너 안에서 Codex가 접근하는 트래픽을 사이드카 프록시로 우회시키는 패턴이 현실적입니다. Docker의 network_mode: none은 이름 그대로 모든 네트워크를 끊기 때문에 OpenAI API 호출조차 불가능해집니다. 실제로 프록시를 통해 화이트리스트를 강제하려면 아래와 같이 별도 네트워크와 egress 프록시를 둡니다.
# docker-compose.yml — 개념적 예시
services:
egress-proxy:
image: mitmproxy/mitmproxy:latest
networks:
- codex-egress
command: >
mitmdump --set block_global=true
--allow-hosts '^(api\.openai\.com|registry\.npmjs\.org|pypi\.org|github\.com)$'
codex-agent:
image: codex-universal:latest
depends_on: [egress-proxy]
networks:
- codex-egress
volumes:
- ./workspace:/workspace:rw
- ./codex-config.toml:/root/.codex/config.toml:ro
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- HTTPS_PROXY=http://egress-proxy:8080
- HTTP_PROXY=http://egress-proxy:8080
command: >
codex exec 'CI 실패 로그를 분석하고 원인을 수정하라'
restart: 'no'
networks:
codex-egress:
driver: bridge
internal: false컨테이너 안에서 Bubblewrap을 다시 사용하려면 호스트 커널의 unprivileged user namespace 허용 여부에 의존합니다. 강화된 쿠버네티스 노드에서는 별도 커널 파라미터 조정이 필요할 수 있으므로, CI 러너 이미지에서 unshare -Urn true가 성공하는지 초기 검증 단계에 넣어두는 편이 안전합니다.
AGENTS.md: 팀 컨벤션을 에이전트 인터페이스로 굳히기
프롬프트에 직접 컨텍스트를 밀어 넣는 방식은 codex exec 호출이 늘어날수록 무너집니다. AGENTS.md는 저장소 루트나 서브패키지에 두는 Markdown 파일로, Codex CLI를 포함한 다수 코딩 에이전트가 공통으로 읽는 컨벤션 파일입니다. 스펙과 채택 도구 목록은 AGENTS.md 프로젝트 사이트에서 확인할 수 있습니다. (표준의 정확한 거버넌스 주체와 채택 저장소 수치는 시점에 따라 달라지므로, 이 글에서는 별도로 인용하지 않습니다.)
에이전트를 교체해도 AGENTS.md만 그대로 두면 팀 컨텍스트를 다시 쓸 필요가 없다는 점이 실질적 이점입니다.
모노레포에서의 배치
모노레포에서는 서브패키지마다 별도 AGENTS.md를 두어, 에이전트가 현재 작업 경로에서 가장 가까운 파일을 우선 참조하도록 구성합니다.
my-monorepo/
├── AGENTS.md ← 전체 공통 컨텍스트
├── services/
│ ├── api/
│ │ └── AGENTS.md ← Python FastAPI 서비스 전용
│ └── frontend/
│ └── AGENTS.md ← TypeScript React 전용
└── packages/
└── shared/
└── AGENTS.md ← 공유 라이브러리 전용처음에는 30줄 이하로 시작
초반부터 100줄이 넘어가면 에이전트가 실제로 중요한 지시(테스트 명령, 린트 규칙)를 상대적으로 덜 반영하는 경향이 있습니다. 아래 정도의 뼈대로 시작해서 팀에서 실제 문제를 만나는 지점을 하나씩 추가하는 편이 안전합니다.
# 프로젝트 컨텍스트
## 기술 스택
- Runtime: Node.js 22, TypeScript 5.4
- 프레임워크: Fastify v5
- 테스트: Vitest (단위), Playwright (E2E)
- 패키지 매니저: pnpm
## 코드 컨벤션
- 함수는 화살표 함수보다 named function 선호
- 에러 핸들링은 Result 타입 패턴 사용 (neverthrow)
- 테스트 파일은 소스와 같은 디렉터리에 .test.ts로 위치
## 테스트 실행 명령
- 단위 테스트: pnpm test
- E2E 테스트: pnpm test:e2e (로컬 서버 필요)
## PR 생성 전 체크리스트
- [ ] pnpm lint 통과
- [ ] pnpm typecheck 통과
- [ ] 관련 테스트 추가 또는 수정
- [ ] CHANGELOG.md 업데이트AGENTS.md는 코드처럼 PR에서 리뷰하는 것이 좋습니다. 컨벤션이 바뀔 때 사람과 에이전트가 같은 시점에 새 규칙을 따르게 됩니다. AGENTS.md 팀 도입 가이드(개인 블로그)에서도 사람이 직접 작성·유지보수한 사례가 자동 생성에만 의존한 경우보다 결과가 안정적이라는 관찰을 소개합니다. 다만 이 관찰의 수치는 학술 연구가 아니라 사례 기반이므로, 팀 내부에서 자체 측정을 병행하는 편이 낫습니다.
CI 파이프라인에서 codex exec 사용하기
codex exec는 Codex CLI의 비대화형(headless) 실행 모드로, 태스크를 완료한 뒤 종료합니다. 셸에서 다른 명령처럼 파이프·리다이렉트가 가능하기 때문에 CI 스텝에 그대로 삽입할 수 있습니다.
codex exec "테스트 실패를 분석하고 수정하라"
codex exec --approval-mode full-auto "린트 에러를 모두 수정하고 커밋하라"
codex exec --max-turns 10 "의존성을 최신 버전으로 업데이트하라"옵션 이름과 승인 모드 표기는 버전마다 다르게 나타난 적이 있으므로, 도입 전에 codex exec --help로 현재 설치 버전의 실제 플래그를 확인하는 것을 권합니다.
빌드 실패 시 자동 수정 워크플로
CI가 실패하면 실패 로그를 분석해 패치 브랜치를 만드는 흐름은 대략 다음과 같습니다.
GitHub Actions에서 실제 워크플로는 아래와 같은 형태가 됩니다. openai/codex-action은 OpenAI 개발자 문서에서 최신 버전과 입력 파라미터를 확인할 수 있습니다.
# .github/workflows/autofix.yml
name: Codex 자동 버그픽스
on:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main, "feature/**"]
jobs:
autofix:
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Codex 자동 수정
uses: openai/codex-action@v1
with:
prompt: |
CI 빌드가 실패했습니다. 실패 로그를 분석하고 원인 코드를
수정한 뒤 변경 사항을 커밋하세요. 테스트도 수정이 필요하면
수정하세요. 단, 테스트 로직 자체를 제거해 통과시키는 방식은
사용하지 마세요.
approval-mode: full-auto
max-turns: "15"
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}이 패턴의 레퍼런스 구현은 OpenAI cookbook의 autofix 예제에 정리되어 있습니다.
티켓에서 PR까지 연결하기
Atlassian 쪽 MCP 서버와 Jira 에이전트 통합 흐름은 이 글 작성 시점 기준으로 빠르게 움직이고 있어, 특정 GA 날짜를 확정해 인용하기보다는 "티켓 웹훅이 codex exec를 트리거하고, 에이전트가 MCP 서버로 이슈 상세를 읽어 구현·PR 생성까지 처리하는" 흐름 자체를 이해해 두는 편이 유용합니다. 개념적으로는 다음과 같은 웹훅 핸들러 형태가 됩니다.
#!/bin/bash
# 개념적 예시 — 실제 배포 시에는 인증·검증 단계 추가 필요
ISSUE_ID=$1
codex exec \
--approval-mode full-auto \
--max-turns 20 \
"Atlassian MCP 서버를 통해 Jira 이슈 ${ISSUE_ID}의 상세를 읽고,
해당 기능을 구현하고, 테스트를 작성한 뒤 PR을 생성하라.
PR 설명에는 이슈 번호와 변경 사항 요약을 포함하라."서브에이전트를 활용한 대규모 리팩토링
Python·TypeScript·Go가 혼재하는 모노레포에서는, 매니저 에이전트가 언어별 서브에이전트에게 각 서브패키지 작업을 위임하고 결과를 합쳐 최종 PR을 구성하는 방식이 유효합니다. 이 구조에서 서브패키지별 AGENTS.md가 결정적입니다. 서브에이전트는 자신이 진입한 디렉터리의 AGENTS.md에서 언어별 컨벤션과 테스트 명령을 자동으로 읽으므로, 매니저는 위임 프롬프트에서 세부 스택 지시를 반복할 필요가 없습니다. 사용 가능한 서브에이전트 기능의 정확한 지원 범위와 옵션은 설치된 Codex CLI 버전의 --help 및 릴리스 노트로 확인하는 것을 권합니다.
도입 전에 검토할 트레이드오프와 자주 겪는 실수
| 축 | 실무에서 확인할 지점 |
|---|---|
| 격리 기본 활성화 | OS 수준 격리는 별도 설정 없이도 켜져 있음. 다만 컨테이너·egress 층은 별도로 쌓아야 함 |
| 계층형 방어 | OS·컨테이너·네트워크 세 층을 독립적으로 조립 가능 |
| 비대화형 실행 | codex exec로 CI 스텝화. 승인 모드와 --max-turns를 반드시 명시 |
| 크로스툴 컨벤션 | AGENTS.md로 에이전트 교체 시에도 컨텍스트 재작성 최소화 |
| 승인 정책 | Suggest / Auto Edit / Full Auto를 저장소 민감도와 브랜치 종류에 따라 분리 적용 |
| 프로젝트 성숙도 | 공식 README가 실험적 상태를 명시하고 있으므로 버전 고정 필수 |
| 데이터 거버넌스 | 기업 코드베이스는 개인 API 키가 아니라 엔터프라이즈 계약과 데이터 보존 정책 확인이 선행되어야 함 |
| 비용 상한 | --max-turns와 토큰 상한, cgroup CPU 상한으로 이중 방어 |
| 보안 업데이트 | 과거 셸 인젝션 관련 수정 이력이 있음. 릴리스 노트와 GitHub Security Advisories를 정기 확인 |
| 컨테이너 내부 커널 요구사항 | 강화된 쿠버네티스에서는 unprivileged user namespace 허용 여부 사전 검증 필요 |
| 패키지 혼동 | 반드시 @openai/codex로 설치. codex npm 패키지는 별개의 무관 패키지 |
실제 팀에서 반복적으로 관찰되는 실패 패턴은 서로 다른 결의 문제들이 얽혀 나타납니다.
--max-turns없이 플레이키 테스트에 Full Auto를 붙이는 경우. 재시도 루프가 스무 번을 넘어가면서 토큰 비용이 예산을 넘긴 사례가 흔합니다. CI에서는 예외 없이--max-turns를 지정하고, 별도로 조직 단위 토큰 상한을 대시보드로 관찰하는 편이 안전합니다.- AGENTS.md 초기 과설계. 100줄 넘게 시작하면 핵심 명령(테스트 실행 방법, 커밋 규칙)이 상대적으로 묻힙니다. 30줄 이하 뼈대에서 시작해 팀이 실제로 겪는 회귀·오해 지점을 하나씩 추가하는 방식이 반영률이 더 높습니다.
- 개인 API 키로 기업 코드 실행. 소스가 소비자 약관 범위로 이동합니다. 도입 전 법무·보안 팀과 함께 엔터프라이즈 계약 조건과 데이터 보존 정책부터 확인해야 합니다.
network_mode: none으로 격리했다고 착각하는 경우. 컨테이너에서 네트워크를 완전히 끊으면 에이전트 자체가 API 호출을 못 하므로 실행이 불가합니다. 화이트리스트를 원한다면 앞 절의 egress 프록시 패턴처럼 별도 네트워크·프록시 컨테이너가 필요합니다.
다음 스텝으로 시도해 볼 만한 순서
Codex CLI 도입은 전면 전환이 아니라 파이프라인의 한 스텝을 대체하는 형태로 시작하는 편이 리스크가 작습니다. 다음 순서를 추천할 수 있습니다.
- 개인 저장소 또는 스테이징 브랜치에서
codex exec --max-turns 5로 작은 태스크(예: 린트 자동 수정) 한두 개를 처음 돌려 봅니다. - 저장소 루트에 30줄짜리 AGENTS.md를 커밋하고, 첫 PR로 리뷰 프로세스를 정착시킵니다. 이후 컨벤션 변경은 AGENTS.md 수정 PR에서 논의합니다.
- 앞 절의 GitHub Actions autofix 워크플로를
feature/**브랜치에서만 활성화해 트래픽을 제한한 상태로 관찰합니다. 실패 로그·PR 품질·토큰 소비를 함께 계측합니다. - 민감한 저장소에서는 Docker 컨테이너 + egress 프록시 조합을 CI 러너 이미지에 미리 굽고, 표준 러너로 승격합니다.
- 서브패키지별 AGENTS.md를 도입해 모노레포에서의 언어·프레임워크 편차를 흡수합니다.
측정할 지표는 세 축으로 잡아 두면 관리가 쉽습니다. 태스크 성공률(에이전트 PR이 사람 수정 없이 머지되는 비율), 스텝당 토큰 비용, 그리고 격리 위반 이벤트(허용 목록 밖 egress 시도, 워크스페이스 밖 쓰기 시도) 카운트입니다. 셋 중 어느 하나가 튀기 시작하면, 원인은 대부분 --max-turns·AGENTS.md 지시 명확도·프록시 화이트리스트 정합성 중 하나에 있습니다.
참고 자료
OpenAI 공식 자료
- OpenAI Codex GitHub 공식 저장소
- OpenAI Codex GitHub Action 문서
- OpenAI Cookbook: CI 자동 버그픽스 예제
- OpenAI 공식 블로그: Windows 샌드박스 구현
- GitHub Security Advisories (openai/codex)
표준·스펙
커뮤니티·개인 블로그
- Codex CLI 승인 모드 정리 (개인 블로그)
- Security Hardening Your Codex CLI Setup (개인 블로그)
- Codex CLI Network Security (개인 블로그)
- Codex CLI Devcontainers & Docker Sandboxes (개인 블로그)
- Docker Sandboxes for Codex CLI (개인 블로그)
- Codex CLI for CI/CD, codex exec 실전 (개인 블로그)
- Ticket-Driven Development with Codex CLI (개인 블로그)
- Codex Exec in CI 가이드 (개인 블로그)
- Codex CLI Guide 2026 (개인 블로그)
- How Claude Code and Codex Sandbox Untrusted Code (Medium)