Skip to content
Published on

[DevOps] Let's EncryptとCertbot完全ガイド:無料TLS証明書

Authors

1. TLS/SSL証明書の基本概念

1.1 なぜTLS証明書が必要か

TLS(Transport Layer Security)証明書はウェブ通信の3つの核心的なセキュリティ要素を提供します。

  • 暗号化(Encryption):クライアントとサーバー間のデータを暗号化し盗聴を防止
  • 認証(Authentication):サーバーが本当にそのドメインの所有者であることを証明
  • 完全性(Integrity):データが伝送中に改ざんされていないことを保証

1.2 証明書チェーン構造

┌───────────────────────┐
│   Root CA Certificate │  <- ブラウザ/OSに内蔵された信頼アンカー
│  (Self-Signed)        │
└───────────┬───────────┘
            │ 署名
┌───────────▼───────────┐
│ Intermediate CA Cert  │  <- 中間CA証明書
│ (Signed by Root CA)   │
└───────────┬───────────┘
            │ 署名
┌───────────▼───────────┐
│  Server Certificate   │  <- ドメイン証明書(Leaf)
│ (Signed by Inter. CA) │
└───────────────────────┘

1.3 公開鍵/秘密鍵ペア

  • 秘密鍵(Private Key):サーバーのみが保有、絶対に外部漏洩禁止
  • CSR(Certificate Signing Request):公開鍵 + ドメイン情報、CAに提出
  • 証明書(Certificate):CAが署名した公開鍵 + ドメイン情報

2. Let's Encryptとは

2.1 概要

Let's EncryptはISRG(Internet Security Research Group) が運営する無料・自動化・オープンな認証機関(CA)です。

主要な特徴:

  • 無料:ドメイン検証(DV)証明書を無料で発行
  • 自動化:ACMEプロトコルで証明書の発行/更新を完全自動化
  • オープン:オープンソースプロトコルとツールを使用
  • 有効期間:90日(短いサイクルでセキュリティ強化)
  • 信頼性:すべての主要ブラウザとOSで信頼

2.2 証明書タイプの比較

タイプ検証レベル発行時間費用Let's Encrypt
DV(Domain Validation)ドメイン所有確認数分無料〜安価対応
OV(Organization Validation)組織確認数日有料非対応
EV(Extended Validation)拡張検証数週間高額非対応

3. ACMEプロトコルの動作原理

ACME(Automatic Certificate Management Environment)は証明書発行を自動化するプロトコルです。

3.1 全体フロー

┌────────┐                          ┌──────────────┐
│ Certbot│                          │ Let's Encrypt│
│(Client)│                          │   (CA/ACME)  │
└───┬────┘                          └──────┬───────┘
    │  1. Account Registration              │
    │──────────────────────────────────────>│
    │  2. Account Created                   │
    │<──────────────────────────────────────│
    │                                       │
    │  3. Order (domain list)               │
    │──────────────────────────────────────>│
    │  4. Authorizations + Challenges       │
    │<──────────────────────────────────────│
    │                                       │
    │  5. Respond to Challenge              │
    │  (HTTP-01 / DNS-01 / TLS-ALPN-01)    │
    │──────────────────────────────────────>│
    │                                       │
    │  6. Challenge Validated               │
    │<──────────────────────────────────────│
    │                                       │
    │  7. Finalize (send CSR)               │
    │──────────────────────────────────────>│
    │  8. Certificate issued                │
    │<──────────────────────────────────────│

3.2 HTTP-01チャレンジ

最も一般的なチャレンジ方式です。ポート80で特定のファイルにアクセスできるかを検証します。

Let's Encrypt -> http://yourdomain.com/.well-known/acme-challenge/TOKEN_VALUE

検証プロセス:
1. Certbotがトークンファイルをウェブサーバーに配置
2. Let's EncryptがHTTPでそのファイルにアクセス
3. ファイル内容が期待値と一致すればドメイン所有を確認

