diff --git a/docs/experiment-index.md b/docs/experiment-index.md new file mode 100644 index 0000000..f9ddf35 --- /dev/null +++ b/docs/experiment-index.md @@ -0,0 +1,77 @@ +# 실험 색인 — 23개 전부 + +각 실험은 **자기 브랜치**에 있고, 해설 문서와 증거 폴더가 한 벌이다. + +| # | 실험 | 브랜치 | 한 줄 결과 | +|---|---|---|---| +| **A-0** | 세션 복제 확인 | `...multinode-cluster-jdbc-ping` | 세션 공유는 Infinispan 이 아니라 **PostgreSQL** 이 한다 | +| **A-1** | TCP 7800 차단 | `...a1-jgroups-transport-block` | 세션은 견디지만 **로그아웃 무효화가 7800 을 탄다** | +| **A-2** | DB 정상 정지 | `...a2-database-loss` | 전면 장애. **그런데 `up` 은 1이었다** | +| **A-3** | DB 강제 종료 | `...a3-database-crash` | **153건 중 4건의 로그인이 사라졌다** | +| **A-4** | 노드 전원 차단 | `...a4-node-loss` | **죽은 파드가 산 파드보다 건강해 보인다** | +| **A-5** | 비대칭 파티션 | `...a5-asymmetric-partition` | 단방향은 자가 치유. 완전 분단도 **한쪽은 산다** | +| **A-6** | 지연 주입 | `...a6-latency-injection` | **200ms → 22초** (왕복 × 풀 큐잉) | +| **A-7** | volatile 비교 | `...a7-volatile-comparison` | **세 결과가 정반대로 뒤집힌다** | +| **A-8** | 롤링 재시작 | `...a8-rolling-restart` | 무중단 + 세션 생존 | +| **B-0** | 자동구성 확인 | `...b0-bff-redis-deploy` | 조회 키에 **session id 가 없다** | +| **B-1** | Redis 세션 | `...b1-redis-session-store` | 세션만 옮겨지고 **토큰은 남는다** | +| **B-2** | 다중 인스턴스 | `...b2-multi-instance-session` | 공유는 되지만 **평문·덮어쓰기·로그아웃 미정리** | +| **B-3** | refresh 경쟁 | `...b3-refresh-token-contention` | 경쟁이 아니라 **세션이 파괴된다** | +| **B-4** | Edge 인가 | `...b4-edge-authorization-scope` | nginx 는 **설정하지 않은 헤더를 덮어쓰지 않는다** | +| **B-5** | Redis 상실 | `...b5-redis-loss-persistence` | **파드가 Ready 인 채로 계속 실패한다** | +| **B-6** | key 회전 | `...b6-key-rotation` | 회전은 안전, **옛 키를 버리는 순간이 위험** | +| **B-7** | cookie secret | `...b7-cookie-secret-rotation` | **겹침 구간이 없고 세션이 고아로 남는다** | +| **C-1** | 다중 앱 SSO | `...c1-multi-app-sso` | **IdP 세션을 죽여도 아무도 로그아웃되지 않는다** | +| **C-2** | 백채널 로그아웃 | `...c2-backchannel-logout` | **받는 쪽을 아무도 구현하지 않았다** | +| **D-1** | 백업·복구 | `...d1-backup-restore` | **빈 데이터베이스가 `200` 을 냈다** | +| **D-2** | 버전 업그레이드 | `...d2-version-upgrade` | **이미지를 되돌려도 스키마는 안 돌아온다** | +| **D-3** | 비밀 관리 | `...d3-secret-management` | **RBAC 만 실제로 감춘다** | +| **D-4** | 인증서 갱신 | `...d4-certificate-renewal` | 구성은 정상. **강제 갱신은 못 했다** | + +## 문서 지도 + +| 문서 | 용도 | +|---|---| +| [`session-lab-prerequisites.md`](session-lab-prerequisites.md) | **먼저 읽을 것** — 왜 이런 걸 재는지 | +| [`experiment-plan.md`](experiment-plan.md) | 23개의 구조도·주입 방법·예측 | +| [`open-questions-coverage.md`](open-questions-coverage.md) | 공개 열린 질문 4개 대조 | +| [`session-lab-concepts.md`](session-lab-concepts.md) | 등장 개념 전체 (13층) | + +## 반복해서 배운 것 — 주입이 안 걸린 사례 + +**아홉 번 있었다. 전부 "아무 일도 없었다" 로 보였다.** + +| 실험 | 안 걸린 주입 | 원인 | +|---|---|---| +| A-1 | NetworkPolicy 로 7800 차단 | conntrack `ESTABLISHED` 가 먼저 통과시킨다 | +| A-3 | `delete --grace-period=0 --force` | 컨테이너 런타임이 SIGTERM → 정상 종료 | +| A-3 | `kill -9 1` | 컨테이너 안에서 **PID 1 은 SIGKILL 을 무시**한다 | +| A-5 | `iptables -I FORWARD 1` | kube-router 가 자기 체인을 위로 재삽입 | +| A-5 | raw 테이블, 반대 노드 | **연결 방향이 뒤집혀 있었다** | +| A-6 | `tc ... dev eth0` | 인터페이스가 `enp1s0` 이다 | +| A-6 | `enp1s0` 에 파드 IP 필터 | VXLAN 캡슐화로 **안 보인다** | +| B-2 | `spring.sql.init` 스키마 | PostgreSQL 에 없는 `blob` 타입 + `continue-on-error` | +| B-7·D-4 | `sudo nginx -T` 등 | **호스트 sudo 가 비밀번호를 요구**한다 | + +**그래서 실험마다 "주입 성공 신호" 를 먼저 정하게 됐다** — +`cluster_size` 하락, `not properly shut down` 로그, iptables 패킷 카운터, +`tc -s qdisc` 의 `Sent`. + +## 예측이 빗나간 곳 + +| 실험 | 예측 | 실제 | +|---|---|---| +| A-1 | 로그아웃 전파는 안 깨진다 | **깨졌다** — 무효화는 7800 을 탄다 | +| A-2 | `up` 이 잡아줄 것 | **1로 평평했다** | +| A-6 | 낙관적 락 충돌이 는다 | **0건** — 로그인은 새 행을 만든다 | +| B-4 | nginx 가 동명 헤더를 덮어쓴다 | **둘 다 도착했다** | +| B-6 | JWKS 캐시가 유예를 준다 | **즉시 401** | + +## 말할 수 있는 것과 없는 것 + +**말할 수 있다** — Keycloak 멀티노드에서 세션과 토큰이 어디에 저장되고 +각 저장소가 죽으면 무엇이 어떻게 실패하는지 **재현하고 복구했다.** +버전에 따라 결론이 뒤집힌다는 것을 **같은 주입으로 양쪽 다 측정했다.** + +**말하면 안 된다** — "운영해봤다", "대규모 트래픽을 다뤄봤다". +규모·시간·다른 사람·실제 사용자·비용은 이 실험대에 없다.