Files
llm-wiki/raw/branch-notes/feature-keycloak-oauth2-proxy-oidc-flow.md
T

52 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-oauth2-proxy-oidc-flow (P1A — oauth2-proxy 구성과 OIDC 흐름) branch-note raw BR-KEYCLOAK-PATTERNS-OVERVIEW-012 project-work-item keycloak-patterns-overview WI-KEYCLOAK-PATTERNS-OVERVIEW-012
DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1
DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1
WI-KEYCLOAK-PATTERNS-OVERVIEW-002
1 feature-keycloak-oauth2-proxy-oidc-flow
keycloak-patterns
branch
keycloak-patterns
p1a
oauth2-proxy
oidc
cookie-session
2026-05-25 in-progress 844b40d60f42d3186b5952aa27da4febf3da180c1f9fe017400afa625e0d7a36

branch: feature-keycloak-oauth2-proxy-oidc-flow (P1A — oauth2-proxy 구성과 OIDC 흐름)

Layer: raw/branch-notes/raw/project-notes/keycloak-patterns-overview Work Item의 직접 자식 branch. oauth2-proxy 단독 컴포넌트의 구성과 OIDC 흐름을 단계별로 분해. nginx 통합은 별도 sub-sub 에서 다룬다. 본 sub-sub-branch는 문서까지만 (documented-only). 실 구성/시연 대상 아님. status_label: in-progress

부모 (필수)

raw/project-notes/keycloak-patterns-overview

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1 AP4는 oauth2-proxy ForwardAuth를 사용한다 oauth2-proxy OIDC flow와 forwarded-user 전달 경계에 적용한다 raw/project-notes/keycloak-patterns-overview
DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1 done-bar는 E2E success와 signature security failure 재현·해결 evidence다 unauthenticated redirect와 login 후 backend 200 재현 증거에 적용한다 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

없음.

목표

P1A 패턴의 핵심 컴포넌트인 oauth2-proxy 자체의 설정과 OIDC handshake를 단계별로 이해. 부모 sub-branch가 다이어그램/sequence 수준을 정리했다면, 본 문서는 provider=keycloak-oidc 설정 + cookie session + OIDC discovery + JWT bearer 검증 경로 각각이 어디서 동작하고 어떤 함정이 있는지를 분리해서 본다.

핵심 질문:

  1. provider=keycloak-oidcprovider=oidc(generic)의 실질 차이는? → role/group claim 매핑 + Keycloak userinfo endpoint 처리.
  2. cookie domain · cookie secret · --whitelist-domain은 각각 어떤 공격 surface를 막는가?
  3. cookie 없이 Bearer JWT를 검증하는 경로는 언제 쓰며, 실제 검증 메커니즘은 무엇인가? (RFC 7662 introspection 호출로 부르지 않음)
  4. 로그아웃 시 Keycloak 세션까지 끊기 위한 흐름은? (RP-Initiated Logout)
  • 이슈:
  • PR:

범위

포함 범위

  • provider=keycloak-oidc 설정 (--client-id, --client-secret, --oidc-issuer-url, --redirect-url)
  • OIDC discovery / scopes / cookie 설정 (--cookie-secret, --cookie-domain, --cookie-secure, --cookie-samesite, --cookie-expire)
  • JWT bearer 검증 경로 vs cookie session 경로 (--skip-jwt-bearer-tokens; RFC 7662 introspection과 구분)
  • 로그아웃 흐름 (RP-Initiated Logout)
  • role/group claim 매핑

제외 범위

  • nginx 통합 (별도 sub-sub feature-keycloak-nginx-auth-request-integration)
  • 헤더 spoofing 방어 (별도 sub-sub feature-keycloak-header-spoofing-defense)
  • Traefik ForwardAuth 대안 비교 (별도 sub-sub feature-keycloak-traefik-forwardauth-alternative)
  • 실 환경 구성 (P3A 한정, 본 sub-sub는 문서까지만)

근거 (필수, 최소 1개+)

상단 2개는 부모 sub-branch에서 인용한 외부 자료를 재참조. 나머지 5개는 2026-07-17 /branch-spec 자동조사로 본 sub-sub-branch 에서 신규 보존 — D5(cookie/session storage) · D6(logout) · D7(whitelist-domain) · D8(JWT bearer 분기) · D9(discovery) 의 UNSUPPORTED 해소용.

Source 정당화하는 결정
raw/official-docs/oauth2-proxy-overview-config-official oauth2-proxy 공식 (Reverse proxy + auth provider integration) — provider=keycloak-oidc 채택 근거
raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official oauth2-proxy ↔ Keycloak OIDC 연동 — cookie session / scope mapping 결정 근거
raw/official-docs/oauth2-proxy-cookie-redirect-flags-official cookie 설정 표준화(D5: --cookie-secret/--cookie-domain/--cookie-secure/--cookie-samesite/--cookie-expire/--cookie-refresh) + open redirect 방어(--whitelist-domain) + OIDC discovery 우회(--skip-oidc-discovery) 근거
raw/official-docs/oauth2-proxy-session-storage-official D5 — session storage 백엔드(cookie vs redis) 선택의 공식 메커니즘 근거 (stateless cookie 저장, 세션 lock 부재, Redis ticket/SETEX 메커니즘, --session-store-type/--redis-connection-url/Sentinel·Cluster 플래그)
raw/official-docs/oauth2-proxy-endpoints-signout-official /oauth2/sign_out 기본 동작(로컬 cookie만 삭제) + rd query parameter/{id_token} placeholder 로 Keycloak end_session_endpoint 트리거하는 메커니즘 — D6 (RP-Initiated Logout) 근거
raw/official-docs/oauth2-proxy-behaviour-cookie-vs-bearer-official oauth2-proxy 요청 검증 분기(opportunistic cookie/JWT 검증, invalid JWT fallback, 401/403/redirect 조건) 근거 — Bearer 경로를 RFC 7662 introspection으로 부를 근거는 없으며, 로컬 JWKS 검증 여부는 별도 확인 필요
raw/official-docs/keycloak-oidc-logout-endpoint-official Keycloak 측 RP-Initiated Logout 요구사항 — end_session_endpoint(/realms/{realm}/protocol/openid-connect/logout) + id_token_hint + post_logout_redirect_uri 파라미터 명세 + Backchannel Logout URL client 설정 — D6 (RP-Initiated Logout) 의 Keycloak 측 근거 (oauth2-proxy 측은 위 oauth2-proxy-endpoints-signout-official 가 커버)

TODO

