Certbot 와일드카드 인증서 DNS-01 발급 방법
Let's Encrypt에서 Wildcard certificate를 발급받으려면 DNS-01 challenge가 필수입니다. TXT 레코드를 이용한 검증 원리와 자동 갱신을 위한 DNS plugin 설치 및 API 활용법을 상세히 설명합니다.
Wildcard certificate에 DNS-01이 필요한 이유
Wildcard certificate는 도메인의 모든 1단계 서브도메인을 보호합니다. *.example.com은 app.example.com, blog.example.com 및 기타 모든 1단계 레이블 이름을 포함합니다. Let's Encrypt는 DNS-01 challenge를 통해서만 wildcard certificate를 발급합니다. 따라서 Certbot은 _acme-challenge.example.com에 TXT 레코드를 게시하여 도메인 DNS에 대한 제어권을 증명해야 합니다. HTTP-01 challenge는 적합하지 않습니다. 토큰 파일을 제공하는 방식은 검증 서버가 파일을 가져온 특정 hostname에 대한 제어권만 증명하기 때문입니다. Wildcard는 도메인 아래의 모든 가능한 이름에 대한 권한을 주장하는 것이며, 전체 네임스페이스를 대변할 수 있는 유일한 공개 레코드는 DNS뿐입니다.
이 요구사항이 이 페이지의 다른 모든 내용을 결정합니다. DNS-01을 통과하려면 수동 방식 또는 DNS 제공업체의 API(application programming interface)를 통해 도메인 존에 TXT 레코드를 생성할 수 있어야 합니다. 수동 방식은 한 번은 작동하지만, 아래에 설명된 이유로 인해 갱신 시 실패합니다. Certbot DNS plugin을 사용하는 API 방식은 자동 갱신이 가능하며, 이것이 권장되는 설정입니다.
이 내용은 Certbot 가이드의 wildcard 장입니다. 일반적인 단일 hostname certificate, web server 설정 및 port 80 규칙은 Certbot with nginx on Ubuntu 24.04 및 Certbot with Apache on Ubuntu 24.04에서 다룹니다.
_acme-challenge TXT 레코드 작동 방식
Certbot이 *.example.com를 요청하면, Let's Encrypt는 무작위 토큰으로 응답합니다. Certbot은 해당 토큰을 ACME(automatic certificate management environment) 계정 키와 결합합니다. 그 다음 SHA-256으로 해시하여 짧은 텍스트 값을 생성합니다. 이 값은 _acme-challenge.example.com에 TXT 레코드로 등록되어야 합니다. Let's Encrypt는 자체 인프라에서 해당 도메인의 권한 있는 네임 서버(authoritative name servers)에 질의를 보냅니다. 읽어온 레코드가 예상한 값과 일치하면, 사용자가 해당 zone을 제어하고 있음이 증명됩니다. zone에 대한 제어권은 그 하위의 모든 이름에 대한 제어권으로 인정됩니다.
대부분의 실패는 다음 두 가지 원인으로 발생합니다:
- 동일한 인증서에 대해
example.com와*.example.com을 요청하면 두 개의 별도 챌린지가 생성됩니다. 두 TXT 레코드는 모두 동일한 이름인_acme-challenge.example.com에 위치해야 합니다. 두 레코드가 동시에 존재해야 합니다. 두 번째 레코드를 추가하는 방식은 올바르지만, 첫 번째 레코드를 두 번째 레코드로 교체하면 첫 번째 챌린지가 실패합니다. - 검증 과정은 권한 있는 서버를 읽어오지만, 서비스 제공업체의 제어 패널에서 새 레코드를 서버로 전파하는 데 1분 이상의 시간이 걸릴 수 있습니다. 검증을 실행하기 전에 외부에서 미리 확인하십시오:
dig +short TXT _acme-challenge.example.com @1.1.1.1명령어 결과에 Certbot이 요청한 값이 출력되면 검증을 진행할 수 있습니다. 아무것도 출력되지 않으면, 잠시 기다린 후 다시 실행하십시오.
한 번 직접 실행해 보기: manual mode
manual mode를 사용하면 DNS 수정을 직접 수행해야 합니다. 이는 자동화하기 전에 작동 원리를 이해하는 가장 좋은 방법입니다.
sudo certbot certonly --manual --preferred-challenges dns -d example.com -d '*.example.com'와일드카드 주변의 따옴표는 shell이 *를 파일명 패턴으로 처리하는 것을 방지합니다. Certbot이 지침과 함께 일시 중지됩니다.
Please deploy a DNS TXT record under the name:
_acme-challenge.example.com.
with the following value:
Jx9mQ2wLr8vTn5cKp0aYdG3hB7fZs4eN1oiRuXqMk6EDNS 제공업체의 패널에서 해당 TXT 레코드를 생성하십시오. 위에서 언급한 dig 명령어로 레코드가 보이는지 확인한 후 Enter를 누르십시오. 이 실행은 bare domain과 wildcard를 모두 요청하므로 Certbot이 두 번 프롬프트를 표시합니다. 인증이 완료될 때까지 두 레코드를 모두 유지하십시오. 성공하면 다음과 같은 메시지가 표시됩니다.
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pemmanual mode가 스스로 갱신할 수 없는 이유
모든 갱신은 새로운 token을 사용하는 새로운 작업입니다. 따라서 TXT 값은 매번 변경됩니다. 오늘 붙여넣은 레코드는 60일 후에는 사용할 수 없습니다. Certbot의 갱신 타이머는 하루에 두 번 자동으로 실행됩니다. 이때 새로운 값을 붙여넣을 사용자가 없으므로, 수동으로 발급된 인증서는 다음과 같은 오류와 함께 갱신에 실패합니다:
Failed to renew certificate example.com with error: The manual plugin is not
working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with
--manual-auth-hook when using the manual plugin non-interactively.')DNS provider의 API를 호출하는 --manual-auth-hook 스크립트를 작성하여 이 요구 사항을 충족할 수 있습니다. 하지만 이 방식은 DNS plugin을 직접 다시 만드는 것과 같습니다. manual mode는 작업 흐름을 학습하거나, DNS 자동화가 불가능한 도메인에서 일회성으로 사용하십시오. Let's Encrypt는 더 이상 만료 이메일을 보내지 않으므로, 90일이 되기 훨씬 전에 미리 알림을 설정해야 합니다. 그 외의 모든 경우에는 plugin을 사용하십시오.
The plugin route: certbot-dns-cloudflare on Ubuntu 24.04
DNS plugin은 DNS 제공업체의 API 자격 증명을 보유하며, 인증서 발급 및 갱신 시 TXT 레코드 작업을 자동으로 수행합니다. Cloudflare는 가장 많이 사용되는 제공업체 플러그인이며 Ubuntu 패키지에 포함되어 있으므로 예시로 사용합니다.
Certbot 가이드는 Ubuntu 24.04에서 apt 패키지 사용을 권장하며, Cloudflare의 경우도 동일합니다:
sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflare버전에 관한 참고 사항입니다. 24.04 아카이브는 Certbot 2.9.0과 함께 이 플러그인을 version 2.0.0으로 제공합니다. apt policy python3-certbot-dns-cloudflare에서 현재 버전을 확인할 수 있습니다. 두 버전이 일치하지 않아도 문제는 없습니다. 24.04의 기반 python3-cloudflare 라이브러리 버전은 2.11.1로, 플러그인이 토큰 지원을 위해 필요로 하는 2.3.1보다 높기 때문에 scoped API token이 정상 작동합니다. 이전 Ubuntu 버전의 라이브러리는 토큰을 지원하기에 너무 오래되었습니다. 온라인에서 apt 플러그인이 Global API Key 사용을 강제한다는 경고를 볼 수 있는 이유는 이 때문입니다. 24.04에서는 해당 문제가 발생하지 않습니다.
Cloudflare 대시보드에서 Global API Key 대신 scoped API token을 생성하십시오. My Profile, API Tokens, Create Token 순으로 이동한 뒤, Zone / DNS / Edit 권한 하나만 부여하고 인증서를 발급할 특정 zone으로 제한하십시오. 생성한 토큰은 root만 읽을 수 있는 파일에 저장해야 합니다:
sudo mkdir -p /root/.secrets
sudo tee /root/.secrets/cloudflare.ini > /dev/null <<'EOF'
dns_cloudflare_api_token = paste_your_scoped_token_here
EOF
sudo chmod 600 /root/.secrets/cloudflare.iniCertbot은 파일 권한을 확인하며, 다른 사용자가 읽을 수 있는 경우 Unsafe permissions on credentials configuration file에 대해 경고를 표시합니다. 이제 다음 명령어를 실행하십시오:
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'플러그인은 API를 통해 TXT 레코드를 생성하고, 짧은 전파 대기 시간을 가진 뒤, 검증을 수행하고, 다시 레코드를 삭제합니다. 만약 zone의 name server가 변경 사항을 반영하는 속도가 느리다면, --dns-cloudflare-propagation-seconds 60를 사용하여 대기 시간을 늘리십시오. 인증서는 /etc/letsencrypt/live/example.com/에 저장됩니다. nginx 또는 Apache의 설정에서 base 가이드에 명시된 대로 fullchain.pem 및 privkey.pem를 지정하고 deploy hook를 포함하십시오.
provider의 plugin이 apt에 없는 경우
24.04 archive는 Cloudflare, Route 53, DigitalOcean 및 generic RFC 2136 interface를 포함한 소수의 provider용 plugin만 패키지로 제공합니다. apt search certbot-dns를 실행하여 목록을 확인하십시오. provider가 목록에 없다면, apt를 우선적으로 권장하는 기존 지침과 달리 다음 절차를 따릅니다. Certbot와 plugin을 snap으로 설치하십시오. 이때 두 개의 renewal timer가 /etc/letsencrypt에 대해 충돌하지 않도록 기존 apt Certbot를 먼저 제거해야 합니다.
sudo apt remove certbot python3-certbot-dns-cloudflare
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-yourprovidersnap plugin은 snap Certbot에만 연결됩니다. apt 버전의 기능을 확장할 수 없으므로 두 설치본이 공존해서는 안 됩니다. 만약 DNS host가 API를 제공하지 않는다면, 도메인의 DNS를 API를 지원하는 provider로 이전하거나, 자체 name server를 운영하며 rfc2136 plugin이 이를 가리키도록 설정해야 합니다.
갱신: 60일 후가 아닌 지금 즉시 확인하십시오
Certbot은 authenticator = dns-cloudflare 및 자격 증명 경로를 포함하여 각 인증서가 어떻게 발급되었는지 /etc/letsencrypt/renewal/example.com.conf에 기록합니다. 따라서 표준 2회/일 타이머가 사용자 개입 없이 인증서를 갱신합니다. staging environment를 사용하여 전체 과정을 연습하십시오:
sudo certbot renew --dry-run성공하면 자격 증명이 유효하며 검증이 처음부터 끝까지 완료됨을 의미합니다. 60일 후의 실제 갱신도 동일한 경로를 따릅니다. 오늘 수행해야 할 두 가지 후속 작업이 있습니다. 첫째, 디스크에 갱신된 인증서가 저장되어도 web server가 이를 다시 로드하기 전까지는 아무런 변화가 없습니다. 따라서 nginx 및 Apache 가이드에 설명된 deploy hook를 설정하십시오. 둘째, 자격 증명 파일을 엄격하게 관리하십시오. 해당 파일을 읽을 수 있는 사람은 DNS zone을 수정할 수 있으며, 이는 이메일을 리다이렉트하거나 자체적인 DNS-01 challenge를 통과하기에 충분한 권한입니다. /root 아래에서 mode 600으로 유지하고, token의 범위를 하나의 zone으로 제한하십시오. 유출이 의심되면 즉시 교체하십시오.
와일드카드가 필요하지 않은 경우
와일드카드는 다수의 서브도메인을 관리하거나 예측할 수 없는 서브도메인을 관리할 때 적합한 도구입니다. 그 외의 모든 경우에는 와일드카드를 기본값으로 사용하는 것이 적절하지 않습니다.
- 하나의 서브도메인 또는 소수의 알려진 서브도메인인 경우: 일반적인 SAN (subject alternative name) 인증서가 더 간단합니다.
certbot --nginx -d example.com -d www.example.com -d app.example.com는 plain HTTP-01을 통해 최대 100개의 이름을 지원하며, 서버에 DNS API 자격 증명을 저장할 필요가 없습니다. - 와일드카드는 정확히 하나의 레이블과 일치합니다.
*.example.com는 bareexample.com를 포함하지 않으므로 위 명령어들은 두 가지를 모두 요청해야 합니다. 또한a.b.example.com도 포함하지 않으며, 이를 위해서는*.b.example.com가 필요합니다. - 모든 서브도메인에는 각각의 개인 키가 존재합니다. 개인 키를 보유한 장비가 침해되면 와일드카드가 포함하는 모든 이름이 동시에 영향을 받습니다.
- Traefik이 컨테이너의 TLS (transport layer security)를 종료하는 경우, Certbot을 사용할 필요가 전혀 없습니다. Traefik이 DNS-01을 통해 직접 와일드카드 인증서를 요청하며, 이때 동일한 종류의 provider token을 사용합니다.
와일드카드가 실제로 유용한 경우: 인증서를 재발급하는 속도보다 빠르게 생성되는 고객별 또는 앱별 서브도메인, 그리고 WireGuard VPN을 통해서만 접속 가능한 서비스와 같이 public port 80이 없는 내부 호스트입니다. DNS-01은 인증 대상 호스트에 연결되지 않으므로, 완전히 폐쇄된 장비도 public trust 인증서를 보유할 수 있습니다.
FAQ
HTTP-01 방식으로 Certbot에서 wildcard certificate를 발급받을 수 있습니까?
아니요. HTTP-01은 특정 hostname에 대한 제어권을 증명합니다. 검증 서버가 해당 이름에서 정확히 토큰 파일을 가져오기 때문입니다. wildcard는 도메인 아래의 모든 이름을 포함하므로, Let's Encrypt는 이를 위해 DNS-01 challenge를 요구합니다. --nginx, --apache, --webroot 및 --standalone 인증기는 모두 HTTP 기반입니다. 유일한 방법은 _acme-challenge.example.com에 TXT 레코드를 수동으로 또는 DNS plugin을 사용하여 배치하는 것입니다.
wildcard certificate가 root domain을 포함합니까?
아니요. wildcard는 정확히 하나의 label과 일치합니다. 따라서 *.example.com는 www.example.com을(를) 포함하지만, bare example.com 또는 a.b.example.com은(는) 포함하지 않습니다. -d example.com -d '*.example.com'를 사용하여 하나의 certificate에 두 이름을 모두 요청하십시오. 이렇게 하면 두 개의 challenge가 생성됩니다. 두 TXT 레코드는 동일한 _acme-challenge.example.com 이름에 위치하므로, 첫 번째 레코드를 삭제하지 않고 두 번째 레코드를 추가해야 합니다.
왜 wildcard certificate가 자동으로 갱신되지 않습니까?
--manual를 사용하여 발급받았기 때문입니다. 매 갱신마다 새로운 TXT 값이 필요합니다. unattended timer는 이 값을 입력할 수 없으므로, 갱신이 An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively 오류와 함께 중단됩니다. certbot-dns-cloudflare와 같은 DNS plugin을 사용하여 certificate를 재발급하거나, provider의 API를 통해 레코드를 수정하는 --manual-auth-hook 및 --manual-cleanup-hook 스크립트를 제공하십시오.
_acme-challenge TXT 레코드가 나타나기까지 얼마나 걸립니까?
DNS provider에 따라 다릅니다. 수 초에서 수 분까지 걸릴 수 있습니다. 검증 과정은 zone의 authoritative servers를 읽습니다. 따라서 dig +short TXT _acme-challenge.example.com @1.1.1.1으로 확인하고, 수동 실행을 계속하기 전에 예상 값이 나타날 때까지 기다리십시오. plugin을 사용하는 경우, 검증 시 레코드를 찾을 수 없다는 보고가 나오면 plugin의 propagation option(예: --dns-cloudflare-propagation-seconds 60)을 통해 내장된 대기 시간을 늘리십시오.
wildcard certificate가 일반 certificate보다 보안에 취약합니까?
암호화 방식은 동일합니다. 차이점은 운영 방식에 있습니다. 하나의 private key가 모든 subdomain을 커버하므로, 키가 유출될 경우 피해 범위가 더 넓습니다. 또한 자동화를 위해 필요한 DNS API 자격 증명 자체가 서버에 저장되는 민감한 정보입니다. 알려진 소수의 subdomain만 운영하는 경우, SAN certificate를 사용하면 두 가지 문제를 모두 피할 수 있습니다. 이 가이드가 wildcard 사용을 건너뛰라고 권장하는 이유입니다.