UbuntuでCertbotを使ったLet’s Encrypt SSL無料取得:実際のエラーからauto-renewalまで

Ubuntu tutorial - IT technology blog
Ubuntu tutorial - IT technology blog

午前2時。スマホが鳴る。監視ツールからアラート:“SSL certificate expired — site returning ERR_CERT_DATE_INVALID”。クライアントから電話が来て、ブラウザが真っ赤な警告を表示しており、売上が1分ごとに失われていると告げられた。

この状況を初めて経験したのは2年前、Ubuntu 20.04の本番サーバーでのことだった。当時はLet’s Encrypt証明書をaptで手動インストールし、auto-renewの設定を忘れていた。そして有効期限が切れても誰も気づかなかった。あの日以来、二度とそんなことが起きないよう誓った。

実際の問題 — なぜサイトに「保護されていない通信」と表示されるのか?

以下の3つの状況が、私がこれまでデバッグしてきたケースの約95%を占める:

  • SSLを最初から設定していない:サーバーを新規セットアップしてHTTPのみで動作させると、Chromeのアドレスバーに「保護されていない通信」と表示され、Google Search Consoleでも警告が出る。
  • 証明書の期限切れ:Let’s Encryptの有効期間は90日。auto-renewが動作しない場合(ファイアウォールがポート80をブロック、DNS変更、cronjobの停止など)、証明書は事前通知なく自動的に期限切れになる。
  • インストール済みだがWebサーバーがロードしない:Certbotは正常に実行されたが、Nginx/ApacheのconfigがリロードされておらずサイトがHTTPのままになっている。

それぞれの状況には異なる原因と修正方法がある — 以下で一つずつ解説する。

なぜLet’s Encryptはエラーが起きやすいのか?

Let’s Encryptは誰でも証明書を発行するわけではない。ACMEメカニズムはこのように動作する:「私はexample.comを所有しています」と申告すると、Let’s Encryptのサーバーは「では証明してください」と要求する — Webサーバーに特別なファイルを配置するか、DNSにTXTレコードを追加する方法で。検証が完了して初めて90日間の証明書が発行される。

すべてが正常に動作している場合は完全自動化される。しかし、チェーンの中の何かがブロックされると、チャレンジが失敗して証明書を取得できなくなる。

よくある原因:

  • UFWまたはクラウド(AWS/GCP/DigitalOcean)のセキュリティグループによるポート80のブロック
  • DNS AレコードがサーバーのIPを正しく指定していない — サーバー移行時によく発生し、DNSの伝播には15〜30分かかる
  • aptからインストールした古いバージョンのCertbotにはバグが多く、snapバージョンでは修正済み
  • CertbotがStandaloneモードを使用している間、WebサーバーがPort 80を占有している

Certbotの3つのインストール方法 — どれを選ぶべきか?

ステップ0:作業前の確認事項

まず何もインストールしない。先にこれらの基本事項を確認する:

# DNSがサーバーを指しているか確認
dig +short yourdomain.com

# ポート80が開いているか確認
sudo ufw status
curl -I http://yourdomain.com

# 古いcertbotがインストールされているか確認
certbot --version

digが正しいサーバーのIPを返し、curlで接続できれば — 準備完了、開始しよう。

方法1:Snap経由でCertbotをインストール(推奨)

Snapは2021年からLet’s Encryptが公式に推奨する方法。理由はシンプル:常に最新バージョンが維持され、自動更新され、システムとのコンフリクトが発生しない。

始める前に注意事項:Let’s Encryptはドメインごとに週5件の重複証明書制限がある。繰り返しテストして失敗が続く場合は、本番のクォータを消費しないよう--stagingフラグを追加すること。

# aptでインストールした古いcertbotを削除(コンフリクト回避)
sudo apt remove certbot

# snap経由でcertbotをインストール
sudo snap install --classic certbot

# certbotコマンドを直接使用するためのシンボリックリンクを作成
sudo ln -s /snap/bin/certbot /usr/bin/certbot

証明書を取得する — certbotにはNginxとApache専用のプラグインがあり、configを自動的に修正する:

# Nginxを使用している場合
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# Apacheを使用している場合
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com

# Webサーバーを設定していない場合(standaloneモード)
sudo certbot certonly --standalone -d yourdomain.com

実行中、certbotは有効期限の警告を受け取るためのメールアドレスとToSへの同意を求める。完了すると、nginx configを自動修正する:SSLブロックの追加、cipher suitesの設定、HTTP → HTTPSリダイレクトの設定。

方法2:aptでインストール(Ubuntu 20.04以前)

snapを使いたくない場合は、aptでも可能:

sudo apt update
sudo apt install certbot python3-certbot-nginx