각 항목 옆에 증거 등급 표기.

  • provider=keycloak-oidc 설정 정리 (--client-id, --client-secret, --oidc-issuer-url, --redirect-url) — 등급: planned
  • OIDC discovery (.well-known/openid-configuration) 호출 시점과 cache 정책 정리 — 등급: planned
  • OIDC scopes 정리 (openid profile email, groups, offline_access) + Keycloak client scope 매핑 — 등급: planned
  • cookie 설정 정리: --cookie-secret 생성(32byte), --cookie-domain, --cookie-secure, --cookie-samesite=lax|strict, --cookie-expire — 등급: planned
  • cookie session 모드 vs Redis session store 모드 비교 — 등급: planned
  • --whitelist-domain 옵션의 역할 (open redirect 방지) 정리 — 등급: planned
  • JWT bearer 검증 경로 (--skip-jwt-bearer-tokens, --extra-jwt-issuers)의 의미와 실제 검증 메커니즘 정리 — RFC 7662 introspection으로 단정하지 않음 — 등급: planned
  • 로그아웃 흐름 정리: /oauth2/sign_out + Keycloak RP-Initiated Logout (end_session_endpoint) 연계 — 등급: planned
  • role/group claim 매핑: oauth2-proxy --allowed-group + Keycloak groups client scope mapper — 등급: planned
  • 학습 시연 단계 정리 (실행 안 함, 문서상의 가상 단계만) — 등급: planned

진행 중 메모

작업하며 떠오른 메모.

  • provider=oidc (generic)도 Keycloak에 동작하지만, keycloak-oidc는 group/role 매핑이 native라 --allowed-group 같은 옵션이 자연스럽게 동작.
  • cookie session 모드는 access_token 자체를 cookie에 넣을 수 있어 nginx 헤더 4kb 한도 함정과 직결 (sub-sub feature-keycloak-nginx-auth-request-integration에서 다룸).

결정 사항 (decisions)

  • 2026-05-25: 본 sub-sub는 oauth2-proxy 단독 컴포넌트 학습으로 한정. nginx 통합·헤더 spoofing 방어·Traefik 대안은 형제 sub-sub 에서. 분리 이유 = 각 토픽의 함정이 서로 독립적이라 한 문서에 합치면 비교가 흐려짐.
  • 2026-07-17 (/branch-spec 자동조사): D5(cookie 속성/session storage) · D6(RP-Initiated Logout) 의 UNSUPPORTED_DECISION 해소. 공식 문서 5건을 신규 보존해 D5a/D5b/D5c 로 분해하고, D6 을 oauth2-proxy 측(O2PE-C1C4) + Keycloak 측(KC-LOGOUT-C1C6) 양측 근거로 승격. 추가로 D7(--whitelist-domain) · D8(JWT bearer 분기) · D9(OIDC discovery) 를 신규 결정으로 분리. / 검토한 대안: logout 은 back-channel logout·로컬 cookie 삭제만 두 대안을 비교했고 → RP-Initiated 채택(back-channel 은 oauth2-proxy 수신 지원 미확인, §Audit BACKCHANNEL_UNVERIFIED). session storage 는 cookie·Redis 비교 → 인스턴스 수가 아니라 세션 payload 크기가 실제 결정 변수임을 확인하고 조건부로 남김. / 근거: raw/official-docs/oauth2-proxy-endpoints-signout-official, raw/official-docs/keycloak-oidc-logout-endpoint-official, raw/official-docs/oauth2-proxy-cookie-redirect-flags-official, raw/official-docs/oauth2-proxy-session-storage-official, raw/official-docs/oauth2-proxy-behaviour-cookie-vs-bearer-official
  • 2026-07-18 (drift 해소): 기존 "token introspection 모드" 명칭을 **"JWT bearer 검증 경로"**로 교정. 공식 문서가 RFC 7662 introspection endpoint 호출을 서술하지 않으므로 두 메커니즘을 동일시하지 않는다. 로컬 JWKS 검증 여부는 실측 전까지 needs-confirmation으로 유지한다.

결정-근거 매핑

