0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ACMを使わない証明書更新:well-known/acme-challenge(HTTP-01)・DNS-01・TLS-ALPN-01の仕組みとcertbot実践

0
Posted at

はじめに

AWS ACM を使っていると、証明書の更新を意識することはほとんどない。DNS 検証にしておけば、期限が来ても勝手に更新される。

では、ACM が使えない環境——EC2 で直接 TLS 終端する、オンプレ、VPS——では何が起きているのか。

そこで登場するのが ACME プロトコル(Let's Encrypt など)による証明書の自動発行・更新だ。

この記事では:

  • ドメイン検証(DV)の仕組み(なぜ「所有確認」が必要か)
  • HTTP-01/.well-known/acme-challenge/ の正体)
  • DNS-01(ワイルドカード証明書はこれ一択)
  • TLS-ALPN-01(443番だけで完結する方式)
  • 更新の自動化(systemd timer と deploy hook)
  • AWS ACM との比較(ACM が隠している作業の正体)

を順番に整理する。コマンドと出力例つき。


前提:ドメイン検証(DV)とは

CA(認証局)は、証明書を発行する前に「申請者がそのドメインの持ち主かどうか」を確認する必要がある。

これが**ドメイン検証(Domain Validation, DV)**だ。

申請者「example.com の証明書をください」
CA  「本当に example.com の持ち主? 証明してみて」
申請者「(ドメインの支配権を使って証明する)」
CA  「OK、発行します」

この「証明してみて」のやりとりを自動化するプロトコルが ACME(Automatic Certificate Management Environment)で、代表的な CA が Let's Encrypt

証明の方法(チャレンジ)は3方式ある:

方式 使うもの ポート ワイルドカード 主な用途
HTTP-01 Web サーバー上のファイル 80 一般的な Web サーバー
DNS-01 DNS の TXT レコード 53(DNS側) ワイルドカード、Webサーバーなし
TLS-ALPN-01 TLS ハンドシェイク 443 80番を開けられない環境

👉 どの方式も証明していることは同じ。「そのドメインの支配権を持っている」こと。Web サーバーに置けるのも、DNS を書き換えられるのも、持ち主だけだから。


HTTP-01:well-known/acme-challenge の正体

最もよく使われる方式。Web サーバーのログで見かける /.well-known/acme-challenge/ へのアクセスは、この検証のためのものだ。

検証の流れ

1. certbot が Let's Encrypt に「example.com の証明書がほしい」と申請
2. Let's Encrypt がトークン(ランダム文字列)を発行
3. certbot がトークンを含むファイルを Web サーバーに配置
   → /var/www/html/.well-known/acme-challenge/<トークン>
4. Let's Encrypt が外部から HTTP でアクセスして確認
   → GET http://example.com/.well-known/acme-challenge/<トークン>
5. 内容が一致すれば検証成功 → 証明書発行

👉 「そのドメインの Web サーバーにファイルを置ける = ドメインの持ち主」という理屈。

certbot で実行する

webroot モード(稼働中の nginx / Apache を止めずに検証できる):

$ sudo certbot certonly --webroot -w /var/www/html -d www.example.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for www.example.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/www.example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/www.example.com/privkey.pem
This certificate expires on 2026-10-14.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

検証中に何が置かれているのか

検証の瞬間、webroot にはこんなファイルが置かれる(通常は一瞬で消えるので --debug-challenges で止めると観察できる):

$ ls /var/www/html/.well-known/acme-challenge/
5jmVXCLIN-Wx7Yjky4TDMIhBWJ0OWZKWMOJFmANLDDU

$ cat /var/www/html/.well-known/acme-challenge/5jmVXCLIN-Wx7Yjky4TDMIhBWJ0OWZKWMOJFmANLDDU
5jmVXCLIN-Wx7Yjky4TDMIhBWJ0OWZKWMOJFmANLDDU.xxN2ZS3giFsw9-N0LOOwtBIrLBHcMcQjLBOwXK0jvvE

ファイルの中身は <トークン>.<アカウント鍵のフィンガープリント>。Let's Encrypt は外部からこう確認する:

$ curl http://www.example.com/.well-known/acme-challenge/5jmVXCLIN-Wx7Yjky4TDMIhBWJ0OWZKWMOJFmANLDDU
5jmVXCLIN-Wx7Yjky4TDMIhBWJ0OWZKWMOJFmANLDDU.xxN2ZS3giFsw9-N0LOOwtBIrLBHcMcQjLBOwXK0jvvE

❌ 「HTTPS の証明書の検証だから 443 番で行われる」は誤解。HTTP-01 の検証は必ず 80 番の HTTP から始まる。まだ有効な証明書がない状態で検証するのだから当然だ(80→443 へのリダイレクトには追従する)。