利点:

  • 最もシンプルで一般的
  • 追加のDNS設定が不要
  • ほとんどのウェブサーバーで簡単に設定

制限:

  • ポート80が外部からアクセス可能でなければならない
  • ワイルドカード証明書の発行不可

3.3 DNS-01チャレンジ

DNS TXTレコードを作成してドメイン所有を証明します。ワイルドカード証明書の発行に必須です。

Let's Encrypt -> DNS照会: _acme-challenge.yourdomain.com TXT

検証プロセス:
1. Certbotがトークンを計算
2. _acme-challenge.yourdomain.com TXTレコードにトークン値を設定
3. Let's EncryptがDNSを照会して値を確認
4. 一致すればドメイン所有を確認

利点:

  • ワイルドカード証明書の発行が可能
  • ウェブサーバーがなくても可能
  • ポート80が開いていなくても良い

3.4 チャレンジ比較表

機能HTTP-01DNS-01TLS-ALPN-01
ポート80なし(DNS)443
ワイルドカードXOX
ウェブサーバー必要OXO
自動化難易度低い中程度(DNS API必要)高い
主な用途一般ウェブサーバーワイルドカード、内部サーバー特殊環境

4. Certbotのインストール

4.1 Ubuntu / Debian

# snapでインストール(推奨)
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

4.2 CentOS / RHEL / Rocky Linux

sudo dnf install epel-release
sudo dnf install certbot
sudo dnf install python3-certbot-nginx

4.3 macOS

brew install certbot

4.4 Docker

docker run -it --rm \
  -v /etc/letsencrypt:/etc/letsencrypt \
  -v /var/lib/letsencrypt:/var/lib/letsencrypt \
  certbot/certbot certonly --help

5. 証明書の発行方法

5.1 Standaloneモード

Certbotが自前のウェブサーバーを起動してHTTP-01チャレンジを処理します。

sudo systemctl stop nginx

sudo certbot certonly --standalone \
  -d example.com \
  -d www.example.com \
  --agree-tos \
  --email admin@example.com \
  --non-interactive

sudo systemctl start nginx

5.2 Webrootモード

既存のウェブサーバーを停止せずに証明書を発行します。

Nginx設定追加:

server {
    listen 80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}
sudo mkdir -p /var/www/certbot

sudo certbot certonly --webroot \
  -w /var/www/certbot \
  -d example.com \
  -d www.example.com \
  --agree-tos \
  --email admin@example.com

5.3 Nginxプラグイン

sudo certbot --nginx \
  -d example.com \
  -d www.example.com \
  --agree-tos \
  --email admin@example.com

5.4 DNSプラグイン(ワイルドカード証明書)

Cloudflare DNSプラグイン:

sudo snap install certbot-dns-cloudflare

cat > /etc/letsencrypt/cloudflare.ini << 'CFEOF'
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
CFEOF
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d "example.com" \
  -d "*.example.com" \
  --agree-tos \
  --email admin@example.com

AWS Route 53 DNSプラグイン:

sudo snap install certbot-dns-route53

sudo certbot certonly \
  --dns-route53 \
  -d "example.com" \
  -d "*.example.com" \
  --agree-tos \
  --email admin@example.com

6. 発行されたファイル構造

/etc/letsencrypt/live/example.com/
  cert.pem       # サーバー証明書のみ
  chain.pem      # 中間CA証明書チェーン
  fullchain.pem  # cert.pem + chain.pem(サーバーで使用)
  privkey.pem    # 秘密鍵
ファイル内容用途
cert.pemサーバー証明書単独使用(めったに使わない)
chain.pem中間CA証明書OCSP Stapling用
fullchain.pemサーバー証明書 + 中間CANginx ssl_certificateに使用
privkey.pem秘密鍵Nginx ssl_certificate_keyに使用

Nginxに適用:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

7. 自動更新の設定

7.1 更新テスト

sudo certbot renew --dry-run

7.2 systemd Timer(推奨)

