前回はファイアウォール(UFW)の初期設定を行いました。
今回はLet's Encryptでワイルドカード証明書の発行と自動更新を実装します
今回のコンポーネント
コンポーネント | 利用したもの |
---|---|
SSL証明書 | Let's Encrypt ワイルドカード証明書 |
DNSサーバ | bind9 |
レジストラ | お名前.com |
SSL証明書
2018年のGoogle Chrome 68以降、ブラウザではSSL対応を行わないと「保護されていません」といった警告が出るようになりました。
Webの安全性だけでなく、ユーザからの信頼を落とさないためにも常時SSL化は必須となってきています。
Webだけでなく、メールに関してもメールサーバ間のやり取りの暗号化ではSSL証明書が必要となります。
SSL証明書の種類や認証レベルはほかにも詳しく記載されたサイト(例:SSLボックス によるSSLについて)がありますので、ご参照ください。
Let's Encryptでワイルドカード証明書
無料のSSLサーバー証明書(以下、SSL証明書)であるLet’s Encryptは、米国の非営利団体であるISRG(Internet Security Research Group)により運営されています。
Let's Encryptは2018年よりACME v2とワイルドカード証明書に対応しており、今回は複数ホスト名での証明書利用を想定しているためワイルドカード証明書を発行します。
Let's Encryptによるドメイン認証
Let's Encryptでは、証明書発行にあたり、対象となるドメインを管理する権限があることを確認するために、Automated Certificate Management Environment(ACME)プロトコルを利用します。
Automated Certificate Management Environment(ACME)は、X.509証明書のドメイン検証、インストール、および管理を自動化するための標準プロトコルです。
Let’s Encrypt の証明書を取得するためには、サードパーティー製 ACME クライアントが必要となりますが、今回はUbuntuのパッケージあるCertbotを利用します。CertbotではVersion 0.22以降でACME v2とワイルドカード証明書に対応しています。
ACMEでの「チャレンジ」
ACMEで定義されている「チャレンジ」を使用してドメイン管理権限の確認を行います。
通常の証明書であればウェブサーバを利用したHTTP-01 Challengeを利用するのが簡単ですが、Let's Encryptの方針で、ワイルドカード証明書の取得の際には必ず DNSを利用したDNS-01 Challengeによる認証を行う必要があります。
DNS-01 Challenge
DNS-01 Challengeは、認証対象となるDNSドメインの TXT レコードに特定の値を設定することを要求されます。
Let’s Encrypt が ACME クライアントにトークンを与えると、ACMEクライアントは与えられたトークンと自身のアカウント鍵から作られた TXT レコードを生成し、そのレコードを DNS の _acme-challenge.<YOUR_DOMAIN>
の値として設定します。
その後、Let’s Encrypt は DNS にてそのレコードを問い合わせ、正しい値と合致すれば証明書の発行に進むことができます。
DNS-01 Challengeにおける課題
HTTP-01チャレンジであれば、自身でコンテンツをおけるWebサイトがあれば認証は可能です。証明書の自動更新の際に必要な動的なトークン更新も比較的容易です。
しかし、ワイルドカード証明書の利用にはDNS-01チャレンジが必要です。初回認証や手動による更新認証であれば、各種レジストラやホスティング事業者などが提供するDNSホスティングサービスでも対応できますが、自動更新の際はDNSサーバのレコードに、ACMEクライアントが生成した値を連携して更新する必要があります。
今回はVPSであり、固定グローバルIPアドレスを保持しているため、VPSサーバ上でDNSサーバ(bind9)を立上げ、自動更新の仕組みを実装します。
DNS-01 Challenge対応 DNSサーバ(bind9)セットアップ
私はお名前.comをレジストラとして利用しています。
これまでは、レジストラのDNSホスティングを利用していたのですが、今回はプライマリサーバをVPSサーバ上でのbindに移行します。
項目 | |
---|---|
ドメイン名 | your-domain.com |
プライマリDNS |
ns.your-domain.com / 12.34.56.78 (自サーバ) |
セカンダリDNS |
2nd.dnsv.jp / 163.44.76.202(ゾーン転送先) |
bindインストール
# apt install bind9
certbot用認証鍵作成
tsig-keygenコマンドで認証鍵を作成し、所有者とパーミッションを変更
認証鍵のためアクセス権には注意
# tsig-keygen certbot-key > /etc/bind/certbot-key.key
# chown bind:bind /etc/bind/certbot-key.key
# chmod 600 /etc/bind/certbot-key.key
ゾーンファイル用ディレクトリ作成
DNS-01 Challenge用の動的更新を許可したdynamic zoneファイル用ディレクトリの作成し、所有者とパーミッションを変更
# mkdir /etc/bind/dynamic
# chown bind:bind /etc/bind/dynamic
# chmod 644 /etc/bind/dynamic/_acme-challenge.your-domain.com.zone
動的更新のためにapparmor設定変更
bindでのファイル操作はapparmorによる制限がかかっているため、dynamic zoneファイル用ディレクトリの許可を追加
設定ファイル「/etc/apparmor.d/usr.sbin.named」下記部位を修正
# vim /etc/apparmor.d/usr.sbin.named
/etc/bind/** r, |
apparmorについては別途記事を書きたいと思います
bind各種設定ファイルの設定
対象ファイル | 変更内容 | 説明 |
---|---|---|
/etc/bind/named.conf |
末尾に以下を追加 include "/etc/bind/named.conf.your-domain-zones"; |
自ドメイン用設定とCertbot用認証鍵の読込を追加 |
/etc/bind/named.conf.local |
下記行のコメントアウトを外す include "/etc/bind/zones.rfc1918"; |
WebARENA (Indigo)ではプライベートIPアドレスを利用していないため、プライベートIPアドレスの逆引きクエリ用のダミーゾーンを読込 |
/etc/bind/named.conf.options |
optionsステートメント末尾に赤字部分を追加
|
■DNSSECを利用しない ■リゾルバ停止 ■BINDバージョンを隠す ■外部クエリ許可 |
/etc/bind/named.conf.your-domain-zones |
zone "your-domain.com" { type master; notify yes; file "/etc/bind/db.your-domain.com"; allow-transfer { 163.44.76.202; }; also-notify { 163.44.76.202; }; check-names ignore; }; zone "_acme-challenge.your-domain.com" { type master; notify yes; allow-transfer { 163.44.76.202; }; also-notify { 163.44.76.202; }; file "/etc/bind/dynamic/_acme-challenge.your-domain.com.zone"; check-names ignore; update-policy { grant certbot-key. name _acme_challenge.your-domain.com. TXT; }; }; |
■自ドメイン設定(zone "your-domain.com") ■ダイナミックゾーン設定(zone "_acme-challenge.your-domain.com") certbot-key. ← 認証に用いる鍵の名前 |
/etc/bind/db.your-domain.com |
下記を追加
|
サブドメイン _acme-challenge のName Serverを自サーバに指定 |
/etc/bind/dynamic/_acme-challenge.your-domain.com.zone | $TTL 3600 ; 1 hour _acme-challenge.hs3.org IN SOA ns.your-domain.com. admin.your-domain.com. ( 2021033101 ; serial 3600 ; refresh (1 hour) 900 ; retry (15 minutes) 2592000 ; expire (4 weeks 2 days) 3600 ; minimum (1 hour) ) NS ns.hs3.org. |
設定完了後はBINDを再起動
# systemctl restart bind9
動作確認
サブドメインを参照し、SOAレコード、NSレコードが適切に設定されていることを確認
# dig any _acme-challenge.your-domain.com @ns.your-domain.com
;; ANSWER SECTION:
_acme-challenge.your-domain.com. 3600 IN SOA ns.your-domain.com. admin.your-domain.com. 2021033101 3600 900 2592000 3600
_acme-challenge.your-domain.com. 3600 IN NS ns.your-domain.com.
ここまで完了したら、Let's Encryptの導入に進みます
Let's Encryptの導入
Certbotインストール
Ubuntu20ではパッケージ化されているのでaptでインストール
# apt install certbot python3-certbot-dns-rfc2136
Let's Encryptアカウント作成
メールアドレスを登録してアカウントを作成します
# certbot register
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): admin@your-domain.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: a
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
IMPORTANT NOTES:
- Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.
Certbot用Credential(認証情報)ファイル作成
「/etc/letsencrypt/dns-rfc2136.ini」を編集
dns_rfc2136_server = 12.34.56.78 ←プライマリDNSサーバの外部から参照可能なアドレスを記載 dns_rfc2136_port = 53 dns_rfc2136_name = certbot-key. ←Certbot用認証鍵の名前を指定(末尾の「.」ドットを忘れないように) dns_rfc2136_secret = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ←/etc/bind/cert-key.keyのsecret記載の内容を記載 dns_rfc2136_algorithm = HMAC-SHA256 |
暗号鍵を記載するため権限を修正
# chmod 600 /etc/letsencrypt/dns-rfc2136.ini
証明書発行テスト
まずはセットアップが問題ないか「--dry-run」オプションをつけて証明書発行をシミュレートします。
オプション「--dns-rfc2136」でRFC2136に基づくダイナミックDNSの利用を宣言、「--dns-rfc2136-credentials /file」でRFC2136に関するパラメータファイルを定義します。
# certbot certonly --dry-run --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/dns-rfc2136.ini -d '*.your-domain.com' -d 'your-domain.com'
DNS-01チャレンジを行いますので多少時間を要します。
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-rfc2136, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for your-domain.com
Waiting 60 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- The dry run was successful.
成功すると「The dry run was successful」と結果が出てきます。
エラーが発生した際は、「/var/log/letsencrypt/letsencrypt.log」を参照し、どの箇所でエラーが発生したかを確認ください。
初回証明書発行
dry-runが成功したら、証明書を発行します。先ほどのコマンドから「--dry-run」を除きます。
# certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/dns-rfc2136.ini -d '*.your-domain.com' -d 'your-domain.com'
dry-runと同じく、DNS-01チャレンジを行いますので待ちます。
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-rfc2136, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for your-domain.com
dns-01 challenge for your-domain.com
Waiting 60 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/your-domain.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/your-domain.com/privkey.pem
Your cert will expire on 2021-06-29. 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"
Reporting to user: If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
「Congraturations!Your certificate and chain have been saved at:」と表示されれば成功です。
作成された証明書関連ファイルは「/etc/letsencrypt/live/you-domain.com」に保存されます。
種類 | ファイル |
---|---|
秘密鍵 | /etc/letsencrypt/live/your-domain.com/privkey.pem |
証明書公開鍵 + 中間証明書 | /etc/letsencrypt/live/your-domain.com/fullchain.pem |
中間証明書 | /etc/letsencrypt/live/your-domain.com/chain.pem |
サーバ証明書 | /etc/letsencrypt/live/your-domain.com/cert.pem |
最近のアプリケーションでは、「privkey.pem」と「fullchain.pem」でほぼ対応しますが、古いapacheやNGINXでOCSP Staplingを行う場合などは「cert.pem」や「chain.pem」を利用することもあります。
証明書自動更新のテスト
まずは証明書発行と同じく更新も「dry-run」でテストします
# certbot --dry-run --debug renew
これまでと同様にDNS-01チャレンジの結果を待ちます
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/your-domain.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator dns-rfc2136, Installer None
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for your-domain.com
dns-01 challenge for your-domain.com
Waiting 60 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/your-domain.com/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates below have not been saved.)
Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/your-domain.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
「Congratulations, all renewals succeeded. The following certs have been renewed:」と表示されれば成功です。
証明書自動更新
certbotをaptでインストールした際は、「/etc/cron.d/certbot」に自動更新スクリプトが含まれていると思います。
内容を確認します
# cat cron.d/certbot
# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc. Renewal will only occur if expiration
# is within 30 days.
#
# Important Note! This cronjob will NOT be executed if you are
# running systemd as your init system. If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob. For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
12時間毎に「certbot -q renew」コマンドが実行することが確認できます。
certbot renewを実施した際は、更新期限30日を切るまでは証明書の更新は実行はされませんので、60日後に確認をお願いします。