HTTP-01 の制約

  • 80 番ポートが外部から到達可能であることが必須。閉じている環境では使えない
  • ワイルドカード証明書(*.example.com)は取得できない
  • ロードバランサ配下で複数台に分散している場合、検証リクエストがファイルを置いたサーバーに届くとは限らない
    • 👉 対策:/.well-known/acme-challenge/ だけ特定サーバーにルーティングする、共有ストレージに置く、または DNS-01 を使う

DNS-01:TXT レコードで証明する

Web サーバーではなく DNS に検証値を置く方式。

検証の流れ

1. certbot が申請 → Let's Encrypt がトークンを発行
2. certbot(または管理者)が TXT レコードを作成
   → _acme-challenge.example.com. IN TXT "<検証値>"
3. Let's Encrypt が DNS を引いて確認
4. 一致すれば検証成功 → 証明書発行

👉 「DNS レコードを書き換えられる = ドメインの持ち主」という理屈。Web サーバーの存在すら不要。

certbot で実行する(手動モード)

仕組みを見るために、まず手動モードで:

$ sudo certbot certonly --manual --preferred-challenges dns -d '*.example.com'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for *.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.example.com.

with the following value:

X4a9pVoh_kT3rGm2LcYuA8sBq1DfE6wZhJnP0iRxOyM

Before continuing, verify the TXT record has been deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

TXT レコードを設定したら、続行する前に自分でも確認する:

$ dig TXT _acme-challenge.example.com +short
"X4a9pVoh_kT3rGm2LcYuA8sBq1DfE6wZhJnP0iRxOyM"

Enter を押すと検証が走り、証明書が発行される:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2026-10-14.

DNS-01 の特徴

  • ワイルドカード証明書(*.example.com)はこの方式でしか取得できない
  • 80/443 番が閉じていても使える(検証は DNS 側で完結)
  • 内部向けサーバー(外部から HTTP 到達不可)の証明書も取得できる

❌ ただし手動モードは運用に耐えない。更新のたびに TXT レコードの値が変わるので、90日ごとに手作業が発生する。

👉 実運用では DNS プロバイダの API と連携するプラグインで自動化する:

# Route 53 の場合
$ sudo certbot certonly --dns-route53 -d 'example.com' -d '*.example.com'

certbot-dns-cloudflarecertbot-dns-google など主要プロバイダのプラグインが揃っている。


TLS-ALPN-01:443番だけで完結する方式

3つ目の方式。HTTP を一切使わず、TLS ハンドシェイクの中で検証する

検証の流れ

1. クライアントが申請 → Let's Encrypt がトークンを発行
2. クライアントが検証値を埋め込んだ「検証専用の自己署名証明書」を用意
3. Let's Encrypt が 443 番に TLS 接続
   → ALPN 拡張で "acme-tls/1" プロトコルを指定
4. サーバーが検証専用証明書を提示 → 中身が一致すれば成功

👉 ALPN(Application-Layer Protocol Negotiation)は TLS ハンドシェイクで「この接続で話すプロトコル」を合意する拡張。HTTP/2 の h2 ネゴシエーションと同じ仕組みを検証に流用している。

certbot は非対応、lego などで使う

certbot は TLS-ALPN-01 に対応していない。Go 製の ACME クライアント lego での例:

$ sudo lego --email admin@example.com --domains example.com --tls run
2026/07/16 10:00:00 [INFO] [example.com] acme: Obtaining bundled SAN certificate
2026/07/16 10:00:01 [INFO] [example.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/1234567890
2026/07/16 10:00:01 [INFO] [example.com] acme: use tls-alpn-01 solver
2026/07/16 10:00:02 [INFO] [example.com] acme: Trying to solve TLS-ALPN-01
2026/07/16 10:00:05 [INFO] [example.com] The server validated our request
2026/07/16 10:00:07 [INFO] [example.com] acme: Validations succeeded; requesting certificates
2026/07/16 10:00:08 [INFO] [example.com] Server responded with a certificate.

使いどころ

  • 80 番を開けられない(ポリシーやファイアウォールの制約)環境
  • Traefik / Caddy などは TLS-ALPN-01 を組み込みでサポートしており、リバースプロキシが証明書取得まで面倒を見る構成で使われる

👉 ニッチに見えるが、「443 しか開いていないサーバーで、DNS API も触れない」ときの選択肢として覚えておくと効く。


更新の自動化:発行して終わりではない

Let's Encrypt の証明書は有効期限90日。意図的に短くして「自動更新しない運用」を許さない設計になっている。

更新のテスト

certbot renew は期限が30日を切った証明書だけを更新する。まずは dry-run で通ることを確認:

$ sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/www.example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simulating renewal of an existing certificate for www.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/www.example.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

👉 更新時は発行時と同じ検証がもう一度走る。webroot なら 80 番が、DNS プラグインなら API 認証情報が、更新時にも有効である必要がある。

定期実行の確認

パッケージ版の certbot は systemd timer(または cron)を自動で仕込む。動いているか確認:

$ systemctl list-timers certbot.timer
NEXT                        LEFT     LAST                        PASSED UNIT          ACTIVATES
Wed 2026-07-16 23:41:00 JST 6h left  Wed 2026-07-16 11:14:32 JST 2h ago certbot.timer certbot.service

1日2回起動し、期限が近い証明書だけ更新する。

更新後の反映:deploy hook

❌ ありがちな事故:「renew は成功しているのに、サイトの証明書が古いまま」。

nginx は起動時に証明書ファイルを読み込む。ファイルが更新されても reload しない限り古い証明書を掴んだままだ。

deploy hook で更新成功時だけ reload させる:

$ sudo certbot renew --deploy-hook 'systemctl reload nginx'

一度 --deploy-hook 付きで実行すると renewal 設定に永続化され、以降の自動更新でも実行される:

# /etc/letsencrypt/renewal/www.example.com.conf(抜粋)
[renewalparams]
authenticator = webroot
webroot_path = /var/www/html
renew_hook = systemctl reload nginx

👉 /etc/letsencrypt/renewal-hooks/deploy/ にスクリプトを置く方法もある。全証明書共通のフックならこちら。


AWS ACM との比較:ACM が隠していたもの

ここまで読むと、ACM の DNS 検証で「この CNAME を DNS に追加してください」と言われる意味がわかる。

$ aws acm request-certificate --domain-name example.com --validation-method DNS
{
    "CertificateArn": "arn:aws:acm:ap-northeast-1:123456789012:certificate/11111111-2222-3333-4444-555555555555"
}

$ aws acm describe-certificate \
    --certificate-arn arn:aws:acm:ap-northeast-1:123456789012:certificate/11111111-2222-3333-4444-555555555555 \
    --query 'Certificate.DomainValidationOptions[0].ResourceRecord'
{
    "Name": "_3d1bf4e6a7c9e2f8b5a0d4c1e6f9a2b3.example.com.",
    "Type": "CNAME",
    "Value": "_9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c.acm-validations.aws."
}

「DNS にレコードを置いてドメインの支配権を証明する」——構造は DNS-01 とまったく同じだ。

違いは、ACM は検証レコードを CNAME で AWS 側に委譲させること。検証値の管理と再検証を AWS が握るので、レコードを置いたままにすれば更新も全自動になる。

👉 つまり ACM は「ACME 相当の検証と更新を、マネージドにして見えなくしたもの」。魔法ではない。

比較表

AWS ACM Let's Encrypt + certbot
検証方式 DNS 検証(DNS-01 相当)/ メール検証(自動更新できず非推奨気味) HTTP-01 / DNS-01 / TLS-ALPN-01
自動更新 完全自動(レコード設置のみ) certbot renew + timer を自前で構成
秘密鍵 取り出せない(標準のパブリック証明書。AWS 内部で管理) /etc/letsencrypt/live/ に平文で保持
使える場所 ALB / CloudFront / API Gateway など AWS マネージドサービスのみ どこでも(EC2・オンプレ・VPS)
有効期限 13ヶ月(自動更新) 90日(自動更新前提)
料金 無料(パブリック証明書) 無料

👉 2025年からは ACM でもエクスポート可能なパブリック証明書(有料オプション)を発行でき、秘密鍵を取り出して EC2 やオンプレに入れることもできる。ただし標準の無料証明書は従来どおり取り出せない。

選び方

  • ALB / CloudFront で TLS 終端する → ACM 一択。秘密鍵の管理からも解放される
  • EC2 やオンプレのサーバー自身で TLS 終端する → ACM の証明書は持ち出せないので certbot(ACME)を使う
  • 両方ある構成 → ALB は ACM、バックエンドや社内向けは certbot、の併用が普通

❌ 「ACM があるから certbot は不要」ではない。ACM の証明書は AWS のマネージドサービスにしかアタッチできない。EC2 の nginx に入れる証明書は、自分で取るか、有料のエクスポート可能証明書を使うしかない。


まとめ

  • 証明書の発行には**ドメインの支配権の証明(DV)**が必要で、ACME はそれを自動化するプロトコル
  • HTTP-01/.well-known/acme-challenge/ にファイルを置く。検証は必ず 80 番から。ワイルドカード不可
  • DNS-01_acme-challenge の TXT レコードで証明。ワイルドカードはこれ一択。運用は DNS プラグインで自動化
  • TLS-ALPN-01:443 番の TLS ハンドシェイクだけで完結。certbot 非対応、lego や Traefik などで
  • 発行して終わりではない。renew の定期実行 + deploy hook での reload までが証明書運用
  • ACM の DNS 検証は DNS-01 と同じ構造。ACM は ACME 相当の作業をマネージドにして見えなくしたもの
  • ACM の証明書は AWS マネージドサービス専用。サーバー自身で TLS 終端するなら ACME クライアントが必要
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?