# /etc/systemd/system/certbot-renew.timer
[Unit]
Description=Certbot renewal timer

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=3600
Persistent=true

[Install]
WantedBy=timers.target
# /etc/systemd/system/certbot-renew.service
[Unit]
Description=Certbot renewal service

[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
sudo systemctl daemon-reload
sudo systemctl enable --now certbot-renew.timer

7.3 Crontab

# crontab -e
0 2,14 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"

7.4 更新フック

# デプロイフック(更新成功時のみ実行)
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'HOOKEOF'
#!/bin/bash
systemctl reload nginx
echo "Certificate renewed and nginx reloaded at $(date)" >> /var/log/certbot-deploy.log
HOOKEOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

8. Rate Limitsの注意事項

制限説明
Certificates per Registered Domain50/週同一ドメインに対して週50個
Duplicate Certificate5/週同一ドメインセットに対して5個
Failed Validations5/時間/アカウント/ホスト名検証失敗制限
New Orders300/3時間新規注文制限

Staging環境の活用:

sudo certbot certonly --standalone \
  --staging \
  -d example.com \
  --agree-tos \
  --email admin@example.com

9. 代替ツール

9.1 acme.sh

純粋なシェルスクリプトで書かれたACMEクライアントです。

curl https://get.acme.sh | sh

acme.sh --issue -d example.com -w /var/www/html

# ワイルドカード(DNS API使用)
acme.sh --issue \
  -d example.com \
  -d "*.example.com" \
  --dns dns_cf \
  --dnssleep 120

# Nginxにインストール
acme.sh --install-cert -d example.com \
  --key-file       /etc/nginx/ssl/example.com.key \
  --fullchain-file /etc/nginx/ssl/example.com.fullchain.pem \
  --reloadcmd     "systemctl reload nginx"

9.2 Caddy(自動HTTPS)

CaddyはウェブサーバーGTRで自動的にHTTPSを処理します。

# Caddyfile
example.com {
    root * /var/www/html
    file_server
}
# これだけでLet's Encrypt証明書の自動発行と更新!

9.3 cert-manager(Kubernetes)

Kubernetes環境で証明書を自動管理します。

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            class: nginx
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts:
        - app.example.com
      secretName: app-tls-secret
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80

10. トラブルシューティング

10.1 一般的なエラーと解決法

チャレンジ失敗:

# HTTP-01チャレンジファイルのアクセス確認
curl -v http://yourdomain.com/.well-known/acme-challenge/test

# ファイアウォール確認(ポート80)
sudo ufw status
sudo iptables -L -n | grep 80

# DNS確認
dig +short yourdomain.com

証明書更新失敗:

sudo certbot certificates
sudo certbot renew --dry-run -v
sudo cat /var/log/letsencrypt/letsencrypt.log

証明書情報確認:

# 証明書内容確認
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text -noout

# 有効期限確認
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -enddate -noout

# リモートサーバー証明書確認
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | \
  openssl x509 -text -noout

11. セキュリティのベストプラクティス

  1. 秘密鍵の保護:privkey.pemの権限を600に設定、rootのみ読み取り可能
  2. 自動更新の監視:更新失敗時のアラート設定
  3. まずStaging:新しい設定は常にStaging環境でテスト
  4. HSTS適用:証明書発行後にHSTSヘッダーを設定
  5. 証明書透明性の監視:crt.shでドメイン証明書発行履歴を監視
  6. バックアップ:/etc/letsencryptディレクトリの定期バックアップ

12. まとめ

Let's EncryptとCertbotは無料TLS証明書の標準となりました。要点整理:

  • HTTP-01チャレンジは最もシンプルだがワイルドカードはDNS-01が必須
  • CertbotのWebrootまたはNginxプラグインが最も実用的
  • 自動更新はsystemd timerまたはcrontabで設定
  • Kubernetes環境ではcert-managerが標準
  • Rate Limitsを理解しテスト時はStagingサーバーを活用
  • 証明書発行後はNginx TLSセキュリティ設定を必ず最適化すべき