각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. 본 sub-sub-branch 의 결정 (D1) 은 학습 범위 분할 (scoping) 결정으로 외부 vendor doc 인용 없음 — UNSUPPORTED_DECISION 으로 표기. TODO 항목 중 외부 vendor doc 으로 뒷받침되는 것은 D2~ 로 분리해 명시.

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 본 sub-sub 는 oauth2-proxy 단독 컴포넌트 학습으로 한정 (nginx 통합 / spoofing 방어 / Traefik 대안 분리) N/A — 학습 범위 분할(organizational). 분기 없음 UNSUPPORTED_DECISION (학습 분할 결정은 내부 scoping — 외부 vendor doc 인용 불요) N/A (organizational decision) 분할이 너무 잘게 쪼개져 다 모았을 때 비교 매트릭스를 다시 합성해야 하는 비용
D2 provider=keycloak-oidc + --client-id + --client-secret + --oidc-issuer-url 4종 파라미터를 oauth2-proxy ↔ Keycloak 연결의 필수 입력으로 채택 Keycloak 17+--oidc-issuer-url=https://<host>/realms/<realm>. 17 미만/auth/realms/<realm> (legacy context path). group/role 을 oauth2-proxy 레벨에서 안 쓸 거면 generic provider=oidc 도 가능하나 D4 의 native 매핑을 잃음 raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official.md#O2PK-C1, raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official.md#O2PK-C2, raw/official-docs/oauth2-proxy-overview-config-official.md#OAUTH2PROXY-C5 official-vendor-doc (oauth2-proxy 공식 + Keycloak 17+ issuer URL 패턴 명시) Keycloak 17+ context-path (/realms/ vs /auth/realms/) 가 reverse-proxy 가 /auth prefix 를 재추가한 환경에서 어떻게 동작하는지 미검증
D3 OIDC scopes 정리: openid profile email + groups (group authorization 필요 시) + offline_access (refresh token 필요 시) — Keycloak client scope 매핑 필요 --allowed-group 을 쓸 때만 groups scope + Group Membership mapper 추가(O2PK-C5). refresh token 이 필요할 때만 offline_access — 불필요하면 빼서 세션 payload 를 줄임(D5a 의 4kb 압력과 직결) raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official.md#O2PK-C5 (groups client scope 필요), raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official.md#O2PK-C3 official-vendor-doc (groups 부분만) + UNSUPPORTED_DECISION (base scope · offline_access 부분) ① client scope 이름이 정확히 groups 가 아닐 때 동작 보장 안 됨 — default vs optional scope 구분 별도. ② base scope 문자열(openid profile email)과 offline_access↔refresh token 관계는 본 branch 근거 raw 에 문자열이 0건O2PK-C3/C5groups client scope + Group Membership mapper 만 증명한다. 이 부분은 OIDC 일반 배경지식에서 온 사용자 임의 결정이며 벤더 권고가 아님(§구현 가이드 1 의 --scope UNSUPPORTED_IMPL_DECISION 참조)
D4 role/group claim 매핑: --allowed-role=<realm role> 또는 --allowed-role=<client>:<client role> + --allowed-group=</group> realm 전역 권한 → --allowed-role=<realm role>. 특정 client 한정 권한 → --allowed-role=<client id>:<client role>. 조직 트리 기반 → --allowed-group=</group> (+ D3 의 groups scope 필수). 인가를 edge 에서 안 하고 backend 로 미룰 거면 셋 다 미설정("valid user" 만 요구, O2PK-C3) raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official.md#O2PK-C3, raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official.md#O2PK-C4 official-vendor-doc 인가(authorization) 실패 시 응답 코드는 여전히 미확인. D8 의 O2PBEH-C2~C4인증(authentication) 단계의 401/403/redirect 만 증명 — role/group 불일치 시의 코드는 별도. nginx error_page 처리에 영향
D5a session storage 백엔드 기본값 = cookie (--session-store-type=cookie, stateless, 클라이언트 저장 + 매 요청 전송, 세션 lock 부재로 동시 refresh 시 재인증 강제 가능) 세션 payload 가 4kb 미만으로 유지되고(= D3 에서 offline_access/과다 role claim 회피) 컴포넌트 최소화가 우선이면 cookie. payload 가 4kb 를 넘길 여지가 있거나 access_token 을 backend 로 전달하면 → D5b(Redis). 인스턴스 개수는 이 선택의 기준이 아님(단일 EC2 여도 4kb 압력은 동일) raw/official-docs/oauth2-proxy-session-storage-official.md#O2PSESS-C1, raw/official-docs/oauth2-proxy-session-storage-official.md#O2PSESS-C2, raw/official-docs/oauth2-proxy-nginx-integration-official.md#O2PN-C6 (4kb 임계값의 실제 owner claim — session-storage 문서가 아니라 nginx 통합 문서에 있음) official-vendor-doc 쿠키 실제 바이트 한도·4kb 초과 시 분할(split) 동작은 이 자료에 없음 — 별도 raw source 필요 (OAUTH2PROXY-SESSION-STORAGE 문서 §Usage Boundaries 참고). Azure/Google federation 사례의 큰 토큰 크기를 Keycloak native 환경에 일반화 금지
D5b Redis session store 채택 시 --session-store-type=redis + --redis-connection-url=redis://host[:port][/db-number] 로 연결하며, 클라이언트에는 ticket({CookieName}-{ticketID}.{secret})만 전달 (세션 본문은 서버측 Redis 에 SETEX 로 암호화 저장). Sentinel/Cluster 는 --redis-use-sentinel=true/--redis-use-cluster=true (상호 배타)로 구성 4kb 초과 위험 또는 access_token 헤더 전달 중 하나라도 해당하면 Redis. 둘 다 아니면 D5a 로 남김(컴포넌트 1개 추가는 P1A 의 "단일 EC2 최소 구성" 과 상충). standalone 이 기본이고 Sentinel↔Cluster 는 상호 배타이므로 동시 지정 금지. "다중 replica" 는 트리거가 아님 — cookie store 는 "completely stateless"(O2PSESS-C1) 라 --cookie-secret 만 공유하면 replica 간 세션이 성립한다. D5a 의 "인스턴스 수는 기준이 아님" 과 정합 raw/official-docs/oauth2-proxy-session-storage-official.md#O2PSESS-C3, raw/official-docs/oauth2-proxy-session-storage-official.md#O2PSESS-C4, raw/official-docs/oauth2-proxy-session-storage-official.md#O2PSESS-C5, raw/official-docs/oauth2-proxy-session-storage-official.md#O2PSESS-C6 official-vendor-doc P1A(단일 EC2, documented-only) 규모에서 Redis 도입이 실제로 "필요"한지는 이 자료가 증명하지 않음 — 메커니즘 존재만 확인. Redis 도입 시 --cookie-secret 관리 부담은 사라지지 않음(ticket 암호화에 계속 사용)
D5c cookie 속성값 표준화 채택: --cookie-secret(seed string, -file 변형은 raw binary 16/24/32byte) / --cookie-domain / --cookie-secure=true(기본값) / --cookie-samesite=""(기본값 — 이때 브라우저가 실제로 어떤 SameSite 로 해석하는지는 O2PCOOKIE-C1 범위 밖) / --cookie-expire=168h0m0s(기본값) / --cookie-refresh(기본 비활성, Keycloak 은 지원 provider 목록에 포함) HTTPS 종단이 있으면 --cookie-secure=true(기본값 유지). 순수 로컬 http:// 시연에 한해서만 false — 이 경우 "로컬 한정 예외" 라벨 필수. --cookie-csrf-samesite 를 따로 안 주면 CSRF 쿠키가 세션 쿠키의 samesite 를 상속(O2PCOOKIE-C6)하므로, samesite 를 조일 때 두 값을 함께 판단 raw/official-docs/oauth2-proxy-cookie-redirect-flags-official.md#O2PCOOKIE-C1, #O2PCOOKIE-C2, #O2PCOOKIE-C3, #O2PCOOKIE-C4, #O2PCOOKIE-C5, #O2PCOOKIE-C6 official-vendor-doc 공식 문서는 각 플래그의 존재·기본값만 증명 — P1A 배포에서 --cookie-samesitelax/strict/none 중 무엇으로 명시할지는 별도 아키텍처 결정(교차 사이트 redirect 여부에 따름), byte 길이 제약이 --cookie-secret-file 행에만 명시돼 --cookie-secret 자체에도 적용되는지는 미확정
D6 RP-Initiated Logout 채택 — /oauth2/sign_out?rd=<Keycloak end_session_endpoint> 형태로 rd query parameter(또는 X-Auth-Request-Redirect 헤더)에 Keycloak end_session_endpoint(/realms/{realm}/protocol/openid-connect/logout)를 지정하고, {id_token} placeholder 로 id_token_hint 를 주입. 전제: 그 도메인이 --whitelist-domain 에 등록돼야 함(D7) Keycloak 세션까지 끊어야 하면 이 결정. oauth2-proxy 로컬 cookie 만 지우면 충분하면 기본 /oauth2/sign_out(rd 없이) — 단 이 경우 IdP 세션이 남아 재접근 시 자동 재로그인(O2PE-C1)되므로 "로그아웃이 안 된 것처럼" 보임. post_logout_redirect_uri 를 쓰려면 client_id 또는 id_token_hint 중 하나를 반드시 동반(KC-LOGOUT-C5) raw/official-docs/oauth2-proxy-endpoints-signout-official.md#O2PE-C1, #O2PE-C2, #O2PE-C3, #O2PE-C4, raw/official-docs/keycloak-oidc-logout-endpoint-official.md#KC-LOGOUT-C1, #KC-LOGOUT-C2, #KC-LOGOUT-C3, #KC-LOGOUT-C4, #KC-LOGOUT-C5, #KC-LOGOUT-C6 official-vendor-doc (파라미터 계약rd/{id_token}/id_token_hint/post_logout_redirect_uri: oauth2-proxy 측 + Keycloak 측 양측 교차 확보, 2026-07-17 UNSUPPORTED_DECISION 해소) + needs-confirmation (경로 문자열) ① oauth2-proxy 가 back-channel logout 수신자로 동작하는지는 공식 문서에서 확인 안 됨(§Audit & Findings BACKCHANNEL_UNVERIFIED). ② logout 후 Keycloak 세션이 실제로 종료되는지는 여전히 실측 대상(§Claims To Verify). ③ rd 대상이 end_session_endpoint 여야 한다는 것은 문서의 권고(convention) 이지 oauth2-proxy 가 강제 검증하지 않음. ④ 경로 /realms/{realm}/protocol/openid-connect/logoutKC-LOGOUT-C1 의 quote 에 없고 claim 서술문에만 존재 → 하드코딩 금지, discovery 응답의 end_session_endpoint 를 읽을 것(§구현 가이드 2 의 3단계 · §Claims To Verify)
D7 --whitelist-domain 에 redirect 허용 도메인을 명시 등록. 서브도메인 전체 허용은 .example.com 또는 *.example.com prefix 사용 Keycloak 이 oauth2-proxy 와 다른 도메인이면 필수 — 미등록 시 D6 의 logout redirect 가 조용히 무시됨(O2PE-C4). 같은 도메인 안에서 상대경로 redirect 만 쓰면 불필요할 가능성 (단정 불가 — 미설정 시 기본 동작이 공식 문서에 없어 Open Risk 참조. 확정 전까지는 안전측으로 항상 명시 등록 권장). 기본 동작은 URL 프로토콜의 default port(80/443)만 허용하므로, 비표준 포트를 쓰면 포트까지 명시 필요(O2PCOOKIE-C7) raw/official-docs/oauth2-proxy-cookie-redirect-flags-official.md#O2PCOOKIE-C7, raw/official-docs/oauth2-proxy-endpoints-signout-official.md#O2PE-C4 official-vendor-doc 미설정 시 기본 동작(외부 도메인 전부 차단인지)이 공식 문서에 명시되지 않음needs-confirmation. 또한 이 옵션의 suffix 매칭에 과거 우회 취약점 이력이 있다고 전해지나 본 라운드에서 검증하지 않았다 (미검증 — §Audit & Findings WHITELIST_CVE_HISTORY, raw 미보존) → 옵션 설정만으로 open redirect 가 닫힌다고 단정 금지
D8 요청 검증 분기: 브라우저 요청은 session cookie 경로, Authorization: Bearer <JWT> 요청은 --skip-jwt-bearer-tokens 경로로 자동 분기(택1 아님 — 한 배포에서 공존). invalid JWT 는 기본 로그인 redirect, --bearer-token-login-fallback=false 면 403 API/M2M 클라이언트가 있으면 --skip-jwt-bearer-tokens 설정 + --bearer-token-login-fallback=false(JSON 클라이언트에 HTML 로그인 페이지 대신 403 반환). 브라우저 전용이면 기본값 유지. 다른 issuer 의 JWT 도 받으려면 --extra-jwt-issuers=<issuer>=<audience> raw/official-docs/oauth2-proxy-behaviour-cookie-vs-bearer-official.md#O2PBEH-C1, #O2PBEH-C2, #O2PBEH-C3, #O2PBEH-C4 official-vendor-doc (실패 3경로O2PBEH-C2/C3/C4) + UNSUPPORTED_DECISION (인증 강제 라우트에서의 cookie/JWT 공존·우선순위) 명칭 경계 — 이 경로는 JWT bearer 검증 경로이며 RFC 7662 introspection 호출로 부르지 않는다. 로컬 JWKS 서명 검증 여부도 behaviour 페이지가 직접 명시하지 않아 미확정이다. ② 헤드라인의 "자동 분기·공존" 은 공식 보장이 아님O2PBEH-C1--skip-auth-route 전용 인용이고 그 does-not-prove 가 강제 라우트에서의 cookie/JWT 순서·우선순위를 범위 밖으로 못박는다. 통과 경로는 실패 경로(C3/C4)의 대우에서 도출한 추론(§구현 가이드 3 · §Claims To Verify)
D9 OIDC discovery 활성(기본) — --oidc-issuer-url 로부터 .well-known/openid-configuration 자동 조회. 우회하려면 --skip-oidc-discovery + --login-url(Authentication endpoint) / --redeem-url(Token redemption endpoint) / --oidc-jwks-url 3종 전부 수동 지정 네트워크로 issuer 에 도달 가능하면 기본값(discovery 활성). 폐쇄망 등으로 issuer 도달이 불가능하면 --skip-oidc-discovery + 3종 수동(이게 O2PCOOKIE-C8 이 실제로 닫는 축). 서명 키를 정적으로 고정하려면 --oidc-public-key-file(PEM) — 단 키 rotation 시 수동 재배포 필요. 기동 순서(Keycloak 이 늦게 뜨는 문제)는 이 결정의 축이 아니다 — 그건 raw/branch-notes/feature-keycloak-docker-compose-stack D3(healthcheck 게이팅)가 owner 이며, discovery 를 끄는 것은 그 문제의 해법이 아님 raw/official-docs/oauth2-proxy-cookie-redirect-flags-official.md#O2PCOOKIE-C8, #O2PCOOKIE-C9 official-vendor-doc discovery 호출 시점(기동 1회 vs 주기적)과 JWKS cache TTL 이 공식 prose 문서에 없음needs-confirmation(§Claims To Verify). 기동 시 discovery 실패가 실제 실패 모드인지 확인되면 대응은 compose D3 로 위임(본 branch 재진술 금지)

