シナプス技術者ブログ

シナプスの技術者公式ブログ。インターネットで、鹿児島の毎日を笑顔にします。

Let’s EncryptのDNS認証を自動化する

こんにちは、サービスシステム運用課の上曽山です。
Let's Encryptのサーバー証明書は、無料であることの他に発行/更新のプロセスが自動化されていることで知られています。ワイルドカード証明書も用意されており便利に使えます。

このワイルドカード証明書ですが、Let's Encryptではドメイン所有権認証をDNSで行う方法でしか発行出来ません。
そして、DNS認証で証明書を発行するためには、DNSサーバーに指定されたTXTレコードを設定する必要があり、完全自動化するにはひと工夫必要です。
今回はcertbotのプラグインを使ってDNS認証を自動化する方法を確認してみます。

検証環境

今回の検証は以下の環境で行っています。
DNSサーバーとWEBサーバーは最低限の設定が終わっている前提で話を進めますのでご了承ください。

用途 名前 バージョン
OS Rocky Linux 9.6
DNSサーバー Bind 9.16.23
WEBサーバー Httpd 2.4.62
ACMEクライアント Certbot 3.1.0


DNS認証を自動化するために

最初に書いた通りLet's EncryptのDNS認証は、DNSサーバーに指定されたTXTレコードを設定して認証を行います。
DNSプロバイダーによっては設定用のAPIが用意されている場合もありますし、がんばれば自作スクリプトで処理することも出来ますが、今回利用するACMEクライアントのcertbotには便利なプラグインが用意されているので、これを使って自動化します。

利用するプラグインはdns-rfc2136
RFC2136に準拠したダイナミックDNSと連携させるためのプラグインになります。

certbot-dns-rfc2136.readthedocs.io

ダイナミックDNSの設定

certbotに触れる前にダイナミックDNSの設定を行います。
本記事内ではexample.jpドメインを使った例を記載します。

  • 認証用サブドメインの委任

ACMEでDNS認証に使うサブドメインは_acme-challenge.<ドメイン名>です。
ダイナミックDNS(ddns.example.jp)と権威DNS(ns.example.jp)は別々のサーバーであるとします。

ACMEからの認証リクエストをダイナミックDNSに委任するため、ゾーンファイルにNSレコードを追加します。
また、example.jpのすべてのサブドメインが同じIPアドレスにアクセスするよう設定します。

example.jpのゾーンファイル

$ORIGIN example.jp.
$TTL 3600       ; 1 hour
@       IN  SOA ns.example.jp.  admin.example.jp. (
                    1970010101  ; serial
                    3600        ; refresh (1 hour)
                    900         ; retry (15 minutes)
                    3600000     ; expire (5 weeks 6 days 16 hours)
                    3600        ; minimum (1 hour)
                                )
                IN  NS  ns.example.jp.
                IN  A   AAA.BBB.CCC.DDD
ns              IN  A   AAA.BBB.CCC.EEE
_acme-challenge IN  NS  ddns.example.jp.
ddns            IN  A   WWW.XXX.YYY.ZZZ
*               IN  A   AAA.BBB.CCC.DDD


  • TSIG鍵作成

ダイナミックDNSで使うTSIG鍵を作成します。
共通鍵作成に使用するのはbind-utilsに含まれているddns-confgenというツールです。
使い勝手が良くなるので実行結果はファイルに書き込んでしまいましょう。

# ddns-confgen -q -k certbot. -a sha512 | tee /var/named/dynamic/certbot.key
key "certbot." {
        algorithm hmac-sha512;
        secret "dummydummydummydummydummydummydummy==";
};
コマンドオプション補足
-q 余計なコメントを省き鍵情報だけを出力する
-k keynameの指定
-a 暗号化アルゴリズムの指定
HMACしか使えないのでハッシュ関数の指定だけで良い

ファイルを作成したらnamedが読み取れるようファイルオーナーを変更します。

# chown named. /var/named/dynamic/certbot.key


  • BIND(named.conf)の設定

named.confに先ほど作成した共通鍵をBINDに読み込ませる設定を追加します。

include "/var/named/dynamic/certbot.key";

続いて_acme-challenge.example.jpのゾーン設定です。
認証リクエスト時のTXTレコードだけ更新出来るよう、update-policyを設定します。

zone "_acme-challenge.example.jp" {
    type master;
    file "dynamic/_acme-challenge.example.jp.zone";
    update-policy {
        grant certbot. name _acme-challenge.example.jp. TXT;
    };
};


  • ゾーンファイル作成

最後に_acme-challenge.example.jpのゾーンファイルを作成します。

_acme-challenge.example.jpのゾーンファイル

$ORIGIN _acme-challenge.example.jp.
$TTL 3600       ; 1 hour
@       IN  SOA ns.example.jp.  admin.example.jp. (
                    1970010101  ; serial
                    3600        ; refresh (1 hour)
                    900         ; retry (15 minutes)
                    3600000     ; expire (5 weeks 6 days 16 hours)
                    3600        ; minimum (1 hour)
                                )
                IN  NS  ddns.example.jp.

ゾーンファイルも忘れずにファイルオーナーを変更してください。

# chown named. /var/named/dynamic/_acme-challenge.example.jp.zone

一通り設定が終わったらnamedを再起動します。

