Files
document-haness/docs/keycloak-session-store/final/evidence/raw/relive-2026-09-17/README.txt
T
DongHyeonkaandClaude Opus 5 024362d096 fix(setup): 실험대를 새로 세워 setup 35편을 밟고 어긋난 명령과 결과를 고친다
기반 가이드 7단계로 실험대를 철거하고 다시 세운 뒤 virtualization setup 9편과
keycloak-session-store 26편을 순서대로 밟았다. 24편은 끝까지, 11편은 되는 데까지
밟았고 밟은 범위를 편마다 적었다.

명령이 못 도는 것을 고쳤다.

- kubectl 을 `kc-lab-1` 에서 치라고 적었는데 그 기계에 kubeconfig 가 없다.
  라벨 639개와 각 편의 「어디서 치는가」를 `[lab host]` 로 옮겼다
- `-o custom-columns=…[0]…` 이 zsh 에서 글로브로 읽혀 안 돈다. 28곳에 따옴표
- busybox `sed` 가 끝 개행을 안 붙여 A-3 의 측정이 언제나 0 이었다
- `--token-file ~/node-token` 뒤에 그 파일을 지우면 k3s agent 가 재부팅을
  못 견딘다. `/etc/rancher/node-token` 으로 옮기는 처방을 재서 넣었다
- 게스트에 없는 도구를 전제로 한 명령 넷 — `conntrack`·`dig`·`strings`·`nginx -v`
- `echo` 와 JWT 헤더가 `"이름" : [ 값 ]` 으로 찍는데 문서는 공백 없이 옮겨 적어
  그 실측으로 만든 grep·sed 가 한 줄도 못 잡는다
- B-0 이 `directAccessGrantsEnabled` 와 계정 완성을 빠뜨려 B-3 이 못 돈다
- D-4·D-4a 가 `test-server` 와 `certbot-renew.*` 를 가리키는데 실제로는
  `kc-lab-edge` 의 `certbot.service` 다
- `virsh setmaxmem --config` 를 `dominfo` 로 판정하면 틀린다. `--inactive` 로
- `LIBVIRT_DEFAULT_URI` 를 rc 에만 넣으면 `ssh host '명령'` 에서 안 먹는다

결과가 조건부인 것을 갈랐다.

- readiness 는 즉시 안 뒤집힌다. A-1·A-2 의 60초 창을 적었다
- 03 의 층 ②③ `301` 은 04 이후의 값이고 그 단계에서는 `404` 다
- A-0 의 로그 필터를 요청 직후에 치면 정반대 결론이 나온다
- A-5 의 한 방향 차단은 잠깐 `1` 이었다 `2` 로 돌아온다

증거는 두 프로젝트의 `evidence/raw/` 에 99벌을 README 와 함께 남겼다. 비밀은
길이만 적었고 화면에 찍힌 토큰은 가렸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 15:59:42 +09:00

