49 KiB
title, source_type, status, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, branch, parent_branch, related_projects, tags, created, target_merge, status_label, contract_packet_sha256
| title | source_type | status | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | branch | parent_branch | related_projects | tags | created | target_merge | status_label | contract_packet_sha256 | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-keycloak-iss-claim-hostname-mismatch (iss claim mismatch 함정 + KC_HOSTNAME 해결) | branch-note | raw | BR-KEYCLOAK-PATTERNS-OVERVIEW-005 | project-work-item | keycloak-patterns-overview | WI-KEYCLOAK-PATTERNS-OVERVIEW-005 |
|
|
1 | feature-keycloak-iss-claim-hostname-mismatch |
|
|
2026-05-25 | in-progress | a528d5f258776792a6803ccea4e7946bc7905bbc6abee798b12b4c68a9afda66 |
branch: feature-keycloak-iss-claim-hostname-mismatch (iss claim mismatch 함정 + KC_HOSTNAME 해결)
Layer:
raw/branch-notes/— raw/project-notes/keycloak-patterns-overview의WI-KEYCLOAK-PATTERNS-OVERVIEW-005직접 branch. P3A는 실 구현 대상. 본 sub-sub는 학습 + 작업 plan 기록 — 실 구현은/home/donghyeon/workspace/keycloak-patterns/. ⚠️ NO_GROUND_TRUTH (2026-07-17 확인):/home/donghyeon/workspace/keycloak-patterns/는 디스크에 존재하지 않는다. raw/project-notes/keycloak-patterns-overview §9 도 "아직 비어 있음 — Phase 2 진입 시 생성" 으로 기록. 따라서 본 노트의 모든 구현 항목은planned이며, 코드로 확인된actually-implemented는 0건이다.
부모 (필수)
raw/project-notes/keycloak-patterns-overview
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1 |
AP1은 public client와 Authorization Code + PKCE를 사용한다 | SPA access token의 issuer 검증과 Keycloak hostname wiring에 적용한다 | raw/project-notes/keycloak-patterns-overview |
DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1 |
done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | iss mismatch 401과 설정 후 복구 log를 완료 evidence로 사용한다 | raw/project-notes/keycloak-patterns-overview |
브랜치 지역 결정
기존 branch-local 결정은 아래
## Decision Evidence Map의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|
선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|
없음.
목표
단일 EC2의 가장 흔한 함정을 의도적으로 재현하고 해결한다. browser는 localhost:8080(또는 EC2 public DNS)으로 Keycloak에 접근하지만, backend는 Docker internal network keycloak:8080을 보면서 JWT의 iss claim이 mismatch — JWT validation 실패. KC_HOSTNAME 설정으로 해결.
면접 질문: "단일 호스트 docker-compose에서 OIDC가 동작 안 했던 경험이 있나요?"
→ "browser가 보는 issuer identity는 http://localhost:8080으로 고정하고, backend의 issuer-uri도 token iss와 같은 localhost 값을 사용합니다. 실제 JWKS fetch는 jwk-set-uri=http://host.docker.internal:8080/.../certs로 분리합니다. 이 구성은 아직 planned이며, 401→200과 key rotation을 로컬에서 검증해야 합니다."
2026-07-18
/sync채택 (D6): Docker dev 기본은 issuer identity와 JWKS network address 분리다.issuer-uri=http://localhost:8080/realms/keycloak-patterns,jwk-set-uri=http://host.docker.internal:8080/realms/keycloak-patterns/protocol/openid-connect/certs를 사용하고 backend에extra_hosts: ["host.docker.internal:host-gateway"]를 둔다.SSRS-JWT-C5에 따라 앞 값은iss문자열 검증, 뒤 값은 실제 key fetch를 담당한다. 실제 구현·검증 전까지는planned이며 과거형 경험으로 표현하지 않는다.
- 이슈:
- PR: (별도 keycloak-patterns repo)
범위
포함 범위
- 의도적 실패 재현 (
KC_HOSTNAME미설정 /KC_HTTP_ENABLED=true만) - backend Spring 로그에
JWT issuer mismatch또는Could not validate iss에러 확인 KC_HOSTNAME=localhost설정 후 양쪽 issuer 일치 검증- 해결 방안 4가지 비교 (2026-07-17: 기존 3가지 A/B/C 에 F 추가 — 조사 결과 F 가 가장 이식성 높은 해법으로 판정, D6):
- (A)
KC_HOSTNAME=localhost+ 컨테이너 간extra_hosts: [host.docker.internal:host-gateway]→ backend가http://host.docker.internal:8080으로 JWKS 호출 - (B)
network_mode: host(Docker hairpin NAT — Linux only) - (C) backend container
/etc/hosts에keycloak을 host gateway에 매핑 (extra_hosts 응용) - (F, 채택) Spring
issuer-uri/jwk-set-uri분리 —issuer-uri는 localhost tokeniss검증,jwk-set-uri는host.docker.internal을 통한 실제 JWKS fetch. Linux backend에는extra_hosts: ["host.docker.internal:host-gateway"]를 둔다. 근거SSRS-JWT-C5+DOCKER-COMPOSE-NET-C3/C4.
- (A)
- EC2 환경에서
KC_HOSTNAME=ec2-xx-xx-xx-xx.compute.amazonaws.com설정 시 변화 (브라우저가 EC2 public DNS로 접근) - frontchannel/backchannel URL 분리 옵션 (
KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true, Keycloak 24+)
제외 범위
- HTTPS / Let's Encrypt cert 발급 (학습 환경 HTTP)
- EC2 보안그룹 / VPC 세팅
- Caddy / nginx reverse proxy 앞단 추가
aud(audience) claim 검증 — 프로젝트 노트 §중복 정합(2026-07-14) 이feature-keycloak-spring-rs-audience-validator를 owner 로 지정. 본 branch 는iss만 다룬다.- realm / client 생성 및 export — raw/branch-notes/feature-keycloak-realm-client-export 소유.
network_mode: "service:<name>"(E) 패턴 — 2026-07-17 조사가 발견한 5번째 대안이나, 더미 anchor 컨테이너 의존 + 포트 관리 비용이 서비스 2개 규모에 과설계라 채택 안 함 (§Audit & Findings A4).
근거 (필수, 최소 1개+)
이 branch의 구현·설계 결정의 근거가 되는 외부 자료. (2026-07-17 정리: 병행 dispatch 로 생긴 중복 항목 제거 +
## Cluster하위에 잘못 생성된### Sources서브섹션을 본 표로 통합.)
| Source | 정당화하는 결정 |
|---|---|
| raw/official-docs/keycloak-hostname-configuration | D1 — Keycloak hostname guide. hostname 설정 의무(KC-HOST-C2) + fraudulent issuer 방지(KC-HOST-C3) + backchannel 분리 capability(KC-HOST-C1,C4) + hostname-strict 기본 true(KC-HOST-C5) |
| raw/official-docs/spring-security-resource-server-jwt | D6 의 핵심 근거 — issuer-uri 는 token iss 값이어야 하고 RS 가 이 값으로 self-configure(SSRS-JWT-C1), discovery 4단계 중 4번이 iss 비교(SSRS-JWT-C2), jwk-set-uri 지정 시 discovery 를 하지 않으며 issuer-uri 는 iss 검증용으로만 남음(SSRS-JWT-C5). D5 의 "iss 검증 = 신뢰의 본질" 보조 근거. ⚠️ 2026-07-17 이전까지 이 branch Sources 에 링크되지 않아 D6 를 놓치고 있었음 (§Audit & Findings A1) |
| raw/official-docs/docker-compose-networking-extra-hosts-official | D6 — extra_hosts custom hostname 매핑(DOCKER-COMPOSE-NET-C3) + host-gateway 특수값(C4) + Linux vs Mac/Win 해석차(C5) = 해결 A/C 의 메커니즘 근거. 서비스명 internal DNS 가 별도 설정 없이 도달(C1,C2) = 해결 F 의 도달성 근거 |
| raw/official-docs/docker-host-network-driver-official | D6 — 해결 (B) network_mode: host 의 플랫폼 제약. Linux native + Docker Desktop 4.34+ opt-in(DOCKER-HOSTNET-C1,C2), Windows 컨테이너 미지원(C3), ports: 무시(C4), Desktop 은 layer 4 한정(C5) |
| raw/official-docs/docker-engine-20-10-release-notes-official | D6 — host.docker.internal 의 Linux dockerd 지원이 20.10.0(2020-12-08)에서 시작(DOCKER-2010-C1). 본문 "최소 Docker 20.10+" 메모의 부분 confirm 근거 |
| raw/official-docs/keycloak-2500-hostname-v2-release-official | D7 — hostname v2 도입(25.0.0) 사유·동작 변경 경고·v1 deprecated(KC-2500-C1~C4). 본문 "옵션 명칭이 자주 바뀜" 메모의 프레이밍 정정 근거 |
| raw/official-docs/keycloak-2600-hostname-v1-removed-official | D7 — 26.0.0 에서 hostname v1 완전 제거(KC-2600-C1) + proxy 옵션 제거(C2). 이 프로젝트가 26.x 고정이므로 v2 가 유일 옵션 집합임을 확정 |
| raw/official-docs/openid-connect-core-id-token-validation | D5 — "RFC 7519 + OIDC Core spec 모두 iss 검증을 mandatory 로 규정" 진술의 미증명 상태를 해소. §3.1.3.7 item 2 (iss MUST exactly match, OIDC-CORE-C3) verbatim quote 가 직접 근거 |
TODO
- 실패 재현:
docker-compose.yml에서KC_HOSTNAME제거,KC_HTTP_ENABLED=true만 — 등급:planned - browser로
http://localhost:8080에서 로그인 → SPA가 token 받음 →/api/me호출 → backend 401 — 등급:planned - backend 로그에서
JwtValidationException/iss claim did not match메시지 캡처 → screenshot/로그 발췌 — 등급:planned - decode된 access token의
issclaim 캡처 (jwt.io 사용) — 등급:planned - 해결 (A):
KC_HOSTNAME=localhost설정 + backendextra_hosts: ["host.docker.internal:host-gateway"]+application.ymlissuer-uri: http://host.docker.internal:8080/...— 등급:planned- 단점: backend가 보는 issuer-uri와 token 안의 iss가 또 mismatch
- 사실 정답은: token issuer와 backend issuer-uri를 정확히 일치시키는 것
-
(2026-07-17 조사) 이 자기 진단은 정확했다 — A 원안은
iss불일치로 기능적으로 실패한다. 다만 "정확히 일치" 를 네트워크 도달성까지 같은 URL 로 달성해야 한다는 전제는SSRS-JWT-C5기준 틀렸다(F 참조).
- 해결 (정답 재정의):
KC_HOSTNAME=localhost+ backendissuer-uri: http://localhost:8080/realms/keycloak-patterns+ backend container가localhost를 host gateway로 매핑 → 컨테이너 내부localhost:8080이 호스트 8080으로 라우팅 — 등급:planned - 해결 (B):
network_mode: host시도 (Linux only) → backend가 host network share →localhost:8080직접 도달 — 등급:planned - 해결 (C): backend container
extra_hosts: ["localhost:host-gateway"]또는keycloak:host-gateway후 issuer-uri 정렬 — 등급:planned - 해결 (F, 기본):
KC_HOSTNAME=localhost유지 + backendissuer-uri: http://localhost:8080/realms/keycloak-patterns+jwk-set-uri: http://host.docker.internal:8080/realms/keycloak-patterns/protocol/openid-connect/certs+extra_hosts: ["host.docker.internal:host-gateway"]→ 401→200 확인 — 등급:planned - EC2 시나리오:
KC_HOSTNAME=ec2-xx.compute.amazonaws.com설정 → browser는 public DNS로 접근, backend도 동일 hostname을 issuer-uri로 — 등급:planned - (Keycloak 24+)
KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true시연: frontchannel은 public hostname, backchannel은 container DNS 자동 분리 — 등급:planned - 네 해결 방안 비교 노트 (장단점 표) — 등급:
planned(2026-07-17: 3→4개. §구현 가이드 §2 의 표가 사전 명세, 실측 후 이 표로 확정) - 학습 정리: "왜 iss claim 검증이 신뢰의 핵심인가" 설명문 작성 — 등급:
planned
진행 중 메모
- iss 검증이 신뢰의 본질: 만약 backend가
iss검증을 안 하면 다른 Keycloak realm(또는 가짜 IdP)의 token도 통과. RFC 7519 + OIDC Core spec 모두iss검증을 mandatory로 규정.-
(2026-07-17) "RFC 7519 + OIDC Core 가 mandatory 로 규정" 은 본 branch Sources 로 미증명 — RFC/OIDC 원문이 Sources 에 없다. Spring 측은
SSRS-JWT-C2(discovery 4단계의 4번이iss비교)로 동작은 확정되나, 스펙이 mandatory 라고 규정한다는 진술은 별개. §Claims To Verify 참조.
-
- Spring
issuer-uri는 두 가지 역할:- OIDC discovery (
/.well-known/openid-configuration) URL 생성 — JWKS endpoint 자동 찾기 - JWT
issclaim 검증 시 기대값
-
(2026-07-17 핵심) 이 메모가 D6 의 씨앗이었다.
SSRS-JWT-C5는 이 두 역할이 분리 가능함을 공식으로 확정한다 —jwk-set-uri를 주면 Spring 은 역할 1(discovery)을 아예 수행하지 않고,issuer-uri는 역할 2(문자열 검증)만 남는다. 함정의 원인은 "두 역할이 한 값에 묶여 있다" 는 기본 auto-config 의 우연한 결합이지 OIDC 의 요구가 아니다.
- OIDC discovery (
- token 안의
iss는 Keycloak이 박음 —KC_HOSTNAME이 결정. backend가 어디서 JWKS를 fetch하든 token 안의iss와 backend 기대값이 일치해야 함.-
(2026-07-17) "backend 가 어디서 JWKS 를 fetch 하든" — 이 표현이 정확히 F 의 원리다. 작성 시점엔 원리를 적어두고 해결 방안에는 반영하지 않았다(§Audit & Findings A1).
-
- 함정 변형: browser는 EC2 public DNS, backend는 docker internal — Keycloak이 어느 hostname으로 발급할지가
KC_HOSTNAME에 의존. 만약 미설정이면 Keycloak이 request Host 헤더 기준으로 추측 → 변동성 발생.-
(2026-07-17)
KC-HOST-C2(hostname 설정 의무 + dynamic resolution 차단)와 정합. 단 미설정 시 startup 이 실패하는지 vs 추측하는지는 hostname guide 의 §Usage Boundaries 가 명시적으로 "본 인용에 없음" 이라고 적은 항목 — §Claims To Verify.
-
KC_HOSTNAME_BACKCHANNEL_DYNAMIC: Keycloak 24+ 신기능. frontchannel URL은KC_HOSTNAME고정, backchannel(=internal service-to-service)은 request로부터 동적으로 결정. 단일 host에서 매우 유용.-
(2026-07-17 정정 2건) ① "24+ 신기능" → 정확히는 hostname v2(25.0.0 도입,
KC-2500-C1/C3)의 옵션이며 26.0 에서 v1 이 제거되어(KC-2600-C1) 26.x 에선 v2 가 유일. v1 의 대응 옵션은hostname-strict-backchannel로 이름뿐 아니라 boolean 극성이 반대였다. ② "단일 host 에서 매우 유용" → 본 branch 에선 유용하지 않다. Spring 기본 auto-config 는issuer-uri문자열로 discovery 를 호출하므로(SSRS-JWT-C2), Keycloak 이 backchannel URL 을 어떻게 응답하든 backend 의 discovery 호출 자체가localhost(=자기 자신)로 나가 네트워크 단계에서 먼저 실패한다. D 를 쓰려면 결국 F 와 같은 Spring 측 분리 설정이 필요 → D 단독의 부가가치는 "여러 client 종류를 한 곳에서 관리" 에 국한 (D6 참조).
-
결정 사항 (decisions)
- 2026-05-25:
KC_HOSTNAME=localhost강제. 이유: 학습 단계 일관성, P3A 본질 함정 시연. - 2026-05-25: 세 해결 방안 모두 학습. 이유: 면접에서 "왜 A가 아니라 B를 골랐냐"에 답하려면 비교가 필수.
- 2026-05-25: EC2 시나리오는 docker-compose 환경에서 시뮬레이션만. 실제 EC2 배포는 별도 마일스톤.
- 2026-05-25: 실패 재현을 먼저, 해결을 뒤에. 이유: 함정의 원인을 코드/로그로 직접 보지 않으면 학습 효과 낮음.
- 2026-07-18: 기본 해결 경로 = (F) issuer/JWKS 분리.
KC_HOSTNAME=localhost와issuer-uri=localhost로 identity를 고정하고,jwk-set-uri=host.docker.internal로 network address를 분리한다. C(localhost:host-gateway)는/etc/hosts중복 우선순위 때문에 fallback 비교군으로 강등한다. - 2026-07-17:
hostname-strict=false는 해법 후보에서 제외. 이유: ① 보안 — dynamic hostname 해석은KC-HOST-C3의 fraudulent issuer 위험을 정면으로 허용, ② 기능 — 애초에 이 함정을 해결하지 못한다 (request 마다iss가 달라져 문자열 일치가 아예 불가). 학습 환경이라도 채택 안 함. 근거: raw/official-docs/keycloak-hostname-configuration
결정-근거 매핑
각 결정의 직접 근거.
선택 조건열(R2)은 "이 조건일 때 이 결정, 다른 조건이면 어떤 대안" — 분기 없으면N/A. (2026-07-17) 기존 D1~D5 의 Supporting Claims 는 보존하고, 자동조사로 확정된 근거를 반영해 D2 의 Open Risk 를 갱신 + D6·D7 신규 추가.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | KC_HOSTNAME=localhost 강제 (학습 단계 일관성, P3A 본질 함정 시연) |
단일 호스트 학습 환경에서 browser 접근점이 localhost 일 때. 대안: browser 가 EC2 public DNS 로 접근하면 KC_HOSTNAME=<public DNS> (D3 의 시뮬레이션 범위). hostname 미설정은 선택지가 아님 — KC-HOST-C2 가 설정을 의무화 |
raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C2 (hostname 설정 의무), raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C3 (fraudulent issuer 방지) |
official-vendor-doc |
hostname-strict=false는 D7에 따라 제외. 최종 wiring owner는 raw/branch-notes/feature-keycloak-single-ec2-no-google D3이며 본 행은 함정 시연 범위다. |
| D2 | 세 해결 방안 모두 학습 (A: KC_HOSTNAME + extra_hosts, B: network_mode host, C: extra_hosts 응용) | 면접에서 "왜 A 가 아니라 B 냐" 에 답해야 하므로 비교 자체가 목표 — 하나만 실습하는 대안은 학습 목표상 기각. (2026-07-17: 비교 대상이 A/B/C → A/B/C/F 로 확장, D6 가 선택 기준을 부여) | raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C1 (frontchannel/backchannel 분리 capability), raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C4 (backchannel-dynamic full URL 요구) |
official-vendor-doc |
network_mode: host (Linux only) / extra_hosts: host-gateway (Docker 20.10+) 의 동작은 본 Source 가 직접 증명하지 않음 — Docker 공식 doc 별도 필요DOCKER-HOSTNET-C1C5, DOCKER-COMPOSE-NET-C3C5, DOCKER-2010-C1 아카이브 완료. 판정 2건: "Linux only" = 부분 refute(Desktop 4.34+ opt-in 지원), "20.10+" = 부분 confirm(host.docker.internal 은 20.10.0 확정, host-gateway 리터럴 자체의 도입 버전은 release notes 로 미확정 — moby/moby#40007 원문 필요) |
| D3 | EC2 시나리오는 docker-compose 환경에서 시뮬레이션만, 실제 EC2 배포는 별도 마일스톤 | 학습 목표가 iss 함정의 이해 이고 EC2 운영이 아닐 때. 대안: 실제 EC2 배포는 프로젝트 §Phase 진행 후 별도 마일스톤 | UNSUPPORTED_DECISION (운영 우선순위 결정) | UNSUPPORTED_DECISION | 실제 EC2 배포 미수행 — 면접/포트폴리오에 EC2 운영 경험을 주장하면 안 된다. Docker dev의 host.docker.internal 선택을 EC2/prod 값으로 일반화하지 않는다. |
| D4 | 실패 재현을 먼저, 해결을 뒤에 (함정의 원인을 코드/로그로 직접 보지 않으면 학습 효과 낮음) | 학습 목적 branch 일 때. 대안: 납기 압박이 있는 실무 branch 라면 해결부터 (본 branch 는 해당 없음) | UNSUPPORTED_DECISION (학습 방법론 결정 — 외부 자료가 뒷받침하지 않는 본 branch 본문의 자체 판단) | UNSUPPORTED_DECISION | 본 결정은 학습 효율 가설. 결과 측정 (실패 재현 전후 이해도 차이) 자체로만 verified 가능 — 외부 source corroborate 불가 |
| D5 | "iss 검증이 신뢰의 본질, iss 검증을 안 하면 다른 realm/가짜 IdP token 통과" 라는 진행 중 메모 통찰 | N/A (분기 없는 원리 진술) | raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C3 (fraudulent issuer 방지 rationale), raw/official-docs/spring-security-resource-server-jwt.md#SSRS-JWT-C1, raw/official-docs/spring-security-resource-server-jwt.md#SSRS-JWT-C2 (issuer-uri 와 token iss 정확 일치 검증) — 2026-07-17: Spring RS 를 본 branch Sources 에 정식 링크 완료 (기존 "보조 인용" 단서 해소) |
official-vendor-doc |
"RFC 7519 + OIDC Core spec 모두 iss 검증을 mandatory" 라는 본문 진술은 본 branch Source (Keycloak hostname + Spring RS) 가 직접 증명하지 않음 — 두 Source 는 구현 동작만 증명. RFC 7519 §4.1.1 또는 OIDC Core §3.1.3.7 정독으로 corroborate 필요 (미해소) |
| D6 | 기본 = (F) issuer identity/JWKS network address 분리 — issuer-uri=http://localhost:8080/realms/keycloak-patterns, jwk-set-uri=http://host.docker.internal:8080/realms/keycloak-patterns/protocol/openid-connect/certs |
Docker dev bridge profile에서 token identity는 browser-visible localhost로 유지하고 backend JWKS fetch만 host gateway로 보낸다. Linux에서는 backend extra_hosts: ["host.docker.internal:host-gateway"]가 필요하다. C/B는 fallback 비교군 |
raw/official-docs/spring-security-resource-server-jwt.md#SSRS-JWT-C5, raw/official-docs/docker-compose-networking-extra-hosts-official.md#DOCKER-COMPOSE-NET-C3, #DOCKER-COMPOSE-NET-C4 |
official-vendor-doc |
realm/JWKS path와 key rotation cache는 runtime 미검증 → needs-confirmation. 실제 401→200 및 key rotation E2E 전에는 planned |
| D7 | hostname-strict=false 를 해법 후보에서 제외 |
N/A — 조건부 아님(단정적 제외). 유일 예외: hostname-debug=true 와 함께 동적 해석 동작 관찰용으로 일시 사용 후 원복 |
raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C3 (명시적 hostname 설정이 fraudulent issuer 를 방지), raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C5 (strict 기본 true, prod 는 항상 true 권장), raw/official-docs/keycloak-hostname-configuration.md#KC-HOST-C2 (기본 동작이 dynamic resolution 을 차단하는 것이 보안 조치) |
official-vendor-doc |
조사가 확보한 "hostname 설정 시 strict 무시 / hostname 미설정 시 backchannel-dynamic 강제 false" Validations 규칙과 password-reset 링크 조작 공격 시나리오는 아직 아카이브 안 됨 (현행 hostname guide 의 신규 claim 후보 KC-HOST-C6). 제외 결정 자체는 위 3개 claim 으로 충분하나, "기능적으로도 해법이 아니다" 의 1차 근거는 미아카이브 → §Claims To Verify |
구현 가이드
본 branch 의 결정(D1·D2·D4·D6·D7)에서 도출되는 구현 detail 만. realm/client 생성(realm-client-export 소유),
aud검증(audience-validator 소유), EC2 실배포(D3 로 제외)는 §범위 Out of scope 로 이관 — 본 § 에 남기지 않는다(R3). ⚠️ 전 항목planned—keycloak-patternsrepo 부재(NO_GROUND_TRUTH). 아래는 사전 명세이며 코드로 확인된 사실이 아니다.
1. 실패 재현 명세 (의도적 mismatch)
Trace: D4(실패 먼저) + D1(
KC_HOSTNAME이iss를 결정) ←KC-HOST-C2. 관찰 대상 메커니즘은SSRS-JWT-C2(discovery 4단계의 4번 =iss를issuer-uri와 비교).
- UNSUPPORTED_IMPL_DECISION: 아래 "재현 조건" 의 구체 조합(
KC_HOSTNAME제거 +KC_HTTP_ENABLED=true만)은 공식 문서가 권고하는 구성이 아니라 함정을 만들기 위한 의도적 오구성이다.KC-HOST-C2는 hostname 설정을 의무화할 뿐 "미설정 시 무엇이 일어나는지" 는 명시하지 않는다(hostname guide §Usage Boundaries 가 스스로 미증명이라고 기록) — 재현 결과는 실측으로만 확정. trade-off: 학습 목적상 공식이 금지한 구성을 일부러 만드는 것이 이 branch 의 가치.
| 항목 | 명세 | 근거 |
|---|---|---|
| Keycloak 설정 | KC_HOSTNAME 미설정, KC_HTTP_ENABLED=true 만 |
D4 재현 조건 (의도적 오구성) |
| backend 설정 | spring.security.oauth2.resourceserver.jwt.issuer-uri: http://keycloak:8080/realms/keycloak-patterns (Docker 내부 DNS — 함정의 원인) |
DOCKER-COMPOSE-NET-C2 (서비스명 DNS 는 도달됨 → 네트워크는 성공하고 검증만 실패하는 것이 이 함정의 교육 포인트) |
| 관찰점 1 | backend 응답: GET /api/me → 401 |
D4 |
| 관찰점 2 | backend 로그의 예외 클래스명 + 메시지 verbatim 캡처. ⚠️ 실패가 어느 단계에서 나는지가 미확정 — SSRS-JWT-C2 의 discovery 4단계 중 (a) 1단계(keycloak:8080 로 Provider Configuration 조회 → 이 호출은 DOCKER-COMPOSE-NET-C2 로 성공하고, 돌아온 메타데이터의 issuer 필드가 설정한 issuer-uri 와 달라서 실패) 인지 (b) 4단계(token iss 를 issuer-uri 와 비교) 인지 아카이브된 claim 이 규정하지 않음. 단계가 다르면 예외 클래스와 교육 포인트가 통째로 바뀐다 (1단계 실패라면 F 의 정당성은 오히려 강화 — F 는 그 단계를 건너뜀). 예외 클래스명으로 판별할 것 |
§Claims To Verify 1행 + 신규 "실패 단계 판별" 행 |
| 관찰점 3 | jwt.io 로 decode 한 access_token 의 iss 실측값 |
§Claims To Verify 2행 |
| 대조 | 관찰점 3(token 의 iss) ≠ backend issuer-uri 임을 두 문자열 나란히 기록 |
D5 (원리) |
2. 해결 경로 4종 설정 명세 (사전 — 실측 전)
Trace: D6(메커니즘 선택) + D2(4종 모두 학습). 각 행의 근거 claim 은 아래 표
근거열.
- UNSUPPORTED_IMPL_DECISION: realm 명
keycloak-patterns는 프로젝트 F3에서 상속한다. JWKS 경로 문자열은 runtime.well-known/openid-configuration의jwks_uri로 재확인해야 하므로 현재needs-confirmation이다. F는 2026-07-18 기본으로 채택했지만 실제 401→200과 key rotation 결과 전까지planned다.
| # | Keycloak 측 | backend 측 (application.yml) |
Docker 측 | iss 일치? (이론 — 실측 전) |
근거 |
|---|---|---|---|---|---|
| A | KC_HOSTNAME=localhost |
issuer-uri: http://host.docker.internal:8080/realms/keycloak-patterns |
extra_hosts: ["host.docker.internal:host-gateway"] |
❌ FAIL — token iss 는 localhost 기준인데 기대값은 host.docker.internal |
DOCKER-COMPOSE-NET-C3,C4 (메커니즘), SSRS-JWT-C1 (issuer-uri 는 iss 값이어야 함 → 불일치 확정) |
| B | KC_HOSTNAME=localhost |
issuer-uri: http://localhost:8080/realms/keycloak-patterns |
network_mode: host (backend) |
✅ PASS | DOCKER-HOSTNET-C1 (Linux native / Desktop 4.34+ opt-in), DOCKER-HOSTNET-C4 (ports: 무시 — compose 재구성 필요), DOCKER-HOSTNET-C3 (Windows 컨테이너 불가) |
| C | KC_HOSTNAME=localhost |
issuer-uri: http://localhost:8080/realms/keycloak-patterns (무변경) |
extra_hosts: ["localhost:host-gateway"] (backend) |
✅ PASS (조건부 — /etc/hosts 중복 우선순위 실측 필요) |
DOCKER-COMPOSE-NET-C4 (host-gateway), DOCKER-2010-C1 (20.10+), DOCKER-COMPOSE-NET-C3 의 Does not prove (기존 entry 재매핑 미언급 = 이 행의 리스크) |
| F (기본) | KC_HOSTNAME=localhost |
issuer-uri: http://localhost:8080/realms/keycloak-patterns + jwk-set-uri: http://host.docker.internal:8080/realms/keycloak-patterns/protocol/openid-connect/certs |
backend extra_hosts: ["host.docker.internal:host-gateway"] |
✅ PASS 예상 | SSRS-JWT-C5, DOCKER-COMPOSE-NET-C3/C4 |
왜 F가 함정을 없애는가 (D6): iss 문자열 검증과 JWKS fetch 주소를 분리한다. 단 Docker dev에서 host.docker.internal 도달을 위해 Linux의 extra_hosts는 여전히 필요하지만, token iss를 network alias로 바꾸지는 않는다.
3. 검증 관측점 (해결 후)
Trace: D6(어느 경로든 동일 기준으로 판정) + D4(before/after 대조가 학습 산출물). 프로젝트 §Branch 분해표의 본 branch 목표 조건("
KC_HOSTNAME미설정 →issmismatch401재현 → 설정으로 해결(로그 before/after)")과 정합.
| 관측점 | 기대 | 캡처 형태 |
|---|---|---|
GET /api/me |
401 → 200 | 응답 상태 + 본문 |
| backend 로그 | iss 관련 예외 소멸 |
before/after 로그 발췌 |
token iss vs issuer-uri |
문자열 동일 | 두 값 나란히 |
| (F 한정) discovery 호출 부재 | backend 가 localhost:8080/.well-known/... 을 호출하지 않음 — SSRS-JWT-C5 의 "will not ping" 실측 |
네트워크 로그 또는 Keycloak access log |
엣지·실패·의존
-
실패·엣지 경로:
KC_HOSTNAME미설정 시 Keycloak startup 동작 — 실패하는지 vs Host 헤더로 추측하는지 미확정. hostname guide §Usage Boundaries 가 "hostname 미설정 시의 정확한 startup 동작은 본 인용에 없음"(KC-HOST-C2의Does not prove)이라고 명시. 재현 시나리오 자체가 이 동작에 의존하므로 실패 재현이 의도대로 안 될 수 있음(startup 자체가 죽으면 401 이 아니라 서비스 부재).- (C)
/etc/hosts중복 entry — fallback 비교군 — base 이미지의127.0.0.1 localhost와extra_hosts: ["localhost:host-gateway"]우선순위가 미확정이라 기본에서 제외했다. 비교 실험 시getent hosts localhost로 확인한다.- 실습 순서 권고:
getent hosts localhost확인을 초반에 배치 — 기본 경로의 성립 여부가 나머지 계획을 좌우하므로.
- 실습 순서 권고:
- (B)
ports:무시 → compose D6 무효화 —DOCKER-HOSTNET-C4: host network mode 에선-p/ports:가 경고만 내고 무시된다. 깨지는 구체 계약은 raw/branch-notes/feature-keycloak-docker-compose-stack D6(keycloak 8080:8080,app 8081:8081,nginx 80:80) — B 를 backend 에 켜면app 8081:8081게시가 무효가 되어 브라우저의 backend 직접 접근이 조용히 깨진다. - (B) Docker Desktop 게이트 —
DOCKER-HOSTNET-C1/C2/C5: 4.34 미만이면 아예 미지원, 이상이어도 수동 활성화 + layer 4 한정. macOS/Windows 개발자는 재현 불가할 수 있음. - (F) JWKS key rotation —
jwk-set-uri수동 지정 시 Keycloak 이 서명 키를 rotate 하면 캐시 갱신이 discovery 경로와 동일하게 동작하는지 미확정(SSRS-JWT-C2가 retry/backoff 를 범위 밖으로 명시) → 장기 실행 시 401 재발 가능. - EC2 확장 시 hairpin NAT — Docker dev의
host.docker.internalprofile을 EC2에 그대로 적용하지 않는다. EC2/prod의 JWKS network address는 배포 topology owner가 별도로 결정해야 한다(needs-confirmation). - 토큰 만료·clock skew — 본 branch 범위 밖(
iss만 다룸).exp/nbf검증 실패를iss함정으로 오진하지 않도록 실패 재현 시 예외 클래스명까지 확인할 것.
-
다른 계약 의존 (대상 브랜치 + Decision ID 입도):
- raw/branch-notes/feature-keycloak-single-ec2-no-google D3 — 최종 Docker dev wiring owner. 2026-07-18 sync에서 F(issuer/JWKS 분리)를 기본으로 정렬했다.
- raw/branch-notes/feature-keycloak-spring-rs-audience-validator D6 — Spring RS wiring owner.
issuer-uri+ explicitjwk-set-uriprofile로 정렬하며 key rotation cache는needs-confirmation이다. - raw/branch-notes/feature-keycloak-spring-rs-audience-validator D5 — 그 D5("JWKS cache 기본 정책 5분, kid mismatch 시 자동 refresh",
UNSUPPORTED_DECISION)는 본 branch D6 의 F Open Risk("jwk-set-uri수동 지정 시 key rotation 캐시 갱신 미확인")와 동일한 미지수다. 두 노트가 같은 공백을 각자 들고 있음 — 해소 시 공동 처리. - raw/branch-notes/feature-keycloak-docker-compose-stack D3 — ⚠️ F 가 이 결정의 트리거 조건을 소거한다. 그 D3(
depends_on: condition: service_healthy)의 선택 조건은 "app 이 startup 시 keycloak JWKS/issuer discovery 에 의존할 때 이 결정" 인데,SSRS-JWT-C5는 F 하에서 RS 가 "will not ping the authorization server at startup" 이라 하고jwk-set-uri의 사용 동기 자체가 "initialize independently from the authorization server" 다 → F 채택 시 D3 의 근거가 약화(첫 요청 시점 도달성만 필요). D3 owner 에게 전파 필요. - raw/branch-notes/feature-keycloak-docker-compose-stack D6 — port 매핑(
keycloak 8080:8080,app 8081:8081,nginx 80:80)이 본 branch §구현 가이드 §2 표의 모든:8080과 F 의jwk-set-uri포트의 출처. 서비스명이keycloak이 아니게 되거나 포트가 바뀌면 F 의jwk-set-uri와 A/C 의extra_hosts대상이 함께 깨진다. 해결 (B) 는 이 D6 을 무효화(위 실패·엣지 참조). - raw/branch-notes/feature-keycloak-docker-compose-stack D5 — localhost identity를 구현하는 Compose consumer. 값 변경 권한은 parent D3에 있고 본 branch D1은 실패 시연만 소유한다.
- raw/branch-notes/feature-keycloak-vanilla-js-spa-pkce D1 — 프로젝트 §Branch 분해표가 본 branch 의 선행 의존으로 지정. 그 D1(
oidc-client-ts우선 채택)이 token 을 실제 발급받는 경로 → token 이 없으면iss를 관찰할 수 없다(재현 자체가 불가). - raw/branch-notes/feature-keycloak-realm-client-export — realm
keycloak-patterns존재가iss문자열(.../realms/keycloak-patterns)의 전제. 프로젝트 F3(단일 공유 realmkeycloak-patterns)이 SSOT.
검증해야 할 주장
(2026-07-17) 자동조사로 판정된 3건은 Status 를
resolved-by-source로 갱신하고 판정 내용을 §Audit & Findings 에 기록. 나머지는 실측으로만 닫힌다.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
KC_HOSTNAME=localhost 미설정 + KC_HTTP_ENABLED=true 만으로 backend 가 정확히 JwtValidationException / iss claim did not match 메시지를 로그에 남기는지 |
Spring Security 6.x 의 정확한 에러 메시지 텍스트는 버전마다 다를 수 있음 | docker-compose 로 환경 띄우고 backend 로그 캡처, 메시지 verbatim 기록 | planned |
Decoded access_token 의 iss claim 이 정확히 http://localhost:8080/realms/keycloak-patterns 형태로 박히는지 |
Keycloak 26.x 의 iss 생성 규칙은 KC_HOSTNAME + realm path 결합이라 가정 — verbatim Source 부재 (hostname guide §Usage Boundaries 가 "정확한 string concatenation 은 본 페이지에 명시 없음" 으로 스스로 기록) |
jwt.io 로 token decode 후 iss 값 캡처 |
needs-confirmation |
해결 (A) KC_HOSTNAME=localhost + backend extra_hosts: ["host.docker.internal:host-gateway"] 조합이 실제 e2e 로 401 → 200 으로 전환되는지 |
본 branch 본문 자체가 "issuer-uri 와 token iss mismatch" 함정을 재인지함 — 정답은 "token issuer 와 backend issuer-uri 를 정확히 일치". 2026-07-17: SSRS-JWT-C1 기준 A 원안은 이론적으로 FAIL 로 판정 — 실측은 "실패함" 을 확인하는 대조군 |
docker-compose 환경 구성 후 GET /api/me 200 응답 확인 (200 이 나오면 오히려 이론 판정이 틀린 것 → 재조사) | planned |
network_mode: host 가 macOS/Windows Docker Desktop 에서 동작 안 함 + Linux only 진술의 정확한 vendor 출처 |
본 branch 본문 메모 — 직접 source 인용 부재 | Docker 공식 doc (network_mode 페이지) 또는 Docker Desktop release note 정독 |
resolved-by-source (2026-07-17) — 부분 refute. raw/official-docs/docker-host-network-driver-official DOCKER-HOSTNET-C1/C2: Linux native + Docker Desktop 4.34+ 에서 opt-in 지원(Settings 수동 활성화). "Linux only" 는 무조건 진술로는 부정확. 단 C5(layer 4 한정) + C3(Windows 컨테이너 불가)로 제약은 실재 |
extra_hosts: host-gateway 의 최소 Docker 버전 (20.10+) 의 정확한 source |
본 branch 본문 메모 — verbatim source 부재 | Docker Compose 공식 spec 또는 docker engine release note 확인 | resolved-by-source (2026-07-17) — 부분 confirm. raw/official-docs/docker-engine-20-10-release-notes-official DOCKER-2010-C1: host.docker.internal 의 Linux dockerd 지원은 20.10.0(2020-12-08) 확정. 단 host-gateway 리터럴 자체의 도입 버전은 이 release notes 페이지로 미확정(문자열이 20.10.23 버그수정 항목에만 등장) → 완전 확정하려면 moby/moby#40007 원문 필요 |
Keycloak 26.x 에서 KC_HOSTNAME_BACKCHANNEL_DYNAMIC 옵션이 실제 frontchannel/backchannel URL 을 자동 분리하는지 |
KC-HOST-C1/C4 가 capability 자체는 증명하나, 본 프로젝트 단일 host 시나리오에서의 실 동작은 별도. 2026-07-17 조사도 공식 근거를 못 찾음 — hostname guide(v2) 본문에 issuer 라는 단어 자체가 없음. v1 문서(24.0.5)에는 "the issuer is also based on the URL set to the frontend endpoints" 가 있었으나 v2 가 재확인하지 않음 |
docker-compose 환경에 옵션 추가 후 두 경로에서 각각 .well-known/openid-configuration 호출해 issuer 값 비교. KC_HOSTNAME_DEBUG=true + /realms/master/hostname-debug 병행 권고 |
planned |
Keycloak 24+ 에서 26.x 까지 KC_HOSTNAME_* 옵션 명칭이 자주 바뀐다는 본문 진술 |
본 branch 본문 메모 — Source 부재 | Keycloak release notes (24, 25, 26) 정독, 옵션 rename 이력 정리 | resolved-by-source (2026-07-17) — 부분 confirm / 프레이밍 refute. KC-2500-C1~C4 + KC-2600-C1: 개편은 실재하나 "26.x 안에서 자주" 가 아니라 25.0.0 에서 v2 도입(v1 deprecated) → 26.0.0 에서 v1 제거 의 1회 대개편이며 26.x point release 간에는 안정적. 정확한 진술: "한 번 크게 바뀌었고 그 시점은 26.0 이전에 종료" |
iss 검증을 안 하면 다른 Keycloak realm 또는 가짜 IdP token 통과하는지 의 실 재현 |
본 branch 본문 메모. KC-HOST-C3 는 일반 rationale 만 — 가짜 IdP 시나리오 직접 증명 안 함 |
두 번째 Keycloak realm 또는 미니멀 fake JWT issuer 띄우고 token 발급 → backend 가 거부하는지 확인 (현재 hostname-strict + audience validator 와 결합) | planned |
(신규 2026-07-17) 해결 (C) 에서 extra_hosts: ["localhost:host-gateway"] 가 base 이미지의 기존 127.0.0.1 localhost entry 를 실제로 이기는지 |
Docker 공식이 이 케이스(기존 hostname 재매핑)를 명시 안 함 — DOCKER-COMPOSE-NET-C3 의 Does not prove 가 경계를 기록 |
컨테이너 내부 getent hosts localhost 로 실제 resolve IP 확인 |
needs-confirmation |
(신규 2026-07-17) 해결 (F) 에서 jwk-set-uri 수동 지정 시 Keycloak key rotation 과의 캐시 갱신 상호작용 |
SSRS-JWT-C2 가 "discovery 실패 시 retry/backoff 정책은 범위 밖" 으로 명시 — rotation 시 JWKS 재fetch 정책 불명 |
raw/official-docs/jwks-keycloak-key-rotation-active-passive 정독 + F 조합에서 키 rotate 후 401 재발 여부 실측 | needs-confirmation |
(신규 2026-07-17) JWKS endpoint 경로 /realms/<realm>/protocol/openid-connect/certs 가 26.x 의 실제 값인지 |
본 branch Sources 중 어느 것도 이 경로 문자열을 증명하지 않음 (구현 가이드 §2 의 UNSUPPORTED_IMPL_DECISION) |
.well-known/openid-configuration 의 jwks_uri 필드 실측값으로 확정 |
needs-confirmation |
| (신규 2026-07-17) "hostname 설정 시 hostname-strict 무시 / hostname 미설정 시 backchannel-dynamic 강제 false" Validations 규칙 + password-reset 링크 조작 공격 시나리오 | 조사가 hostname guide(v2) 원문에서 확보했으나 아직 아카이브 안 됨 — D7 의 "기능적으로도 해법이 아니다" 논거의 1차 근거 | 현행 raw/official-docs/keycloak-hostname-configuration 에 KC-HOST-C6 로 추가 아카이브 (기존 파일 갱신 — 신규 파일 아님) |
needs-confirmation |
(신규 2026-07-17, depth 감사 F4) 실패 재현 시 401 이 discovery 1단계(메타데이터 issuer 필드 대조)에서 나는지 token iss 검증 4단계에서 나는지 |
SSRS-JWT-C2 는 4단계를 나열할 뿐 어느 단계가 mismatch 를 먼저 잡는지 규정 안 함(그 claim 의 Does not prove 는 retry/backoff 만 배제). §구현 가이드 §1 재현 구성은 issuer-uri: http://keycloak:8080/... 이라 discovery 호출 자체는 성공한다 → 실패 지점이 두 후보로 갈림 |
backend 로그의 예외 클래스명으로 판별 (discovery 단계 실패면 JwtDecoderInitializationException 계열, iss 검증 실패면 JwtValidationException 계열로 추정 — 실측으로 확정). 필요 시 Spring §Startup Expectations 잔여 문단을 기존 raw 에 추가 아카이브 |
needs-confirmation |
(신규 2026-07-17, depth 감사 F7) SSRS-JWT-C5 의 "will not ping … at startup" 이 first-request 시점 discovery 까지 배제하는지 |
SSRS-JWT-C2 는 discovery 가 "at the first request containing a JWT" 에 시작된다고 함 → "startup 에 안 한다" 가 "영원히 안 한다" 를 verbatim 으로 닫지는 않음. §제목("… JWK Set Uri Directly") + "Consequently" 인과 구조상 discovery 자체를 건너뛴다는 독해가 자연스러우나 명시 아님 |
§구현 가이드 §3 의 "(F 한정) discovery 호출 부재" 관측점으로 실측. 부수적으로 spring-security-resource-server-jwt.md 의 SSRS-JWT-C5 Does not prove 열에 이 경계 1줄 추가 권고 |
needs-confirmation |
Audit & Findings (2026-07-17 /branch-spec 자동조사)
본 § 는 조사 결과 중 결정으로 흡수되지 않은 발견·정합 권고만 보존 (CLAUDE.md §15.5 R3 — 이관 history 는 별도 § 에).
| ID | 유형 | 발견 | 조치 |
|---|---|---|---|
| A1 | MISSING_EVIDENCE_LINK (해소됨) |
raw/official-docs/spring-security-resource-server-jwt 는 이미 이 repo 에 아카이브돼 있었고 SSRS-JWT-C5 가 D6 의 정답을 담고 있었으나, 본 branch 의 Sources 표에 링크되지 않아 해결 방안이 Docker 레이어(A/B/C)로만 좁혀져 있었다. 노트의 §진행 중 메모("backend 가 어디서 JWKS 를 fetch 하든")는 이미 원리를 알고 있었으나 해결 방안에 반영되지 않음 |
2026-07-17 Sources 표에 정식 링크 + D6 신설 + In scope 에 F 추가 (해소) |
| A2 | CROSS_BRANCH_DECISION_TENSION |
parent D3과 본 D6가 Docker layer vs issuer/JWKS split을 달리 가리켰음 | 해소 (2026-07-18) — parent D3을 owner로 유지하고 F profile을 기본으로 정렬. 본 D1은 실패 시연 범위만 소유 |
| A7 | CROSS_BRANCH_DECISION_CONFLICT |
audience-validator D6의 discovery-only wiring과 본 D6의 explicit jwk-set-uri가 충돌했음 |
해소 (2026-07-18) — Spring RS owner도 explicit jwk-set-uri Docker dev profile을 허용하도록 정렬. key rotation cache는 runtime needs-confirmation으로 남김 |
| A8 | TRIGGER_CONDITION_ERODED (미해소 — depth 감사 F3 발견) |
raw/branch-notes/feature-keycloak-docker-compose-stack D3(depends_on: condition: service_healthy)의 선택 조건은 "app 이 startup 시 keycloak JWKS/issuer discovery 에 의존할 때" 인데, SSRS-JWT-C5("will not ping … at startup" + "initialize independently from the authorization server")에 따라 F 는 그 트리거 조건을 약화시킨다(첫 요청 시점 도달성만 필요) → 완전 소거 여부는 F7 확정에 종속 — SSRS-JWT-C5 의 "at startup" 이 first-request 시점 discovery 까지 배제하는지가 needs-confirmation 이므로, 현재 정확한 강도는 "약화"다 |
본 branch 결정 아님(owner = docker-compose-stack). §엣지·실패·의존에 명시 완료. F 승인 시 D3 owner 에게 전파 필요 — healthcheck 를 유지할지(다른 이유: PostgreSQL 의존 등)는 그 branch 판단 |
| A3 | DOC_DRIFT (부모 노트, 미해소) |
부모 노트 D5 는 "realm 1개 + client 1개(spa-client, public)" 라고 적었으나, 프로젝트 노트 F3(고정 결정, SSOT)은 "단일 공유 realm keycloak-patterns, 패턴당 client 1개 — spa-public / token-mediating-confidential / bff-confidential / edge-proxy" 로 client 명이 다름 |
본 branch 범위 밖(부모 노트 소유). 본 branch 는 realm 명 keycloak-patterns(F3 정합)만 사용하고 client 명은 참조 안 함. /sync 대상으로 보고 |
| A4 | ALTERNATIVE_REJECTED |
조사가 5번째 대안 E — network_mode: "service:<anchor>" (더미 anchor 컨테이너의 network namespace 를 Keycloak+backend 가 공유 → 컨테이너 안에서 localhost:8080 이 문자 그대로 동작, 플랫폼/버전 게이트 없음)를 발견. iss 판정 PASS |
채택 안 함 — anchor 컨테이너가 죽으면 두 서비스 네트워크 전체가 죽고, 공유 서비스의 모든 포트를 anchor 의 ports: 에 나열해야 함. 서비스 2개 규모에 과설계. §범위 Out of scope 에 기록. 서비스 3개+ 가 동일 localhost identity 를 요구하면 재검토 |
| A5 | DROPPED_CANDIDATE |
조사가 "컨테이너 IP 를 docker inspect 로 확인해 browser/backend 모두 그 IP 로 접속" 안을 탈락시킴 — docker compose up 재기동마다 IP 가 바뀌어 issuer-uri/KC_HOSTNAME 고정 불가(재현성 없음) |
기록만. 실습 불필요 |
| A6 | UNARCHIVED_EVIDENCE |
조사가 확보한 hostname guide(v2) Validations 규칙("hostname 설정 시 hostname-strict 무시" / "hostname 미설정 시 backchannel-dynamic 강제 false")과 password-reset 링크 조작 공격 시나리오 verbatim 은 D7 의 핵심 논거이나 아직 아카이브 안 됨. 기존 raw/official-docs/keycloak-hostname-configuration 의 신규 claim(KC-HOST-C6) 후보 — 신규 파일이 아니라 기존 파일 갱신이라 wiki-source-summarizer 계약 밖 |
§Claims To Verify 에 등록. 다음 세션에 수기 또는 wiki-doc-author mode=migrate 로 기존 파일에 추가 권고 |
마주친 문제
⚠️ (2026-07-17) 아래 3건은 "(구현 시작 후 추가)" 로 적혀 있으나 구현은 시작된 적이 없다(repo 부재 — NO_GROUND_TRUTH). 실제로는 예상 문제 메모이며, 2026-07-17 자동조사가 공식 문서로 판정했다. 실측 근거가 아니므로 면접에서 "겪었다" 로 말하면 안 된다.
- (구현 시작 후 추가)
network_mode: host사용 시 macOS/Windows Docker Desktop에서 동작 안 함 — Linux only.- → 부분 refute (
DOCKER-HOSTNET-C1/C2): Docker Desktop 4.34+ 에서 opt-in 지원. "동작 안 함" 은 4.34 미만 또는 미활성 시에 한정. 단 layer 4 한정(C5).
- → 부분 refute (
- (구현 시작 후 추가)
extra_hosts: host-gateway동작이 Docker 버전에 따라 다름 — 최소 Docker 20.10+.- → 부분 confirm (
DOCKER-2010-C1):host.docker.internal의 Linux dockerd 지원 = 20.10.0.host-gateway리터럴 자체의 도입 버전은 미확정.
- → 부분 confirm (
- (구현 시작 후 추가) Keycloak 26.x에서
KC_HOSTNAME_*옵션 명칭이 자주 바뀜 — 공식 문서 버전 확인 필요.- → 프레이밍 refute (
KC-2500-C1~C4,KC-2600-C1): "자주" 가 아니라 25.0.0 v2 도입 → 26.0.0 v1 제거의 1회 대개편. 26.x 안에서는 안정적.
- → 프레이밍 refute (
묶음
- raw/official-docs/docker-compose-networking-extra-hosts-official
- raw/official-docs/docker-engine-20-10-release-notes-official
- raw/official-docs/docker-host-network-driver-official
- raw/official-docs/keycloak-2500-hostname-v2-release-official
- raw/official-docs/keycloak-2600-hostname-v1-removed-official
- raw/official-docs/keycloak-hostname-configuration
- raw/official-docs/openid-connect-core-id-token-validation
- raw/official-docs/spring-security-resource-server-jwt
본 sub-sub-branch 는 leaf — 자식 자료 없음. Phase 3 P3A 실 구현 또는 외부 산출물 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화. (2026-07-17) 근거 자료는 §Sources 표가 SSOT — 본 § 에 중복 나열하지 않는다(병행 dispatch 가 만든
### Sources서브섹션 제거).
오류 기록 (이 sub-sub-branch 작업 중 발생)
- (없음 — 현재 documented-only 단계)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — Phase 3 실 구현 단계에 누적)
관련 일일 노트
완료 후 정리
실패 재현 → 해결 검증 전체를 로그/screenshot으로 캡처 시
planned→actually-implemented/locally-verified승급. 본 sub-sub는 학습 가치가 핵심 — 실제로 함정을 "맞아본" 경험이 면접 자산.
- PR 링크: (별도 keycloak-patterns repo)
- 리뷰 메모:
- 머지 결과 / 배포 환경: 로컬 docker-compose / 단일 EC2 시뮬레이션
- wiki 추출 대상:
actually-implemented항목: (구현 후 채움)locally-verified항목: (구현 후 채움)prod-verified항목: (없음)
- 추출하지 않을 항목: 현재 전부
planned.