Files
llm-wiki/vault/30-knowledge/projects/ca-tmpl/security-baseline-jwt-actuator-secrets.md
T

14 KiB

title, source_type, status, confidence, tags, related_projects, last_reviewed
title source_type status confidence tags related_projects last_reviewed
ca-tmpl - Security Baseline 결정 (JWT + Actuator + Secrets) project verified high
ca-tmpl
security
jwt
oauth2
secrets
actually-implemented
locally-verified
ca-tmpl
2026-07-02

ca-tmpl - Security Baseline 결정 (JWT + Actuator + Secrets)

Layer: wiki/projects/ — ca-tmpl skeleton 내 보안 baseline 결정 사실 문서. 일반 개념/표준 정의는 wiki/concepts/security-baseline-jwt-actuator-secrets 참고.

프로젝트 컨텍스트

ca-tmpl은 Clean Architecture 기반 Spring Boot skeleton/template 저장소다. 이 문서가 다루는 범위는 운영 계약(raw/project-notes/ca-skeleton-operational-contract §18 Control Plane Contract, §29 Group G-B 외부 근거 인덱스) 중 보안 baseline 세 축의 설계 결정이다.

세 축:

  1. 데이터면 인증/인가: JWT Resource Server + AuthN/AuthZ matrix 12행 + JWKS 10분 refresh + clock skew tolerance 60s + key rotation overlap 24h + public path snapshot diff.
  2. 제어면 (Actuator): management port 9001 분리 + prod allowlist (health / prometheus / info) + heapdump/threaddump/env/configprops/shutdown prod forbidden + loggers prod read-only + metrics network ACL default.
  3. Secrets / Config: prod = secret manager OR mounted env, local만 .env 허용. no-runtime-reload default, @RefreshScope 금지. JWT signing key 24h overlap / DB credential dual-bind 60s / API key restart-reload / HMAC salt 90d rotation.

진행 상태: C2 부분 구현 + 로컬 검증 완료. JWT Resource Server filter chain, lazy JWT decoder, security error classifier/envelope entry point, actuator management policy, secret source/reload guard는 코드화되어 있다. secret manager 연동과 실제 rotation automation은 아직 없다.

실제 구현 내용 (actually-implemented)

  • adapter-webSecurityConfigSecurityFilterChainoauth2ResourceServer를 구성한다.
  • JwtDecoderConfigSupplierJwtDecoder로 JWKS discovery를 lazy 처리하고 JwtTimestampValidator(Duration.ofSeconds(60)), issuer, audience validator를 명시한다.
  • SecurityErrorClassifier와 envelope entry point/denied handler 테스트가 filter-layer 보안 실패를 API error envelope으로 분류한다.
  • MethodSecurityConfig, RequiresPermission, AuthorizationPort, AuthorizationContractTest가 framework-free method authorization path를 구성한다.
  • app-bootstrapManagementSecurityConfig, sample management config, SecretSource*, SecretReloadContractTest가 actuator/secret baseline 일부를 코드화한다.

로컬/dev 검증 (locally-verified)

  • ./gradlew check 통과(2026-07-02, BUILD SUCCESSFUL, 114 tasks).
  • SecurityErrorClassifierTest, JwtDecoderConfigTest, EnvelopeAuthenticationEntryPointTest 등 web security/error path 테스트가 통과한다.
  • ManagementActuatorSecurityContractTest, ActuatorSecurityHttpTest가 management port/exposure/loggers read-only 정책을 검증한다.
  • SecuritySettingsTest, SecretSourceTest, SecretSourceValidatorTest, SecretReloadContractTest가 설정/secret source/reload guard를 검증한다.

운영 검증 (prod-verified)

없음. ca-tmpl은 skeleton/template이며 운영 배포 대상이 아니다. prod 환경에서 JWT 검증 latency·JWKS rotation·secret rotation·actuator endpoint 노출을 측정한 적이 없다.

문서/계획만 존재 (documented-only / planned)

다음 항목은 구현된 baseline과 아직 documented-only / planned로 남은 영역을 분리한다. 면접/블로그에서 구현 범위와 혼동하면 안 된다.