구현 가이드

본 sub-sub-branch 는 documented-only — 실 구성/시연 대상이 아니다. 따라서 본 §는 "코드를 어디에 쓸 것인가" 가 아니라 학습 시연 문서상의 가상 구성 명세(TODO 마지막 항목)로 읽는다. P3A 실 구현 단계에서 이 명세가 실제 config 의 출발점이 된다. 3-rule (CLAUDE.md §15.5) 적용: 각 sub-section 은 Decision ID + Supporting Claim ID 를 Trace 하고, 근거 없는 detail 은 UNSUPPORTED_IMPL_DECISION 라벨 + trade-off 한 줄을 단다.

1. oauth2-proxy 기동 플래그 세트 (문서상 가상 구성)

Trace: D2(O2PK-C1/O2PK-C2/OAUTH2PROXY-C5) + D3(O2PK-C3/O2PK-C5) + D4(O2PK-C3/O2PK-C4) + D5a·D5c(O2PSESS-C1/O2PSESS-C2, O2PCOOKIE-C1~C6) + D9(O2PCOOKIE-C8/C9).

  • UNSUPPORTED_IMPL_DECISION: --cookie-samesite=lax명시 하는 것 — 공식 문서는 기본값이 ""(빈 문자열)임만 증명하고(O2PCOOKIE-C1) 어느 값을 쓰라고 권고하지 않는다. trade-off: OIDC 콜백이 cross-site top-level GET redirect 라 lax 가 CSRF 쿠키를 통과시키는 최소값으로 판단 — strict 는 콜백 실패 위험, none 은 CSRF 표면 확대. P3A 실측 전까지 확정 아님.
  • UNSUPPORTED_IMPL_DECISION: --cookie-secret 을 32byte 로 생성하는 관행 — 공식 문서는 16/24/32byte 제약을 --cookie-secret-file(raw binary) 행에만 명시하고(O2PCOOKIE-C3) --cookie-secret(seed string) 자체에 같은 제약이 걸리는지는 서술하지 않는다. trade-off: AES-256 을 쓰는 32byte 가 세 허용값 중 최댓값이라 안전측 선택.
  • UNSUPPORTED_IMPL_DECISION: --cookie-domain 의 값 — D5c 결정문이 이 플래그를 "표준화 대상" 으로 열거하나, 보존된 claim(O2PCOOKIE-C1~C6) 중 --cookie-domain 을 다루는 것은 없다(C1 samesite / C2 secure / C3 secret / C4 expire / C5 refresh / C6 csrf-samesite). trade-off: oauth2-proxy 와 앱이 같은 host 면 미설정(host-only cookie)이 최소 표면; 서브도메인으로 분리되면 .example.com 로 넓혀야 하나 그만큼 쿠키 전송 범위가 커짐. 미설정 시 host-only 가 되어 서브도메인 구성에서 로그인 루프의 원인이 될 수 있음 — P3A 에서 실측 필요.
  • UNSUPPORTED_IMPL_DECISION: --scope 의 base 값 openid profile email 과 "offline_access 는 refresh 필요 시만" 조건 — O2PK-C5(groups scope + mapper 필요) / O2PK-C3(인가 확장) 어느 것도 base scope 문자열이나 offline_access ↔ refresh token 관계를 서술하지 않는다(OIDC 일반 배경지식). trade-off: openid 는 OIDC 필수, profile emailX-Auth-Request-Email 등 헤더 전달용 관행 — 공식 vendor doc 의 권고 아님.
