왜 샌드박스 인증이 어려운가?
LLM 기반 에이전트(예: OpenCode, Claude Code)가 점점 더 많이 사용되면서, 외부 API 호출을 안전하게 처리하는 방법이 중요해졌습니다. 특히 샌드박스 내에서 실행되는 코드는 신뢰할 수 없기 때문에, API 토큰을 직접 주입하는 전통적인 방식은 위험합니다. 토큰이 노출되면 공격자가 이를 악용할 수 있기 때문입니다.
이 글에서는 Cloudflare Sandbox의 outbound Workers라는 기능을 이용해, 에이전트가 외부 서비스를 호출할 때 제로 트러스트(Zero Trust) 원칙을 적용하는 방법을 단계별로 알아보겠습니다. 국내 클라우드 환경(NCP, KT Cloud 등)에서도 Workers 유사 기능이 있다면 동일한 패턴을 적용할 수 있습니다.
기존 인증 방식의 한계
| 방식 | 장점 | 단점 |
|---|---|---|
| API 토큰 직접 주입 | 구현 간단 | 토큰 노출 위험, 만료 관리 복잡 |
| OIDC 워크로드 아이덴티티 | 단기 토큰으로 보안 향상 | 서비스 통합 어려움, 자체 토큰 교환 서버 필요 |
| 커스텀 프록시 | 완전한 유연성 | 프록시 구축/운영 복잡, 성능 이슈 |
이상적인 인증 메커니즘은 제로 트러스트, 단순함, 유연성, 성능, 투명성을 모두 만족해야 합니다. outbound Workers가 이 모든 조건을 어떻게 충족하는지 살펴보겠습니다.

실전: outbound Workers로 제로 트러스트 인증 구현하기
1. 기본: 요청 로깅 및 차단
가장 간단한 예제로, 모든 아웃바운드 HTTP 요청을 가로채서 GET만 허용하고 나머지는 차단하는 코드입니다.
class MySandboxedApp extends Sandbox {
static outbound = (req, env, ctx) => {
// GET이 아닌 요청은 모두 차단하고 로그를 남깁니다.
if (req.method !== 'GET') {
console.log(`컨테이너가 ${req.method} 요청을 보냄: ${req.url}`);
return new Response('허용되지 않음', { status: 405 });
}
// GET 요청은 정상적으로 전달합니다.
return fetch(req);
};
}
이 프록시는 샌드박스 VM과 동일한 머신에서 실행되므로 지연 시간이 거의 없습니다. Workers 대시보드에서 모든 로그를 확인할 수 있어 관측 가능성도 확보됩니다.
2. 제로 트러스트 자격 증명 주입
가장 강력한 활용 사례는 비밀 토큰을 샌드박스에 절대 노출하지 않고 외부 서비스에 안전하게 전달하는 것입니다.
class OpenCodeInABox extends Sandbox {
static outboundByHost = {
"my-internal-vcs.dev": (request, env, ctx) => {
// 요청 헤더에 비밀 토큰을 추가합니다.
// 샌드박스 내 코드는 이 토큰을 절대 볼 수 없습니다!
const headersWithAuth = new Headers(request.headers);
headersWithAuth.set("x-auth-token", env.SECRET);
return fetch(request, { headers: headersWithAuth });
}
};
}
여기서 핵심은 env.SECRET이 샌드박스가 아닌 outbound Worker의 환경 변수로만 존재한다는 점입니다. 또한 ctx.containerId를 활용해 샌드박스 인스턴스별로 다른 토큰을 주입할 수도 있습니다.
static outboundByHost = {
"my-internal-vcs.dev": async (request, env, ctx) => {
// KV는 저장/전송 중 암호화됩니다.
const authKey = await env.KEYS.get(ctx.containerId);
const requestWithAuth = new Request(request);
requestWithAuth.headers.set("x-auth-token", authKey);
return fetch(requestWithAuth);
}
}
3. 동적 네트워크 제어
초기에는 npm 패키지를 다운로드하기 위해 GitHub, npmjs.org에 접근을 허용하고, 설치가 끝나면 즉시 차단하는 시나리오입니다.
class MySandboxedApp extends Sandbox {
static outboundHandlers = {
async allowHosts(req, env, { params }) {
const url = new URL(request.url);
if (params.allowedHostnames.includes(url.hostname)) {
return await fetch(newRequest);
} else {
return new Response(null, { status: 403 });
}
},
async noHttp(req) {
return new Response(null, { status: 403 });
}
};
}
async function setUpSandboxes(req, env) {
const sandbox = await env.SANDBOX.getByName(userId);
// 초기: GitHub, npm만 허용
await sandbox.setOutboundHandler("allowHosts", {
allowedHostnames: ["github.com", "npmjs.org"]
});
await sandbox.gitClone(userRepoURL);
await sandbox.exec("npm install");
// 설치 완료 후 모든 HTTP 차단
await sandbox.setOutboundHandler("noHttp");
}
이렇게 하면 네트워크가 열려 있는 시간을 최소화하여 공격 표면을 줄일 수 있습니다.
4. MITM 프록시로 HTTPS 트래픽 제어
HTTPS 요청도 제어하려면 TLS 복호화가 필요합니다. Cloudflare Sandbox는 각 인스턴스마다 **임시 인증 기관(CA)**을 생성하여 샌드박스 내부에 설치합니다.
export class MyContainer extends Container {
interceptHttps = true;
}
MyContainer.outbound = (req, env, ctx) => {
// 모든 HTTP(S) 요청이 이 훅을 통해 전달됩니다.
return fetch(req);
};
이 방식은 완전히 투명하게 동작합니다. 샌드박스 내 애플리케이션은 프록시 존재를 인지하지 못합니다.
5. 고급: WorkerEntrypoint를 활용한 세밀한 제어
IP 범위나 호스트 이름 패턴(glob 매칭)으로 요청을 가로챌 수 있습니다.
export class MyWorker extends WorkerEntrypoint {
fetch() {
return new Response(this.ctx.props.message);
}
}
// 컨테이너 내부
this.ctx.container.start({ enableInternet: false });
const outboundWorker = this.ctx.exports.MyWorker({ props: { message: 'hello' } });
await this.ctx.container.interceptOutboundHttp('15.0.0.1:80', outboundWorker);
// 이후 15.0.0.1:80으로 가는 모든 HTTP 요청은 "hello"를 반환합니다.
// 동적으로 변경 가능! 기존 연결에도 영향 없음
const secondOutboundWorker = this.ctx.exports.MyWorker({ props: { message: 'switcheroo' } });
await this.ctx.container.interceptOutboundHttp('15.0.0.1:80', secondOutboundWorker);
// 호스트 이름, CIDR(IPv4/IPv6) 모두 지원
await this.ctx.container.interceptOutboundHttp('example.com', secondOutboundWorker);
await this.ctx.container.interceptOutboundHttp('*.example.com', secondOutboundWorker);
await this.ctx.container.interceptOutboundHttp('123.123.123.123/23', secondOutboundWorker);
모든 프록시는 샌드박스 VM과 동일한 머신에서 로컬로 실행되므로, 컨테이너와 Worker 간 통신은 "인증 없이"도 안전합니다.

