AI 에이전트가 제 비밀번호를 못 보게 만들었습니다: keygate에 Bitwarden 붙인 기록
AI 코딩 에이전트가 .env와 API 키를 그대로 읽어버리는 게 불편해서, key-amnesia를 포크해 제가 쓰던 비밀번호 관리자 Bitwarden을 붙인 keygate를 만들고 테스트한 기록.
Claude Code, Codex, Cursor 같은 AI 코딩 에이전트를 매일 씁니다. 그런데 이 에이전트들이 프로젝트 안에 있는 .env 파일이나 API 키를 그냥 읽을 수 있다는 게 계속 마음에 걸렸습니다. .gitignore에 .env를 넣는 건 git으로부터 시크릿을 지키는 방법이지, 지금 이 디렉터리에서 파일을 읽을 수 있는 에이전트로부터 지키는 방법은 아니었습니다.
그러다 key-amnesia를 봤습니다. 그리고 여기에 제가 원래 쓰던 비밀번호 관리자인 Bitwarden을 붙이면 딱 맞겠다는 생각이 들어서, 직접 만들어봤습니다. 이 글은 그 과정과 테스트하면서 만난 문제, 그리고 실제 사용법을 정리한 기록입니다.
1. key-amnesia: 에이전트에게 “기억상실”을 준다는 아이디어
key-amnesia의 접근은 명확했습니다. 시크릿은 암호화된 vault에 저장해두고, 에이전트는 그 값을 직접 보는 대신 값을 쓰는 명령어만 트리거합니다. ka run --secret API_KEY -- curl ...처럼 실행하면, API_KEY는 자식 프로세스의 환경변수로만 주입되고, 에이전트가 보는 출력에서는 그 값이 애초에 등장하지 않습니다. 혹시 명령어 출력에 값이 그대로 찍히더라도, key-amnesia가 그걸 먼저 가로채 지웁니다.
한 가지 더: 세션을 새로 열 때 필요한 마스터 비밀번호는 에이전트가 절대 입력할 수 없는 별도의 콘솔 창에서만 물어봅니다. 에이전트는 그 창을 읽을 수도, 타이핑할 수도 없습니다.
“에이전트는 기억하지 못한다(amnesia)“는 이름 그대로였습니다. 제가 겪던 불편함을 정확히 겨냥한 도구였습니다.
2. 그런데 저는 이미 Bitwarden을 쓰고 있었다
딱 하나 걸리는 게 있었습니다. key-amnesia는 자체 암호화 vault(Argon2id + SecretBox)를 새로 만듭니다. 즉 이 도구를 쓰려면 마스터 비밀번호를 하나 더 기억해야 하고, 시크릿을 저장하는 곳도 하나 더 생깁니다.
저는 이미 비밀번호 관리자로 Bitwarden을 쓰고 있었습니다. API 키나 DB 비밀번호도 이미 거기 들어 있는 게 많았고요. 굳이 별도의 vault를 새로 배우고 관리하기보다는, 이미 신뢰하고 쓰던 Bitwarden을 백엔드로 붙이면 되지 않을까 싶었습니다. key-amnesia는 Apache-2.0으로 공개돼 있었으니, 포크해서 시도해볼 수 있었습니다.
3. 포크하고 Bitwarden 백엔드를 붙이다
작업은 시크릿 저장/조회 인터페이스를 추상화하는 것부터 시작했습니다. key-amnesia의 로컬 vault든 Bitwarden이든 같은 방식으로 다룰 수 있도록, unlock / lock / load_secrets / set_secret / list_names 같은 메서드를 가진 SecretBackend 추상 클래스를 만들고, 그 위에 BitwardenBackend를 구현했습니다. Bitwarden CLI(bw)로 key-amnesia라는 전용 폴더를 만들고, 그 안에 시크릿 하나당 Secure Note 하나(제목이 이름, 노트 내용이 값)로 저장하는 방식입니다. CLI 이름은 ka에서 kg로, 프로젝트 이름은 keygate로 바꿨습니다.
솔직히 이 부분은 혼자 타이핑한 게 아니라 Claude Code와 함께 짰습니다. 실제로 git 커밋 로그를 보면 첫 기능 커밋(09:20)부터 PyPI에 keygate-cli로 배포한 커밋(09:53)까지 33분이 걸렸습니다. 코드를 새로 발명했다기보다는, 이미 잘 만들어진 key-amnesia의 구조(출력 스크러빙, subprocess 실행, PreToolUse 훅) 위에 백엔드 하나를 갈아 끼운 작업에 가깝습니다.
이 33분 안에 작은 사고도 하나 있었습니다. key-amnesia에서 물려받은 테스트 픽스처에는 시크릿 탐지 기능을 테스트하기 위한 가짜 자격 증명이 들어 있는데, GitHub에 푸시하자 GitGuardian이 이걸 진짜 시크릿 유출로 오탐했습니다. 첫 반응은 “테스트를 지우자”였고, 실제로 tests/ 전체를 지우는 커밋을 만들었습니다. 하지만 이건 key-amnesia가 검증해온 테스트 스위트 전체를 버리는 대가였다는 걸 바로 깨닫고, 64초 뒤 그 커밋을 되돌렸습니다. 대신 .gitguardian.yml에 tests/** 경로를 오탐 예외로 등록하는 쪽으로 고쳤습니다.
배운 것: 스캐너가 시끄럽다고 원인(테스트 코드)을 지우는 건 제일 쉬운 해결책이지만, 대체로 틀린 해결책입니다. 문제가 나를 귀찮게 하는 지점과 실제로 고쳐야 하는 지점은 다를 때가 많습니다. 여기서는 “가짜 자격 증명이 있다”는 사실이 아니라 “스캐너가 그걸 구분하지 못한다”는 게 진짜 문제였고, 스캐너 설정을 고치는 게 맞는 방향이었습니다.
4. 테스트하다 만난 문제: 명령어 하나에 2~5초
기능을 붙인 뒤에는 실제 Bitwarden CLI로 테스트했습니다. bw login으로 로그인하고, kg unlock → kg set → kg run 순서로 하나씩 직접 돌려봤습니다.
문제는 바로 드러났습니다. kg unlock뿐 아니라 kg set, kg run 같은 명령어 하나하나가 전부 2~5초씩 걸렸습니다. 원인을 보니, 명령어를 실행할 때마다 매번 bw 인증을 처음부터 다시 하고 있었습니다. 시크릿을 하나 주입해서 명령 하나 실행하는 데 몇 초씩 걸리면, 에이전트가 반복적으로 kg run을 호출하는 흐름에서는 그 지연이 누적됩니다.
고친 방식은 이렇습니다. kg unlock 시점에만 Bitwarden에 인증하고, 그때 시크릿 값 전체를 로컬 캐시(~/.keygate/secrets_cache.json, 권한 0600)에 한 번 내려받아 둡니다. 그 뒤 kg run이나 kg list는 bw를 다시 호출하지 않고, 이 로컬 캐시 파일만 읽습니다. kg lock을 하면 이 캐시 파일이 즉시 삭제됩니다.
kg unlock 때만 Bitwarden과 통신하고, kg run은 로컬 캐시만 읽는다. 에이전트가 보는 건 스크러빙된 출력뿐이다.
kg unlock 한 번은 여전히 2~3초 걸리지만(사람이 마스터 비밀번호를 직접 입력하는 구간이라 크게 문제되지 않습니다), 그 이후 kg run과 kg list는 체감상 즉시 실행됩니다.
배운 것: 속도 문제는 결국 “어디서 반복하고 있는가”의 문제였습니다. 매번 해야 하는 일(인증)과 한 번만 하면 되는 일(같은 세션 안에서의 조회)이 뒤섞여 있으면, 필요 이상으로 매번 비싼 작업을 반복하게 됩니다. 인증과 조회를 시간 축으로 분리하고 나니 자연스럽게 풀렸습니다.
5. 이렇게 씁니다
설치는 pip 한 줄입니다. Bitwarden CLI(bw)가 먼저 설치돼 있어야 합니다.
# Bitwarden CLI 설치 (macOS)
brew install bitwarden-cli
# keygate 설치
pip install keygate-cli
# 또는 격리된 환경으로
pipx install keygate-cli
처음 한 번은 Bitwarden에 로그인하고, keygate가 Bitwarden을 쓰도록 설정합니다.
# 1. Bitwarden 로그인 (최초 1회)
bw login
# 2. keygate가 Bitwarden 백엔드를 쓰도록 설정
mkdir -p ~/.keygate
echo '{"backend":"bitwarden"}' > ~/.keygate/config.json
# 3. 잠금 해제 (여기서만 Bitwarden과 통신하고, 로컬에 캐시)
kg unlock
그 다음부터는 이렇게 씁니다.
# 저장된 시크릿 이름 목록 (값은 안 보임)
kg list
# 시크릿을 주입해서 명령 실행
kg run --secret API_KEY -- curl https://api.example.com
# 환경변수 이름을 다르게 매핑
kg run --secret DB_PASS --as DB_PASS=PGPASSWORD -- psql
# 새 시크릿 저장 (Bitwarden에 Secure Note로 추가)
kg set MY_NEW_KEY
# 다 썼으면 잠그기 (로컬 캐시 즉시 삭제)
kg lock
Claude Code나 Codex, Cursor에 물릴 때는 ./install.sh로 스킬 파일과 PreToolUse 훅을 한 번에 등록할 수 있습니다. 훅이 등록되면 에이전트가 kg set이나 kg reveal처럼 값을 노출시킬 수 있는 명령을 임의로 실행하려는 것도 막습니다.
6. key-amnesia에게
keygate는 key-amnesia(Apache-2.0)의 포크입니다. 출력 스크러빙, subprocess 실행, 시크릿 탐지, PreToolUse 훅 같은 핵심은 거의 그대로 가져왔고, 제가 더한 건 Bitwarden 백엔드와 그에 맞춘 캐시 구조뿐입니다. 원본이 이미 이 문제(“에이전트가 사람 대신 시크릿을 안전하게 쓸 수 있게 하려면 어떻게 설계해야 하는가”)를 진지하게 풀어놓은 덕분에, 저는 제가 원래 쓰던 도구 하나를 갈아 끼우는 데만 집중할 수 있었습니다.
이미 Bitwarden을 쓰고 있고, AI 에이전트가 프로젝트 안의 .env나 API 키를 그대로 읽고 있는 게 불편했던 분이라면 한번 써보셔도 좋을 것 같습니다.
- GitHub 저장소
- PyPI 패키지
- 원본 프로젝트: key-amnesia