certbot 또는 Nginx Proxy Manager 의 갱신이 실패하고 로그에 다음이 남는다.
Failed to renew certificate npm-7 with error: Some challenges have failed.
...
Detail: 203.0.113.10: Fetching http://example.com/.well-known/acme-challenge/<token>:
Timeout during connect (likely firewall problem)
이 메시지는 원인을 이미 다 말하고 있다.
type: http-01 : HTTP-01 방식으로 검증했다.addressesResolved / addressUsed : DNS 는 정상이며 CA 가 그 IP 로 접속을 시도했다.Timeout during connect : 그 IP 의 80 포트에 도달하지 못했다. 응답이 없다는 뜻이며, 방화벽 · 공유기 포트포워딩 · ISP 차단 순으로 의심한다.404 나 Invalid response 가 나왔다면 도달은 했고 챌린지 파일을 못 찾은 것이므로 웹 루트 · 프록시 경로 문제로 방향이 다르다. 두 경우를 구분해야 헛다리를 짚지 않는다.
Let's Encrypt 문서 기준으로 HTTP-01 은 다음을 요구한다.
즉 "443 만 열어 두고 80 은 막아 뒀다" 면 HTTP-01 은 성립하지 않는다.
# 도메인이 가리키는 주소
dig +short example.com
# 외부망(모바일 테더링 등)에서 80 도달 여부
curl -I --connect-timeout 5 http://example.com/
# 챌린지 경로가 실제로 서빙되는지
curl -i http://example.com/.well-known/acme-challenge/test
curl 이 타임아웃이면 경로 어딘가에서 패킷이 버려지는 것이다. 사내망 안에서만 테스트하면 공유기의 헤어핀 NAT 때문에 되는 것처럼 보일 수 있으므로 반드시 외부망에서 확인한다.
# RHEL 계열
firewall-cmd --permanent --add-service=http
firewall-cmd --reload
# Ubuntu
ufw allow 80/tcp
컨테이너로 운영한다면 포트 매핑도 본다.
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:2
ports:
- "80:80"
- "443:443"
- "81:81"
가정 · 소규모 회선은 ISP 가 80 을 막아 두는 경우가 있다. 이때는 HTTP-01 을 쓸 수 없다.
DNS-01 은 _acme-challenge.<도메인> 에 TXT 레코드를 넣어 검증하므로 인바운드 포트가 필요 없고 와일드카드 발급도 된다. 갱신을 자동화하려면 DNS 서비스의 API 플러그인을 쓴다.
certbot certonly --dns-route53 -d example.com -d '*.example.com'
수동 모드(--manual --preferred-challenges dns)는 갱신 때마다 사람이 TXT 를 넣어야 하므로 운영에는 맞지 않는다.
Let's Encrypt 는 실패한 검증에도 한도를 둔다. 같은 오류로 계속 재시도하면 일정 시간 동안 아예 요청이 거부되어 원인을 고친 뒤에도 발급이 막힌다.
--force-renewal 을 반복하지 않는다.certbot certonly --staging --webroot -w /var/www/html -d example.com
스테이징에서 발급된 인증서는 브라우저가 신뢰하지 않는다. 경로 검증용으로만 쓰고 실제 발급은 옵션을 뺀 뒤 다시 한다.
# 실제로 발급하지 않고 전체 절차만 돌려 본다
certbot renew --dry-run
# 등록된 인증서와 남은 기간
certbot certificates
인증서 유효기간은 90일이며 만료 30일 전부터 갱신이 이뤄지도록 타이머를 걸어 두는 것이 일반적이다. 갱신은 성공하는데 브라우저에 옛 인증서가 보인다면 갱신 후 웹 서버 재적재(--deploy-hook)가 빠진 것이다.