168 lines
15 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
2026-09-17 에 기반 가이드 7단계로 실험대를 처음부터 다시 세우고 A-0 을 그대로 다시
밟은 기록이다. 명령과 출력은 그때 화면에 나온 것을 그대로 옮겼다.
a0-01-baseline.txt 기준선 1~5 — 노드·파드 배치, 파드 IP, ISPN000094 (2), jgroups_ping coord 하나, \dt 100행, 세션 0행
a0-02-cache-entries.txt 기준선 6 — Prometheus 의 vendor_statistics_approximate_entries_unique 통째로와 tr/grep 으로 자른 형태
a0-03-inject-reset.txt 주입 — 세션 행 삭제와 rollout restart, 새 파드 IP
a0-04-test0.txt 시험 0 ①~⑦ — 로그인, 토큰 길이, 클레임 디코드, sid, CID, 두 노드에 grep -c
a0-05-claims.txt JWT 클레임 전체 (패딩을 채워 디코드) — sub 이 없다는 것을 확인
a0-06-test0-cross-node.txt 시험 0 ⑧⑨ — 반대편 refresh 200 · 같은 sid, 로그아웃 204, 발급 노드에서 400 Session not active
a0-07-test0b-cache-delta.txt 시험 0b — 로그인 한 번에 발급 노드만 +1, 반대편 +0
a0-08-test0c-ownership.txt 시험 0c — 몰아준 쪽만 늘어 9 + 5 = 14, DB 총계 14
a0-09-test0d-sql.txt 시험 0d 첫 시도 — 문장 로깅을 켜고 요청 직후에 걸렀더니 반대편 문장이 안 나왔다
a0-10-test0d-retry.txt 같은 필터를 몇 초 뒤에 — 그래도 안 나온 판. 09 와 함께 「너무 일찍 걸렀다」의 증거다
a0-11-test0d-window.txt 그 시간대에 반대편이 낸 모든 줄 — 디스커버리 질의뿐이었다
a0-12-test0d-controlled.txt 대기를 넣고 다시 — 반대편 pid 가 낸 sid 줄 6개, 세션 테이블 문장 6/12
a0-13-test0d-sql-lines.txt 반대편이 날린 여덟 문장과 pid 로 거른 BEGIN~COMMIT 경계. 끝에 문장 로깅을 되돌렸다
비밀은 옮기지 않았다. 관리자 비밀번호는 길이(19)만 확인했고 값은 어느 파일에도 없다.
A-1 (JGroups 7800 차단)
a1-01-inject.txt 매니페스트 원문과 apply · 적용 시각
a1-02-verify.txt 정책이 잡은 파드, 포트 셋(9000=200 · 8080=200 · 7800=exit 7), 클러스터는 아직 2, /proc/net/tcp6 의 1E78
a1-03-conntrack.txt conntrack 을 두 노드에서 — 빈 화면이 나왔다
a1-04-conntrack-missing.txt 그 빈 화면의 까닭 — conntrack 미설치. 2>/dev/null 이 command not found 를 지웠다
a1-05-conntrack-installed.txt 처방 검증 — 깔고 나니 ESTABLISHED [ASSURED] 두 줄이 나온다
a1-06-forced-partition.txt 파드를 지워 분단 확정 — 두 노드가 각각 1 을 보고
a1-07-readiness.txt 분단된 노드가 0/1 · Ready=False, Endpoints 가 하나로, health/ready 503 과 Failing since
a1-08-readiness-commands.txt 문서가 적은 명령 그대로 — describe 는 조건표를 내고, custom-columns 는 zsh 에서 안 돈다
a1-09-quoting-and-health.txt 따옴표를 씌운 형태와 헬스 본문을 내는 명령. 정문은 Host 헤더로 200
a1-10-cross-node-under-partition.txt 분단 중 네 단계 — 200 · 204 · 200. 로그아웃이 전파되지 않았다 (토큰은 길이만 남기고 가렸다)
a1-11-recover.txt 정책 삭제 뒤 30~60초 안에 두 노드가 다시 2
A-2 (데이터베이스 정상 종료)
a2-01-baseline-probe.txt 상주 탐침 a2-probe 와 정지 전 토큰 (길이만)
a2-02-inject.txt 정지 전 네 경로 200×4, replicas=0, 파드 삭제, Deployment 0/0
a2-03-during-outage.txt 정지 직후 — 네 경로 500×4, JWKS·well-known 200, Connection refused, 그런데 ready 는 아직 true
a2-04-outage-progression.txt 약 50초 뒤 ready false,false · 정문 503 으로 뒤집히는 자리
a2-05-recover.txt replicas=1 에서 36초 만에 ready true,true · 정문 200 · 재시작 0
A-3 (데이터베이스 크래시)
a3-01-design-check.txt 로그인 트랜잭션에도 SET LOCAL synchronous_commit TO OFF 가 붙는다 · WAL 설정 네 줄
a3-02-prepare.txt 세션 비우기, 파드 셋 상태, 루프 파일 주입
a3-03-attempt1.txt 시도 ① 첫 판 — /tmp/sids 가 0줄이었다 (아래 04 가 그 까닭)
a3-04-sids-no-newline.txt busybox sed 가 끝 개행을 안 붙인다 — 9384바이트 0줄. echo 로 감싼 처방을 재서 10줄 확인
a3-05-attempt1-redo.txt 고친 루프로 다시 — 8초에 109건, force delete 는 유실 0
a3-06-attempt2.txt 시도 ② kill -9 1 — RESTARTS 0, 400 = 400, 유실 0
a3-07-attempt3.txt 시도 ③ 백엔드 SIGKILL — crash recovery 로그와 395 vs 392
a3-08-loss-count.txt 문서의 comm 방법으로 센 유실 3건과 그 sid 세 개
a3-09-revert.txt 문장 로깅 off 확인, 세션 정리, 파드 재시작, 탐침 삭제
A-4 (노드 상실)
a4-01-baseline.txt virsh uri·VM 셋·파드 배치·PV 가 kc-lab-2 에 고정된 것·관측 스택 위치
a4-02-inject-4a.txt kc-lab-2 영향 범위와 virsh destroy. 하이퍼바이저는 shut off 인데 쿠버네티스는 +10초에도 Ready
a4-03-4a-progression.txt +28초 Ready/정문 000 → +66초 NotReady/정문 503. 이벤트 NodeNotReady 와 context deadline exceeded
a4-04-eviction-and-recover.txt +4분 49초에 deletionTimestamp. Deployment 는 대체 파드(Pending), StatefulSet 은 대체 없이 Terminating
a4-05-recovery-progression.txt VM 을 켠 뒤 Ready 로 돌아오고 파드가 서고 정문이 200 이 되기까지
a4-06-agent-token-missing.txt 복구가 막힌 진짜 까닭 — 유닛이 지워진 ~/node-token 을 기다린다. 파일을 되돌리자 Ready
a4-07-token-remedy.txt 처방 — /etc/rancher/node-token(root 600)으로 옮기고 유닛을 돌리면 홈 사본을 지워도 뜬다
A-5 (비대칭 분단)
a5-01-attempt1.txt 시도 ① filter FORWARD — 카운터는 9·22 로 올라가는데 클러스터는 2 그대로. 게스트 셸의 $K1 이 비어 있는 것도 여기서 확인
a5-02-attempt2-3.txt 시도 ② 보내는 쪽 raw — 카운터 0. 시도 ③ 받는 쪽 raw — 카운터 16·20, 멤버 1
a5-03-one-direction-watch.txt 한 방향만 막은 채 2분 — 잠깐 1 이었다가 2 로 돌아온다 (문서의 「열린 방향으로 다시 붙는다」)
a5-04-bidirectional.txt 양방향 — 멤버 1·1 과 jgroups_ping 의 coord 가 둘 다 t
a5-05-revert.txt 두 체인을 비우자 2 와 coord 하나로 복귀
A-6 (지연 주입)
a6-01-attempt1-2.txt 시도 ① eth0 가 없다 · ip -brief link 목록 · enp1s0 는 노드 IP 만, flannel.1 은 파드 IP 가 보인다 (문서가 미검증으로 둔 두 줄)
a6-02-inject.txt 주입 전 로그인 0.064~0.068s, flannel.1 에 prio·netem·filter 세 줄
a6-03-effect.txt 주입 뒤 1.86~1.91s (문서의 1,872ms 와 28배가 그대로) · netem 83 pkt · tc filter show 의 match 0a2a011d
a6-04-revert.txt qdisc del 뒤 noqueue 로 돌아가고 시간도 0.04~0.07s 로 복귀
A-7 (volatile 로 A층 넷을 다시)
a7-01-switch-volatile.txt 세션 비우기, args 를 --features-disabled=persistent-user-sessions 로 patch, 롤아웃
a7-02-cross-node-volatile.txt 교차 노드 refresh 200 인데 DB 세션 행은 (0 rows)
a7-03-block-7800.txt 캐시가 keycloak-0 에만 1, 그 뒤 NetworkPolicy 로 7800 차단 — 멤버 1·1
a7-04-partition-breaks-sharing.txt 분단 + volatile 에서 400·400·400 (persistent 의 200·204·200 과 정반대)
a7-05-revert.txt NetworkPolicy 삭제와 args 원복
A-7a (그 500 의 원인과 캐시 온도 셋)
a7a-01-setup.txt 문장 로깅 on, volatile 전환, StatefulSet 밖의 탐침
a7a-02-repro-A.txt 재현 A 완전 냉시동 — 로그인이 400 unauthorized_client
a7a-03-repro-A-sql.txt 문서의 grep 이 한 줄도 못 잡는다. 속 예외가 JDBCConnectionException 쪽이었다
a7a-04-repro-B.txt 재현 B 로그인 한 번으로 덥힌 뒤 DB 정지 — refresh 가 500 unknown_error
a7a-05-repro-C-and-revert.txt 재현 C — DB 를 켜니 같은 refresh 가 200. 로깅 off 와 args 원복까지
A-8 (롤링 재시작)
a8-01-baseline.txt 파드·args·DB 0건, 토큰과 sid 를 탐침에 담고 대조군 refresh 200, 그 sid 의 DB 행
a8-02-restart.txt 재시작 전 캐시 0, rollout restart, AGE 로 교체 확인. (12번은 탐침의 낡은 K0 때문에 000 이 났다)
a8-03-main-test.txt 새 IP 로 다시 — 재시작 전 토큰이 200, created_on 은 그대로이고 last_session_refresh 만 갱신 (토큰은 길이만 남기고 가렸다)
D-3 (비밀 노출 네 경로)
d3-01-secret-paths.txt Secret 목록·describe 의 길이 표시·키 이름·길이, 카나리아 주입, 암호화 Disabled, state.db 0 / -wal 1
d3-02-paths-and-revert.txt strings 미설치, RBAC 는 둘 다 no, 카나리아 삭제 뒤 -wal 이 1 → 7 로 늘었다
d3-03-strings-missing.txt binutils 를 깔면 strings 가 생기고, 안 깔아도 grep -c 로 같은 7 이 나온다
B층 준비 (B-0 의 실행 가능한 부분)
b0-01-image-import.txt 워크스테이션에서 두 이미지를 빌드해 docker save | ssh | k3s ctr images import 로 두 노드에
b0-02-realm-and-user.txt kcadm 로그인, realm keycloak-patterns · client bff-confidential · user labuser 생성
b0-03-deploy-bff.txt bff-redis.yaml 적용 — Secret·PVC·redis·bff·Service·Ingress
b0-04-bff-status.txt bff 두 replica 와 redis 가 1/1. imagePullPolicy Never 로 반입 이미지가 붙었다
B-3 (refresh 회전 경쟁) — 전제 검증까지
b3-01-setup.txt realm 세 값(revokeRefreshToken false · maxReuse 0 · lifespan 60), 탐침, CS길이 14
b3-02-secret-and-token.txt B-0 이 없다고 한 비밀 대조를 만들어 「두 값이 같다」. 그런데 토큰 요청이 unauthorized_client
b3-03-direct-grant-missing.txt 원인 — directAccessGrantsEnabled 가 false. 켜니 invalid_grant 로 바뀐다
b3-04-account-not-fully-set-up.txt 둘째 원인 — labuser 의 emailVerified false. 고치니 토큰이 나온다 (rt=735자)
b3-05-race.txt 회전을 켜고 같은 토큰으로 동시에 5번 — 400 넷·200 하나, 이긴 토큰도 400 (토큰 조각은 가렸다)
b3-06-revert.txt revokeRefreshToken 원복과 탐침 삭제
D-1 (백업과 복구)
d1-01-backup.txt 기준선 101 테이블·세션 6건, pg_dump 로 976873 bytes·8569 줄·CREATE TABLE 101
d1-02-destroy-restore.txt DROP SCHEMA 뒤 남은 테이블 0 인데 정문은 200, 6초 만에 복구되고 ERROR 0, 101/6 으로 복귀
D-2 (판올림과 롤백)
d2-01-baseline.txt 태그 26.7.0, databasechangelog 210, 마지막 다섯 줄, 세션 6, quay 에 26.7.3 존재(200)
d2-02-upgrade.txt 26.7.3 정방향 85초 — 스키마 210 그대로, 세션 보존, 정문 200
d2-03-rollback-and-reverse.txt 롤백 67초 성공. 역방향 26.0 은 liquibase Validation Failed 로 keycloak-1 만 못 뜨고 정문은 200
d2-04-revert.txt 체크섬 줄(1 changesets check sum)과 26.7.0 원복
B-6 (키 회전) — 전제까지만
b6-01-deploy-echo.txt echo.yaml 로 header-lab 네임스페이스와 echo 두 replica (반입 이미지로 뜬다)
b6-02-jwks-before.txt JWKS 를 Host 헤더로 받는다. RS256 1개, kid 둘
b6-03-providers.txt kcadm 세션이 파드 교체로 사라진 것과 재로그인. 문서가 「빈 결과」라던 질의가 실제로는 결과를 낸다
b6-04-old-token.txt 토큰 1397자. echo 는 유효한 토큰에 401, 토큰 없이는 200
b6-05-echo-401.txt 그 까닭 — echo 의 issuer/jwk URI 가 https 이고 닿지 않는다. kid 를 뽑으려면 공백을 허용해야 한다
b6-06-kid-and-issuer.txt 두 URI 원문, issuer=000(exit 7), 헤더의 "kid" : "…" 공백 형태와 두 sed 비교
B-4 (신원 헤더 위조) — TLS 를 Host 헤더로 대체해 실행
b4-01-forged-headers.txt Ingress 네 줄, echo 응답 전문(들여쓰고 콜론 양옆에 공백), 대조군은 비어 있다
b4-02-forged-headers-retry.txt 동명 헤더 둘 · 값 안의 쉼표 · 위조한 신원이 전부 그대로 도착. 문서의 콜론 붙인 grep 은 한 줄도 못 잡는다
C-2 (백채널 로그아웃) — 브라우저 없이 되는 두 단계
c2-01-backchannel-probe.txt 세 후보 경로가 전부 302 이고 Location 이 로그인 시작점. 워크스테이션 grep 도 빈 출력(디렉터리는 있다)
B-7a / B-7 (엣지 세션) — 전제에서 막힌 자리를 쟀다
b7a-01-oauth2-proxy.txt oauth2-proxy 배포가 롤아웃 타임아웃. redis-cli dbsize 0 과 빈 스캔은 정상 동작
b7a-02-oauth2-proxy-blocked.txt 까닭 — 기동 시 OIDC 디스커버리가 https://auth.hyeonworks.com 으로 가고 connection refused. CrashLoopBackOff
B-0 / B-1 / B-2 — 브라우저 없이 되는 측정
b1-01-beans.txt /actuator/beans 빈 437개, authorizedClientService 가 JdbcOAuth2AuthorizedClientService, RedisSessionConfiguration 존재. 문서의 미검증 두 줄이 돈다
b2-01-jdbc-table.txt oauth2_authorized_client 표는 생겨 있고 행은 0 (행은 브라우저 로그인 뒤에 생긴다)
D-4 / D-4a (인증서 갱신과 배포 훅) — 인증서 없이 되는 부분
d4-01-renewal-automation.txt certbot 은 kc-lab-edge 에 있고 test-server 에는 타이머가 없다. 유닛 이름은 certbot.timer/.service (certbot-renew.* 아님). 기본 유닛에 ExecStartPost 도 --deploy-hook 도 없고 훅 디렉터리도 비었다. certificates 는 No certificates found
d4a-01-hook-state.txt test-server 에 nginx 프로세스가 없다(엣지에 있다). 훅 디렉터리 total 8. lab host 와 엣지의 시계가 94초 어긋나 있다
B-5 (Redis 상실)
b5-01-baseline.txt Redis PONG·키 없음·save 와 appendonly·/data 가 PVC redis-data
b5-02-redis-down.txt replicas=0 뒤 health 는 DOWN 인데 readiness 는 UP. 파드 둘 다 1/1, 엔드포인트도 ready true,true
b5-03-revert.txt replicas=1 로 되살리자 health 가 다시 UP
C-1 (여러 앱의 SSO) — 브라우저 없이 되는 세션 정리 구간
c1-01-clear-sessions.txt logout-all 을 쳐도 세션이 6건 그대로. 자식 1633 · 부모 6 순서로 지워야 한다. flushall·롤아웃 뒤 둘 다 0. 롤아웃이 kcadm 세션을 날린다
이 폴더가 덮는 범위
-------------------
2026-09-17 에 기반 가이드 7단계로 실험대를 철거하고 새로 세운 뒤 kss 26편을 순서대로
밟은 기록이다. 끝까지 밟은 편이 17, 되는 데까지 밟은 편이 9 다. 어느 편이 어디까지인지는
SSOT 의 「2026-09-17 재현 — 어디까지 밟았고 무엇이 막았나」와 각 기록의
「이 실험대에서 아직 못 밟은 단계」에 적혀 있다.
막은 것은 하나다 — https://auth.hyeonworks.com 이 서지 않는다. 와일드카드 인증서
(Cloudflare API 토큰)와 호스트의 libvirt guest_input 구멍(호스트 sudo)이 둘 다 필요하다.
비밀은 어느 파일에도 값으로 들어 있지 않다. 비밀번호는 길이(19·22)만 적었고, 화면에
찍혀 나온 토큰은 <REDACTED n자> 로 가렸다.