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 |
|
|
1 | feature-keycloak-oauth2-proxy-oidc-flow |
|
|
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 검증 경로 각각이 어디서 동작하고 어떤 함정이 있는지를 분리해서 본다.
핵심 질문:
provider=keycloak-oidc와provider=oidc(generic)의 실질 차이는? → role/group claim 매핑 + Keycloak userinfo endpoint 처리.- cookie domain · cookie secret ·
--whitelist-domain은 각각 어떤 공격 surface를 막는가? - cookie 없이 Bearer JWT를 검증하는 경로는 언제 쓰며, 실제 검증 메커니즘은 무엇인가? (RFC 7662 introspection 호출로 부르지 않음)
- 로그아웃 시 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+ Keycloakgroupsclient 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 수신 지원 미확인, §AuditBACKCHANNEL_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/C5 는 groups 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-samesite 를 lax/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/logout 은 KC-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 email은X-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-C5 — base 값 + 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/logout—KC-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/logout — discovery 응답에서 읽을 것(하드코딩 금지) |
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~C4는 authentication 단계 전용이라 이 경로를 덮지 못한다(D4 Open Risk). 코드가 안 정해지면 raw/branch-notes/feature-keycloak-nginx-auth-request-integrationD2(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_set→proxy_set_header헤더 전파) +D5(4kb cookie split 대응) — 본 branch 의/oauth2/auth엔드포인트(O2PE-C5: 202/401 만 반환, nginxauth_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-contractSingle-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 에서 재정의하지 않는다.
- raw/branch-notes/feature-keycloak-nginx-auth-request-integration
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-oidc 와 provider=oidc (generic) 의 실질 차이는 group/role claim native 추출 여부 |
O2PK-C5 는 groups 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 |
마주친 문제
- 아직 없음(문서 단계).
묶음
- raw/official-docs/keycloak-oidc-logout-endpoint-official
- raw/official-docs/oauth2-proxy-behaviour-cookie-vs-bearer-official
- raw/official-docs/oauth2-proxy-cookie-redirect-flags-official
- raw/official-docs/oauth2-proxy-endpoints-official
- raw/official-docs/oauth2-proxy-endpoints-signout-official
- raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official
- raw/official-docs/oauth2-proxy-nginx-integration-official
- raw/official-docs/oauth2-proxy-overview-config-official
- raw/official-docs/oauth2-proxy-session-storage-official
- raw/official-docs/security-jwt-rfc-7519-validation
본 sub-sub-branch 는 leaf — 자식 자료 없음. Phase 3 P3A 실 구현 또는 외부 산출물 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
근거 자료
- raw/official-docs/oauth2-proxy-overview-config-official
- raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official
- raw/official-docs/oauth2-proxy-cookie-redirect-flags-official — D5c (cookie 속성값 표준화: samesite/secure/secret/expire/refresh) +
--whitelist-domain+--skip-oidc-discovery - raw/official-docs/oauth2-proxy-session-storage-official — D5a/D5b (session storage 백엔드: cookie vs redis)
- raw/official-docs/oauth2-proxy-endpoints-signout-official
- raw/official-docs/oauth2-proxy-behaviour-cookie-vs-bearer-official
- raw/official-docs/keycloak-oidc-logout-endpoint-official — D6 (RP-Initiated Logout) Keycloak 측 근거:
end_session_endpoint/id_token_hint/post_logout_redirect_uri/Backchannel Logout URL
오류 기록 (이 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/직접 승급 없음.
- 본 sub-sub-branch 전체가