Files
keycloak-pattern/docs/session-store-lab-roadmap.md
T

8.5 KiB

세션 저장소 실험 축 — 계획과 진행

develop-keycloak-session-store 브랜치가 담당하는 작업의 전체 지도. 무엇이 끝났고 무엇이 남았는지를 여기서 추적한다.

왜 별도 축인가

네 인증 패턴(AP1~AP4)은 브라우저와 토큰의 관계를 비교한다. 이 축은 그것과 직교하는 질문을 다룬다 — 세션과 토큰이 서버 쪽 어디에 저장되고, 그 저장소가 죽으면 무슨 일이 벌어지는가.

초기 검토에서 전제 하나가 교정됐다. Keycloak은 Redis를 세션 저장소로 지원하지 않는다. 그래서 이 축은 두 계층으로 갈린다.

계층 저장소 해당 패턴
A. Keycloak 자체 임베디드 Infinispan + PostgreSQL 네 패턴 공통
B. 애플리케이션 세션 Redis AP2 / AP3 / AP4

A층은 네 패턴과 무관하게 공통이고, B층은 서버 세션을 갖는 세 패턴에만 존재한다. 그래서 이 축을 AP1~AP4 어디에도 넣지 않고 별도로 둔다.

기존 브랜치 레지스트리에 넣지 않는 이유

docs/keycloak-branch-manifest.tsvscripts/audit-keycloak-branches.sh정확히 39개 브랜치를 강제하고, 각 브랜치가 외부 노트 파일과 1:1로 대응하는지 검사한다.

if [ "$expected_count" -ne 39 ]; then
  echo "manifest must contain exactly 39 Keycloak branches" >&2

이 축의 브랜치를 manifest에 추가하면 그 감사가 깨진다. 원래 39개는 완결된 인벤토리이므로 건드리지 않고, 이 축은 이 문서로 추적한다.

진행 상황

✅  환경 구축
✅  2홉 프록시 헤더 계약
──────────────────────────────────────────────
    Keycloak 멀티노드 클러스터 형성
    persistent vs volatile 세션
    refresh token 동시 갱신 경쟁
    Redis 애플리케이션 세션 + SSO
    장애 주입과 복구

완료 — 환경 구축

2노드 k3s 실험대. 상세는 deploy/lab/README.md, 개념은 session-lab-concepts.md.

브라우저 ─https─▶ 호스트 nginx(TLS 종료) ─▶ Traefik ─▶ Pod
                                            kc-lab-1 / kc-lab-2

왜 Docker Compose가 아닌가 — 한 커널에서 "노드 죽이기"는 프로세스 죽이기일 뿐이다. 노드 간 방화벽·비대칭 파티션·진짜 노드 상실은 독립된 커널 두 개가 있어야 성립한다.

완료 — 2홉 프록시 헤더 계약

two-hop-proxy-header-contract.md · 증거 evidence/two-hop-proxy-headers/

확인한 것docs/reverse-proxy-headers.md의 1홉 계약이 2홉에서 성립하지 않는다. 원인이 둘이었고 스위치가 셋이었다.

스위치 하는 일
nginx proxy_set_header 헤더를 만든다
Traefik forwardedHeaders.trustedIPs 받은 헤더를 전달할지 버릴지
forward-headers-strategy / KC_PROXY_HEADERS 도착한 헤더를 읽을지

여기에 프록시 우회 차단(NetworkPolicy)까지 더해야 신뢰 경계가 닫힌다. "헤더를 믿는다"와 "앞에 반드시 프록시가 있다"는 한 쌍이다.

다음 작업에 미치는 영향 — Keycloak을 올릴 때 KC_PROXY_HEADERS=xforwardedKC_HOSTNAME=https://auth.hyeonworks.com을 근거를 갖고 넣을 수 있고, 로그인이 깨져도 프록시 원인은 배제하고 볼 수 있다.


다음 — Keycloak 멀티노드 클러스터 형성

브랜치: feature/keycloak-multinode-cluster-jdbc-ping

확인할 것

  • Keycloak 2개 파드가 서로를 발견해 하나의 클러스터를 이루는가
  • k3s 기본 디스커버리는 jdbc-ping — PostgreSQL의 JGROUPS_PING 테이블로 서로를 찾는다. 멀티캐스트가 필요 없다
  • TCP 7800이 막히면 어떻게 되는가 — 디스커버리는 DB로 하지만 실제 클러스터 통신은 7800이다. 막으면 "DB에는 서로 등록되는데 클러스터가 안 붙는" 증상이 나온다. 단일 노드에서는 재현 불가능한 고장이며, 이 실험대를 2노드로 만든 이유 중 하나다

