feat: /api/admin/* 내부망 게이트 + 부트스트랩 엔드포인트 #50

Merged
xhh merged 2 commits from feature/admin-private-origin-bootstrap into main 2026-04-23 02:46:13 +09:00
Owner

Summary

관리 엔드포인트를 공개 인터넷에서 원천 차단하고, CLI 없이 Tailscale 브라우저에서 첫 write 키를 발급할 수 있게 한다. v0.3.0 배포 직전 마지막 조각.

서버

  • require_private_origin 의존성: XFF 첫 IP 가 _TRUSTED_NETWORKS (RFC1918 + Tailscale CGNAT + loopback + link-local) 면 통과, 공개 IP 면 403
  • admin.router 재구성: prefix dep = private_origin, 일반 엔드포인트는 추가로 require_scope("write"), POST /bootstrap 만 scope 면제
  • /bootstrap: 활성 write 키가 0개일 때만 write 스코프 포함 키 발급
  • stdlib is_private 는 TEST-NET 까지 True 처리하는 함정이 있어 명시적 allowlist

UI

  • 7_API_Keys.py — 401 대응 안내. 하단 '신규 발급' 폼이 scope=write 로 제출 시 일반 POST 가 401 이면 같은 payload 로 /bootstrap 자동 폴백. 발급된 원문 키를 사이드바에 즉시 주입.

회귀 테스트

  • tests/api/test_private_origin.py 20 신규 케이스 (IP 판정 파라미터 11, 게이트 4, bootstrap 5). 85 → 105 tests.

문서

  • CLAUDE.md '관리 엔드포인트 보호 — private origin 게이트' 섹션

Test plan

  • ruff clean
  • 105 tests pass
  • NAS 배포 후 Tailscale 브라우저에서 https://stock-admin.xhhan.com 접속 → API Keys 페이지 → 부트스트랩 발급 동작 확인
  • 공개 인터넷에서 curl -X POST https://stock.xhhan.com/api/admin/keys/bootstrap ... 403 확인
## Summary 관리 엔드포인트를 공개 인터넷에서 원천 차단하고, CLI 없이 Tailscale 브라우저에서 첫 write 키를 발급할 수 있게 한다. v0.3.0 배포 직전 마지막 조각. ### 서버 - `require_private_origin` 의존성: XFF 첫 IP 가 `_TRUSTED_NETWORKS` (RFC1918 + Tailscale CGNAT + loopback + link-local) 면 통과, 공개 IP 면 403 - `admin.router` 재구성: prefix dep = private_origin, 일반 엔드포인트는 추가로 `require_scope("write")`, `POST /bootstrap` 만 scope 면제 - `/bootstrap`: 활성 write 키가 0개일 때만 write 스코프 포함 키 발급 - stdlib `is_private` 는 TEST-NET 까지 True 처리하는 함정이 있어 명시적 allowlist ### UI - `7_API_Keys.py` — 401 대응 안내. 하단 '신규 발급' 폼이 scope=write 로 제출 시 일반 POST 가 401 이면 같은 payload 로 `/bootstrap` 자동 폴백. 발급된 원문 키를 사이드바에 즉시 주입. ### 회귀 테스트 - `tests/api/test_private_origin.py` 20 신규 케이스 (IP 판정 파라미터 11, 게이트 4, bootstrap 5). 85 → 105 tests. ### 문서 - CLAUDE.md '관리 엔드포인트 보호 — private origin 게이트' 섹션 ## Test plan - [x] ruff clean - [x] 105 tests pass - [ ] NAS 배포 후 Tailscale 브라우저에서 `https://stock-admin.xhhan.com` 접속 → API Keys 페이지 → 부트스트랩 발급 동작 확인 - [ ] 공개 인터넷에서 `curl -X POST https://stock.xhhan.com/api/admin/keys/bootstrap ...` 403 확인
공개 인터넷에서 관리 엔드포인트 원천 차단. CLI 를 거치지 않고 Tailscale
브라우저에서 바로 첫 write 키를 발급받을 수 있게 한다.

서버
----
- `api/auth.py::require_private_origin` — XFF 첫 IP 가 사설/CGNAT/loopback
  allowlist 안이거나 XFF 없음이면 통과, 공개 IP 면 403.
  stdlib `is_private` 는 TEST-NET (203.0.113.0/24) 까지 True 로 치는 이슈가
  있어 명시적 `_TRUSTED_NETWORKS` 리스트 사용.
- `api/routers/admin.py` 재구성:
  - 라우터 prefix dep: `[Depends(require_private_origin)]` (공통)
  - 일반 엔드포인트: 추가로 `Depends(require_scope("write"))`
  - `POST /bootstrap`: scope 면제. 활성 write 키가 0개일 때만 발급 허용.
    write 스코프 포함 필수 (읽기 전용 키 부트스트랩 금지). 중복 이름 409.

UI
--
- `frontend/streamlit/pages/7_API_Keys.py` — 401 대응 분기 추가:
  사이드바 키 미입력/활성 키 0개 상황에서 부트스트랩 폼을 노출. 발급 성공
  시 원문 키를 `st.session_state["api_key"]` 에 즉시 주입해 같은 세션에서
  바로 admin 조작 가능.

회귀 테스트
----------
- `tests/api/test_private_origin.py` 신규 20건:
  - `_is_private_ip` 파라미터화 11건 (RFC1918, CGNAT, loopback, 공개 IP,
    TEST-NET, 잘못된 문자열)
  - 공개 XFF 403 / 사설 XFF 는 스코프 체커로 진행
  - 부트스트랩: 빈 DB 성공 / read-only 422 / 중복 401 / 공개 XFF 403 /
    전 키 폐기 후 재bootstrap 가능

85 → 105 tests pass (+20).

CLAUDE.md 에 "관리 엔드포인트 보호 — private origin 게이트" 섹션 추가.

v0.3.0 배포 직전 마지막 조각. CLI 부트스트랩 단계 완전 제거.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore: 신규 발급 폼에 bootstrap 자동 폴백
All checks were successful
Tests (PR) / pytest (pull_request) Successful in 27s
d01e02418c
#46/#44 흐름에서 'write 키가 하나도 없는 최초 상태' 에 사용자가 CLI 로
apikey.py 를 돌려야 한다고 오해할 여지가 남아 있었다. 신규 발급 폼이
일반 POST /api/admin/keys 가 401 을 돌려주면 같은 payload 로 /bootstrap
에 자동 폴백하도록 변경.

- scope=write 또는 read,write 일 때만 폴백 (bootstrap 이 read-only 를
  거부하는 서버 정책과 일치)
- 발급된 원문 키를 세션 사이드바에도 즉시 주입 → 같은 세션에서 목록
  조회/조작이 바로 됨
- 상단의 별도 '부트스트랩 모드' 폼은 제거. 401 시에는 안내 info 메시지만
  표시해 "기존 키 있으면 사이드바 / 최초이면 scope=write 로 발급" 안내.

105 tests pass (변경 없음).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
xhh merged commit e737aafe71 into main 2026-04-23 02:46:13 +09:00
xhh deleted branch feature/admin-private-origin-bootstrap 2026-04-23 02:46:13 +09:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
xhh/financial-data-platform!50
No description provided.