LabHub
学习 学习路径 课程

证书已经续期,浏览器却还显示旧证书

一行重定向挡住了续期

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

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.dnsNameswww.lab.internal, spec.durationspec.renewBefore(시간 단위 h), spec.issuerRef.kind: ClusterIssuer.
  8. /root/acme/08-report.md 에 사고를 정리하세요. HTTP-01, 증거로 본 301, 예외로 둔 경로 .well-known/acme-challenge, 그리고 renewBefore 가 왜 사고를 늦게 드러내는지가 들어가야 합니다.

참고

키 인가 파일 두기

/root/acme/challenges/zk3r9QmZ7Vf1Yb2Ld8Ns4Hj6Xc5Pa0Wt 에 키 인가 문자열(토큰.지문)을 넣으세요.

RFC 8555 의 키 인가는 token, 마침표, 계정 키 지문을 이은 문자열입니다. 파일 이름은 토큰 그대로, 내용은 그 한 줄입니다. 지문은 지시문에 주어져 있습니다.

엣지 띄우고 챌린지 받기

/root/acme/edge.json(리다이렉트 off, 챌린지 경로 예외)을 만들어 엣지를 띄우고, 챌린지 URL 의 본문을 /root/acme/02-fetch.txt 에 저장하세요.

JSON 키 이름은 지시문 그대로입니다. 엣지는 nohup 으로 뒤에 두고 로그는 /root/acme/edge.log 로 보내세요. curl -s 로 받은 본문이 1단계의 키 인가와 같아야 합니다.

검증 서버에게 확인받기

acme_va.py 를 돌려 출력을 /root/acme/03-valid.txt 에 저장하세요. 첫 줄이 VALID 여야 합니다.

--url 은 챌린지 URL, --expect 는 키 인가 문자열입니다. 출력 첫 줄이 판정이고 그 아래 따라간 경로가 한 줄씩 나옵니다.

리다이렉트가 챌린지를 먹는다

edge.json 을 리다이렉트 on·예외 없음으로 바꾸고, curl -sI 출력을 /root/acme/04-curl.txt 에, 검증 서버 출력을 /root/acme/04-broken.txt 에 저장하세요.

python3 한 줄로 JSON 을 고치면 실수가 적습니다. curl -sI 의 첫 줄에 301, Location 헤더에 https 가 보이고, 검증 서버는 301 → 404 를 따라간 끝에 INVALID 를 냅니다.

챌린지 경로만 예외로

리다이렉트는 켜 둔 채 exempt_paths 에 챌린지 경로를 넣고, 검증 출력을 /root/acme/05-fixed.txt 에, curl -sI http://127.0.0.1:8080//root/acme/05-app.txt 에 저장하세요.

고치는 것은 예외 목록 한 줄입니다. 앱 경로(/)는 여전히 301 로 HTTPS 에 가야 하고, 챌린지 경로만 HTTP 에서 바로 200 을 줘야 합니다.

갱신이 막혔는지 보는 한 줄

/root/acme/probe.sh <http://host:port> 를 만들고(3xx 면 REDIRECT·1, 아니면 OK·0) http://127.0.0.1:8080 의 출력을 /root/acme/06-probe.txt 에 저장하세요.

curl -s -o /dev/null -w '%{http_code}' 로 상태 코드만 받아 case 로 가르면 됩니다. 404 는 OK 입니다 — 파일이 없는 것은 갱신을 막지 않고, 리다이렉트만 막습니다. 채점기는 301 서버와 404 서버를 하나씩 띄워 돌려 봅니다.

cert-manager 로 선언하기

/root/acme/certificate.yaml 에 Certificate(cert-manager.io/v1, secretName, dnsNames 에 www.lab.internal, duration·renewBefore 는 h 단위, issuerRef.kind ClusterIssuer)를 쓰세요.

문서의 예시 구조를 따르되 값은 이 실습의 것으로. duration 은 renewBefore 보다 커야 하고 둘 다 Go 시간 문자열(h·m·s)입니다. 다른 네임스페이스에서도 쓰는 발급자는 ClusterIssuer 입니다.

사고 보고서

/root/acme/08-report.md 에 HTTP-01 이 막힌 경위, 증거(301), 예외로 둔 경로, renewBefore 가 사고를 늦게 드러내는 이유를 적으세요.

4단계의 curl 결과가 증거이고 5단계의 예외 목록이 고침입니다. 발급 직후에는 증상이 없고 renewBefore 시점에야 챌린지가 다시 도는 것이 '늦게 드러나는' 이유입니다.