Ubuntu

Ubuntu18.04 Let's Encrypt ワイルドカード証明書の自動更新

当ブログのコメントで情報交換をさせていただいた際、自動でワイルドカード証明書の更新ができるというお話を伺ったので、調べてみようと考えた。
実際、ついつい更新を忘れそうになるので自動更新がありがたい。



広告


やること。

 

学習

改めてLet's Encryptで証明書を取得する、ということはどういうことなのか整理してみる。

まず、Let's Encryptで証明書を取得するには、ACMEプロトコル(Automatic Certificate Management Environment プロトコル)を扱うソフトウェアを使う、おすすめはCertbotとされている。
Webサーバーと認証局との間の相互作用を自動化するための通信プロトコル

FAQのなかでワイルドカード証明書を発行していますか?の問いに対しては、DNS-01を使用するACMEv2プロトコルでの認証が必要、とされている。
Let's Encrypt / ACME v2 Production Environment & Wildcards

証明書を取得する「チャレンジ」には幾つかのタイプがある。前回の記事ではDNS-01を使用して手動で取得することを書いていて、今でもこの手順で更新を続けている。
certbotコマンド実行→表示されたTXTキーをDNSに設定→再度表示されたキーをDNSに設定、という感じ。

証明書取得にあたってレート制限がある。
たとえば登録ドメインごとの証明書数は1週間に50個までとなっている。最初にトライした当時はもっと全然少なかった、あるいは、更新もこれに含まれていた?せいなのか、ダイナミックDNSサービスを利用している自宅サーバーは操作しようとすると常に制限に引っかかっていたので、利用を断念していた。

計画

ワイルドカード証明書を発行できるのはDNS-01サーバーで、かつ、ACMEv2プロトコルで認証をする。

この場合、DNSのTXTレコードにキーを登録する必要がある。
自分でDNSを運用していればそこに書き込めば良いのだろうし、利用しているサービスがあるなら、提供されているサービスを上手く使って更新

ウチで利用させていただいているサービスは、ログインしてからでないとLet's Encryptの証明書取得に関する情報が表示されないので、ここでは参考URLを示さない。

レート制限については、証明書の発行は1週間に50枚だが、証明書の更新には制限が掛かっていないようなので、どこかで運良く発行できれば運用できるだろう。

コマンド実行にあたり、取得のスクリプトを1つ用意。
取得と同じコマンドで更新ができる(再発行の扱いにはならないらしい)ので良しとする。

実際にやってみるとコマンドは失敗することがあるので、クーロンで毎日SSL証明書の開始日or終了日を見て60日を超えていたら証明書を更新する。

証明書の取得

前回書いたコマンドが黒いところで、追加しようとしているのが赤いところ。
テストをする場合は server にステージング環境を設定すること。じゃないと制限に引っかかって他の人に迷惑が掛かるかもしれない。

sslcert_create 的なスクリプト

# certbot certonly \
--manual \
-d hogeserver.hogeddns.jp \
-d *.hogeserver.hogeddns.jp \
--server https://acme-v02.api.letsencrypt.org/directory \
--preferred-challenges dns-01 \
--agree-tos \
-m hogehoge@hogeserver.hogeddns.jp \
--manual-auth-hook /hogehoge/auth-hook.php \
--manual-cleanup-hook /hogehoge/cleanup-hook.php \
--manual-public-ip-logging-ok

※hogeserver.hogeddns.jpは架空のドメイン(って書かないとコメントで怒られることが…)。

追加部分をマニュアルで確認してみる。
ubuntu manuals / certbot

--agree-tos

ACMEサーバー加入者同意次項への同意を示す。
おそらく、前回の記事で書けていない最初の登録で使用する情報なのだろうと思われる。

-m yourmailaddr

重要なアカウント通知が送られてくるメールアドレスを指定する。
これも前回の記事で書けていない情報なのだろう。

--manual-auth-hook

認証のためのコマンド、または、パスを指定することになっている。
ここで呼び出されるスクリプトは、以下の環境変数が使える。

変数名内容
$CERTBOT_DOMAIN認証されるドメイン
$CERTBOT_VALIDATION検証文字列
$CERTBOT_TOKENHTTP-01でチャレンジで要求されるファイル名
$CERTBOT_AUTH_OUTPUT認証スクリプトの標準出力

自宅サーバーで利用させていただいているサービスでは、このタイミングで必要な情報をかき集めてサーバーに送るらしいスクリプトが提供されている。
サーバー側でそれらの情報を元によしなに処理をしてくれているものと思われる。

自前でDNSを管理しているのならば、ここでTXTレコードを書き出してあげれば良さそう。

--manual-cleanup-hook

クリーンアップ処理のために呼び出されるコマンド、または、パスを指定することになっている。マニュアルの書き方からみて、使える変数はauth-hookと同じ。

利用させていただいているサービスが提供されているスクリプトも同様で、サーバー側でよしなに処理してくれていると思われる。

自前でDNSを管理しているのならば、ここでTXTレコードを削除してあげる感じかと思われる。

--manual-public-ip-logging-ok

いつもcertbotコマンドを実行すると、以下の質問を受ける。

NOTE: The IP of this machine will be publicly logged as having requested this certificate. If you're running certbot in manual mode on a machine that is not your server, please ensure you're okay with that.

Are you OK with your IP being logged?

注:このマシンのIPは、この証明書を要求したものとして公開ログに記録されます。 サーバー以外のマシンでcertbotを手動モードで実行している場合は、問題がないことを確認してください。

IPがログに記録されても大丈夫ですか?

答えはYESしかないわけだけれども、毎回聞かれていたこの質問。
パラメーターでOKと回答することで、コマンド実行中に聞かれなくなる。

renew

最初に書いたコマンドを実行すると、初回の取得もできるし、更新もできる(Let's Encryptでは実際に更新動作になった)。

ただ、Certbotとしては更新にrenewというパラメーターを用意しており、それを使う場合のやり方をメモ。
Algernon's Laboratory / `certbot renew`でワイルドカード証明書の更新

# certbot renew \
--manual \
--server https://acme-v02.api.letsencrypt.org/directory \
--preferred-challenges dns-01 \
--manual-auth-hook /hogehoge/auth-hook.php \
--manual-cleanup-hook /hogehoge/cleanup-hook.php

※試していない。元記事に幾つか加えてウチの場合に対応してみた。

証明書の自動更新

Integration Guideによれば、証明書は有効期限の30日前になったら更新するように勧められている。

幾つか見たQ&Aでは、証明書を毎日更新しようと思っているとか書いているケースが幾つかあり、それはリソースの無駄遣いだぞと迷惑がられていた。そりゃそうだ。

自動更新中に先程のような問い合わせがでないように(もしかしたらリソースを無駄に食い潰す迷惑を掛けているかも)、実行タイミングを決めることにする。
ComputingforGeeks / How To Check SSL Certificate Expiration with OpenSSL
逆引きシェルスクリプト / 日付と日付の引き算をする方法

sslcert_update 的なスクリプト

#!/bin/bash

# 現在日付
NDATESEC=`date +%s`

# 証明書の満了日付
EDATE=`openssl x509 -in /etc/letsencrypt/live/hogeserver.hogeddns.jp/cert.pem -noout -enddate | awk -F "=" '{print $2}'`
EDATESEC=`date -d"$EDATE" +%s`

# 証明書満了までの日数
EXPRD=`expr \( $EDATESEC - $NDATESEC \) / 60 / 60 / 24`

# 満了日まで30日を切ったら更新を実行
if [ $EXPRD -lt 30 ]
then
    n=1
    while :
    do
        # 証明書を更新
        source sslcert_create && break

        # 3回実行したけれど失敗していた
        if [ $n -ge 3 ]
        then
            echo "SSL証明書の更新失敗"
            exit 1
        fi

        # カウンタを上げて2分待ってリトライ
        n=$[n+1]
        sleep 2m
    done

    # 新しい証明書を読み込ませる
    echo "SSL証明書の更新成功"
    systemctl reload apache2
fi

※赤文字部分で Let's Encryptで更新した証明書 を書いておけば動くだろう。
※sslcert_createは証明書の取得で書いたコマンドをスクリプトにしたもの。

こうして計算した$EXPRDが30日を切ったら、更新を実行するようにして、cronに入れておけば良さそう。
ついでに、たまに更新に失敗する感じなのでリトライ機能を入れてみた。

結果を確認できるのは2ヶ月後。

2023/12/09 追記
Ubuntu 22.04でこのスクリプトがうまく動かない問題が発生した。
詳しくはこちら

やったこと

はじめて証明書を発行しようとするとき表示されるメッセージ

自宅サーバーは今までオレオレ証明書で保護していた。それで全然困らないんだけれど、自動更新できるようになるなら…と初挑戦。そこで、前回記事には書けていない「初めてのメッセージ」が出てきたのでメモ。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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

Let's Encryptプロジェクトの創設パートナーであり、Certbotを開発している非営利団体であるElectronic Frontier Foundationとメールアドレスを共有してもよろしいですか?
Webを暗号化する作業、EFFニュース、キャンペーン、およびデジタルの自由をサポートする方法についての電子メールをお送りします。

喜んでYを押した。

短期間で証明書を更新しようとすると表示されるメッセージ

自宅サーバー用の証明書を取得しようとして何度かエラーになったため、ステージング環境を使って動作を確認した。
その後に本番環境で証明書を取得しようとしたところで以下のメッセージが表示された。

Cert not yet due for renewal

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/hogeserver.hogeddns.jp.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):

証明書はまだ更新の予定がありません

要求したドメインまたは証明書名とまったく同じで、有効期限が近づいていない既存の証明書があります。
(参照:/etc/letsencrypt/renewal/hogeserver.hogeddns.jp.conf)

何をしたいですか?
- - -
1:今のところ既存の証明書を保持します
2:証明書を更新して交換します(7日あたり最大5つまで)
- - -
適切な番号[1-2]を選択してから[Enter]を選択します(キャンセルするには「c」を押します):

このメッセージがウチのサーバーの中で出されているのか、Let's Encryptのサーバーから出されているのか分かっていない。
でも、こうしたサーバーの制限事項はそれぞれ違っているのだろうから、Let's Encryptのサーバーに聞きに行っているのかなと想像。

さいごに

2ヶ月後に動作確認。楽しみ。

ところで…やりたいことを探していたときには見つからない情報が、ある程度進んできて記事が整理できたあたりでドサドサと見つかるあの感じ。名前付いてるのかな?
今回はホントこれだった。

2021/04/23
なんとなく証明書を確認してみたら、あれ?今日発行されている?
ということでCronの動作結果を見てみたところ更新に成功していた。これで安心!

広告

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他