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