sudo certbot --nginx -d yourdomain.com

実際のデメリット:UbuntuのaptリポジトリのバージョンはSnapより数ヶ月遅れていることが多い。certbot 1.12(apt版)のバグに遭遇したが、snap版の2.xではとっくに修正済みだった。

方法3:DNSチャレンジによるワイルドカード証明書

複数のサブドメイン(api.example.comapp.example.comcdn.example.comなど)があり、1つの証明書で全てをカバーしたい場合、ワイルドカードが解決策:

sudo certbot certonly \
  --manual \
  --preferred-challenges dns \
  -d "*.yourdomain.com" \
  -d yourdomain.com

DNSチャレンジでは手動でDNSにTXTレコードを追加する必要がある — DNS APIがなければ自動化できない。Cloudflare API keyで自動化できる場合のみこの方法を使い、そうでなければ各サブドメインを個別にインストールする方がシンプルだ。

本番環境対応のセットアップ — 2024年から使用している手順

あの午前2時の証明書期限切れ事件の後、本番環境に適用する前にUbuntu 22.04のステージング環境でこの手順を徹底的にテストした。必要な作業の全内容はこちらだ:

1. Nginxで証明書をインストールして取得する

# Nginxが起動しているか確認
sudo systemctl status nginx

# certbot snapをインストール
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# 証明書を取得 — certbotがnginx configを自動修正
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com \
  --email [email protected] \
  --agree-tos \
  --no-eff-email

--no-eff-emailフラグはEFFからのプロモーションメールを受け取らないようにするためのもの。

2. 自動更新の確認

certbot snapは自動更新のためのsystemd timerを自動でインストールする。インストール直後に確認する:

# timerがアクティブか確認
sudo systemctl status snap.certbot.renew.timer

# 更新プロセスをテスト(実際には更新しない)
sudo certbot renew --dry-run

--dry-runは実際の証明書を変更せずに更新プロセス全体をシミュレートする。ここでエラーが出れば、証明書の自動更新時にも同じエラーが発生する — 今のうちに検出できた方がよい。

3. インストール済み証明書の確認

# 全証明書と有効期限を表示
sudo certbot certificates

# 出力例:
# Found the following certs:
#   Certificate Name: yourdomain.com
#     Domains: yourdomain.com www.yourdomain.com
#     Expiry Date: 2026-05-28 (VALID: 89 days)
#     Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem

4. 更新後にNginxをリロードするdeployフックの設定

certbotが更新した後、新しい証明書を適用するためにWebサーバーをリロードする必要がある。certbot snapは通常これを自動処理するが、常に信頼できるわけではない。確実を期すために明示的なdeployフックを作成するのがベストだ:

# 現在のdeployフックを確認
ls /etc/letsencrypt/renewal-hooks/deploy/

# 更新成功後にnginxを自動リロードするフックを作成
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'EOF'
#!/bin/bash
nginx -t && systemctl reload nginx
EOF

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

これ以降、certbotの更新が成功するたびにnginxが自動リロードされる。新しい証明書がインストールされたのにnginxが古い期限切れ証明書を引き続き提供するという状況はなくなる。

よくあるエラーの対処法

エラー:”Problem binding to port 80″

# NginxがPort 80を占有している — 一時停止し、証明書を取得してから再起動
sudo systemctl stop nginx
sudo certbot certonly --standalone -d yourdomain.com
sudo systemctl start nginx

エラー:”Connection timed out”

# UFWによるポート80のブロック
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload

クラウドを使用している場合、UFWはファイアウォールの一層に過ぎない — Security Group(AWS)またはFirewallルール(GCP/DigitalOcean)でもポートを開放する必要がある。

証明書が期限切れになった場合の緊急更新

# 即座に強制更新
sudo certbot renew --force-renewal

# 更新後にnginxをリロード
sudo nginx -t && sudo systemctl reload nginx

インストール後のSSLグレードの確認

# ターミナルからの簡易チェック
curl -vI https://yourdomain.com 2>&1 | grep -E "SSL|TLS|expire|issuer"

より詳細に確認したい場合はssllabs.com/ssltestのSSL Labsを使用する。A+からFまでのグレードを付け、設定の弱点(古いcipher suite、HSTSが無効など)を正確に指摘してくれる。certbotのデフォルト設定で正しく構成されたサイトは通常AまたはA+を取得できる。

最後に、Let’s Encryptからのメールを無視しないこと。certbot実行時にメールアドレスを登録していれば、有効期限の30日前、20日前、10日前に警告が届く。メールが届いたのにauto-renewが動作しない場合は、すぐにデバッグが必要なことを意味する。実際の有効期限まで待ってはいけない。その時、もう午前2時かもしれない。

Share: