Let’s EncryptによるSSLサーバー証明書の自動更新設定

isdはじめに

Let’s Encryptは、無料のSSLサーバー証明書発行プロジェクトです。
コマンドラインで証明書を自動発行、更新できるの、とても便利なサービスを提供しています。

・Lets’ Encrypt (Official)
https://letsencrypt.org/

・Let’s Encrypt 総合ポータル(日本語の説明が詳しい)
https://letsencrypt.jp/

以前、公開ベータプログラムだったときに記事を書いたのですが、その後、2016年4月12日に正式サービスとなりました。

・Let’s EncryptでSSLサーバー証明書を取得してみた
https://inaba-serverdesign.jp/blog/20151217/lets-encrypt.html

詳しいインストール手順は「Let’s Encrypt 総合ポータル」のLet’s Encryptの使い方を参照していただくことにして、ここでは、有効期限が90日の証明書を自動更新する設定について書きます。

isd証明書の自動更新設定

Let’s Encryptで証明書を取得、更新する場合は、Certbotクライアント certbot-auto コマンドを使用します。

更新する場合のコマンドは、サブコマンド renew を使用して以下のようになります。

 # ./certbot-auto renew --non-interactive

 

ただし上記の場合、ドメイン使用権者の認証にCertbotクライアントに内蔵されているWebサーバーを使用するため、ApacheやNginxなどのWebサーバーを一時的に停止する必要があります。

そこで、Webサーバーの停止、起動と証明書の更新、問題があったときのアラートメール通知やログ書き出しといった一連の処理をシェルスクリプトにまとめて、cronで定期的に実行するようにします。

CentOS 7, Nginxの場合、こんな感じのシェルスクリプトを用意します。
certbotコマンドは、/usr/local/certbot にインストールされているものとします。

 # vi /root/bin/update_sslcert.sh
 
--
#!/bin/sh
#
LOGFILE=/var/log/update_sslcert.log
COMMAND=/usr/local/certbot/certbot-auto
WEBSERVER_STOP_COMMAND="systemctl stop nginx"
WEBSERVER_START_COMMAND="systemctl start nginx"

MAILTO=root
 
echo "===== Update SSL Cert =====" >> ${LOGFILE}
echo "`date` Update SSL Cert start" >> ${LOGFILE}
 
${COMMAND} renew \
  --pre-hook "$WEBSERVER_STOP_COMMAND" \
  --post-hook "$WEBSERVER_START_COMMAND" \
  --non-interactive >> ${LOGFILE}
STATUS=$?
  
if [ "$STATUS" != 0 ]; then
    echo "Update SSL Cert failed" >> ${LOGFILE}
    echo "Update SSL Cert failed" |\
    mail -s "Update SSL Cert in `hostname`" ${MAILTO}
fi
 
echo "`date` Update SSL Cert end" >> ${LOGFILE}
 
# EOF
--

 // 実行権限の追加
 # chmod 755 /root/bin/update_sslcert.sh

 

サブコマンド renew のオプションとして、証明書の取得処理前後のシェル実行コマンドを指定できる –pre-hook, –post-hook を利用することで、Nginxの停止、起動を行うのがポイントです。

–pre-hook, –post-hook の引数となる変数

WEBSERVER_STOP_COMMAND="systemctl stop nginx"
WEBSERVER_START_COMMAND="systemctl start nginx"

のところは、

CentOS 7, Apacheの場合は、

WEBSERVER_STOP_COMMAND="systemctl stop httpd"
WEBSERVER_START_COMMAND="systemctl start httpd"

CentOS 6/Amazon Linux, Nginxの場合は、

WEBSERVER_STOP_COMMAND="/etc/init.d/nginx stop"
WEBSERVER_START_COMMAND="/etc/init.d/nginx start"

CentOS 6/Amazon Linux, Apacheの場合は、

WEBSERVER_STOP_COMMAND="/etc/init.d/httpd stop"
WEBSERVER_START_COMMAND="/etc/init.d/httpd start"

とすればよいでしょう。

仕様では、証明書を更新できるのは有効期限の30日以内のときで、期限まで30日以上あるときは更新コマンドを実行しても、証明書ファイルは更新されません。
このため、更新処理は週に一度実行するようにしてみます。
(毎日だと多すぎるけど、月に一度だと万一更新に失敗したときに期限切れとなってしまうため)

以下は毎週水曜日14:50に実行する例です。

 # crontab -e
 
--
# Update SSL Cert
50 14 * * 4 /root/bin/update_sslcert.sh > /dev/null
--

 

※例として平日の昼間にしたのは、何らかの不具合でWebサーバーが停止したままとなってもすぐ対処できるようにするためです。一時的にWebサーバーが停止すると困る場合は、夜間・早朝でよいと思います。

