__anthropic_api_key__ 같은 가짜 문자열만 들어있고, 요청이 실제로 바깥으로 나가는 그 순간에 프록시가 서버 쪽 메모리에서 진짜 값으로 바꿔치기한다.
이 문서가 파고드는 건 "시크릿 매니저 하나 더 나왔다"가 아니다 — Go로 MITM 프록시를 처음부터 구현하고, TLS를 중간에서 끊었다가 다시 잇고, 자격증명을 헤더에 주입하면서도 로그에는 절대 남기지 않는 일을 실제 프로덕션 코드로 어떻게 해내는가이다.
CONNECT 하이재킹 · SNI별 인증서 즉석 발급 · Argon2id KEK/DEK 키 래핑 · SSRF 차단 · 사람 승인 게이트 — 보안 엔지니어링 교과서에 나오는 패턴이 한 저장소 안에 전부 들어있다.
(저장소: Infisical/agent-vault · Go 1.25 + React 19 · MIT · 별 1,908 · 포크 109 · 최신 릴리스 v0.39.0 · Go 소스 약 35,818줄 · 테스트 파일 54개)
Agent Vault는 MITM(중간자) 프록시다. 즉 당신의 TLS 연결을 중간에서 끊고 내용을 들여다본다. 이건 버그가 아니라 설계다 — 그래야 헤더에 진짜 키를 꽂아 넣을 수 있다. 다만 이 말은 Agent Vault 서버를 장악한 사람은 당신 에이전트의 모든 API 트래픽을 평문으로 볼 수 있다는 뜻이기도 하다.
그래서 공식 문서도 운영 시 에이전트와 별도 호스트에 배포하고, 프록시 포트(14322)는 사설망에만 열고, 관리 UI(14321)는 TLS + IP allowlist로 막으라고 권한다. README 스스로 "Preview — 활발히 개발 중, API가 바뀔 수 있음"이라고 밝히고 있으니 프로덕션 도입 전에 버전 고정은 필수다.
에이전트를 HTTPS_PROXY=http://토큰@agent-vault:14322 환경에서 실행시키면, 그 프로세스가 만드는 모든 아웃바운드 HTTP 요청이 Agent Vault를 반드시 거쳐 간다. Agent Vault는 목적지 호스트를 보고 "아, api.anthropic.com이네" 하고 판단해서, 암호화 저장소에서 진짜 x-api-key를 꺼내 헤더에 꽂은 뒤 전달한다. 에이전트 프로세스의 메모리·환경변수·로그 어디를 뒤져도 진짜 키는 없다.
회사 법인카드 대신 쓰는 "결제 대행 창구"를 생각하면 된다. 신입사원에게 법인카드 실물을 주면, 그 사람이 카드를 잃어버리거나 사기 전화에 속아 번호를 불러줄 위험이 있다. 대신 회사는 "결제할 일 있으면 창구에 말해"라고 한다. 신입은 "이 거래처에 30만원 결제해주세요"라고 요청서만 내고, 창구 직원이 금고에서 카드를 꺼내 대신 긁는다.
여기서 신입사원 = AI 에이전트, 결제 창구 = Agent Vault, 법인카드 = API 키다. 신입이 아무리 속아 넘어가도, 애초에 카드 번호를 모르니 불러줄 수가 없다. 이게 이 프로젝트 전체를 관통하는 단 하나의 아이디어다.
ANTHROPIC_API_KEY를 뱉는다. 사람이라면 "이상한데?" 하고 멈추지만, 모델은 텍스트를 텍스트로 처리할 뿐이다.Agent Vault가 노리는 지점이 정확히 여기다. 프롬프트 인젝션을 막으려고 하는 게 아니라 — 그건 근본적으로 어렵다 — 인젝션이 성공하더라도 훔쳐 갈 물건 자체를 그 방에 두지 않는 쪽으로 문제를 우회한다. 에이전트가 속아서 환경변수를 통째로 출력해도, 나오는 건 __anthropic_api_key__라는 무의미한 문자열이다.
"시크릿 관리 도구는 이미 널렸는데 왜 또?"가 당연한 첫 반응이다. 답은 기존 도구들이 전부 "시크릿을 돌려주는(return)" 모델이라는 데 있다. 애플리케이션이 볼트에 물어보면 볼트가 값을 준다. 그 순간부터 값은 애플리케이션 손에 있고, 애플리케이션이 LLM 에이전트라면 그 값은 프롬프트 인젝션 사거리 안에 들어온다.
| 방식 | 키가 에이전트 손에 들어가나? | 한계 |
|---|---|---|
.env 파일 | 예 — 프로세스 환경변수에 그대로 | 인젝션 한 방에 전량 유출. 감사 로그 없음, 폐기(rotation)도 수동 |
| HashiCorp Vault 1Password | 예 — 조회 API가 값을 반환 | "누가 꺼내 갔나"는 알 수 있지만, 꺼낸 뒤 어디에 쓰였는지는 모름 |
| mitmproxy / squid | 아니오 | 범용 프록시라 자격증명 브로커링을 하려면 애드온을 직접 작성해야 함 |
| Agent Vault | 아니오 — 값을 반환하지 않고 대신 주입 | MITM 구조라 프록시 서버 자체가 새로운 신뢰 중심점이 됨 |
이 한 글자 차이가 전부다. 반환 모델에서는 시크릿의 수명이 애플리케이션이 죽을 때까지다. 주입 모델에서는 시크릿의 수명이 HTTP 요청 하나가 나가는 수 밀리초다. 공격 표면의 시간축 자체가 다르다.
실무에서 이게 채택률을 가른다. Agent Vault를 붙이는 데 에이전트 코드를 한 줄도 고칠 필요가 없다. HTTPS_PROXY 환경변수는 30년 된 표준이라 curl, Python requests, Node fetch, Go http.Client가 전부 알아서 존중한다. 그래서 Claude Code, Codex, Cursor, opencode, Hermes, OpenClaw, nanoclaw 각각에 대한 quickstart 문서가 존재하는데, 내용은 사실상 전부 "환경변수 두 개 꽂으세요"다.
회사에 새 보안 정책을 도입할 때, "모든 직원이 새 소프트웨어를 배우세요"는 실패한다. "건물 정문에 검색대를 하나 세웠고, 여러분은 평소처럼 걸어 나가면 됩니다"는 성공한다. Agent Vault는 후자다. 에이전트는 자기가 프록시를 거치는지도 모른다.
단순 프록시를 넘어서 실제 팀이 쓸 만한 살이 붙어 있다는 점이 트렌딩에 오른 이유다.
| 기능 | 설명 |
|---|---|
| Vault (멀티테넌시) | 프로젝트·팀별로 자격증명 묶음을 분리. 에이전트 토큰마다 접근 가능한 vault가 정해짐 |
| 에그레스 필터링 | 어떤 호스트로 나갈 수 있는지 allowlist 통제. 미허용 호스트는 403 |
| 감사 로그 | 어떤 에이전트가 언제 어느 서비스를 호출했는지 전량 기록 (바디·쿼리는 저장 안 함) |
| Proposal 워크플로우 | 에이전트가 "이 서비스 쓰고 싶다"고 제안 → 사람이 UI에서 승인 → 즉시 반영 |
| 컨테이너 격리 | Docker + iptables로 에이전트가 프록시를 우회하는 것 자체를 물리적으로 차단 |
| OAuth 자동 갱신 | 만료된 토큰을 프록시가 알아서 리프레시하고 요청을 재시도 |
| 관리 웹 UI | React 19 SPA가 Go 바이너리에 임베드돼 단일 파일로 배포 |
| 사전 정의 카탈로그 | Anthropic·OpenAI·Stripe·GitHub·Slack 등 22개 서비스 템플릿 내장 |
만든 곳이 Infisical이라는 점도 신뢰에 기여한다. 이미 오픈소스 시크릿 매니저로 자리 잡은 회사라, "에이전트 시대용 두 번째 제품"이라는 맥락이 자연스럽다. 릴리스는 goreleaser로 자동화돼 있고 GitHub Actions build provenance attestation과 cosign 서명까지 붙는다 — 공급망 보안을 신경 쓰는 팀이라는 신호다.
| 영역 | 라이브러리 | 역할 |
|---|---|---|
| CLI 프레임워크 | spf13/cobra | server·run·vault·agent·proposal 등 서브커맨드 트리 |
| 대화형 TUI | charmbracelet/huh·lipgloss | 마스터 비밀번호 입력 폼 등 예쁜 CLI 대화창 |
| ORM / DB | gorm.io/gorm + sqlite·postgres 드라이버 | SQLite(기본, 제로 설정) ↔ PostgreSQL(HA 운영) 듀얼 백엔드 |
| 암호화 | 표준 crypto/aes·cipher + x/crypto | AES-256-GCM(자격증명·CA 키), Argon2id(마스터 비번 KDF) |
| 시크릿 소스 | infisical/go-sdk | Infisical 인스턴스를 외부 동적 시크릿 스토어로 연동 |
| 텔레메트리 | posthog/posthog-go | 익명 사용 이벤트 (자격증명 값은 절대 미포함) |
| 동시성 | golang.org/x/sync | singleflight — OAuth 리프레시 중복 방지 |
| 출력 포맷 | go-pretty·fatih/color | CLI 테이블·컬러 출력 |
modernc.org/sqlite(순수 Go 재구현)를 쓰는 것도 CGO를 피하려는 선택이다 — 보통 SQLite는 C 라이브러리라 CGO가 필요하다.web/)React 19.2 + @tanstack/react-router SPA, 빌드는 Vite 8, 스타일은 Tailwind CSS 4, 언어는 TypeScript 6. 여기서 흥미로운 건 배포 방식이다 — 빌드 산출물(web/dist)이 Go 바이너리 안에 임베드(internal/server/webdist)된다. 즉 관리 UI를 서빙하는 별도 웹서버가 없다. Go 바이너리 하나가 API도 프록시도 UI도 전부 처리한다.
| 수단 | 내용 |
|---|---|
| Docker | 3단계 멀티스테이지 빌드: Node 26(프론트) → Go 1.26.4(정적 바이너리) → alpine 3.24 런타임(non-root UID 65532) |
| install.sh | macOS(Intel/ARM)·Linux(x86_64/ARM64) 원클릭 설치 스크립트 |
| fly.toml | Fly.io 배포 지원 (내장) |
| goreleaser | 릴리스 자동화 + provenance attestation + cosign 서명 |
| PostgreSQL HA | 여러 인스턴스가 같은 DB 공유. CA 상태도 DB에 저장해 팟 간 동기화 |
이 장이 이 문서의 심장이다. HTTPS 요청 하나가 에이전트 프로세스에서 출발해 진짜 API 서버에 도착할 때까지, Agent Vault 내부에서 벌어지는 13단계를 따라간다. 세 지점에 주목하자 — 세션 인증(누구냐), 자격증명 주입(진짜 키 갈아 끼우기), 감사 로그(무슨 일이 있었나).
이 구조가 성립하려면 진짜 키가 흘러갈 수 있는 모든 경로를 막아야 한다. 코드는 그걸 여러 겹으로 처리한다.
forwardRequest 안, 서버 프로세스 메모리에서만 잠깐 존재하고 요청이 나가면 사라진다.InjectResult.Headers에는 코드 주석으로 "SECRET 값 — 절대 로그 금지"가 박혀 있다.Proxy-Authorization)은 브로커 전용 헤더로 분류돼 업스트림으로 절대 전달되지 않는다 — 에이전트 토큰과 진짜 API 키는 완전히 분리된 두 신뢰 도메인이다.crypto/rand 256비트로 만들고 SHA-256 해시만 DB에 저장. 원본은 발급 시 딱 한 번만 노출된다.Connection, Proxy-Authorization 등). Agent Vault는 여기에 자기만의 규칙을 더해서, 에이전트↔프록시 사이에서만 의미 있는 헤더(X-Vault, Proxy-Authorization)를 업스트림으로 넘기기 전에 반드시 벗겨낸다. 안 그러면 에이전트 토큰이 실제 API 서버 로그에 찍힐 수 있다.가장 기술적으로 까다로운 부분이다. HTTPS를 중간에서 들여다보려면 TLS를 한 번 끊어야 하는데, 그러려면 api.anthropic.com인 척하는 인증서가 필요하다. Agent Vault는 이걸 즉석에서 만든다.
에이전트 쪽에서 이 루트 CA를 신뢰하게 만드는 것도 세심하다. 언어 런타임마다 CA 인증서를 찾는 환경변수가 제각각이라, Agent Vault는 SSL_CERT_FILE·NODE_EXTRA_CA_CERTS·REQUESTS_CA_BUNDLE·CURL_CA_BUNDLE·GIT_SSL_CAINFO·DENO_CERT를 한꺼번에 세팅해준다. 이건 몰래 하는 공격이 아니라, 양쪽이 합의한 의도적 MITM이라는 점이 핵심이다.
일부 프록시는 편의를 위해 HTTPS 요청을 몰래 평문 HTTP로 낮춰서 처리한다. Agent Vault는 이걸 명시적으로 거부한다. https:// 대상은 반드시 CONNECT 터널을 강제하고, 평문 스트리핑을 하지 않는다. 대신 http:// 업스트림용으로는 CONNECT 없는 absolute-form 포워드 프록시 요청을 같은 포트에서 별도 경로로 처리한다.
요청이 들어오면 "이 호스트/경로에 어떤 자격증명을 붙일까"를 정해야 한다. 규칙이 여러 개 겹칠 수 있으니(*.github.com vs api.github.com), 가장 구체적인 규칙이 이기도록 점수를 매긴다. nginx의 location 매칭과 똑같은 종류의 문제다.
| 우선순위 기준 | 설명 |
|---|---|
| 호스트 티어 | 정확 매칭(exact) > 와일드카드(*.github.com, 정확히 1단계 서브도메인만) |
| 포트 특정성 | 포트를 명시한 규칙이 우선 |
| 경로 리터럴 길이 | 더 긴 경로 패턴이 우선 |
| 선언 순서 | 위 조건이 같으면 먼저 선언된 규칙 |
Auth 타입은 5종이다 — bearer(Authorization: Bearer), basic(Basic 인증), api-key(임의 헤더), custom({{ KEY }} 플레이스홀더), passthrough(자격증명 주입 없이 호스트 allowlist·TLS 가로채기·감사로그만). 특히 치환(Substitution)은 헤더뿐 아니라 URL 경로·쿼리·바디·웹소켓 프레임까지 스캔할 수 있는데, 이건 In 필드로 스코프를 명시해야만 켜지는 opt-in 보안 경계다.
에이전트가 http://169.254.169.254/(클라우드 메타데이터)로 요청해 IAM 자격증명을 훔치려는 고전적 공격이 있다. SafeDialContext가 모든 아웃바운드 연결 직전에 목적지 IP를 검사해 RFC1918 사설망·loopback·link-local을 차단하고, 메타데이터 엔드포인트는 allowlist에 넣어도 무조건 막는다. 호스트명을 IP로 해석한 뒤 그 해석된 IP로 직접 연결해서 DNS 리바인딩(검사 후 IP를 바꿔치기하는 공격)도 봉쇄한다.
부팅 시 랜덤 256비트 DEK(Data Encryption Key)를 만들어 모든 자격증명과 CA 개인키를 암호화한다. 마스터 비밀번호를 걸면 Argon2id(time=3, memory=64MiB, parallelism=4)로 KEK(Key Encryption Key)를 유도해 DEK를 감싼다. KEK 자체는 저장하지 않고 쓰고 나면 즉시 메모리에서 지운다(WipeBytes). 비밀번호를 바꿔도 모든 데이터를 재암호화할 필요 없이 KEK만 다시 감싸면 되는 경량 설계다.
미등록 호스트로 가는 요청은 정책에 따라 통과하거나(passthrough), deny 정책이면 403 + proposal_hint를 돌려준다. 에이전트는 POST /v1/proposals로 "이 서비스 쓰고 싶어요"를 제안하고, 사람이 UI/CLI에서 값을 채워 승인한다(pending → applied/rejected/expired, 7일 TTL). 승인 즉시 서비스+자격증명이 원자적 트랜잭션으로 vault에 반영되고, 에이전트는 재시도만 하면 된다. 자동화에 사람의 승인 게이트를 안전하게 끼워 넣는 상태 머신의 좋은 예다.
HTTPS_PROXY 방식의 약점은 "에이전트가 그 환경변수를 무시하기로 결심하면 끝"이라는 점이다(협조적 모델). vault run --container 모드는 Docker per-invocation 브리지 네트워크 + iptables egress lock으로, 에이전트 컨테이너가 프록시 외의 아웃바운드를 시도조차 못 하게 물리적으로 막는다(비협조적 모델).
| 디렉토리 | 역할 |
|---|---|
cmd/ | Cobra CLI 전체(약 50개 파일). run.go(에이전트 프로세스 래핑), server.go, vaults.go, run_container.go(격리 실행), skill_cli.md(에이전트에게 배포되는 스킬 파일) |
internal/mitm/ | MITM 프록시 본체 — proxy.go(디스패치)·connect.go(CONNECT+TLS 종료)·forward.go(주입+포워딩)·websocket.go |
internal/brokercore/ | 런타임 접착제 — 세션 해석·자격증명 주입·바디 치환·프록시 인증 파싱 |
internal/broker/ | 순수 설정 라이브러리 — 서비스 YAML 스키마, 호스트/경로 매칭 스코어링. 런타임 의존성 없음 |
internal/ca/ | 소프트웨어 CA — 루트 생성·leaf 즉석 발급·LRU 캐시·DB/파일 이중 저장 |
internal/crypto/ | AES-256-GCM 암복호화 + Argon2id KDF |
internal/netguard/ | SSRF·사설망·메타데이터 차단 |
internal/isolation/ | Docker 컨테이너 격리 실행(네트워크·볼륨·firewall 스크립트) |
internal/store/ | GORM 기반 SQLite/Postgres 듀얼 마이그레이션(50+ 순차 마이그레이션). 이름 변경 이력이 코드로 남아 설계 진화 추적 가능(secrets→credentials, namespace→vault) |
internal/requestlog/ | 감사 로그 배치 싱크(1024 버퍼, 100개/250ms 플러시) + 보존 정책 |
internal/ratelimit/ | 4계층 레이트리미터(AUTH·AUTHED·PROXY·GLOBAL) |
internal/oauth/ | OAuth 자동 리프레시(singleflight로 중복 방지) |
internal/catalog/ | Anthropic·Stripe·GitHub·Slack 등 22개 사전 정의 서비스 템플릿 |
internal/server/ | HTTP 관리 API + 프론트엔드 임베드(webdist) |
web/ | React 19 + Vite 8 + Tailwind 4 관리 UI |
sdks/sdk-typescript/ | 오케스트레이터용 TS SDK(@infisical/agent-vault-sdk) |
examples/ | nginx 공개 UI 프록시, Daytona+OpenAI Realtime, Slack 에이전트 템플릿 |
docs/ | Mintlify 문서(learn·quickstart·reference·agents·self-hosting) |
broker와 brokercore가 나뉜 게 이 코드베이스의 설계 감각을 보여준다. broker = 순수 규칙 계산기(입력을 받아 "어떤 자격증명을 붙일지" 판단만, 부작용 없음), brokercore = 실제로 DB를 뒤지고 헤더를 세팅하는 손. 순수 로직과 부작용을 분리하면 broker는 DB 없이도 단위 테스트가 가능하다 — 테스트 파일이 54개인 이유 중 하나다.
services:
- name: api-anthropic-com
host: api.anthropic.com
auth:
type: api-key
key: ANTHROPIC_KEY
header: x-api-key
# 서비스 규칙을 vault에 등록
agent-vault vault service set -f stripe-services.yaml --vault my-vault
# 에이전트를 프록시 아래에서 실행 (환경변수 자동 주입)
agent-vault run -- claude
agent-vault vault run -- agent
// inject.Headers 키를 미리 벗겨내는 것이 "주입값이 항상 이긴다"
// 보안 불변식을 지킨다. 성능 최적화가 아니다.
func ApplyInjection(src, dst http.Header, inject *InjectResult, extraStrip ...string) {
strip := make(map[string]bool, len(extraStrip)+len(inject.Headers))
// ... 클라이언트 헤더 복사 + hop-by-hop/브로커 헤더 제거 ...
for k, v := range inject.Headers {
dst.Set(k, v) // Set = 덮어쓰기. 에이전트가 보낸 값은 무시된다
}
}
tlsConf := &tls.Config{
MinVersion: tls.VersionTLS12,
NextProtos: []string{"http/1.1"},
GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
sni := hello.ServerName
if sni == "" { sni = host }
return p.ca.MintLeaf(sni) // 이 도메인용 인증서를 지금 만든다
},
}
var alwaysBlocked = []net.IPNet{
parseCIDR("169.254.169.254/32"), // AWS/GCP/Azure IMDS
parseCIDR("fd00:ec2::254/128"), // AWS IMDSv2 IPv6
}
| 주제 | 구체적으로 무엇을 |
|---|---|
| Go 프록시 구현 | net/http.Hijacker로 연결을 가로채 tls.Server로 TLS를 끊는 저수준 패턴. CONNECT vs absolute-form 두 규약을 한 리스너에서 처리 |
| 동적 인증서 발급 | mkcert가 하는 일을 서버에서 — GetCertificate 콜백 + SNI + ECDSA + LRU 캐시 |
| KEK/DEK 키 래핑 | 비밀번호-옵셔널 암호화 설계(패스워드 없이도 동작, 있으면 더 안전) |
| SSRF / DNS 리바인딩 방어 | "해석→검증→검증된 IP로 직접 dial" 패턴 |
| 매칭 스코어링 엔진 | exact > wildcard, 경로 길이, 선언 순서로 최적 매치 고르기(nginx location 문제) |
| GORM 듀얼 백엔드 | SQLite↔PostgreSQL 하나의 코드로, 50+ 순차 마이그레이션 운용 |
| 논블로킹 감사 로그 | 버퍼가 차면 드롭(카운터만 증가)하는 hot-path 안전 패턴 |
| 사람 승인 상태 머신 | 자동화에 승인 게이트를 끼우는 pending→applied/rejected/expired 설계 |
| 항목 | 요구사항 |
|---|---|
| 빌드용 Go | Go 1.25+ (go.mod 기준). 릴리스 바이너리는 CGO_ENABLED=0 정적 빌드라 런타임 의존성 없음 |
| OS / 아키텍처 | macOS(amd64/arm64), Linux(x86_64/arm64) |
| DB | SQLite 기본(설정 불필요) / DATABASE_URL 지정 시 PostgreSQL(멀티 인스턴스 HA) |
| 컨테이너 격리 모드 | Docker 필요 |
| 포트 | 14321(관리 API/UI), 14322(MITM 프록시 — CONNECT + absolute-form 겸용) |
# 설치
curl --proto '=https' --tlsv1.2 -fsSL https://get.agent-vault.dev | sh
# 서버 기동
export AGENT_VAULT_MASTER_PASSWORD=your-password
agent-vault server -d
# 에이전트 실행
agent-vault run -- claude
docker run -it -p 14321:14321 -p 14322:14322 \
-e AGENT_VAULT_MASTER_PASSWORD=your-password \
-v agent-vault-data:/data infisical/agent-vault
Agent Vault 서버를 장악당하면 모든 에이전트의 API 트래픽과 저장된 자격증명이 노출된다. 공식 권장: 에이전트와 별도 호스트에 배포, 프록시 포트(14322)는 사설망에만, 관리 UI(14321)는 TLS + IP allowlist로 보호. 그리고 "Preview" 단계이니 버전(예: v0.39.0)을 고정하고 업그레이드 전 릴리스 노트를 확인할 것.
SQLite 기본 설정으로 agent-vault server를 띄우고, 아무 API(예: httpbin.org)에 대한 서비스 규칙을 하나 만든다. 그다음 agent-vault run -- env | grep -i key로 에이전트 환경변수를 출력해서, 진짜 키가 아니라 __..__ 더미 값만 들어있는 걸 직접 확인한다. "인젝션이 성공해도 훔칠 게 없다"를 몸으로 이해하는 단계.
net/http.Hijacker로 CONNECT를 가로채고, 자체 서명 인증서로 tls.Server 핸드셰이크를 완료한 뒤, 지나가는 요청의 Host를 로그로 찍는 최소 프록시를 직접 짠다. Agent Vault의 connect.go를 참고 삼아 축소판을 만들어보면 MITM의 원리가 손에 잡힌다.
internal/broker의 매칭 로직만 떼어내, api.github.com·*.github.com·* 세 규칙이 경합할 때 어떤 게 이기는지 결정하는 함수를 스스로 작성하고 테스트를 짠다. exact>wildcard, 경로 길이, 선언 순서 우선순위를 직접 구현해보면 "구체성 스코어링"이라는 흔한 문제 유형을 익힐 수 있다.
netguard를 대상으로, DNS 리바인딩(처음엔 공인 IP를 주고 두 번째 조회에서 169.254.169.254로 바꾸는)으로 메타데이터 엔드포인트에 도달할 수 있는지 로컬 DNS 서버를 세워 시험한다. "해석된 IP로 직접 dial"이 왜 리바인딩을 막는지 코드로 확인하는 방어 검증 실습.
POST /v1/proposals로 제안이 생기면 Slack으로 승인 버튼을 보내고, 버튼 클릭으로 승인 API를 호출하는 브릿지를 만든다. examples/의 Slack 에이전트 템플릿을 출발점으로, 상태 머신(pending→applied)에 외부 승인 채널을 연결하는 실전 통합.
| 주차 | 주제 | 무엇을 |
|---|---|---|
| 1주차 | HTTP 프록시 기초 | 포워드 vs 리버스 프록시, CONNECT 메서드, hop-by-hop 헤더, HTTPS_PROXY 규약. mitmproxy 문서로 개념 잡기 |
| 2주차 | TLS와 PKI | 인증서 체인, CA·leaf, SNI, 신뢰 저장소. Go crypto/tls·crypto/x509로 CA 만들고 leaf 서명해보기 |
| 3주차 | 시크릿 관리 패턴 | KEK/DEK 래핑, Argon2id/scrypt KDF, 봉투 암호화(envelope encryption). AWS KMS·HashiCorp Vault 모델과 Agent Vault 비교 |
| 4주차 | 에이전트 보안 | 프롬프트 인젝션, 최소 권한, 에그레스 필터링, 감사 로그. OWASP LLM Top 10 훑고 Agent Vault가 각 항목을 어떻게 다루는지 매핑 |
CONNECT host:443 요청을 프록시가 가로채(Go에서는 Hijacker 인터페이스로) 그 TCP 연결의 주도권을 뺏는 것. 그래야 프록시가 그 자리에서 TLS 서버 행세를 할 수 있다.api.anthropic.com에 접속하려는데요"라고 평문으로 알려주는 필드. Agent Vault는 이걸 읽어서 그 도메인용 가짜 인증서를 즉석 발급한다.gh attestation verify로 검증한다. 공급망 공격(빌드 과정에 악성코드 주입)을 막는 최신 관행.