2018/06/30(土)Certbotによるワイルドカード証明書と自動更新の設定

どこよりも簡単なワイルドカード証明書設定メモ。


2018年からLet's Encryptがワイルドカード証明書に対応しました。

これを利用するためにはdns-01という、DNSのTXTレコードを使った認証が必要で、このDNS認証手順を自動化する必要があります

DNSレジストラサービスを使用している場合を想定して、dns-01認証の方法をまとめました。

構成

  • Debian Stretch
  • Apache
  • BIND
  • 設定対象DNS : example.jp

DNSは外部サービスを使用しているものとします。外部サービスでも、CloudfireやAWS、Google Cloud DNSといった有名サービスを使っている場合、DNSサーバは不要です。検索すれば、それらの設定方法が出てきます。

サブドメイン権限の移譲

example.jpを管理しているDNSサーバに対し、_acme-challenge というサブドメインを設定し、bindを動かしている(動かす)サーバに権限を移譲します。

_acme-challenge	IN NS acme-ns.example.jp.
acme-ns		IN A  203.0.113.11

外部サービスの場合、書き方が異なる場合がありますが、IPを指定して権限を移譲してください。この設定により、別に用意したDNSサーバで、TXTレコードを適切に設定すれば、dns-01認証が通るようになります。

CNAMEで処理する方法もあるのですが、その場合、CNAMEしたドメインに対し _acme-challenge.alias.dom を認証するのでややこしくなります。*1

_acme-challenge	IN CNAME acme-c.example.jp.
	--> *.example.jp の認証: _acme-challenge.acme-c.example.jp.

*1 : 一度この方向で設定していたのですが、認証ドメインとTXTレコード設定ドメインが異なると後々わかりにくいので止めました。

ネームサーバを設定

bindをnsupdateによるDDNS構成(RFC2136)に対応させるため、BINDを設定します。入れてない場合はインストール。

apt install bind9 dnsutils

※dnsutilsはnsupdateに必要です。

/etc/bind/name.conf.local

# /etc/bind/name.conf.local
zone "_acme-challenge.example.jp" {
	type master;
	file "/etc/bind/master/_acme-challenge.example.jp";
	allow-update {
		127.0.0.1;
		::1;
	};
	check-names ignore;
};

ドメイン名に"_"(アンダースコア)を含むため「check-names ignore」は必須です。

/etc/bind/master/_acme-challenge.example.jp

# /etc/bind/master/_acme-challenge.example.jp
$TTL 60
@	IN SOA acme-ns.example.jp. root.example.jp. (
		1          ; serial
		86400      ; refresh (1 day)
		3600       ; retry (1 hour)
		86400      ; expire (1 day)
		60         ; minimum (1 minute)
	)
	NS	acme-ns.example.jp.

SOAレコードを以下のように書くと失敗します

@	IN SOA ns._acme-challenge.example.jp. root.example.jp. (

アンダースコアを含む名前を master-name に書くことはbindではご法度のようで、例え「check-names ignore;」してログにエラーなく正常に読み込まれたと出てもテストするとこのゾーンが動作しません。ハマります、ご注意ください。

bindの再起動(起動)

設定にエラーがないか確認してから、bindを再起動します。

cd /etc/bind
named-checkconf
named-checkzone _acme-challenge.example.jp master/_acme-challenge.example.jp
service bind9 restart

TXTレコードを更新できるか確認しておきます。「-l」を忘れずに。

nsupdate -l
> add _acme-challenge.example.jp 60 TXT "test"
> send
> quit
dig TXT _acme-challenge.example.jp

certbotの導入

apt install certbot

ワイルドカード証明書の自動アップデートを実現するため、2つほどスクリプトを導入します。内容をコピペしてください。

/etc/letsencrypt/renewal-hooks/dns-01-auth.sh

#!/bin/sh

# dns-01 is VALIDATION set, TOKEN not set
[ "$CERTBOT_VALIDATION" = "" ] && exit 0
[ "$CERTBOT_TOKEN"     != "" ] && exit 0

echo	"delete _acme-challenge.$CERTBOT_DOMAIN TXT\n"\
	"add    _acme-challenge.$CERTBOT_DOMAIN 10 IN TXT \"$CERTBOT_VALIDATION\"\n" | nsupdate -l

/etc/letsencrypt/renewal-hooks/dns-01-clean.sh

#!/bin/sh
[ "$CERTBOT_VALIDATION" = "" ] && exit 0
[ "$CERTBOT_TOKEN"     != "" ] && exit 0

echo "delete _acme-challenge.$CERTBOT_DOMAIN TXT\n" | nsupdate -l

2つとも実行属性を付けておきます。

cd /etc/letsencrypt/renewal-hooks
chmod +x dns-01-auth.sh
chmod +x dns-01-clean.sh

ワイルドカード証明書の取得

ここまでで、ワイルドカード証明書を取得する準備が整いました。あとは以下のコマンドを実行するだけです。

certbot certonly -d example.jp -d *.example.jp -m your@example.com \
  --preferred-challenges dns-01 \
  --server https://acme-v02.api.letsencrypt.org/directory \
  --manual \
  --manual-auth-hook /etc/letsencrypt/renewal-hooks/dns-01-auth.sh \
  --manual-cleanup-hook /etc/letsencrypt/renewal-hooks/dns-01-clean.sh

少し長いですが確実に入力してください。

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.jp/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.jp/privkey.pem
   Your cert will expire on 2018-00-00. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

こんな感じで表示されれば成功です。

今の設定は /etc/letsencrypt/renew/example.jp に保存されていますので、自動的に更新されます。

一応、更新のテストをしておきます。

certbot renew --force-renewal --dry-run

「--dry-run」を付けないと実際に更新されますので注意してください。

Apacheへの組み込み

証明書を設定します。nginxでも何でも構いませんが、Apacheの設定例。

<VirtualHost *:443>
        DocumentRoot    /var/www/example.jp
        ServerName      example.jp
        ServerAlias     *.example.jp

        SSLEngine               on
        SSLCertificateKeyFile   /etc/letsencrypt/live/example.jp/privkey.pem
        SSLCertificateFile      /etc/letsencrypt/live/example.jp/fullchain.pem
</VirtualHost>

Apacheを自動再起動させる 2020/08/03

/etc/letsencrypt/renewal-hooks/post 以下にスクリプトファイルを置くと、更新時に実行されます(非更新時には実行されません)。

deploy/ に置くと複数のドメインを更新した場合複数回実行されるので、post/ に置いてください。

/etc/letsencrypt/renewal-hooks/post/apache2

#!/bin/sh
service apache2 restart

※実行属性を忘れずに付けてね。

動作確認。

certbot renew --dry-run

その他

起動タイミングの変更

オリジナルファイルは /lib/systemd/system/certbot.timer にあるので内容をコピーして書き換えます。

systemctl edit certbot.timer

OnCalenderの設定はsystemd.timeのマニュアル参照。

cronについて

/etc/cron.d/certbot にもリニューアル設定が残っていますが、systemd環境ではこのファイルは使われていません。よって修正は不要です。

cron.d/certbot:0 */12 * * * root
 test -x /usr/bin/certbot -a \! -d /run/systemd/system 
 && perl -e 'sleep int(rand(3600))' && certbot -q renew

紛らわしていので削除しておきたいのですが、certbotを更新するたびにファイルを作成されるので、放置しておくのが良いでしょう……。

参考