인증서는 갱신됐는데 브라우저는 옛것을 보여줬다 · 리다이렉트 한 줄이 갱신을 막았다 · 실습
리다이렉트 한 줄이 갱신을 막았다
목표
HTTP-01 챌린지를 파드 안에서 통과시키고, HTTP→HTTPS 리다이렉트가 챌린지를 먹어 갱신이 막히는 사고를 재현한 뒤, 챌린지 경로만 예외로 두어 고칩니다. 상시 확인용 probe.sh 와 cert-manager Certificate 매니페스트를 작성합니다.
왜 중요한가
ACME 자동 갱신은 첫 발급이 성공하면 그 뒤 두 달 동안 아무 증상이 없습니다. 그 사이에 넣은 리다이렉트 한 줄이 갱신 시점에 챌린지를 막고, 그때는 아무도 그 변경을 떠올리지 못합니다. 홈랩에서 실제로 겪은 사고이고, 재현하면 한눈에 보입니다. 재료는 둘입니다 — /opt/app/edge.py 는 설정 파일(/root/acme/edge.json)을 요청마다 다시 읽는 엣지(HTTP 8080·HTTPS 8443)이고, /opt/app/acme_va.py 는 리다이렉트를 따라가며 본문을 키 인가와 비교하는 검증 서버입니다.
단계
1. 토큰 zk3r9QmZ7Vf1Yb2Ld8Ns4Hj6Xc5Pa0Wt, 계정 키 지문 Q7pVw2xK9mN4rT1sB6yL0cH3fJ8dG5eA 입니다. /root/acme/challenges/zk3r9QmZ7Vf1Yb2Ld8Ns4Hj6Xc5Pa0Wt 파일에 키 인가 문자열을 넣으세요.
2. /root/acme/edge.json 을 만드세요 — http_port 8080, https_port 8443, redirect_to_https false, exempt_paths 에 /.well-known/acme-challenge/, challenge_dir /root/acme/challenges. python3 /opt/app/edge.py --config /root/acme/edge.json 을 띄우고 curl -s http://127.0.0.1:8080/.well-known/acme-challenge/<토큰> 의 본문을 /root/acme/02-fetch.txt 에 저장하세요.
3. python3 /opt/app/acme_va.py --url http://127.0.0.1:8080/.well-known/acme-challenge/<토큰> --expect <키인가> 의 출력을 /root/acme/03-valid.txt 에 저장하세요. 첫 줄이 VALID 여야 합니다.
4. 사고 재현: edge.json 에서 redirect_to_https 를 true 로, exempt_paths 를 빈 목록으로 바꾸세요. curl -sI http://127.0.0.1:8080/.well-known/acme-challenge/x 의 출력을 /root/acme/04-curl.txt 에, 검증 서버의 출력을 /root/acme/04-broken.txt 에 저장하세요(INVALID 여야 합니다).
5. 고치기: 리다이렉트는 켜 둔 채 exempt_paths 에 /.well-known/acme-challenge/ 를 다시 넣으세요. 검증 서버 출력을 /root/acme/05-fixed.txt 에(VALID), curl -sI http://127.0.0.1:8080/ 을 /root/acme/05-app.txt 에(여전히 301) 저장하세요.
6. /root/acme/probe.sh <http://host:port> 를 만드세요. <base>/.well-known/acme-challenge/probe-check 의 상태 코드가 3xx 면 REDIRECT 를 출력하고 1, 그 밖(200·404 등)이면 OK 를 출력하고 0 으로 끝나야 합니다. http://127.0.0.1:8080 에 대한 출력을 /root/acme/06-probe.txt 에 저장하세요.
7. /root/acme/certificate.yaml 에 cert-manager Certificate 를 쓰세요 — apiVersion: cert-manager.io/v1, spec.secretName, spec.dnsNames 에 www.lab.internal, spec.duration 과 spec.renewBefore(시간 단위 h), spec.issuerRef.kind: ClusterIssuer.
8. /root/acme/08-report.md 에 사고를 정리하세요. HTTP-01, 증거로 본 301, 예외로 둔 경로 .well-known/acme-challenge, 그리고 renewBefore 가 왜 사고를 늦게 드러내는지가 들어가야 합니다.
참고
- edge.json 은 요청마다 다시 읽히므로 고친 뒤 엣지를 재기동할 필요가 없습니다.
- 검증 서버는 실패해도 출력을 냅니다.
> 파일로 저장하면 종료 코드와 무관하게 남습니다. curl -sI는 HEAD 요청으로 상태줄과 헤더만 보여 줍니다.Location:헤더가 어디로 가는지 보세요.- 흔한 실수: 4단계에서
exempt_paths를 비우지 않으면 사고가 재현되지 않습니다. 7단계에서renewBefore: 15d처럼d를 쓰면 cert-manager 는 받아 주지 않습니다.
단계 8개
- 키 인가 파일 두기
- 엣지 띄우고 챌린지 받기
- 검증 서버에게 확인받기
- 리다이렉트가 챌린지를 먹는다
- 챌린지 경로만 예외로
- 갱신이 막혔는지 보는 한 줄
- cert-manager 로 선언하기
- 사고 보고서