# systemctl restart named-chroot.service


certbotのインストール

それでは証明書発行の準備に取り掛かります。
WEBサーバーに移動して、certbotをインストールするためEPELリポジトリを追加します。

# dnf config-manager --enable crb
# dnf install -y epel-release

リポジトリを追加したらcertbot、ダイナミックDNSと連携するためのプラグインcertbot-dns-rfc2136をインストールします。

# dnf install -y certbot python3-certbot-dns-rfc2136


certbot-dns-rfc2136の使い方

  • TSIG鍵情報の確認

設定のためにTSIG鍵の情報が必要になるため改めて内容を確認しましょう。

TSIG鍵の内容

key "certbot." {
        algorithm hmac-sha512;
        secret "dummydummydummydummydummydummydummy==";
};


  • 資格情報作成

TSIG鍵の内容を元にダイナミックDNSの資格情報を作成します。
マニュアルページにテンプレートがあるのでコピペして使いましょう。
ファイルは共通鍵の情報を含むため、念を入れて隠しディレクトリに保存するようにします。

# mkdir /etc/letsencrypt/.secrets/
# vi /etc/letsencrypt/.secrets/rfc2136.ini

rfc2136.iniの内容

# Target DNS server (IPv4 or IPv6 address, not a hostname)
dns_rfc2136_server = WWW.XXX.YYY.ZZZ
# Target DNS port
dns_rfc2136_port = 53
# TSIG key name
dns_rfc2136_name = certbot.
# TSIG key secret
dns_rfc2136_secret = dummydummydummydummydummydummydummy==
# TSIG key algorithm
dns_rfc2136_algorithm = HMAC-SHA512
# TSIG sign SOA query (optional, default: false)
dns_rfc2136_sign_query = false
設定内容補足
dns_rfc2136_server ホスト名は使えないのでIPアドレスを設定する必要がある
dns_rfc2136_name ダブルクォーテーションは不要
dns_rfc2136_secret
dns_rfc2136_algorithm アルファベットは大文字にする必要がある
dns_rfc2136_sign_query オプション設定なので無くても良い

ファイルを作成したら、またまた念を入れてrootユーザーだけが読み取れるようにしましょう。

# chown -R root. /etc/letsencrypt/.secrets/
# chmod -R 400 /etc/letsencrypt/.secrets/


サーバー証明書の発行

  • 証明書発行

準備が整ったのでワイルドカード証明書を発行します。
最初は--dry-runを付けて、プラグイン、ダイナミックDNSの設定に間違いが無いか確認しましょう。
今回は検証目的なので--register-unsafely-without-emailで管理者メールアドレスの登録を省略しています。

# certbot certonly \
  --dry-run \
  --dns-rfc2136 \
  --dns-rfc2136-credentials /etc/letsencrypt/.secrets/rfc2136.ini \
  --server https://acme-v02.api.letsencrypt.org/directory \
  -d example.jp \
  -d *.example.jp \
  --agree-tos \
  --register-unsafely-without-email
コマンドオプション補足
--dry-run 証明書を発行せず実行テストを行う
--dns-rfc2136 証明書発行に使うプラグインの指定
--dns-rfc2136-credentials TSIGの資格情報ファイル名
--server ACMEサーバーのURL
-d サーバー証明書を取得するドメイン
--agree-tos 利用規約に同意する
--register-unsafely-without-email Let's Encryptにメールアドレスを登録しない


以下、コマンド実行後に表示されるメッセージです。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Simulating a certificate request for example.jp and *.example.jp
Waiting 60 seconds for DNS changes to propagate
  (ここで60秒の待機時間が発生する)
The dry run was successful.

初回発行時にはDNS情報が伝播するのを待つ時間(propagate time)が発生します。
dry runで問題が無ければ--dry-runを外してサーバー証明書を発行しましょう。


  • 証明書確認

無事に証明書が発行されれば/etc/letsencrypt/live/<ドメイン名>/の下にシンボリックリンクが作成されるので確認してみましょう。

# ls -l /etc/letsencrypt/live/example.jp/
total 4
lrwxrwxrwx 1 root root  42 Nov 21 11:21 cert.pem -> ../../archive/example.jp/cert1.pem
lrwxrwxrwx 1 root root  43 Nov 21 11:21 chain.pem -> ../../archive/example.jp/chain1.pem
lrwxrwxrwx 1 root root  47 Nov 21 11:21 fullchain.pem -> ../../archive/example.jp/fullchain1.pem
lrwxrwxrwx 1 root root  45 Nov 21 11:21 privkey.pem -> ../../archive/example.jp/privkey1.pem
-rw-r--r-- 1 root root 692 Nov 21 11:21 README


  • WEBサーバー設定

上記のファイル名を設定ファイルに書いてサービスを再起動すれば設定完了です。

設定ファイルの内容

<VirtualHost *:443>
    ServerName example.jp
    ServerAlias www1.example.jp www2.example.jp

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

httpdサービス再起動

# systemctl restart httpd.service

最後に

ダイナミックDNSさえ用意出来れば、Let's EncryptのDNS認証は難しくないことが分かりました。
サーバー証明書の有効期限短縮が予定されていることですし、ボチボチ自動更新出来る環境を整えていこうと思います。