メインコンテンツに移動
vps eye catch
Let's Encrypt

前回はファイアウォール(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,
/etc/bind/dynamic/** rw,      ← ここを追加
/var/lib/bind/** rw,
/var/lib/bind/ rw,
/var/cache/bind/** lrw,
/var/cache/bind/ rw,

apparmorについては別途記事を書きたいと思います

bind各種設定ファイルの設定

対象ファイル 変更内容 説明
/etc/bind/named.conf

末尾に以下を追加

include "/etc/bind/named.conf.your-domain-zones";
include "/etc/bind/certbot-key.key";

自ドメイン用設定とCertbot用認証鍵の読込を追加
/etc/bind/named.conf.local

下記行のコメントアウトを外す

include "/etc/bind/zones.rfc1918";

WebARENA (Indigo)ではプライベートIPアドレスを利用していないため、プライベートIPアドレスの逆引きクエリ用のダミーゾーンを読込

/etc/bind/named.conf.options

optionsステートメント末尾に赤字部分を追加


options{

        dnssec-validation no;
        recursion no;
        version "unknown";
        allow-query {any;};


}

■DNSSECを利用しない
dnssec-validation no;

■リゾルバ停止
recursion no;

■BINDバージョンを隠す
version "unknown";

■外部クエリ許可
allow-query {any;};

/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")
- プライマリDNS(type master)
- セカンダリへの通知ON(notify yes)
- zone fileを指定(file "zone-filename")
- ゾーン転送先サーバと通知先の指定(allow-transfer/also-notify)
- 名前チェックを無効化(check-names ignore)

■ダイナミックゾーン設定(zone "_acme-challenge.your-domain.com")
- _acme_challenge.your-domain.comのTXTレコードへの動的編集を許可(この部分)

certbot-key.   ← 認証に用いる鍵の名前
name [fqdn] [type] ← 変更対象

/etc/bind/db.your-domain.com

下記を追加


; Subdomain for Certbot auto renewal
_acme-challenge IN      NS      ns.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日後に確認をお願いします。

 

 

次回はNGINX、MariaDB、phpMyAdminをセットアップします