플래그 값 (학습 시연 가정) 근거 비고
--provider keycloak-oidc D2 / O2PK-C1 generic oidc 대비 role/group native 매핑 확보
--client-id / --client-secret (realm client 에서 발급) D2 / O2PK-C1 Usage 예시는 confidential client 형식
--oidc-issuer-url https://<keycloak host>/realms/<realm> D2 / O2PK-C2 Keycloak 26.x = 17+ → /auth prefix 없음
--scope openid profile email (+groups 조건부) D3 / O2PK-C5base 값 + offline_access 조건은 UNSUPPORTED_IMPL_DECISION(위 참조) groups--allowed-group 쓸 때만(이건 O2PK-C5 근거 있음). offline_access 는 refresh 필요 시만 — 넣으면 세션 payload 가 커져 D5a 의 4kb 압력 상승
--cookie-domain (미정 — 배포 토폴로지 의존) UNSUPPORTED_IMPL_DECISION(위 참조 — 보존 claim 없음) 같은 host 면 미설정(host-only), 서브도메인 분리 시 .example.com. 미설정 + 서브도메인 = 로그인 루프 위험
--allowed-role / --allowed-group 조건부 (D4 선택 조건 표 참조) D4 / O2PK-C4 미설정 시 "valid user" 만 요구
--session-store-type cookie (기본, 단일 EC2 학습 구성) D5a / O2PSESS-C1 4kb 압력 시 redis (D5b)
--cookie-secure true (기본값 유지) D5c / O2PCOOKIE-C2 로컬 http:// 시연에 한해 false — 예외 라벨 필수
--cookie-expire 168h0m0s (기본값 유지) D5c / O2PCOOKIE-C4 0 이면 브라우저 종료 시 만료
--whitelist-domain Keycloak 도메인 (D6 전제) D7 / O2PCOOKIE-C7·O2PE-C4 미등록 시 logout redirect 무시
--code-challenge-method S256 D2 / O2PK-C6 PKCE — 형제 branch feature-keycloak-pkce-flow-stages 가 owner

2. 로그아웃 URL 조립 (D6 의 실제 형태)

Trace: D6(O2PE-C1C4, KC-LOGOUT-C1C6) + D7(O2PCOOKIE-C7).

  • 근거 있는 결정: rd 파라미터 · {id_token} placeholder · id_token_hint/post_logout_redirect_uri 요구사항 (O2PE-C2, O2PE-C3, O2PE-C4, KC-LOGOUT-C3, KC-LOGOUT-C5, KC-LOGOUT-C6) — 양측 공식 문서 verbatim 으로 뒷받침됨.
  • UNSUPPORTED_IMPL_DECISION: 3단계의 경로 문자열 /realms/<realm>/protocol/openid-connect/logoutKC-LOGOUT-C1 의 evidence quote 전문은 "The logout endpoint logs out the authenticated user." 뿐이고 경로는 quote 에 없다(claim 서술문에만 존재). 같은 claim 이 "이 경로가 discovery 문서의 end_session_endpoint 필드 값과 동일하게 노출된다는 명시적 문장은 이 인용에 없음" 을 자인. trade-off: 경로를 하드코딩하지 말고 discovery 응답의 end_session_endpoint 를 읽는 것이 안전 — 하드코딩은 Keycloak context-path 변경(D2 의 17+ 이슈)에 취약. needs-confirmation (§Claims To Verify).

중요: --backend-logout-url 이라는 플래그는 oauth2-proxy 공식 endpoints 문서에 존재하지 않는다(§Audit & Findings MECHANISM_DRIFT). 실제 메커니즘은 rd query parameter + placeholder 치환이다.

단계 조립 근거
1. 로그아웃 진입 사용자를 /oauth2/sign_out?rd=<urlencoded end_session URL> 로 redirect O2PE-C1, O2PE-C2
2. oauth2-proxy 동작 자신의 세션 cookie 만 삭제 → rd 대상으로 redirect. rd 도메인이 --whitelist-domain 미등록이면 redirect 무시 O2PE-C1, O2PE-C4
3. end_session_endpoint https://<keycloak host>/realms/<realm>/protocol/openid-connect/logoutdiscovery 응답에서 읽을 것(하드코딩 금지) UNSUPPORTED_IMPL_DECISION (경로가 KC-LOGOUT-C1 quote 에 없음 — claim 서술문만)
4. id_token_hint 주입 rd URL 안에 {id_token} placeholder 를 넣으면 oauth2-proxy 가 실제 ID Token 으로 치환 O2PE-C3
5. post_logout_redirect_uri 쓰려면 client_id 또는 id_token_hint 중 하나 필수 + client 의 Valid Post Logout Redirect URIs 에 등록돼 있어야 함 KC-LOGOUT-C5, KC-LOGOUT-C6

3. 요청 검증 분기 (D8)

Trace: D8(O2PBEH-C1~C4).

  • UNSUPPORTED_IMPL_DECISION: 아래 통과 2행(cookie 검증 후 통과 / valid JWT → 세션 없이 통과) — 근거인 O2PBEH-C1--skip-auth-route 로 인증이 스킵된 라우트 전용 인용이며("Authentication is not enforced, but the proxy will opportunistically attempt to validate…"), 같은 claim 의 Does-not-prove 가 "스킵되지 않은(=인증 강제) 라우트에서 cookie/JWT 를 각각 어떤 순서·우선순위로 시도하는지는 본 인용 범위 밖" 이라 못박는다. 즉 인증 강제 라우트의 통과 동작은 공식 미서술 — 아래 2행은 실패 경로(C3/C4)의 대우(對偶)에서 도출한 추론이다. trade-off: 실패 경로가 명시적으로 정의된 이상 통과 경로가 그 여집합이라고 보는 것이 합리적이나, 공식 보장은 아님. 실패 3행(C2/C3/C4)은 근거 있는 결정.
  • 이 경로의 명칭은 §Audit & Findings NAMING_DRIFT 참조(구현 detail 이 아니라 용어 문제).
요청 형태 oauth2-proxy 동작 근거
session cookie 보유 브라우저 요청 cookie 검증 후 통과 UNSUPPORTED_IMPL_DECISION (추론 — C3/C4 실패 경로의 대우. O2PBEH-C1 은 skip-auth-route 전용)
Authorization: Bearer <valid JWT> (--skip-jwt-bearer-tokens 설정 시) JWT 검증 후 세션 없이 통과 UNSUPPORTED_IMPL_DECISION (추론 — 상동. §Claims To Verify 실측 대상)
Authorization: Bearer <invalid JWT> 기본: 로그인 페이지 redirect O2PBEH-C3
위 + --bearer-token-login-fallback=false 403 Forbidden O2PBEH-C4
미인증 + Accept: application/json 401 Unauthorized (redirect 아님) O2PBEH-C2

엣지·실패·의존

R4(깊이 게이트) 캡처용. 정상 경로 외에 학습 시연/후속 구현에서 부딪힐 실패·엣지와 다른 계약 의존.

  • 실패·엣지 경로:

    • 로그아웃이 조용히 실패: rd 대상(Keycloak) 도메인이 --whitelist-domain 에 없으면 oauth2-proxy 가 에러 없이 redirect 를 무시한다(O2PE-C4). 결과적으로 oauth2-proxy cookie 만 지워지고 Keycloak 세션은 살아남아, 재접근 시 자동 재로그인(O2PE-C1)되어 "로그아웃이 안 된 것처럼" 보인다. D6 과 D7 이 한 몸인 이유 — D7 없이 D6 만 설정하면 D6 은 무효.
    • 로그아웃 확인 화면: id_token_hint 없이 end_session_endpoint 를 호출하면 Keycloak 이 사용자에게 로그아웃 확인을 요구할 수 있다(KC-LOGOUT-C3) → 무인 redirect 흐름이 사용자 클릭에서 멈춤.
    • post_logout_redirect_uri 거부: client 의 Valid Post Logout Redirect URIs 에 미등록이면 거부(KC-LOGOUT-C6), client_id/id_token_hint 둘 다 없으면 거부(KC-LOGOUT-C5).
    • 동시 요청 세션 충돌: cookie store 는 세션 lock 이 없어 동시 refresh 시 충돌 → 강제 재인증 가능(O2PSESS-C2). 단일 EC2 여도 다중 탭/병렬 XHR 이면 발생 — 인스턴스 수와 무관.
    • 세션 4kb 초과: access_token 을 cookie 에 담으면 4kb 한도에 걸릴 수 있고, nginx 는 auth_request 응답의 Set-Cookie 만 복사하므로 분할 쿠키가 유실될 수 있다 → 로그인 루프. 대응 owner 는 형제 branch(아래 의존 참조). 단, Keycloak native user store(Google federation 없음)라 Azure/Google federation 사례보다 토큰이 작을 가능성 — 실측 전까지 확정 불가.
    • API 클라이언트에 HTML 로그인 페이지 반환: invalid JWT 의 기본 동작이 로그인 redirect(O2PBEH-C3)라 JSON 클라이언트가 HTML 을 받는다 → --bearer-token-login-fallback=false 로 403 전환(O2PBEH-C4) 필요.
    • 인가 거부(authentication 성공 + authorization 실패): 로그인은 됐으나 --allowed-role/--allowed-group 에 안 맞는 사용자의 응답 코드가 미확정이다. O2PK-C3 이 "인가 실패 시 401 vs 403 의 정확한 의미는 본 인용에 명시 없음" 을 자인하고, O2PBEH-C2~C4authentication 단계 전용이라 이 경로를 덮지 못한다(D4 Open Risk). 코드가 안 정해지면 raw/branch-notes/feature-keycloak-nginx-auth-request-integration D2(subrequest 2xx/401/403 contract)의 error_page 분기도 못 닫는다 — 그쪽 계약과 맞물린 미결.
    • discovery 기동 순서: Keycloak 이 아직 ready 가 아닌 시점에 oauth2-proxy 가 discovery 를 호출하면 기동에 실패할 수 있음 — 공식 문서로 미확인(§Claims To Verify). 확인될 경우 대응 owner 는 raw/branch-notes/feature-keycloak-docker-compose-stack D3(healthcheck 게이팅)이며, --skip-oidc-discovery(D9)는 이 문제의 해법이 아니다 — D9 는 issuer 도달 불가(폐쇄망) 축이지 기동 순서 축이 아님.
  • 다른 계약 의존 (대상 브랜치 + 그 Decision ID 병기):

    • raw/branch-notes/feature-keycloak-nginx-auth-request-integration D2(subrequest 2xx/401/403 contract) + D3(auth_request_setproxy_set_header 헤더 전파) + D5(4kb cookie split 대응) — 본 branch 의 /oauth2/auth 엔드포인트(O2PE-C5: 202/401 만 반환, nginx auth_request 용)와 D5a 의 4kb 압력이 이 계약을 통해 실현된다. 그쪽 계약이 바뀌면 본 branch D5a/D5c 의 cookie 전제가 영향받음.
    • raw/branch-notes/feature-keycloak-header-spoofing-defense D1(백엔드 ingress-뒤 격리) — 본 branch 는 인증 결과를 헤더로 전달하는 지점까지만 다루고, 그 헤더의 위조 방어는 이 계약이 owner.
    • raw/branch-notes/feature-keycloak-docker-compose-stack D3(healthcheck 로 의존성 강제 — depends_on: condition: service_healthy + Keycloak /health/ready) — 기동 순서 게이팅의 owner 는 이 계약이다. 그쪽 D3 의 선택 조건이 문자 그대로 "app 이 startup 시 keycloak JWKS/issuer discovery 에 의존할 때 이 결정" 이라 본 branch D9(discovery)와 정확히 맞물린다. 본 branch 는 discovery 측 조건(끌지 말지)만 소유하고 게이팅 메커니즘은 이 계약을 참조만 한다 — 재진술 금지(rules/consistency-contract Single-Owner).
    • raw/branch-notes/feature-keycloak-https-termination-caddy-nginx D1/D2(Cloudflare Tunnel / Caddy edge TLS 종단) + D5(TLS 1.2+ / HSTS 강제) — D5c 의 --cookie-secure=true 전제(HTTPS 종단 존재)가 이 계약에 의존. 종단이 없으면 D5c 의 기본값 유지가 로컬 시연에서 로그인 루프를 만든다.
    • raw/branch-notes/feature-keycloak-pkce-flow-stages D1(PKCE method = S256 만 정리 대상) — --code-challenge-method=S256(O2PK-C6)의 PKCE 단계 분해는 그쪽이 owner. 본 branch 는 플래그 존재만 인용.
    • raw/branch-notes/feature-keycloak-refresh-rotation-and-logout D4(명시적 revoke 와 logout 분리) — 본 branch D6 은 logout 만 소유하고 executable revoke/logout 시나리오는 그쪽 경계. rotation 정책은 raw/branch-notes/feature-keycloak-refresh-token-rotation D1이 소유하며, --cookie-refresh(O2PCOOKIE-C5)와의 상호작용은 본 branch 에서 재정의하지 않는다.

