raw — 명령 출력 원문 (정본) =================== 이 폴더가 증거의 정본이다. SVG 는 표현물이고 여기 원문 없이는 만들지 않는다. 파일 이름은 <실험 폴더>__<원본 파일명> 이다. 원본 저장소에서는 실험마다 폴더가 나뉘어 있었고 파일명이 겹쳤다(01-baseline.txt 등). 이 저장소의 감사 스크립트는 raw/ 최상위만 훑으므로 이름을 평평하게 펴고 접두사로 구분했다. 각 파일의 실행 메타는 ../meta/<같은 이름>.json 에 있다. 명령 전문은 그 json 의 sourceDoc 이 가리키는 해설 문서의 「재현 절차」 절에 있다. [a1-jgroups-transport-block] a1-jgroups-transport-block__01-baseline-cluster.txt 차단 전 — 양쪽이 뷰 ID 5·멤버 2로 일치, JGROUPS_PING 코디네이터 1명 a1-jgroups-transport-block__02-control-before-block.txt 대조군 — 차단 전 교차 노드 refresh 200, JGroups 지표 전부 0 a1-jgroups-transport-block__03-block-applied.txt NetworkPolicy 적용. 빈 측정값을 "변화 감지"로 오판한 기록 a1-jgroups-transport-block__04-after-block-state.txt 차단 43초 후 — 지표 무변화, JGROUPS_PING 그대로 a1-jgroups-transport-block__05-conntrack-problem.txt 핵심 문제 — ESTABLISHED [ASSURED] 로 기존 연결이 살아 있음. FD_SOCK2 의 57800 포트도 함께 드러남 a1-jgroups-transport-block__06-partition-observed.txt 임시 curl 파드 폴링의 실패 — 빈 값·개수 불일치 a1-jgroups-transport-block__07-cluster-size.txt vendor_cluster_size 가 25분 내내 2 — 분단이 일어나지 않았다는 결정적 증거 a1-jgroups-transport-block__08-restart-forced-partition.txt 재연결 강제 후 2 → 1 a1-jgroups-transport-block__09-cross-node-under-partition.txt 본 시험 — 교차 refresh 200(예측 적중), 로그아웃 후 재갱신 200(예측 빗나감) a1-jgroups-transport-block__10-logout-not-propagated.txt 기제 확정 — DB 행 0건인데 keycloak-0 캐시에 1건, 헬스체크 cluster health: DOWN a1-jgroups-transport-block__11-service-impact.txt 분단 노드가 Service 에서 빠짐. ready=[10.42.0.35] notReady=[10.42.1.67], 외부 로그인 200 a1-jgroups-transport-block__12-recovery.txt 90초 만에 자동 재형성, merge3_get_num_merge_events = 1, 코디네이터 재선출 [a2-database-loss] a2-database-loss__01-baseline.txt 정지 전 — 양쪽 Ready, cluster_size=2 a2-database-loss__02-setup-sessions.txt 양쪽 노드에 세션 하나씩. 캐시는 각자 노드에만 a2-database-loss__03-four-paths.txt 네 경로 전부 500. 캐시를 가진 노드도 실패 — refresh 는 쓰기다 a2-database-loss__04-health-and-service.txt 전면 장애 증거 — Ready 파드 0개, ready 주소=[], 외부 503, database connections: DOWN. JWKS·.well-known 은 200 a2-database-loss__05-recovery.txt up=1 인 채로 503. DB 복귀 15초 후 재시작 0회로 자동 회복, 세션 5건 생존 [a3-database-crash] a3-database-crash__01-crash-injection.txt 첫 시도 실패 — 파드 안 백그라운드 루프가 exec 종료와 함께 죽어 0건 수집 a3-database-crash__02-design-check.txt 핵심 설계 확인 — 로그인 트랜잭션도 SET LOCAL synchronous_commit TO OFF 로 커밋한다 a3-database-crash__03-loss-measurement.txt --grace-period=0 --force 주입 a3-database-crash__04-comparison.txt 유실 0건 — 그러나 crash recovery 가 안 돌았다. 죽인 적이 없는 것 a3-database-crash__05-true-crash.txt kill -9 1 시도 — 컨테이너 안에서 PID 1 은 SIGKILL 을 무시한다 a3-database-crash__06-backend-kill-crash.txt 성공한 주입 — 백엔드에 SIGKILL → not properly shut down / redo starts / redo done a3-database-crash__07-loss-result.txt 결과: 153건 중 4건 유실. 토큰은 발급됐는데 세션 행이 없는 sid 목록 a3-database-crash__08-wal-settings.txt (원본 README 에 설명이 없다 — 파일 머리말 참조) [a4-node-loss] a4-node-loss__01-baseline.txt 차단 전 — 양쪽 Ready, PVC 가 kc-lab-2 에 못박혀 있음(재배치 불가의 원인), 외부 200 a4-node-loss__02-worker-node-killed.txt virsh destroy kc-lab-2 — 40초간 노드가 Ready 로 남아 있고 외부는 이미 000. 이후 503 a4-node-loss__03-state-during-loss.txt 죽은 파드가 ready=true, 산 파드가 ready=false. up 은 정확히 0. unreachable taint a4-node-loss__04-eviction-timing.txt tolerationSeconds=300 — 5분 뒤 축출, 새 postgres 는 Pending a4-node-loss__05-recovery.txt FailedScheduling: didn't match PersistentVolume's node affinity, StatefulSet DESIRED=2 CURRENT=1. 노드 복귀 후 60초 a4-node-loss__06-control-plane-inventory.txt kc-lab-1 에 있는 것 목록 — Traefik replicas=1 a4-node-loss__07-control-plane-loss.txt virsh destroy kc-lab-1 — 외부 000, kubectl 불통. 그런데 crictl ps 로 보면 keycloak-0 은 Running a4-node-loss__08-control-plane-recovery.txt 60초 만에 복귀 [a5-asymmetric-partition] a5-asymmetric-partition__01-injection.txt 첫 시도 — iptables -I FORWARD 1 a5-asymmetric-partition__02-injection-verify.txt 실패 확인 — kube-router 가 자기 체인을 위로 재삽입, DROP 규칙 0 패킷 a5-asymmetric-partition__03-raw-table-injection.txt raw 테이블로 이동. 그래도 0 패킷 a5-asymmetric-partition__04-correct-direction.txt 연결 방향이 반대였다. 수신측 노드로 옮기니 19/21 패킷 차단 a5-asymmetric-partition__05-reconnect-observed.txt 핵심 — 연결이 열린 방향으로 뒤집혀 재연결. 뷰 변화 없음, suspected=0 a5-asymmetric-partition__06-view-history-and-cleanup.txt 뷰 ID 이력. 주입 이후 변화 없음 확인 a5-asymmetric-partition__07-bidirectional-block.txt 양방향 차단 → 뷰 14, 멤버 1개씩. 그런데 ready=[10.42.1.77] 로 한쪽은 남고 외부 200 a5-asymmetric-partition__08-coordinator-and-recovery.txt 15] (2) [a6-latency-injection] a6-latency-injection__01-baseline.txt 배치 설명(A/B 가 되는 이유), agroal_* 지표 목록, 기준선 70ms / 66ms a6-latency-injection__02-delay-injected.txt 첫 시도 실패 — Cannot find device "eth0" (Debian 은 enp1s0) a6-latency-injection__03-flannel-injection.txt 성공 — flannel.1 에 걸어야 파드 IP 가 보인다. netem Sent 150 pkt 로 검증. k0=41ms vs k1=1872ms a6-latency-injection__04-pool-under-load.txt 동시 20건 — 응답이 1.9초에서 22.2초까지 계단. blocking_time_max=20000ms, max_used_count=19, readiness 프로브 타임아웃 a6-latency-injection__05-recovery.txt 해제 즉시 43ms / 51ms 회복. 낙관적 락 충돌 0건(예측 빗나감) [a7-volatile-comparison] a7-volatile-comparison__01-switch-to-volatile.txt --features-disabled=persistent-user-sessions 적용, args 확인 a7-volatile-comparison__02-a0-rerun.txt DB 0건인데 교차 노드 refresh 200 — 경로가 DB 에서 클러스터로 바뀌었다 a7-volatile-comparison__03-a8-rerun-restart.txt 롤링 재시작 후 400 Session not active — persistent 에서는 200 이었다 a7-volatile-comparison__04-a1-rerun-partition.txt 7800 차단 시 교차 노드 400 — persistent 에서는 200. 대조군(같은 노드)은 200 유지 a7-volatile-comparison__05-a2-rerun-db-loss.txt DB 정지 중 새 로그인 200(persistent 에서는 500), refresh 는 500 a7-volatile-comparison__06-restore-persistent.txt 원복 확인 — args: ["start"], 로그인 후 DB 1건, 외부 200 [a7a-volatile-cause] a7a-volatile-cause__01-cause-determined.txt 가설이 틀렸다. REVOKED_TOKEN 이 아니라 CLIENT_SCOPE_CLIENT 조회다. 문장 로깅으로 잡고, 실패 로그가 그 SQL 을 직접 지목한다. 그리고 같은 설정에서 캐시 온도만으로 400/500/200 셋이 나온다 [a8-rolling-restart] a8-rolling-restart__01-restart-availability.txt 재시작 전 로그인(sid 기록), 롤링 재시작 중 외부 진입점 9회 모두 200 a8-rolling-restart__02-session-survival.txt 재시작 전 토큰으로 refresh 200, DB 행 생존(last_session_refresh 갱신 확인), 세션 수 151 → 151, 캐시 0으로 초기화, 파드 나이 44초/66초 [b0-bff-redis-deploy] b0-bff-redis-deploy__01-deploy.txt 배포 전 자원, Redis·BFF 롤아웃, 두 노드에 하나씩 배치됨 b0-bff-redis-deploy__02-autoconfiguration.txt 첫 조회 시도(파싱 실패)와 외부 진입점 HTTP 200 b0-bff-redis-deploy__03-beans-analysis.txt B-0 의 답 — InMemoryOAuth2AuthorizedClientService, AuthenticatedPrincipalOAuth2AuthorizedClientRepository, Redis·Spring Session 없음 [b1-redis-session-store] b1-redis-session-store__01-servicelinks-trap.txt enableServiceLinks: false 적용 후 롤아웃 성공 — 쿠버네티스가 주입한 REDIS_PORT=tcp://... 가 설정을 덮어쓴 문제 b1-redis-session-store__02-autoconfig-after.txt 핵심 — 빈 321→402(+81). sessionRepository → RedisSessionRepository 로 바뀌었지만 authorizedClientService 는 InMemory 그대로 b1-redis-session-store__03-redis-contents.txt Redis 키 1개, 필드는 SPRING_SECURITY_CONTEXT 뿐. 토큰 없음. Java 직렬화(\xac\xed), TTL 1772초 [b2-multi-instance-session] b2-multi-instance-session__01-jdbc-store-deploy.txt JDBC 저장소로 배포. 테이블이 조용히 안 만들어졌다 b2-multi-instance-session__02-schema.txt 원인 — 기본 DDL 은 blob(PostgreSQL 에 없음), -postgres.sql 판본이 따로 있다. PRIMARY KEY (client_registration_id, principal_name) — 조회 키 문제가 DDL 에 박혀 있다 b2-multi-instance-session__03-plaintext-tokens.txt Q3 검증 2번 — bytea 안이 JWT 문자열 그대로. 디코드하면 {"alg":"HS512",...} b2-multi-instance-session__04-overwrite-test.txt Q1 검증 3번 — 같은 사용자 재로그인 시 행 수 1 그대로, issued_at 과 md5 만 바뀜 = UPDATE(덮어쓰기) b2-multi-instance-session__05-logout-cleanup.txt Q1 검증 4번 — Redis 0키 / PostgreSQL 1행 잔존 / Keycloak SSO 2세션 잔존 [b3-refresh-contention] b3-refresh-contention__01-concurrent-refresh.txt 같은 토큰으로 동시 5회 — 1개만 200, 나머지는 Maximum allowed refresh token reuse exceeded 와 Session doesn't have required client 두 종류 오류 b3-refresh-contention__02-session-impact.txt ★ 이긴 요청의 새 토큰조차 400. user_session 행은 남아 있고 revoked_token 은 0건 b3-refresh-contention__03-client-session-removed.txt 기제 확정 (대조군 포함) — 경쟁 세션 client_sessions=0, 정상 세션 client_sessions=1 b3-refresh-contention__04-policy-comparison.txt 정책 3종 비교 — rotation OFF 는 5/5 성공·세션 생존, maxReuse=1 은 여전히 세션 파괴 [b4-edge-authorization] b4-edge-authorization__01-header-handling.txt ① 동명 헤더가 둘 다 도착(['admin','editor'])하고 값 안의 쉼표를 구분자와 구별할 수 없다 · ② 8KB 에서 Tomcat 400, 16KB 에서 연결 끊김 — 자르지 않고 거부 · ④ 위조 신원 헤더가 그대로 도착, JWT 경로는 401 [b5-redis-loss] b5-redis-loss__01-baseline.txt 정지 전 — Redis 1키, PostgreSQL 1행, save/appendonly no, 외부 200 b5-redis-loss__02-redis-down.txt 정지 후 — /bff/token-boundary HTTP 000(멈춤), /actuator/health 503, 파드는 1/1 Ready 유지, Lettuce 재연결 스택 b5-redis-loss__03-health-groups.txt 핵심 — /actuator/health 503 인데 /actuator/health/readiness 는 {"status":"UP"}. Service 엔드포인트에 두 파드 모두 남아 있다 b5-redis-loss__04-persistence.txt 복구는 자동(재시작 0회) · AOF 를 켰는데 파드 삭제 후 dbsize 0 · PVC 를 붙인 뒤 written-on-pvc 생존 [b6-key-rotation] b6-key-rotation__01-before-rotation.txt 회전 전 — 토큰 kid=OY-caYDN..., JWKS RS256 1개, /api/me 200 b6-key-rotation__02-rotation.txt 우선순위 200 공급자 추가 → JWKS RS256 2개, 새 토큰은 새 kid, 옛 토큰도 새 토큰도 200 (무중단) b6-key-rotation__03-old-key-removed.txt 옛 공급자 제거 → JWKS 1개, 옛 토큰 즉시 401. 리소스 서버 재시작 후에도 동일 [b7-cookie-secret] b7-cookie-secret__01-deploy.txt 양 노드에 replica 하나씩. / 302, /ping 200 b7-cookie-secret__02-cookie-portability.txt curl 로 흐름을 완주하려던 시도 — 파드 IP 는 호스트에서 안 닿는다 b7-cookie-secret__03-rotation.txt --cookie-secret string 단수 확인 · 교체 후 session ticket cookie failed validation · Error removing session · Redis 에 고아 세션 2개 [b7a-orphan-session] b7a-orphan-session__01-orphan-lifecycle.txt 회전 2회로 고아가 누적하는 것 · TTL 이 1초/초로 줄고 요청으로 갱신되지 않는 것 · Redis 만으로는 구분 불가(이름·타입·크기 동일, 값 암호화) · redis-cli del 로 지워도 산 세션은 200 · TTL 역산 정리 규칙이 1초 오차로 맞는 것 [c1-multi-app-sso] c1-multi-app-sso__01-baseline.txt 초기화 시도 — logout-all 이 안 먹어 세션 4개가 남았다 c1-multi-app-sso__02-after-app1-login.txt app1 로그인 후 — user session 1 · client session 1 · Redis 1 · DB 1행 c1-multi-app-sso__03-after-app2-visit.txt app2 방문 후 client session 1 → 2, bff-confidential 과 oauth2-proxy 가 같은 user session 에 붙음. Redis 에 두 종류 세션 c1-multi-app-sso__04-sso-session-killed.txt IdP 세션 삭제 후 — 앱 세션 셋 다 남아 있다. realm 을 join 해 보고서야 남은 것이 master 세션임을 확인 [c2-backchannel-logout] c2-backchannel-logout__01-current-state.txt 두 클라이언트 모두 backchannelLogoutUrl 없음 · BFF 소스에 oidcLogout 없음 · 후보 경로 셋 다 302(핸들러 없음) c2-backchannel-logout__02-configure-idp.txt IdP 쪽에만 backchannel.logout.url 설정 (점 표기는 실패, JSON 으로 성공) c2-backchannel-logout__03-logout-attempt.txt 살아 있는 세션(1)에 로그아웃 → IdP 세션 0, Redis 세션은 1 그대로. Keycloak·BFF 로그에 흔적 없음 c2-backchannel-logout__04-reachability.txt Keycloak 파드가 app1.hyeonworks.com 에 HTTP 200 으로 닿는다 — 네트워크 문제가 아님 [d1-backup-restore] d1-backup-restore__01-backup.txt pg_dump --clean --if-exists — 395KB · 101 테이블 · 세션 데이터 포함 d1-backup-restore__02-destruction.txt DROP SCHEMA public CASCADE → 테이블 0개. 그런데 외부는 HTTP 200 — Keycloak 이 realm 캐시로 서빙한다 d1-backup-restore__03-restore.txt 깨지는 것과 안 깨지는 것(certs 200 / well-known 500 / 토큰 400) · 복구 1초 · 오류 0건 · 데이터 완전 일치 · 재시작 0회 [d2-version-upgrade] d2-version-upgrade__01-pre-upgrade.txt 백업 396KB · 이미지 26.7.0 · 마이그레이션 210건 · 세션 4 d2-version-upgrade__02-rollback-attempt.txt 26.0 으로 내리자 Running(0/1) → Error → CrashLoopBackOff. liquibase.exception.ValidationFailedException d2-version-upgrade__03-roll-forward.txt 서비스는 HTTP 200 유지(ready 주소 1개) · 오류 원인 1 changesets check sum · 26.7.0 복귀 후 마이그레이션 210·세션 4 그대로 [d3-secret-management] d3-secret-management__01-base64-not-encryption.txt 실험대의 모든 비밀이 명령 네 줄로 평문 출력. describe 는 14 bytes 만 보여줘 착각을 준다 d3-secret-management__02-at-rest.txt Encryption Status: Disabled · state.db 안에 비밀번호 평문 2회 일치 · 파드 안에서는 KEYCLOAK_CLIENT_SECRET=bff-lab-secret 환경변수 · default SA 는 읽을 수 없음 [d4-certificate-renewal] d4-certificate-renewal__01-certificate-state.txt SAN 3개(와일드카드 아님) · 체인 4단계, Verify return code: 0 · certbot-renew.timer enabled·active, 11시간 전 실행 · 88일 남음 · sudo: a password is required 로 강제 갱신 불가 d4-certificate-renewal__05-control-no-injection.txt 대조군 1 — 잡음 바닥. 0.2초 × 900회 / 180초 동안 900 전부 200, 오류 0. 중앙 98ms · p95 195ms. TLS 핸드셰이크 900/900 = 매 요청이 새 연결 d4-certificate-renewal__06-inflight-control.txt 대조군 2 — '진행 중이던 요청' 장치. 845KB 를 --limit-rate 20k 로 받아 요청을 42초간 살려 둔다. 무주입 시 200 · 845361바이트 · 연결수 1 d4-certificate-renewal__07-renewal-hook-missing.txt nginx 는 reload 된 적이 없다 — 마스터 585·워커 586 이 같은 시각 기동, 22.4시간째. 유닛은 ExecStart=certbot -q renew 가 전부. crt.sh 는 SCT 가 박힌 인증서를 0건으로 답한다 d4-certificate-renewal__08-inflight-artifact.txt 76건 실패는 서버 탓이 아니다 — 같은 순간 폴링 49건 전부 200, 연결수=0, 50µs, 재현 0/100. 대조군이 오보를 막았다 d4-certificate-renewal__09-serial-timeline.txt 일련번호 564표본. 08:10:51 ~ 08:58:47 옛 것 → 08:58:52 새 것 d4-certificate-renewal__10-reload-poll-window.txt reload 전후 60초 새 연결 원문 — 비200 0건, 최대 373ms d4-certificate-renewal__11-inflight-full.txt in-flight 전체 50건. 08:58:40 시작 요청이 08:58:52 reload 를 관통해 845361바이트 전량 수신 d4-certificate-renewal__12-certbot-state.txt cert2.pem 09-04 17:22:13 기록됨 · renewal-hooks/{deploy,post,pre}/ 셋 다 비었음 · 플러그인 목록에 nginx 없음 d4-certificate-renewal__13-verdict.txt 판정 전문 — 38분 25초 공백(428회 관측) + reload 무중단(8856건 0실패) [d4a-deploy-hook] d4a-deploy-hook__01-hook-verified.txt 판정 전문. 훅 실행 로그 · 워커 PID 교체 · 시계 보정과 SCT 교차검증 · D-4 와의 대조 d4a-deploy-hook__02-certbot-with-hook.txt certbot renew --force-renewal 원문. Hook 'deploy-hook' ran 과 all renewals succeeded d4a-deploy-hook__03-after-state.txt 실행 후 nginx 프로세스와 서빙 인증서 [followup] followup__01-d2-forward-upgrade.txt D-2 정방향 26.7.0 → 26.7.3. 백업 396KB · 87회 요청 전부 200(무중단) · 마이그레이션 210 → 210(스키마 변경 없음) · 세션 3 유지 · Infinispan 16.0.12 → 16.0.14 followup__02-d2-rollback-same-schema.txt 스키마가 안 바뀌면 롤백이 된다 — 26.7.3 → 26.7.0 성공. 다만 전환 순간 000 1회(3초 타임아웃) followup__03-b4-role-propagation.txt B-4 ③ IdP 에서 값을 바꿔도 12회 요청·6초 동안 옛 값. 세션 삭제 후 재인증에서야 새 값 followup__04-observability-gap.txt B층에 관측이 없다 — Prometheus 는 keycloak·kubelet·node-exporter·prometheus 만 긁는다. Redis·BFF·PostgreSQL 지표가 0개 followup__05-command-reproducibility.txt 재현 절차 명령을 실제로 돌려본 기록. 산문이던 측정 장치를 셸 실행형으로 바꾼 뒤 실행 검증 — 4건 중 1건(동시 20건 부하)이 조용히 실패했고 상주 탐침 방식으로 고쳐 20/20 수집 [keycloak-multinode-cluster] keycloak-multinode-cluster__01-cluster-formed.txt 파드 배치·클러스터 뷰 로그·JGROUPS_PING·OIDC discovery·자원 [session-replication] session-replication__01-cross-node-session.txt 클러스터 2멤버 확인 → keycloak-0 로그인 → 같은 sid 가 양쪽에서 보임 → keycloak-1 이 refresh 성공(200) → keycloak-1 로그아웃 → keycloak-0 갱신 실패(400) → DB 행 삭제 확인 session-replication__02-cache-delta.txt 로그인 하나를 사이에 둔 양쪽 노드의 캐시 계수기. keycloak-1 은 전부 +0 session-replication__03-cache-ownership.txt 로그인을 반대편에 몰아준 결과. 요청을 받은 노드에서만 엔트리가 는다. 캐시 합 7+5 = DB 12 session-replication__04-read-path-sql.txt PostgreSQL 문장 로깅으로 잡은 keycloak-1 이 실제로 날린 SQL. SELECT ... FROM OFFLINE_USER_SESSION 로 남의 세션을 읽고 UPDATE ... where VERSION=$5 로 쓴다. 같은 트랜잭션에 SET LOCAL synchronous_commit TO OFF 가 들어 있다 [two-hop-proxy-headers] two-hop-proxy-headers__01-environment.txt 수정 전 세 계층의 설정 스냅샷 two-hop-proxy-headers__02-measurements.txt 수정 전 측정 · 대조 실험 · 위조 테스트 · 분배 two-hop-proxy-headers__04-after-fix.txt 최종 측정 · 위조 테스트 · 분배 two-hop-proxy-headers__05-networkpolicy.txt (원본 README 에 설명이 없다 — 파일 머리말 참조)