시크릿 회전
운영 중 시크릿을 회전(rotate)할 때 따라야 할 절차와, 각 시크릿이 회전되었을 때 시스템 컴포넌트에 미치는 영향 범위를 정리합니다. 본 페이지는 회전 영향의 단일 출처입니다 — 다른 모든 페이지는 본 페이지로 링크만 합니다.
이 문서는 코드 레포의
docs/operations/secrets.md와docs/operations/secret_rotation.md두 파일을 흡수했습니다. 원본은 개발자용 백업으로 그대로 유지됩니다.
TL;DR — 회전 영향 매트릭스
| 시크릿 | 영향 범위 | 사용자 영향 | 회전 후 필수 조치 |
|---|---|---|---|
ENCRYPTION_SALT | server/auth/crypto.py Fernet 키 파생. 모든 거래소(CCXT/Alpaca/KIS) API 키 + KIS 계좌번호가 동일 SALT로 암복호화 | 전체 사용자 거래 중단. 기존 암호 컬럼 일괄 복호화 불가 | 1) 사용자 일괄 알림 → 2) 거래소 키 재등록 강제(UI gate) → 3) 자동 거래 일시 중단 |
AUTH_SECRET_KEY | JWT 서명 키 (server/auth/jwt.py) | 발급된 모든 JWT 무효 → 전 사용자 재로그인 | 1) 클라이언트 캐시 무효화 안내 → 2) 401 응답 시 자동 로그아웃 동작 확인 |
LIVE_CONFIRM_SECRET | paper→live 승급 confirm token HMAC. 빈값이면 AUTH_SECRET_KEY로 폴백 | 진행 중인 confirm 이메일 모두 무효 | 1) 사용자에게 재발급 안내 → 2) 회전 24h 윈도 |
DB_PASSWORD (TimescaleDB) | DATABASE_URL 패스워드 부분 | 회전 자체는 데이터 무영향. 컨테이너 재시작 필요 | 1) Postgres ALTER USER → 2) .env 갱신 → 3) compose up -d |
OPENAI_API_KEY (LiteLLM master) | analysis-worker가 LLM 호출 시 사용 | 분석 보고서 신규 생성 즉시 401. 거래는 무영향 | LiteLLM proxy 키 갱신 → analysis-worker만 재시작 (compose restart analysis-worker) |
ALPACA_API_KEY / _SECRET_KEY | 사용자 단위 페이퍼/실거래 라우팅. 개별 사용자 키는 DB의 암호화 컬럼. 환경변수는 폴백/시스템 키만 | 환경변수 회전 → 시스템 키 사용 작업만 영향. 개별 사용자 키 회전은 DB 갱신 | UI에서 사용자가 신규 등록 → 자동 재연결 |
KIS_APP_KEY / _APP_SECRET / _ACCOUNT_NO | 위와 동일. 추가로 KIS는 토큰 캐시 사용 → 회전 시 토큰 재발급 필요 | 진행 중 주문 reconcile에서 일시 실패 가능 | UI에서 재등록 → reconciler 다음 패스에서 자동 복구 |
TELEGRAM_TOKEN / TELEGRAM_CHAT_ID | 알림 발송 | 알림만 영향. 거래/리스크 무영향 | .env 갱신 → compose restart api analysis-worker position-reconciler |
DART_API_KEY / FRED_API_KEY | 매크로/공시 데이터 폴백 | LLM 분석 품질 저하. 거래 무영향 | .env 갱신 → analysis-worker 재시작 |
GRAFANA_WEBHOOK_SECRET | Grafana → API 알림 브리지 인증 | 알림만 영향 | Grafana / API 양쪽 동시 갱신 |
RECAPTCHA_SECRET_KEY | 라이브 승급 사용자 검증 | 비어 있으면 검증 스킵 (개발용). 운영에서 필수 | UI 환경변수 동시 갱신 |
회전 절차 (공통)
- 새 값 발급 — 비밀 저장소(Azure Key Vault / GitHub Encrypted Secrets)에 신규 값 추가.
- 그레이스 윈도우 — 가능하면 구 값 회수 전 24h 유예.
- VM
.env갱신 —~/quantai/.env수정.chmod 600 .env유지. - 영향 서비스 재시작 — 위 매트릭스의 "회전 후 필수 조치" 컬럼 참조.
- 검증 —
infra/healthcheck.sh실행 + 사용자 표본(1~3명) 거래 흐름 확인. - 사후 알림 — 사용자 영향이 있는 항목(SALT/AUTH_SECRET)은 Telegram + 이메일.
ENCRYPTION_SALT 회전 (위험도: 최상)
ENCRYPTION_SALT를 회전하면 DB에 저장된 모든 거래소 API 키, KIS
계좌번호 등 암호화 컬럼이 즉시 복호화 불가 상태가 됩니다. 정상
절차는 다음과 같습니다.
-
사전 공지 — 최소 48h 전 사용자 공지 (UI 배너 + Telegram).
-
유지보수 모드 진입:
# API에 503을 반환하는 nginx maintenance 페이지로 라우팅ssh azureuser@<vm-ip> 'sudo nginx -s reload -c /etc/nginx/maintenance.conf' -
데이터 백업:
ssh azureuser@<vm-ip> \'cd ~/quantai && sudo -E docker compose -f docker-compose.prod.yml exec -T \timescaledb pg_dump -U quant quantai | gzip' \> ~/backup-pre-salt-rotation-$(date -u +%Y%m%dT%H%M%SZ).sql.gz -
.env갱신 —ENCRYPTION_SALT새 값으로 변경. -
암호 컬럼 정리 마이그레이션 —
exchange_keys,equity_account_credentials, 기타 암호 컬럼을 NULL로 초기화하고is_active=false로 표기. (마이그레이션 스크립트는alembic/versions/<rev>_clear_encrypted_after_salt_rotation.py형태로 회전 시 1회용으로 작성.) -
재시작:
bash scripts/rolling_restart.sh -
재등록 안내 — 사용자 로그인 시 "거래소 API 키를 재등록해주세요" UI 강제.
-
자동 거래 비활성 —
FEATURE_EQUITY_LIVE를 임시로false처리. 사용자가 신규 키 등록을 마치면 자동 재활성화.
AUTH_SECRET_KEY 회전 (위험도: 중)
JWT 서명 키 회전은 DB 영향이 없습니다. 회전 즉시 모든 활성 세션이 무효화됩니다.
# 1. .env에서 AUTH_SECRET_KEY 새 값
# 2. api 컨테이너만 재시작
docker compose restart api
- 프론트엔드는 401 응답 시 자동 로그아웃이므로, 사용자는 다음 요청 시 로그인 화면으로 이동.
- WebSocket 연결은 끊긴 후 클라이언트가 재연결 시 새 토큰 사용.
LIVE_CONFIRM_SECRET이 빈값이면 AUTH_SECRET_KEY로 폴백하므로,
AUTH_SECRET_KEY 회전 시 발급된 모든 paper→live confirm token도 무효
됩니다. 사용자가 confirm 이메일 미사용 상태라면 재발급 안내.
DB_PASSWORD 회전 (위험도: 하)
# 1. Postgres에서 패스워드 변경
docker compose exec timescaledb psql -U quant -d quantai \
-c "ALTER USER quant WITH PASSWORD 'new-strong-password';"
# 2. .env 갱신
sed -i 's/^DB_PASSWORD=.*/DB_PASSWORD=new-strong-password/' .env
# 3. 재시작 (api / 워커 / Grafana 모두 재기동 필요 — Grafana도 같은 패스워드 사용)
docker compose -f docker-compose.prod.yml up -d
검증: docker compose exec api alembic current이 정상 응답.
OPENAI_API_KEY 회전 (위험도: 하)
# 1. LiteLLM proxy 측에서 새 master_key 발급
# 2. .env의 OPENAI_API_KEY 갱신
# 3. analysis-worker만 재시작
docker compose restart analysis-worker
거래 / 페이퍼 모드는 영향 없음. 분석 큐 적재된 작업이 잠시 401로 실패할 수 있으나 retry로 자동 회복.
자동화 — scripts/deploy_azure.sh와의 관계
배포 스크립트는 시크릿을 직접 회전하지 않습니다. 다만:
- 배포 시 VM의
.env는 그대로 둠 (이미 회전된 값 보존). deploy_azure.sh --rollback은 이미지만 되돌리고 시크릿은 건드리지 않음.- 회전 도중 배포가 발생하면 새 컨테이너가 새 시크릿을 즉시 picks up — 중간 상태 최소화를 위해 회전과 배포를 동시에 수행하지 않음 (Change Window 분리).
사후 검증 체크리스트
-
infra/healthcheck.sh모든 항목 OK -
analysis-worker로그에서 401/403 없음 -
position-reconciler한 사이클 정상 (broker_order_events에 신규 audit 행) - 표본 사용자 1~3명: 로그인 → 대시보드 로딩 → 페이퍼 주문 1건
- Telegram 알림 채널에 회전 완료 메시지 수신