CentOS 6で、「Let’s Encrypt 総合ポータル CentOS 6 で発生するエラーの対処法」に記載されている対処方法で、SCLリポジトリからPython 2.7をインストールした場合は、次のようにPython 2.7環境でシェルスクリプトを実行するようにします。

--
# Update SSL Cert
50 14 * * 4 scl enable python27 "/root/bin/update_sslcert.sh" > /dev/null
--

 

これで、Let’s EncryptによるSSLサーバー証明書が自動更新されます。
実際に証明書を更新したときのログは以下のようになります。

 # less /var/log/update_sslcert.log

===== Update SSL Cert =====
2016年  6月 29日 水曜日 14:50:01 JST Update SSL Cert start

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/ssltest.inaba-serverdesign.jp.conf
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/ssltest.inaba-serverdesign.jp/fullchain.pem
-------------------------------------------------------------------------------

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/ssltest.inaba-serverdesign.jp/fullchain.pem (success)
2016年  6月 29日 水曜日 14:50:09 JST Update SSL Cert end

 

lsコマンドで各証明書ファイルを確認すると、秘密鍵、証明書ファイルのリンク先が更新されたことがわかります。

 # ls -l /etc/letsencrypt/live/ssltest.inaba-serverdesign.jp/

--
lrwxrwxrwx 1 root root 45  6月 29 14:50 cert.pem -> ../../archive/ssltest.inaba-serverdesign.jp/cert2.pem
lrwxrwxrwx 1 root root 46  6月 29 14:50 chain.pem -> ../../archive/ssltest.inaba-serverdesign.jp/chain2.pem
lrwxrwxrwx 1 root root 50  6月 29 14:50 fullchain.pem -> ../../archive/ssltest.inaba-serverdesign.jp/fullchain2.pem
lrwxrwxrwx 1 root root 48  6月 29 14:50 privkey.pem -> ../../archive/ssltest.inaba-serverdesign.jp/privkey2.pem
--

 

なお、初めて自動更新設定をしたときは、renewサブコマンドの強制実行オプション –force-renewal を利用して証明書ファイルが正しく更新されることを確認するとよいでしょう。

isdWebサーバーを停止せずに自動更新する方法

上記の方法では、証明書を更新する際にNginxやApacheによるWebサーバーを一時的に停止するため、Webサイトを閲覧できない時間帯が数秒間生じます。

Let’s Encryptでは、証明書取得時のドメイン使用権者の認証で、Certbotクライアントに内蔵されているWebサーバーを使用する代わりに、Webサーバーの DocumentRoot ディレクトリ以下に認証用のファイルを設置することで認証を行う方法「Webrootプラグイン」も用意されています。
これを使用することで、Webサーバーを停止せずに証明書の取得・更新ができます。
コマンドラインは次のようになります。
(ここでは、DocumentRoot は /usr/share/nginx/html とします。)

 # ./certbot-auto renew --webroot -w /usr/share/nginx/html --non-interactive

 

これにより、DocumentRoot の /usr/share/nginx/html 配下に .well-known/ ディレクトリが作成され、その下に認証用の一時ファイルが生成されます。

たたし、Webサーバーの設定で .(ドット)を含むディレクトリへのアクセスが許可されている必要があります。
また、.well-known ディレクトリは、このコマンドの実行後も削除されません。

個人的には、

  • Nginx, ApacheのConfigや .htaccess によるアクセス制御に注意する必要がある。
  • コンテンツ領域に余計なディレクトリ、ファイルが生成されるのは好ましくない。

という理由で、このWebrootプラグインを使用するよりは、Webサーバーを一時停止する方法のほうがよいように思います。

(参考)
・Let’s Encrypt総合ポータル コマンド解説(コマンドリファレンス)
https://letsencrypt.jp/command/

・Let’s Encrypt総合ポータル ユーザーガイド
https://letsencrypt.jp/docs/using.html

isdまとめ

Let’s EncryptによるSSLサーバー証明書を自動更新する設定方法について書きました。

Googleの検索ランキングアルゴリズムでHTTPSを考慮するようになったことや、HTTP/2化の流れもあり、今後はフルHTTPSサイトが増えていくでしょう。
開発・検証環境や低予算のWebサイトで、ドメイン認証(DV)タイプでもよい場合は、Let’s Encryptによる無料SSLサーバー証明書の使用を検討されるとよいと思います。

稲葉サーバーデザインでは、「無料SSLサーバー証明書の無期限使用設定」の料金プランを用意しています。
初期設定費用はいただきますが、Webサーバーの設定も行いますし、Let’s Encryptサービスが廃止にならない限り無料・無期限でサーバー証明書を使用できるので、1年以上使用するのであればお得だと思います。
作業内容、料金は「料金プラン」のページをご参照ください。

(関連記事)
・AWSで使用できる無料SSLサーバー証明書~AWS Certificate Managerについて
https://inaba-serverdesign.jp/blog/20160629/aws_certificate_manager_ssl.html