D1. JWT Resource Server 채택 (actually-implemented / locally-verified)

  • 결정: 데이터면 인증을 OAuth2 Resource Server + JWT (spring-boot-starter-oauth2-resource-server) 로 표준화.
  • 검토한 대안:
    • Session + Cookie — 분산 session store 비용, stateless 확장성 손실.
    • OAuth2 Authorization Code (issuance flow) — 본 baseline은 검증 side이므로 직교. issuance 자체는 별도 IdP.
    • mTLS (RFC 8705 sender-constrained token) — PKI 운영 비용 + public client(SPA/mobile) 운영 어려움.
    • API key + HMAC (AWS SigV4 류) — webhook/외부 호출 인증에는 적합하나 일반 사용자 인증 모델이 아님.
    • OPA (Open Policy Agent) — 외부 호출 latency + sidecar 운영. 인가 정책 2~3종에는 과한 인프라.
  • 채택 이유: framework-neutral skeleton 가정과 정합 (Spring Security 6 표준 경로), revocation 한계는 short expiry + JWKS rotation overlap으로 완화.
  • 설계만 동결한 파라미터: JWKS refresh 10분 + unknown kid 시 on-demand refresh, clock skew 60s, key rotation overlap 24h, AuthN/AuthZ matrix 12행, public path snapshot diff.

D2. Actuator management port 9001 분리 + prod allowlist (actually-implemented / locally-verified)

  • 결정: management.server.port=9001 별도 포트 + prod allowlist=health,prometheus,info + 그 외 prod forbidden.
  • 검토한 대안:
    • Single port (8080) + path ACL — cloud ingress의 path 매칭 신뢰도, filter ordering / regex 우회 risk.
    • mTLS for management — 강하지만 cert 운영 부담.
    • Network ACL only (VPC SG / NetworkPolicy) — port가 같으면 비즈니스 트래픽과 분리 정책이 복잡.
    • Istio sidecar AuthorizationPolicy — mesh 도입 전제, skeleton의 framework-neutral 가정 위배.
  • 채택 이유: 외부 노출 차단을 네트워크 경계 단순화(다른 포트 = 다른 ingress 정책)로 풀어 single-port + path ACL의 우회 위험을 피함.
  • 설계만 동결한 파라미터: heapdump/threaddump/env/configprops/shutdown prod 차단, loggers prod read-only, metrics scrape는 internal network ACL default.

D3. Secrets: secret manager OR mounted env + restart-only rotation + HMAC salt 90d (partially-implemented)

  • 결정: prod source = (secret manager) OR (mounted env), .env는 local 전용. __LOCAL_DEV_ sentinel로 prod 오탑재 차단. @RefreshScope 금지 / no-runtime-reload default. JWT signing key 24h overlap, DB credential dual-bind 60s, API key restart-reload, HMAC salt 90d rotation.
  • 검토한 대안:
    • Vault dynamic secrets (short lease) — @RefreshScope + bean 재생성을 전제 → connection pool/캐시 lifecycle과 충돌, 본 계약(@RefreshScope 금지)과 정면 충돌.
    • External Secrets Operator (ESO) — K8s native, 단 etcd 평문 저장은 cluster operator 책임 (이중 신뢰 경계).
    • Doppler / 1Password SDK — dev 머신 보호에 강점이나 SaaS 외부 의존.
  • 채택 이유: runtime reload를 거부하면 bean lifecycle / connection pool 충돌이 사라지고, rotation은 명시적 dual-bind window로만 처리. HMAC salt 90d 주기는 NIST SP 800-57 cryptoperiod 권고 범위 내에서 누적 노출/downstream re-hash 비용을 절충한 값.

Secret source abstraction과 local/prod guard는 구현되어 있으나, 외부 secret manager/Vault/KMS 통합 및 실제 rotation automation은 미구현이다.

D4. 한국 보안 사례 reference 추가 (2026-05-22) (documented-only)

  • 추가된 reference (raw 출처만, 구현 변경 없음):
  • 영향: Group G-B Actuator 결정의 한국 도메인 사례 근거 보강. 현재 ca-tmpl의 actuator exposure/management security contract와 함께 보조 근거로만 사용한다.
  • 여전히 미확보: 한국 기업의 JWT Resource Server 구현 사례, secret manager / Vault 운영 사례 직접 source는 미발견 — follow-up 후보로 유지.

면접에서 말할 수 있는 범위

자신 있게 답할 수 있는 질문

  • JWT vs Session 선택 기준 — stateless 확장성, revocation trade-off, cookie 운영 비용, 클라이언트 타입에 따른 결정 근거.
  • JWKS rotation 주기 설계 — 10분 refresh + unknown kid 시 on-demand refresh + 24h overlap window의 근거.
  • Management port 분리 이유 — single-port + path ACL의 filter ordering / regex 우회 risk 대비 별도 포트의 네트워크 경계 단순화.
  • Secret rotation 방식 (dual-bind) — JWT key 24h overlap / DB credential dual-bind 60s / API key restart-reload가 왜 다른지.
  • HMAC salt 90d rotation 근거 — NIST SP 800-57 cryptoperiod 권고 + 누적 노출량 한도 + downstream re-hash 비용 절충.

