Files
keycloak-pattern/docs/evidence/d4-certificate-renewal
DongHyeonkaandClaude Opus 5 0d84921a56 docs(d4): the renewal unit has no reload, and nginx has not reloaded in 22 hours
The forced renewal did not change the certificate being served — 161 serial
samples over 13 minutes, all identical, notBefore still Sep 3.

Two things were readable without sudo and both matter.

nginx has never been reloaded: master 585 and worker 586 share a start time
and both are 80529 seconds old. A reload keeps the master and replaces the
workers, so the original fork still being there means no reload has happened
since Sep 3 19:00. nginx reads the certificate at startup and holds it in
memory; without a reload it serves the old one no matter what is on disk.

And the unit that is supposed to renew has nothing that would reload it:

  [Service]
  Type=oneshot
  ExecStart=/usr/bin/certbot -q renew
  PrivateTmp=true

No ExecStartPost, no --deploy-hook. The timer runs twice a day and exited
0/SUCCESS both times today, which is precisely why this stays invisible —
the renewal is not due for 88 days, so nothing has failed yet.

What remains undetermined is whether the user's --force-renewal failed, or
succeeded and left new files that nginx never picked up. Telling those apart
needs /etc/letsencrypt, which is root-only. If it is the latter, that is the
most valuable thing this lab has produced: "renewal succeeded" and "the new
certificate is being served" are different events.

CT logs looked like a way around the permission problem and were not: the
served certificate carries two valid SCTs, yet crt.sh returns zero rows for
auth.hyeonworks.com. Same shape as A-2's `up` metric — the observing tool
sees a subset of the truth.

Also recorded: 76 in-flight failures at 08:15:04 are a local artifact, not a
server event. The concurrent 0.2s poll was clean across the same second,
num_connects was 0, and time_total was 50 microseconds. Not reproducible in
100 retries. The monitor now records curl's exit code so a recurrence is
diagnosable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 17:28:01 +09:00
..

D-4 — 인증서 갱신 증거

2026-09-04 17:2517:35 KST 해설: docs/experiment-d4-certificate-renewal.md

파일 무엇을 보여주는가
01-certificate-state.txt SAN 3개(와일드카드 아님) · 체인 4단계, Verify return code: 0 · certbot-renew.timer enabled·active, 11시간 전 실행 · 88일 남음 · sudo: a password is required 로 강제 갱신 불가
05-control-no-injection.txt 대조군 1 — 잡음 바닥. 0.2초 × 900회 / 180초 동안 900전부 200, 오류 0. 중앙 98ms · p95 195ms. TLS 핸드셰이크 900/900 = 매 요청이 새 연결이다
06-inflight-control.txt 대조군 2 — '진행 중이던 요청' 측정 장치. 845KB 번들을 --limit-rate 20k 로 받아 요청을 42초간 살려 둔다. 무주입 시 코드 200 · 845361바이트 · 연결수 1

핵심 세 줄

  1. 인증서가 이름 3개만 담는다. B-7 에서 oauth2-proxy 를 올릴 호스트가 없어 Grafana 의 app2 를 빌려야 했던 실제 비용이 여기서 나왔다.
  2. 체인이 완전하다 — 단계가 4개이므로 fullchain.pem 을 쓰고 있다. 1개면 cert.pem 실수이며 캐시 없는 클라이언트에서만 깨진다.
  3. 강제 갱신은 아직 못 했다. 호스트 sudo 가 비밀번호를 요구한다. 타이머가 active 라는 것은 "갱신이 된다"의 확인이 아니다.
  4. 주입 전에 대조군을 먼저 잡았다. 평시 실패가 0/900 이므로, 갱신 중 비200 이 한 번이라도 나오면 갱신 탓으로 귀속할 수 있다. 대조군 없이 주입부터 했다면 그 한 번을 해석할 수 없었다 — A-6 에서 −41% 를 "영향 없음"이라 적었던 실수가 바로 그것이다.
  5. 폴링만으로는 D-4 의 질문에 답할 수 없다. 핸드셰이크 900/900 이 말해주듯 매 요청이 새 연결이라, 재는 것은 "새 연결을 받아주는가" 뿐이다. 계획서가 물은 "진행 중이던 요청"은 42초짜리 in-flight 장치로 따로 잡는다.