Files
document-haness/docs/keycloak-session-store/final/evidence/raw/relive-2026-09-17
DongHyeonkaandClaude Opus 5 32e39e20aa fix(setup): 실험대에서 35편을 끝까지 밟고 어긋난 명령·결과 31건을 고친다
test-server 를 비우고 다시 세운 뒤 Setup 기록 35편(virtualization 9 ·
keycloak-session-store 26)을 문서에 적힌 명령 그대로 쳤다. 어긋난 자리를
기록과 SSOT 양쪽에 실측과 함께 넣었다.

막히던 것
- 04 의 인증서 경로가 live/hyeonworks.com 이라 nginx 가 [emerg] 로 안 떴다.
  실제 계보는 live/auth.hyeonworks.com 이고 「문제가 생기면」은 진단이 거꾸로였다
- 인증서가 와일드카드가 아니다. SAN 이 auth·app1·app2 셋뿐이라 그 밖의 이름은
  TLS 에서 끊기고 curl 이 exit 60 · %{http_code} 000 을 낸다. SSOT 안에서
  두 문단이 서로 어긋나 있었다
- A-7 14번 ①이 kc-lab-1 에서 여섯 줄 다 실패하는데 마지막 date 만 「차단」을 찍는다

검사가 실패할 수 없던 자리
- B-1 의 세션 키 고르기는 앞 단계가 $KEY 를 채워 둬서 루프가 한 건도 못 맞혀도
  통과한다. KEY= 로 비우고 키마다 1/0 을 찍게 바꿨다
- k3s-agent 유닛의 sed -i 는 패턴에 $HOME 이 들어 있어 아무 줄도 안 바꾼 채 성공한다

certbot
- renew --dry-run 의 종료 코드는 성공도 0, 실패도 0, 다른 사유의 실패는 1 이다.
  본문의 renew failure(s) 로만 판정할 수 있다
