Vaultwarden 복구
AI 요청 프롬프트
https://docs.axelabs.ai/ops/runbook/vault-recovery 따라 axe-vaultwarden 복구 진행해줘.
진행:
1. 현재 증상 진단 — docker ps + curl /identity/.well-known/openid-configuration + 사용자 보고로 시나리오 1~6 중 어느 분기인지 식별
2. 시나리오 식별 후 추가 분기 확인 (PostgreSQL vs SQLite backend / Timshel fork vs mainline / customer 측 axe.axelabs.ai vs realchoice 측)
3. 페이지의 각 명령 실행 + 검증, 매 step 결과 받고 다음. backup 또는 force-recreate 등 destructive 명령 직전 사용자 확인
4. 함정 발생 시 페이지 "함정" 표 따라 우회 (column 명 verifier vs code_verifier / digest pin / 시나리오 6 = 영구 손실 — 복구 불가)
5. 복구 완료 검증 (SSO 로그인 + vault item 조회 + admin endpoint) + 사고 원인 /ops/known-gaps 한 줄 (재발 방지)본인 AI session = Claude Code / Cursor / ChatGPT 데스크탑 / Claude.app / 기타.
페이지 본문 = 사람이 직접 read 도 가능, AI 도 참고. AI 가 본 페이지 fetch 후 위 진행 순서대로 사용자와 step-by-step interactive 풀어나감.
axe-vaultwarden 서비스가 죽거나 OIDC 가 깨졌을 때.
시나리오 1 — 컨테이너 죽음
docker ps -a | grep vault
# axe-vaultwarden Exited (1)
# 단순 재시작
cd /Users/axe/.axe/vault
docker compose up -d --force-recreate
sleep 5
curl -sk https://localhost:8222/identity/.well-known/openid-configuration | head -5설정 정상이면 정상 가동.
시나리오 2 — OIDC 깨짐 (sso_nonce 누락)
D-ops-12 에서 다룬 알려진 이슈. Timshel fork 가 sso_nonce 테이블을 알아서 생성 안 함. 2026-05-25 정정 (Truvia realchoice 검증) — Timshel fork 의 실제 column 명 = verifier (NOT code_verifier). PostgreSQL / SQLite backend 분기 명령 별도.
증상:
ERROR: relation "sso_nonce" does not exist (PostgreSQL)
또는
sqlite> .schema sso_nonce → empty (SQLite)PostgreSQL backend (axec axe-vaultwarden 패턴)
docker exec axe-vaultwarden-postgres psql -U vaultwarden -d vaultwarden -c "
CREATE TABLE IF NOT EXISTS sso_nonce (
state TEXT PRIMARY KEY,
nonce TEXT NOT NULL,
verifier TEXT NOT NULL,
redirect_uri TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
"
docker compose restart axe-vaultwardenSQLite backend (soohunkang/vault repo + Vaultwarden 공식 image, 예: realchoice 의 ~/vault)
# vault-app 컨테이너 안의 sqlite3 (또는 host 측에서 docker exec)
docker exec vault-app sqlite3 /data/db.sqlite3 <<'EOF'
CREATE TABLE IF NOT EXISTS sso_nonce (
state TEXT PRIMARY KEY,
nonce TEXT NOT NULL,
verifier TEXT NOT NULL,
redirect_uri TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
EOF
# 만약 위 schema 작성 후 column 명 mismatch 로 fail 보이면 (이전에 잘못된 column 이름으로 만든 경우):
docker exec vault-app sqlite3 /data/db.sqlite3 "ALTER TABLE sso_nonce RENAME COLUMN code_verifier TO verifier;"
docker compose restart vault-app(db.sqlite3 경로는 DATA_FOLDER env 또는 /data volume mount 기준. compose.yaml 확인 후 적용.)
시나리오 3 — Microsoft SSO 갑자기 안 됨
원인 후보:
- Vaultwarden app 의 client_secret 만료
- redirect_uri 등록 잘못됨
- AZURE_TENANT_ID 또는 AZURE_VAULTWARDEN_APP_ID 잘못 설정
확인:
# .env 검증
cat /Users/axe/.axe/vault/.env | grep -E 'AZURE_(TENANT_ID|VAULTWARDEN)'
# 컨테이너 env 검증
docker exec axe-vaultwarden env | grep -E 'SSO_(AUTHORITY|CLIENT)'해결:
- customer IT 측 Azure 에서 새 client_secret 발급
.env의AZURE_VAULTWARDEN_CLIENT_SECRET=새 값으로docker compose up -d --force-recreate
시나리오 4 — 컨테이너 손상 / 데이터 손실
# 1. backup 확인
restic -r /Users/axe/.axe/backups/local snapshots \
--password-file <(security find-generic-password -w -s axe.backup.restic.local -a axe-cli) \
--tag vault
# 2. 가장 신선한 vault snapshot 복원
# `axe restore` 는 Phase 5 stub — 현재는 restic 직접 + docker volume restore:
restic -r /Users/axe/.axe/backups/local \
--password-file <(security find-generic-password -w -s axe.backup.restic.local -a axe-cli) \
restore <snapshot_id> --target /tmp/vault-restore --include '/Users/axe/.axe/vault/*'
# 그 후 docker volume 또는 .yml volume mount 위치에 복사
# 3. 컨테이너 재시작
cd /Users/axe/.axe/vault
docker compose up -d --force-recreate
# 4. Microsoft SSO 시도
open https://axe.axelabs.ai/vault시나리오 5 — Timshel fork 업데이트
Timshel fork 의 새 release 가 나왔을 때.
⚠️ 신중하게. Timshel 은 mainline Vaultwarden 보다 작은 PR queue → 가끔 breaking change.
# 1. 현재 digest pin 확인
grep ghcr.io /Users/axe/.axe/vault/docker-compose.yml
# 2. 새 digest 확인 (Timshel 의 GitHub Releases)
# https://github.com/Timshel/vaultwarden/releases
# 3. 비프로덕션 환경에서 테스트 (있다면)
# 4. backup 전체 (vault + DB)
# 수동 backup (현재 `axe backup` subcommand 없음 → restic 직접):
restic -r /Users/axe/.axe/backups/local \
--password-file <(security find-generic-password -w -s axe.backup.restic.local -a axe-cli) \
backup /Users/axe/.axe/vault --tag vault-pre-upgrade
# 5. .yml 의 image: line 갱신
vim /Users/axe/.axe/vault/docker-compose.yml
# image: ghcr.io/timshel/vaultwarden@sha256:<new>
# 6. 재시작
docker compose up -d --force-recreate
# 7. 검증
# - admin 로그인
# - Microsoft SSO 로그인
# - 기존 collection 의 item 조회
# - 새 item 생성/조회
# 8. 실패 시 rollback
docker compose down
# .yml 의 image: 옛 digest 로 되돌림
docker compose up -dbw CLI data.json cache stale recovery (운영자 측)
서버 측 복구가 아닌 운영자 본인 머신의 bw CLI 회복 절차. server-side vault patch deploy 후 local data.json cache 가 stale 상태로 잔존하면서 발생.
증상:
[Encrypt service] MAC comparison failedbw unlock 또는 bw get <name> 시 위 에러 → bw 명령 전체 실패.
언제 발생: server-side vault patch deploy 후 (axe.2 → axe.3 등). bw CLI 의 local data.json 에 cryptoSymmetricKey cache 가 옛 schema 의 wrapped key 보유 → 새 server 가 내려준 패치 후 shape 와 mismatch → decrypt 시 MAC mismatch. bw unlock 자체는 cache 무효화 안 함.
빈도: server patch 마다 1회 — 2026-05-22, 2026-05-26 두 차례 재발 확정. patch shape 변경 시 매번.
표준 회복 절차 (운영자 본인 머신, 약 30초):
bw logout
bw config server https://axe.axelabs.ai/vault
bw login # email + MP + Entra MFA (interactive)
bw unlock # MP — session token stdout
export BW_SESSION="...token..."
bw get password <known-secret> # 검증이후 Keychain 의 axe.vault.session entry 갱신:
bw unlock --raw | security add-generic-password -s 'axe.vault.session' -a "$(whoami)" -w "$(cat)" -U자동화 후보 (영구 fix, B-bw-cache-stale-autoheal):
axe vault reset신설 — 위 4단계를 한 명령으로_bw_get_password헬퍼가MAC comparison failed패턴 감지 시 자동 reset + retryaxe ship vaultpost-deploy hook 이 patch shape 변경 시 osascript 알림 — 운영자 본인에게 “logout/login 필요” pre-warn
함정:
| 함정 | 결과 | 회피 |
|---|---|---|
| ai@ personal vault 가 약 37 items → fresh login 시 sync 약 30초 대기 | bw login 직후 bw get 호출 시 일부 item 미반영 | login 직후 bw sync 명시 + 약 30초 대기 |
bw login 시 BW_CLIENTID / BW_CLIENTSECRET env 잔존 시 API key 모드로 진입 | SSO 흐름과 다른 인증 경로 — 일반 사용 OK 단 본 회복 절차는 interactive 가 가장 안전 | 회복 시 unset BW_CLIENTID BW_CLIENTSECRET 후 bw login |
~/.config/Bitwarden CLI/ (또는 $BITWARDENCLI_APPDATA_DIR) 의 data.json 직접 삭제 시도 | clean state 안 됨 + session token 잔존 | bw logout 권장 (data.json + session 모두 정리) |
Keychain 의 axe.vault.session 옛 token 잔존 | axe secret * 호출이 옛 invalid session 으로 NO_SESSION_IN_KEYCHAIN | login 후 위 security add-generic-password ... -U 로 갱신 필수 |
시나리오 6 — 운영자 master password 분실
⚠️ 이건 영구 손실. Vaultwarden 의 master password 는 vault 의 encryption key. 모르면 모든 item 복호화 불가.
회피:
- master password 는 종이 메모 + 신뢰할 수 있는 가족 (또는 다른 안전한 곳)
- 매월 1회 master password 로 로그인 시도 (잊지 않게)
- Bitwarden export 정기 + 그 export 파일 자체도 vault 의 master password 로 암호화
분실 시 — vault 폐기, 모든 item 재발급. 회사 운영 1주일 이상 마비 가능. 그래서 종이 백업 필수.
함정
| 함정 | 결과 | 회피 |
|---|---|---|
Timshel digest pin 안 함 (:latest) | 무작위 업데이트 | 항상 @sha256:<digest> |
| backup 안 함 | sso_nonce 깨졌을 때 복구 불가 | 매일 자동 backup |
| .env 평문 commit | secret 노출 | .gitignore + Keychain |
| master password 단일 보관 | 분실 시 영구 손실 | 종이 + 가족 이중화 |
| 분기 drill 안 함 | restore 절차 실제 검증 안 됨 | com.axe.restore-drill 자동화 |