セキュリティ

証明書の手動更新は、もう限界。acme.shで発行から自動更新まで構築する方法

この記事は約9分で読めます。

前回はWindows Server向けのwin-acmeと、Linuxでも使えるlegoを紹介しました。今回は、シェルスクリプトで動くACMEクライアント「acme.sh」を使います。

証明書の期限切れは、利用者には「サイトに安全に接続できない」という警告として現れます。「担当者が更新を忘れた」は、訪問者には関係ありません。証明書の有効期間が短くなるほど、手作業で予定表を回す運用は危うくなります。更新担当者の記憶力を最後の防壁にするのは、そろそろやめましょう。

この記事では、Linux上のNginxで公開する example.com を例に、発行・配置・反映・定期実行の確認まで進めます。example.com は説明用ドメインです。実行時は管理する実際のドメイン名に置き換えてください。

acme.shとは

acme.shは、認証局とのACME通信、ドメイン認証、証明書の取得と更新を行うクライアントです。インストール時に定期実行のcronを登録し、更新が必要か毎日確認します。ただし、取得した証明書をNginxに配置し、更新後に読み直す設定まで済ませて、初めてサイトの更新が自動化されます。

以下はUbuntu系Linux、Nginx、HTTP-01認証、Let’s Encryptを例にします。acme.shの初期設定の認証局はバージョンや設定によって異なるため、この記事では明示的に指定します。商用CAのACMEを利用する場合は、提供元のディレクトリURL、アカウント登録方法、認証方式、商品条件に従ってください。

発行前の準備

  • example.com のDNSが、このWebサーバを指している。
  • インターネットから http://example.com/.well-known/acme-challenge/ にポート80でアクセスできる。
  • Nginxの example.com 用サイト設定と、Webルート /var/www/example.com/public を用意している。
  • 管理者が sudo を使える。cron、curl、git、opensslを利用できる。

HTTP-01認証では、認証局が指定URLへアクセスします。ロードバランサーやCDNを使う場合は、認証要求が正しいWebルートまで届くようにしてください。ポート80を開けられない環境やワイルドカード証明書では、後述のDNS-01を検討します。

まず、Webルートを用意します。既存サイトであれば、そのサイトの実際のWebルートを使ってください。

sudo mkdir -p /var/www/example.com/public

Nginxには、少なくとも example.com のHTTPリクエストをこのWebルートから配信する設定が必要です。新規サイト向けの最小例です。既存の設定を上書きせず、対象のserverブロックに合わせてください。

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com/public;

    location ^~ /.well-known/acme-challenge/ {
        try_files $uri =404;
    }
}

設定を反映する前に構文を確認します。

sudo nginx -t
sudo systemctl reload nginx

nginx -t は設定の検査、reload は稼働中のNginxに設定を読み直させる操作です。認証用ディレクトリへのアクセスがリダイレクトや認証画面に吸い込まれないか、外部ネットワークからも確認しておきます。

acme.shをインストールする

今回は公式リポジトリから取得し、rootの管理領域へインストールします。メールアドレスはご自身が受信できるものに変えてください。

sudo git clone https://github.com/acmesh-official/acme.sh.git /opt/acme.sh-source
sudo /opt/acme.sh-source/acme.sh --install -m admin@example.com
sudo /root/.acme.sh/acme.sh --version
コマンド役割
git clone公式のソースを取得します。
--install -macme.shを配置し、アカウント連絡先メールアドレスを設定します。
--versionインストールされた版を確認します。

上記は新規導入例です。/opt/acme.sh-source がすでに存在する場合は、既存の導入状態を確認してください。以降は sudo /root/.acme.sh/acme.sh とフルパスで実行します。インストーラーは通常、日次のcronも登録しますが、後で必ず確認します。

利用する認証局を明示します。

sudo /root/.acme.sh/acme.sh --set-default-ca --server letsencrypt

HTTP-01で証明書を発行する

sudo /root/.acme.sh/acme.sh --issue \
  --server letsencrypt \
  -d example.com \
  -w /var/www/example.com/public

--issue は新しい証明書の取得、-d は対象ドメイン、-w はNginxが公開するWebルートです。acme.shが認証用ファイルを一時的に配置し、認証局がHTTP経由で確認します。取得に失敗したら、DNS、ポート80、Webルート、リダイレクト、CDNの経路を順番に確認します。