주의사항 및 한계
- 성능 오버헤드: MITM 프록시를 사용하면 TLS 핸드셰이크가 추가로 발생합니다. 지연 시간이 매우 민감한 애플리케이션은 주의가 필요합니다.
- 인증서 신뢰 문제: 샌드박스 내부에 설치된 임시 CA는 해당 샌드박스에서만 유효합니다. 표준 컨테이너에서는
sudo update-ca-certificates등으로 수동 설정이 필요할 수 있습니다. - 복잡한 정책:
outboundHandlers를 여러 개 정의할수록 관리 복잡도가 증가합니다. 정책을 코드로 관리하므로 버전 관리와 테스트가 필수입니다. - 국내 클라우드 호환성: 현재 이 기능은 Cloudflare Workers 생태계 전용입니다. 국내 클라우드에서 유사한 기능을 구현하려면 자체 프록시 서버(예: Envoy, Nginx)를 VM 내에 배포해야 할 수 있습니다.
다음 단계 학습 방향
- Cloudflare Sandbox 공식 문서에서 더 다양한 예제를 확인하세요.
- OIDC와 outbound Workers를 결합한 워크로드 아이덴티티 연합 패턴을 학습해보세요.
wrangler dev를 이용한 로컬 개발 환경 설정 방법을 익히면 생산성이 크게 향상됩니다.
![]()
결론
outbound Workers는 샌드박스 환경에서 제로 트러스트, 동적 제어, 투명성을 모두 만족하는 강력한 인증/네트워크 제어 솔루션입니다. 단 몇 줄의 JavaScript로 API 토큰을 안전하게 주입하고, 동적으로 네트워크 정책을 변경하며, 모든 트래픽을 관찰할 수 있습니다.
특히 LLM 에이전트가 점점 더 많은 권한을 필요로 하는 현재, 이러한 접근 방식은 필수적입니다. "신뢰할 수 없는 코드"에 "최소 권한"만 부여한다는 원칙을 코드 레벨에서 실현할 수 있습니다.
함께 보면 좋은 글