적당히 답할 수 있는 질문

  • OPA vs in-process AUTHZ trade-off — 외부 호출 latency / sidecar 운영 / 정책 코드 분리 가치 / 정책 종수 임계.
  • Vault dynamic secrets vs static lease@RefreshScope 강제와 bean lifecycle 충돌, dynamic secret이 본 계약과 왜 충돌하는지.
  • clock skew tolerance 30s vs 60s — NTP drift 가정, 발급자/검증자 분산도, expired vs replay 창 trade-off.

답하면 안 되는 질문 (모른다고 해야 함)

  • "JWT Resource Server baseline을 구현했다" — 가능. 단 IdP 운영/JWKS rotation 실측은 없음.
  • "Secret rotation을 운영에서 돌려봤다" — prod 적용 사례 없음. dual-bind window는 설계 값.
  • "Actuator endpoint 보안 침투 테스트 결과" — pentest 수행 안 함.
  • "JWKS rotation 시 latency가 얼마였다" — 측정 안 함.
  • "Vault/Secrets Manager를 ca-tmpl에 연결해서 돌려봤다" — 어떤 secret manager와도 통합하지 않음.

과장 금지 지점

  • "JWT는 안전하다"는 단정 금지. token theft 시 stateless 검증은 즉시 revocation이 어렵다. JWKS rotation overlap + short expiry는 완화책일 뿐 근본 해결책이 아니다.
  • "Vault가 secret 관리의 표준"이라는 표현 금지. dynamic secrets는 @RefreshScope 흐름을 전제하며, ca-tmpl의 @RefreshScope 금지 계약과 정면 충돌. 채택 가능한 표준이 단일하지 않다.
  • 한국 보안 기술블로그 사례 참조 범위 한정. 2026-05-22 기준 ca-tmpl이 직접 참조하는 한국 사례는 Actuator 노출 정책 영역에 한정된다 (raw/company-tech-blogs/security-woowahan-actuator-safe-usage / raw/company-tech-blogs/security-toss-actuator-healthcheck). JWT Resource Server 운영, secret manager 통합, JWKS rotation 같은 영역의 한국 도메인 직접 사례는 부재 — 인용 시 영역을 actuator로 명시할 것.
  • "Actuator를 닫아두면 안전하다"는 단정 금지. allowlist + 네트워크 경계 + 인증의 다층 방어가 필요하다. info만 열어도 build/commit 메타데이터가 attack surface가 될 수 있다.
  • "AuthN/AuthZ matrix 12행 전체가 E2E로 검증됐다"고 말하면 안 됨. 주요 security/error path와 method authorization contract는 테스트되지만, 모든 matrix row의 외부 IdP 통합 검증은 없다.

Blog-topic ingest: secret-source-port-restart-only-rotation (2026-07-02)

raw/blog-topics/secret-source-port-restart-only-rotation-2026-07-02 는 secret source를 문자열 규칙이 아니라 SecretSource port, restart-only rotation, @RefreshScope 금지 계약으로 닫은 이유를 블로그로 풀기 위한 raw seed다.

  • canonical 반영 범위: secrets/source/rotation 정책 글감을 security baseline canonical에 연결했다.
  • blogify 전 조건: 충족. 이 문서는 2026-07-02 기준 코드와 ./gradlew check로 검증됨.
  • 블로그 전 과장 방지: Vault/KMS dynamic secret 운영이나 secret manager 통합을 구현한 것처럼 쓰지 않고, restart-only contract 범위로 제한한다.
  • raw/blog-topics/framework-free-method-authorization-clean-architecture-2026-06-08: Spring Security annotation을 application layer에 직접 붙이지 않고 plain annotation + authorization port + adapter method-security로 분리하는 글감. Spring Security 자체를 부정하지 않고 ca-tmpl layer boundary 선택으로 제한한다.
  • raw/blog-topics/spring-security-filter-layer-error-envelope-2026-06-08: Spring Security 인증/인가 실패가 filter layer에서 entry point / denied handler로 처리되어 ControllerAdvice에 도달하지 않는다는 점을 envelope 통일과 연결하는 글감. heuristic 분류의 한계를 유지한다.

관련 개념

Sources

Canonical project SSOT

Branch-notes (결정 동결 위치)

Cluster / 묶음