Let's Encrypt の証明書をワイルドカードなやつにして自動更新できるようにした

2018 年 3 月,ついに Let's Encrypt がワイルドカードな証明書の発行に対応したので,所有するサーバの証明書をワイルドカードなものにリプレイスしてみました. 今まではサブジェクト代替名でサブドメインを列挙するスタイルをとっていて地味に面倒だったのと,証明書を読むと internal 向けのドメインがバレちゃうというのがあり,ワイルドカードな証明書に置き換えることでこれらを解決することができました.

この記事のゴール

この記事では以下について述べます.今回作業する環境は ArchLinux ですが,certbotcertbot の cloudflare プラグインのパッケージさえあれば他の OS も動くと思います.

  1. ksswre.net*.ksswre.net の証明書を manual モードで取得する
  2. 上で取得した証明書を自動更新できるようにする (自動更新のため CloudFlare の Free プランを併用)

試行錯誤をしながら作業を行ったので 1 時間弱くらいかかりましたが,最終的にはワイルドカードな証明書が certbot renew で自動更新できるようになりました.

前提

certbot を使います.ArchLinux なので pacman -S certbot すると certbot が降ってきた気がします.なければいれましょう.

ワイルドカードな証明書を取得する

おもむろに以下のコマンドをうちます.ワイルドカード証明書を取得するには dns-01 認証が必要なので,manual モードにして認証形式を手動で設定します. また,2018 年 4 月現在でワイルドカード証明書を発行するには ACME v2 が必要なので --server オプションを付けて認証サーバを変更します.メールアドレスや IP アドレスを記録するが良いかなどとインタラクティブな操作で確認されるので Y か N を打って答えます.

# certbot certonly --manual -d '*.ksswre.net' -d ksswre.net -m 'mail@example.com' --agree-tos --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory

しばらくすると以下のような画面がでます.これは dns-01 認証のためのもので,ドメインを本当に所有しているかどうか DNS の TXT にワンタイムトークンを追加することで認証します.

-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.ksswre.net with the following value:

XXXXXXXXXXXXXXXXXXXXXXXXXXXX

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue

ドメイン管理画面を開き (以下は StarDomain の例),ワンタイムトークンを DNS の TXT レコードに追記します. f:id:k5342:20180406230657p:plain

追記したら Enter を押します.うまいこといくと以下のような表示があるはずです.

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/privkey.pem
   Your cert will expire on 2018-07-05. 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"
 - 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

これより,サーバ証明書などが取得できたので nginx.conf を編集し,証明書を新しいものに切り替えます.nginx.conf の抜粋.

ssl_certificate /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ksswre.net-0002/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/ksswre.net-0002/chain.pem;

Nginx を再起動すると,うまいこといってるのがわかります.PostfixDovecot の設定なども同様にお忘れなくやりましょう. f:id:k5342:20180406232900p:plain

このままでは自動更新できない

証明書が更新できるか確認しましょう.--force-renewal オプションを付けて強制更新モードにして一旦走らせてみると,エラーでコケることがわかります.

# certbot renew --force-renewal
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/ksswre.net-0002.conf
-------------------------------------------------------------------------------
Could not choose appropriate plugin: 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.',)
Attempting to renew cert (ksswre.net-0002) from /etc/letsencrypt/renewal/ksswre.net-0002.conf produced an unexpected 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.',). Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem (failure)

manual モードの場合はインタラクティブな操作が必要で certbot renew で更新するには --manual-auth-hook オプションで認証用ホックを自前で用意する必要があるそうです.ホックを自前で書くのは面倒なので調べてみたら,certbot の CloudFlare プラグインを使う方法があるそうです.以下のサイトが有用でしたので参考にさせていただきました. a244.hateblo.jp

CloudFlare プラグインを併用して自動更新する

CloudFlare プラグインを使うためには,既存のネームサーバを CloudFlare に変更する必要がありますが,CloudFlare に変更すると DNS TXT レコードの書き換えと dns-01 認証をプラグインが全て自動で行ってくれます.便利です.CloudFlare は個人サイトの規模であれば Free プランで十分なので,無料で運用できます.

このプラグインを使うには,ArchLinux では新しいパッケージをインストールする必要があるのでインストールしておきます.

# pacman -S certbot-cloudflare

上のサイトを参考にしながら CloudFlare でアカウントをとり,ネームサーバを CloudFlare に変更し,管理パネルの Get your API key から Global API Key を取得し,certbot に設定しておきます.CloudFlare の CDN (Proxy) は今回は必要ないのでオフにしておき,ネームサーバだけ有効にしておきます.

設定ができたら,certbot で上のセクションで取得した証明書をリニューアルします.以下のコマンドを走らせます.

# certbot certonly --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini -d ksswre.net -d '*.ksswre.net' --server https://acme-v02.api.letsencrypt.org/directory

プラグインとして CloudFlare 用のものが選択されていることがわかります.

Plugins selected: Authenticator dns-cloudflare, Installer None
Cert not yet due for renewal

しばらくすると,既に同じドメインで取得した証明書をリプレイスして良いか聞いてくるので,そうしてもらいます. そのあと少し放置すると,CloudFlare のネームサーバを通して dns-01 認証を行われ,正常に証明書が更新されます. この間,上のセクションで使用した manual モードのようにインタラクティブな操作は発生せず,全て自動で進みます.便利.

You have an existing certificate that has exactly the same domains or certificate name you requested and isn't close to expiry.
(ref: /etc/letsencrypt/renewal/ksswre.net-0002.conf)

What would you like to do?
-------------------------------------------------------------------------------
1: Keep the existing certificate for now
2: Renew & replace the cert (limit ~5 per 7 days)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for ksswre.net
dns-01 challenge for ksswre.net
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/ksswre.net-0002/privkey.pem
   Your cert will expire on 2018-07-05. 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"
 - 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

これで CloudFlare プラグインを使ってワイルドカードな証明書が自動更新できるようになりました.

# certbot renew --force-renewal
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/ksswre.net-0002.conf
-------------------------------------------------------------------------------
Plugins selected: Authenticator dns-cloudflare, Installer None
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for ksswre.net
dns-01 challenge for ksswre.net
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/ksswre.net-0002/fullchain.pem
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/ksswre.net-0002/fullchain.pem (success)
-------------------------------------------------------------------------------

この後は,certbot renew を定期的に投げれば良いので,crontab や systemd-timer を使って定期的にコマンドが走るようにします.これで自動更新は完成です.