Audit & Findings

/branch-spec 자동조사(2026-07-17) 중 기존 노트 본문과 공식 문서가 어긋난 지점. CLAUDE.md §2 drift-surface 원칙에 따라 사용자 작성 본문을 자동 rewrite 하지 않고 정합 권고만 남긴다.

ID 내용 근거 권고
NAMING_DRIFT 해소(2026-07-18)--skip-jwt-bearer-tokens/--extra-jwt-issuers 경로를 **"JWT bearer 검증 경로"**로 통일했다. 공식 문서가 RFC 7662 introspection endpoint 호출을 서술하지 않으므로 introspection과 동일시하지 않는다. 다만 .well-known/jwks.json 참조는 로컬 JWKS 검증을 시사할 뿐 메커니즘을 직접 증명하지 않는다. raw/official-docs/oauth2-proxy-behaviour-cookie-vs-bearer-official.md#O2PBEH-C1 + 같은 문서 §Usage Boundaries 현재 용어를 유지하고, 로컬 JWKS 검증인지 여부만 §Claims To Verify에서 실측한다.
MECHANISM_DRIFT 자동조사 초기 가설(및 일부 2차 자료)은 로그아웃이 --backend-logout-url 플래그로 동작한다고 전제했으나, oauth2-proxy 공식 endpoints 문서에 backend.logout 문자열이 0건(agent grep 확인). 실제 메커니즘은 rd query parameter(또는 X-Auth-Request-Redirect 헤더) + {id_token} placeholder 치환이며, rd 대상이 end_session_endpoint 여야 한다는 것은 문서의 권고(convention) 이지 강제 검증이 아니다. raw/official-docs/oauth2-proxy-endpoints-signout-official.md#O2PE-C2, #O2PE-C3 D6 과 §구현 가이드 2 는 이미 정정된 메커니즘으로 작성됨. 외부 블로그가 --backend-logout-url 을 언급하면 버전/오정보 의심.
BACKCHANNEL_UNVERIFIED oauth2-proxy 가 back-channel logout 수신자(Keycloak 이 Logout Token 을 POST 하는 대상)로 동작하는지 공식 endpoints 문서에서 확인 안 됨 — backchannel/logout token 문자열 0건(agent grep). Keycloak 측에는 client Backchannel logout URL 설정이 존재(KC-LOGOUT-C7)하므로 Keycloak 은 보낼 수 있으나 oauth2-proxy 가 받을 수 있는지가 미확인. raw/official-docs/oauth2-proxy-endpoints-signout-official.md §Usage Boundaries + keycloak-oidc-logout-endpoint-official.md#KC-LOGOUT-C7 back-channel logout 은 본 branch 에서 채택하지 않음(D6 은 RP-Initiated 방식). 다중 client SSO 요구가 생기면 재검토 — 그때 oauth2-proxy 수신 지원 여부부터 공식 확인. 커뮤니티 이슈(#1224)는 미지원을 시사하나 이슈 트래커는 공식 근거 아님.
WHITELIST_CVE_HISTORY --whitelist-domain 의 suffix 매칭에 과거 취약점 이력이 있다고 전해짐 — 미검증(raw 미보존). WebSearch 로 식별자 존재만 확인했을 뿐 GHSA/CVE 원문을 대조하지 않았다: CVE-2021-21291(.example.com 등록 시 badexample.com 도 매칭됐다는 suffix 매칭 결함으로 전해짐), GHSA-j7px-6hwj-hpjg / GHSA-5m6c-jp6f-2vcv / GHSA-qqxw-m5fj-f7gv (open-redirect 우회로 전해짐). 위 ID·메커니즘은 인용이 아니라 후속 확인 대상이다. WebSearch 로 식별자 존재만 확인 — raw 미보존, verbatim 미확보, 원문 미대조 D7 을 "이 옵션을 켜면 open redirect 가 닫힌다"로 단정 금지. 버전 currency(수정 릴리스 이후 고정)가 defense-in-depth 로 필요. 정식 인용하려면 GHSA 페이지를 별도 wiki-source-summarizer 로 보존해야 함 — 본 라운드 미수행.
SESSION_STORAGE_4KB_ABSENT session storage 공식 페이지에 4k/4096/split 문자열이 0건 — 4kb cookie split 함정의 근거는 이 페이지가 아니라 nginx 통합 페이지(raw/official-docs/oauth2-proxy-nginx-integration-official, "Nginx normally only copies the first Set-Cookie header ... if your cookies are larger than 4kb, you will need to extract additional cookies manually")에 있다. raw/official-docs/oauth2-proxy-session-storage-official.md §Usage Boundaries D5a 의 Open Risk 에 반영 완료. 4kb 대응의 owner 는 raw/branch-notes/feature-keycloak-nginx-auth-request-integration D5.

검증해야 할 주장

공식 vendor docs 가 옵션의 존재와 형식을 증명해도 내 학습 시연에서의 정확한 동작은 별개. 다음은 P3A 또는 학습 시연 단계에서 실측해야 할 주장.