- --dry-run 은 staging 서버를 쓰는데 renewal/*.conf 의 account= 는 운영 계정을
  가리킨다. 실패한 dry-run 이 staging 계정을 하나 더 만들어 다음 실행이 계속 멎는다
- 훅을 755 로 놓고 시뮬레이션이 성공해도 Running deploy-hook command 는 안 나온다.
  certbot 2.1.0 에는 --run-deploy-hooks 도 없다
- 강제 갱신은 실제로 쳤고 서빙까지 닿았다. serial 06F3E0EF…1373 → 065547…3DF1,
  notAfter Dec 3 → Dec 16, nginx worker 2629 4712 → 4745 4754

독자가 칠 수 있는 형태로
- 안 되는 형태가 번호 붙은 단계에 앉아 있던 8곳을 뒤집고, 되는 형태를 ①로 올렸다
- 랩 안에서 공개 이름을 치는 curl 65줄에 --resolve 를 붙였다. 붙인 형태를 실제로
  쳐서 문서가 적은 값과 같은지 확인했다
- 힙독·sed -i·echo >>·&&·|| 를 편집기 + 파일 리스팅 + 분할 형태로 바꿨다
- 닫는 코드펜스가 빠져 뒤 200여 줄의 블록 종류가 뒤집혀 있던 곳을 포함해 3곳을 고쳤다

관문: check_body PASS · check_prose error 0 · check_evidence 두 프로젝트 문제 없음 ·
verify-tech-log-tree error 0 · verify-project-layout error 0 · 코드펜스 전수 0건

남은 것: B-0 주입은 keycloak-pattern 저장소의 소스를 고치고 이미지를 다시 구워야
해서 안 했다(unknown).

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

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자> 로 가렸다.

--- 2026-09-17 추가 (인증서 대기 중에 잰 것) ---
ext-01..02              밖에서 http 로 닿는지, X-Forwarded-For 가 오는지
ext-03..05, ext-09,11   traefik-forwarded-headers.yaml 적용 전/후/되돌린 뒤 앱이 받은 헤더
ext-06..07              인증서 상태와 발급 전제 (플러그인 O, cloudflare.ini X)
ext-08                  http 만으로 어디까지 되는지 (well-known 200 · token 401)
ext-10, ext-12          출발지를 덮는 것이 무엇인지 — 호스트 sudo 가 막아 못 가림
ext-13                  BFF 로그인 체인이 https://auth 로 가서 막히는 것
b6-01..09               B-6 키 회전을 문서대로 밟은 기록. 키 조작은 재현, 401/200 판정은 못 냄
ext-14                  파드에서 공개 이름이 100.83.212.4 로 풀리고 80·443 이 닫혀 있는 것
ext-15                  CoreDNS 를 *.override 로 고치려다 CrashLoopBackOff 난 기록
ext-16                  *.server 로 고쳐 넣어 CoreDNS 가 뜬 기록
ext-17                  고친 뒤 파드에서 엣지 192.168.122.10:80 이 열리는 것
d4-01                   D-4 5·6·7 절. 7 절이 test-server 에서 빈손이고 nginx 는 엣지에 있다
d4a-01                  D-4a 5~8 절. 훅 작성·설치·실행과 워커 PID 가 갈리는 것
b7-01, b7-02            CoreDNS 를 고친 뒤 oauth2-proxy 가 두드리는 주소가
                        100.83.212.4 에서 192.168.122.10 으로 바뀐 것과, 빌린 Grafana Ingress 복구
misc-01                 오래 미검증이던 명령 넷 — cookie secret 길이 32·32, R 함수가 빈 출력,
                        certbot renew --dry-run 이 인증서 0장에서 내는 줄
b2-01                   --scan | xargs del 이 tr -d '\r' 없이도 도는 것과, od -c 로 본 줄 끝
b2-02                   b2 ②③④ 의 세션 수 — DB 5 와 관리 API 4 가 다른 것
b0-01..03               TLS 뒤 b0 상태 확인 — BFF 가 B-1 상태이고 /actuator/beans 가 밖에서 200
b1-01..05               B-1 을 끝까지 — 로그인, Redis 키 둘, head -1 이 엉뚱한 키를 집는 것
b6-10..18               B-6 을 끝까지 — 대조군 200, 추가는 무중단, 제거 뒤 401/200 이 번갈아 나오는 것
b7-03..08               B-7 을 끝까지 — 클라이언트가 없어 Client not found, 만든 뒤 회전까지
b7a-01                  회전 뒤 남은 고아 세션과 TTL 이 갱신 없이 줄어드는 것
d4a-07-dryrun-after-removing-duplicate-staging-account.txt
    kc-lab-edge 에서 `sudo certbot renew --dry-run`. staging ACME 계정이 둘이라
    "Please choose an account" 로 멎던 것을, 나중에 생긴 계정 하나를 지운 뒤 다시 잰 것.
    certbot exit=0 · "Congratulations, all simulated renewals succeeded".
    실패했을 때도 0 이 나온 적이 있어 종료 코드로는 성공과 실패가 안 갈린다는 근거.

--- 2026-09-17 저녁 추가 (A-6 관찰·복구를 마저 밟은 기록) ---
a6-05-observe-prep.txt  A-6 준비 1~5 — 배치와 파드 IP, a6-probe 파드, PW 길이 19,
                        구간별 시간 한 번, 두 노드 20회씩 기준선(k1 44ms · k0 40ms),
                        `grep "^agroal_"` 가 레이블과 값까지 열일곱 줄로 나오는 것
a6-06-observe.txt       A-6 주입 재현과 관찰 1~5 — flannel.1 세 줄, 카운터 0 pkt → 18 pkt,
                        주입 뒤 k1 1865ms · k0 40ms, 동시 20건 1.87→22.25 초 계단 스무 줄,
                        sort / sort -g 가 같고 sort -g -k2 만 듣는 것, 부하 직후 풀 지표
                        (blocking_time_max 20000.0 · max_used_count 18.0),
                        readiness 프로브 context deadline exceeded, 낙관적 락 로그 0
a6-07-revert-checklist.txt  A-6 복구와 확인표 여덟 줄 — flannel.1 noqueue, enp1s0 은
                        기본값 fq_codel, 회복 뒤 k1 42ms · k0 39ms, 엔드포인트 주소 둘,
                        awaiting/active 0, 탐침 NotFound, 밖에서 auth.hyeonworks.com 이
                        100.83.212.4 로 풀려 443 이 닫혀 000 이고 --resolve 로 짚으면 200

--- 2026-09-17 저녁 추가 (A-7 을 1번부터 복구까지 다시 밟은 기록) ---
a7-06-prep-and-switch.txt   A-7 1~11 — 전환 전 args ["start"], DB 143행, 탐침, 교차 refresh 200,
                            기능 목록이 두 줄, DELETE 144, patch 가 "patched" 를 내는 것,
                            전환 뒤 로그인 5회 200 과 DB (0 rows), 캐시 지표 96줄 원문
a7-07-observe.txt           A-7 12~15 — 교차 refresh 200, 롤링 재시작 뒤 400 Session not active,
                            14 ① 블록을 kc-lab-1 에서 문서 그대로 쳤을 때 kubectl 이 막히고
                            iptables 가 Bad argument 를 내며 ssh 가 Host key verification failed 이고
                            그래도 "차단" 시각이 찍히는 것, 규칙이 0개인 것, lab host 에서 다시 친
                            형태와 cluster_size 2→1(+75초)·해제 뒤 1→2(+60초), coord=t 둘,
                            대조군 200 · 시험군 400, DB 정지 뒤 500 / 200
a7-08-revert-checklist.txt  A-7 복구와 확인표 아홉 줄 — args ["start"], git diff 빈 출력,
                            로그인 뒤 세션 행 1, raw PREROUTING 비어 있음, cluster_size 양쪽 2,
                            탐침 NotFound, 밖은 000 이고 --resolve 로 짚으면 200

--- 2026-09-17 저녁 추가 (B-5 를 1번부터 복구까지 다시 밟은 기록) ---
b5-04-baseline.txt          B-5 준비 1~5 — 파드 배치, --scan 이 빈 출력(키 0), save 가
                            3600 1 300 100 60 10000 이고 appendonly yes, spec.volumes 에
                            kube-api-access-* 가 함께 있는 것, PVC 둘 Bound, 공개 이름으로는
                            세 경로가 000 이고 --resolve 로 짚으면 200/302/200, health 그룹 셋
b5-05-redis-down.txt        B-5 주입 1 과 관찰 — Redis 0/0, BFF 로그가 pollConnect 가 아니라
                            ConnectionWatchdog "Cannot reconnect ... Connection refused",
                            bff 둘 1/1 RESTARTS 0, 엔드포인트 true,true, 세 경로 200/000/000,
                            /actuator/health 는 --max-time 10 이면 000 이고 60.046452 초 뒤 503
                            (redis: QueryTimeoutException), token-boundary 는 90초까지 무응답
b5-06-persistence.txt       B-5 주입 2 와 지속성 시험 — 볼륨을 떼도 spec.volumes 가 빈 줄이
                            아니라 kube-api-access-* 하나, appendonlydir 생성, 파드 삭제 뒤
                            dbsize 0 인데 appendonly 는 yes(매니페스트 args 가 이미 yes),
                            PVC 를 되돌린 뒤 dbsize 4 와 written-on-pvc, 네 지표 시계열 0개
b5-07-revert-checklist.txt  B-5 복구와 확인표 여덟 줄 — apply 후 redis 1/1, PVC Bound,
                            appendonly yes, bff 둘 1/1, 엔드포인트 둘, b5:* 삭제 1건이고 남은 키는
                            bff:session:sessions 셋, 밖은 000 이고 --resolve 로 짚으면 200
d4a-09-dryrun-with-deploy-hook.txt
    /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh 를 755 로 놓은 뒤의
    `sudo certbot renew --dry-run`. 시뮬레이션은 성공(exit=0)했는데
    "Running deploy-hook command" 줄은 없다 — certbot 2.1.0 의 dry-run 은 deploy 훅을 건너뛴다.
d4a-10-dryrun-dns-propagation-flake.txt
    같은 명령이 바로 앞뒤로는 성공했는데 이 판만 실패한 것. 사유는 DNS 전파로,
    "During secondary validation: DNS problem: NXDOMAIN looking up TXT for
    _acme-challenge.auth.hyeonworks.com" · propagation-seconds 기본값 30. certbot exit=1.
d4a-11-deploy-hook-not-called-in-dry-run.txt
    위 판정의 근거 넷 — 훅 파일의 권한·크기, 성공한 출력에서 "deploy-hook" 을 센 값 0,
    certbot 2.1.0, `--help all` 에 --run-deploy-hooks 가 없다는 것(센 값 0).
c2-05-probe-reachability-relive.txt
    C-2 §5 의 임시 탐침 파드(curlimages/curl:8.11.1)를 2026-09-17 재구성 랩에서 다시 띄운 것.
    app1.hyeonworks.com 이 192.168.122.10(엣지 게스트)으로 풀리고 HTTPS 200.
    원래 실행의 100.83.212.4(tailnet 헤어핀)와 주소가 다르다 — 지금은 CoreDNS 의
    coredns-custom 항목이 그 일을 한다. networkpolicy 는 여전히 없다.
d4-01-before-force-renewal.txt / d4-02-force-renewal.txt / d4-03-after-force-renewal.txt
    D-4·D-4a 의 강제 갱신을 2026-09-17 에 kc-lab-edge 에서 실제로 친 것.
    01 = 치기 전 archive 목록·nginx worker PID·서빙 인증서의 serial/날짜
    02 = `sudo certbot renew --force-renewal` 전문. 배포 훅이 돌았고 certbot exit=0
    03 = 친 뒤의 같은 세 가지 + 세 이름의 HTTPS 응답.
    serial 06F3E0EF…1373 → 065547991777…3DF1, notAfter Dec 3 → Dec 16,
    worker 2629 4712 → 4745 4754. 갱신이 서빙까지 닿았다는 근거.