주의 — Traefik이 replica 1이라 그 파드가 있는 노드를 죽이면 진입점 자체가 사라진다. 노드 상실 실험 전에 Traefik을 2개로 늘릴지 결정해야 한다.

설정 근거 — 헤더 계약에서 확정한 값을 그대로 쓴다.

KC_HOSTNAME=https://auth.hyeonworks.com
KC_HOSTNAME_STRICT=true
KC_PROXY_HEADERS=xforwarded
KC_HTTP_ENABLED=true

persistent vs volatile 세션

브랜치: feature/keycloak-persistent-vs-volatile-sessions

확인할 것

  • Keycloak 26 기본값은 persistent-user-sessions — 세션이 DB가 진실의 원천이다
  • --features-disabled=persistent-user-sessions로 volatile 전환 시 비교
  • PostgreSQL을 죽이면 각각 어떻게 되는가
  • 노드 하나를 죽이면 세션이 살아남는가
  • 롤링 배포 시 로그아웃되는가 ← 운영에서 가장 자주 겪는 시나리오

이것이 "세션을 DB에 둘 때 vs 안 둘 때"의 Keycloak 버전이다.

refresh token 동시 갱신 경쟁

브랜치: feature/keycloak-refresh-token-concurrency

확인할 것

  • realm은 이미 revoke refresh token + max reuse 0 (refresh-token-rotation.md)
  • 같은 RT로 두 노드에 동시 refresh하면 몇 개가 성공하는가
  • 재사용 탐지가 세션 전체를 무효화하는가
  • Infinispan DIST_SYNC 복제 지연이 경쟁 윈도우에 미치는 영향

단일 노드에서는 관찰할 수 없는 항목이다.

Redis 애플리케이션 세션 + SSO

브랜치: feature/keycloak-redis-app-session-store

확인할 것

  • B층 도입 — BFF(Spring Session Data Redis), oauth2-proxy(--session-store-type=redis)
  • Redis가 죽으면 앱 세션은 전멸하지만 Keycloak SSO 세션은 살아 있다 → 재로그인이 무프롬프트로 끝난다. "SSO를 추가하면 뭐가 달라지나"의 답
  • Redis appendonly 유무에 따른 복구 차이
  • 백채널 로그아웃 — Keycloak에서 로그아웃했을 때 Redis의 앱 세션도 죽이려면 sid → session 역인덱스가 필요하다. Redis 스키마 설계의 실질적 지점
  • 앱 두 개(app1, app2)로 SSO 체감

장애 주입과 복구

브랜치: feature/keycloak-failure-injection-recovery

확인할 것

주입 방법
노드 상실 virsh destroy — 프로세스 kill 이 아닌 진짜 상실
비대칭 파티션 한쪽 게스트의 인바운드만 nftables 로 차단
JGroups 7800 차단 NetworkPolicy — 운영에서 쓸 방식 그대로
DB 상실 PostgreSQL 파드 정지
Redis 상실 Redis 파드 정지
지연 주입 게스트 안에서 tc netem — 커널이 분리돼 있어 안전

복구 절차를 각각 기록한다. 이 실험대가 재현할 수 없는 것(실제 AZ 간 RTT, ALB 고유 동작, 규모)은 스코프에서 제외한다.

스코프에서 제외한 것

항목 이유
성능·처리량 측정 단일 물리 머신의 숫자는 운영에 대해 아무것도 말해주지 않는다
실제 AZ 간 지연 한 박스 안이라 재현 불가. tc netem 으로 근사만
ALB 고유 동작 자체 스티키 쿠키·60초 idle timeout 은 실물 ALB 가 있어야 한다
PostgreSQL HA 스코프 폭발. "죽으면 어떻게 되나"까지가 현실적 선
멀티 사이트 / cross-site Infinispan 로컬에서 "사이트"가 가짜라 배우는 것이 적다

이 실험대가 검증하는 것은 계약(정합성)이지 성능이 아니다.

관련 문서

문서 내용
session-lab-concepts.md 등장 개념 전체 (가상화·네트워크·k3s·TLS·패키지)
two-hop-proxy-header-contract.md 첫 실험의 측정·진단·수정
deploy/lab/README.md 실험대 구축·복구 절차
four-pattern-tradeoff-matrix.md AP1~AP4 비교. "server session" 행이 B층 대상
refresh-token-rotation.md 회전 계약 (1홉·단일 노드 가정)
reverse-proxy-headers.md 1홉 헤더 계약 원본