Claim Why uncertain How to verify Status
provider=keycloak-oidcprovider=oidc (generic) 의 실질 차이는 group/role claim native 추출 여부 O2PK-C5groups client scope mapper 가 필요하다고 명시하지만, generic oidc provider 가 동일 mapping 으로 동작하는지의 비교 vendor doc 미확보 두 provider 로 동일 Keycloak realm 에 연결한 oauth2-proxy 컨테이너 2개 띄우고 --allowed-group=/dev 동작 비교 needs-confirmation
OIDC discovery (.well-known/openid-configuration) 호출 시점과 cache TTL O2PCOOKIE-C8/C9 (D9) 로 discovery 우회 방법(--skip-oidc-discovery + 수동 endpoint 3종)은 확보했으나, discovery 를 켰을 때 언제 호출되는지(기동 1회 vs 주기적)와 JWKS cache TTL 은 공식 prose 문서에 서술 없음. 관련 플래그(--oidc-jwks-cache-duration 류)도 overview 페이지에서 미발견 → 소스코드(providers/oidc.go) 확인이 필요할 수 있음 oauth2-proxy 시작 후 wireshark/tcpdump 로 discovery endpoint 호출 빈도 측정 needs-confirmation
oauth2-proxy 기동이 Keycloak ready 에 의존하는지 (docker-compose 기동 순서 함정) discovery 가 기동 시 issuer 에 도달해야 한다면, Keycloak 이 늦게 뜰 때 oauth2-proxy 가 죽는다. 공식 문서에 기동 순서 요구사항 서술 없음 — 커뮤니티 이슈에만 신호 존재(공식 근거 아님) Keycloak 을 의도적으로 늦게 기동시킨 뒤 oauth2-proxy 컨테이너의 exit code / 재시도 로그 확인. 실패하면 게이팅으로 대응 — 메커니즘은 raw/branch-notes/feature-keycloak-docker-compose-stack D3 가 owner(본 노트 재진술 금지) needs-confirmation
--skip-jwt-bearer-tokens 경로가 로컬 JWKS 서명 검증인지, 별도 network 검증을 수행하는지 (§Audit NAMING_DRIFT) behaviour 페이지는 "opportunistically attempt to validate"(O2PBEH-C1) 로만 서술하고 메커니즘을 명시하지 않는다. overview 페이지의 --extra-jwt-issuers 설명이 .well-known/jwks.json 을 참조해 로컬 검증을 시사하지만 verbatim 확정은 아니다. 따라서 현재 명칭은 중립적인 "JWT bearer 검증 경로"로 한정한다. Bearer JWT 요청 중 Keycloak /protocol/openid-connect/token/introspect 접근 로그와 JWKS 조회를 함께 관찰한다. Keycloak을 내린 상태에서 JWKS 캐시만으로 검증이 통과하는지도 확인한다. needs-confirmation
cookie session 모드 vs Redis session store 모드의 성능/운영 차이 O2PSESS-C1~C6 (D5a/D5b) 로 두 모드의 메커니즘(stateless cookie / ticket+SETEX)과 플래그는 확보. 그러나 P1A(Keycloak native user store, Google federation 없음) 에서 세션이 실제로 4kb 를 넘는지, Redis round-trip 이 latency 에 얼마나 기여하는지는 수치 미확보 — 4kb 초과 사례는 Azure federation 사례라 일반화 불가 로그인 후 브라우저 devtools 로 _oauth2_proxy cookie 실제 바이트 측정(4kb 대비) → 단일 oauth2-proxy 에 Redis backend 연결 후 cookie 크기 / login latency 비교 planned
RP-Initiated Logout 호출 시 Keycloak 세션이 실제로 종료되는지 D6 은 O2PE-C1C4 + KC-LOGOUT-C1C6 으로 메커니즘 근거는 확보(UNSUPPORTED 해소). 다만 공식 문서는 옵션·파라미터의 존재를 증명할 뿐 내 구성에서 세션이 실제로 끊기는지는 증명하지 않음 logout 후 Keycloak admin console 의 active session 조회 + cookie 재제출 시 재로그인 강제 여부 확인 planned
인가 거부 시 실제 응답 코드/본문 (401 vs 403 vs 로그인 루프) D4 Open Risk 가 자인 — O2PK-C3 은 인가 실패 코드의 의미를 명시 안 하고, O2PBEH-C2~C4 는 authentication 단계 전용. raw/branch-notes/feature-keycloak-nginx-auth-request-integration D2의 error_page 분기가 이 값에 의존 허용 role 이 없는 사용자로 로그인 후 보호 경로 요청 → 응답 코드/본문 확인. /oauth2/auth subrequest 응답도 함께 확인(202/401 만 반환하는지 — O2PE-C5 와 대조) needs-confirmation
인증이 강제된(=--skip-auth-route 아닌) 라우트에서 Bearer-only 요청이 session cookie 없이 통과하는지 O2PBEH-C1스킵된 라우트 전용 인용이고, 그 Does-not-prove 가 "강제 라우트에서 cookie/JWT 를 어떤 순서·우선순위로 시도하는지는 범위 밖" 이라 자인. §구현 가이드 3 의 통과 2행은 실패 경로의 대우에서 도출한 추론이지 공식 보장 아님 일반(비스킵) 경로에 Authorization: Bearer <valid JWT> 만 담아 요청 → cookie 없이 202/200 이 오는지 확인. 또는 configuration/overview 페이지를 별도 raw 로 보존해 verbatim 확정 needs-confirmation
Keycloak 26.x 의 end_session_endpoint 실측값이 /realms/{realm}/protocol/openid-connect/logout 인지 KC-LOGOUT-C1 의 evidence quote 전문은 "The logout endpoint logs out the authenticated user." 뿐 — 경로 문자열은 quote 에 없고 claim 서술문에만 있다. 하드코딩하면 Keycloak context-path 변경(D2 의 17+ 이슈)에 취약 curl https://<keycloak host>/realms/<realm>/.well-known/openid-configuration | jq -r .end_session_endpoint 로 실제 노출값 확인 → D6/§구현 가이드 2 의 3단계 경로와 대조 needs-confirmation
rd 도메인이 --whitelist-domain 미등록일 때 logout 이 조용히 실패하는지 O2PE-C4 가 "리다이렉트가 무시된다" 고 명시하나, 무시 시 사용자에게 보이는 최종 화면(에러 페이지 vs 기본 sign-out 페이지)은 서술 없음 — D6 의 가장 현실적인 실패 모드라 실측 가치 높음 Keycloak 도메인을 --whitelist-domain 에서 뺀 상태로 logout 시도 → 최종 랜딩 화면 + Keycloak 세션 잔존 여부 확인 planned
--whitelist-domain 옵션이 open redirect 공격을 실제로 차단하는지 O2PCOOKIE-C7 (raw/official-docs/oauth2-proxy-cookie-redirect-flags-official.md) 로 옵션의 존재·문법(서브도메인 wildcard/포트 지정)은 확인됐으나, 미설정 시 기본 동작(전체 차단 여부)과 실제 공격 시나리오에서의 차단 여부는 공식 문서에 없음 공격 시나리오 (rd=https://evil.example.com) 로 redirect 시도 후 oauth2-proxy 응답 확인 planned

마주친 문제

  • 아직 없음(문서 단계).

묶음

본 sub-sub-branch 는 leaf — 자식 자료 없음. Phase 3 P3A 실 구현 또는 외부 산출물 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.

근거 자료

오류 기록 (이 sub-sub-branch 작업 중 발생)

  • (없음 — 현재 documented-only 단계)

면접 준비 (이 작업에서 나올 수 있는 면접 질문)

  • (없음 — Phase 3 실 구현 단계에 누적)

관련 일일 노트

완료 후 정리

본 sub-sub-branch는 문서까지만. wiki 추출은 root branch의 비교 매트릭스 시점에 일괄 처리.

  • PR 링크:
  • 리뷰 메모:
  • 머지 결과 / 배포 환경: 해당 없음 (문서 단계)
  • wiki 추출 대상 (verified만, wiki/projects/로만 추출):
    • actually-implemented 항목: 없음
    • locally-verified 항목: 없음
    • prod-verified 항목: 없음
  • 추출하지 않을 항목 (planned / documented-only / abandoned):
    • 본 sub-sub-branch 전체가 documented-only 등급. P1A 부모 sub-branch와 함께 추후 비교 매트릭스 / wiki/concepts/keycloak-deployment-patterns.md에 인용되는 형태로만 활용. wiki/projects/ 직접 승급 없음.