acme.shの既定はECC鍵です。サーバや連携先にRSA鍵が必要な場合は、要件を確かめたうえで発行時に --keylength 2048 などを指定してください。以下の配置例は既定のECC証明書を前提とします。

証明書をNginxへ配置する

証明書が取得できても、Nginxが古いファイルを読み続ければサイトは更新されません。公開用の配置先を作ります。

sudo mkdir -p /etc/nginx/ssl/example.com

次に、acme.shへ配置先と更新後の再読み込みコマンドを登録します。

sudo /root/.acme.sh/acme.sh --install-cert -d example.com --ecc \
  --key-file /etc/nginx/ssl/example.com/key.pem \
  --fullchain-file /etc/nginx/ssl/example.com/fullchain.pem \
  --reloadcmd "systemctl reload nginx"

key.pem は秘密鍵、fullchain.pem はサーバ証明書と中間証明書を含むファイルです。--reloadcmd により、更新後にNginxが新しい証明書を読み込みます。acme.sh内部の ~/.acme.sh/ にある証明書ファイルをWebサーバから直接参照する運用は避け、必ず --install-cert で公開用のパスへ配置します。秘密鍵は公開ディレクトリに置かず、アクセス権を絞って管理してください。

Nginxの443番ポート用serverブロックに、次の証明書パスを指定します。既存のサイト設定に組み込んでください。

server {
    listen 443 ssl;
    server_name example.com;
    root /var/www/example.com/public;

    ssl_certificate     /etc/nginx/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com/key.pem;
}
sudo nginx -t
sudo systemctl reload nginx

Nginxの設定検査が成功してから反映します。初回の --install-cert 時点でまだ443番の設定がなく、再読み込みコマンドが失敗した場合は、443番の設定を完成させたあと、上の --install-cert を再実行してください。

自動更新の設定を確認する

sudo crontab -l
sudo /root/.acme.sh/acme.sh --list
sudo /root/.acme.sh/acme.sh --info -d example.com --ecc

cronに acme.sh --cron の行があるか確認します。--list は管理中の証明書、--info は対象ドメインの更新予定や保存された設定の確認に使います。cronが見当たらない場合、インストール時のメッセージやcronの導入状態を確認し、acme.shの公式手順に沿って日次実行を設定してください。同じ処理のcronを二重登録しないことも大切です。

日次実行は「毎日必ず新しい証明書を発行する」という意味ではありません。acme.shが更新の必要性を判定し、必要なときに取得、配置、Nginxの再読み込みを行います。動作確認のために --force を繰り返すと、認証局の発行制限に触れる可能性があります。

実際に利用者へ提示される証明書も確認します。

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

-servername はSNIで確認対象のホスト名を指定します。表示された有効期限と発行元が想定どおりかを見てください。cron登録済み、ファイル更新済み、公開中の証明書更新済みの3点を確認して、ようやく一連の作業が完了です。期限監視も別途設定しておくと、更新失敗を早期に発見できます。

よくあるつまずき

症状確認すること
HTTP認証に失敗するDNSの宛先、ポート80、Webルート、CDN・リダイレクト・認証制限を確認。
発行されたのにサイトは古い証明書のまま--install-cert の配置先、Nginxの証明書パス、--reloadcmd の成否を確認。
定期更新されないsudo crontab -l、cronサービス、実行ユーザー、acme.shの実行ログを確認。
ワイルドカードを取得したいHTTP-01では取得できません。DNS-01とDNS事業者のAPI連携を検討。

ログを残したい場合は、--cron を実行する設定に --log オプションを付ける方法などがあります。ログの保存先と権限を決め、失敗を検知できるようにしてください。DNS-01を手でTXTレコードを置く方式で実行すると、次回も手作業が必要です。自動更新が目的ならDNS APIに対応した設定を選びます。

まとめ

証明書の自動化は、コマンド1回で発行して終わりではありません。認証局から取得する、サーバへ配置する、Webサーバに読み直させる、失敗に気づけるところまでつながって、初めて運用できます。

「更新日は覚えているはず」という運用は、サイトの信頼を人の記憶に預けています。今日のうちに自動更新の経路を作り、公開中の証明書まで確認しましょう

コメント

タイトルとURLをコピーしました