10
Help us understand the problem. What are the problem?

posted at

updated at

acme-dnsを使ってLet's Encryptのワイルドカード証明書を更新する

API で TXT レコードを変更できない DNS を利用しているドメインの証明書を dns-01 で更新できないかと思ってやってたのでメモ
Let's Encryptのフォーラムのコメントで acme-dns が紹介されていたので

この記事の1年ぐらい前に同じことをする別の方法の記事がQiitaにすでにあったのでリンクしておく
VALUE-DOMAIN でも DNS認証がしたい! (Let's Encryptの証明書の更新の自動化)

状況・条件

  • DNS
    • API で TXT レコードを変更できない
    • NS レコードを設定できる
  • ワイルドカード証明書を使う
  • ネームサーバーを変えない
  • systemd-resolved などのリゾルバを動かしたまま使う

この方法が適さない状況

acme-dns に使うドメインは NS レコードを設定できるのが前提。例えば無料で Dot TK とかのドメインが使える Freenom の DNS では NS レコードを設定できないので使うことができない。ネームサーバーの変更ができないようなレジストラはないと思うが諦めるしかない。
ネームサーバーを変更するならば Cloudflare DNS などの TXT レコードの API での変更がサポートされているようなところを最初から選べば良い。

サンプル環境

  • ドメイン: example.org
  • グローバル IP アドレス: 203.0.113.1

手順

  1. https://github.com/joohoi/acme-dns#installation に従ってインストール
  2. config.cfg の編集
  3. certbot の renewal pre/post hook と --manual-auth-hook を用意する
  4. 53/udp を解放

用意

acme-dns は https化されていることが前提なので、あらかじめ証明書を取得しておく必要がある。これは TXT レコードの手動変更とかで取ればいいと思うのでこの辺とか参考にしてワイルドカード証明書を発行する
Let's Encrypt (certbot) でワイルドカード証明書できた!

systemd で管理する

$GOBIN/acme-dns/usr/local/bin/acme-dns へ symlink しておくので $GOPATH/src/github.com/joohoi/acme-dns/acme-dns.service を書き換える必要もなくなる。なので symlink で systemd の管理下へ置く
Installation では /etc/systemd/system/acme-dns.service へ移せとあるけど、/usr/lib/systemd/system/acme-dns.service にしておく
こうすると systemctl <enable|disable> acme-dns をしても困らない1

config.cfg

  • DNS のリゾルバと共存させるにはサーバーのグローバルIPを利用する 2
  • 更新の時に Nginx を止めるようにしているので今回はリバースプロキシは利用しないが、止めなければリバースプロキシを使うことで acme-dns で証明書を設定する必要がなくなる 3
    • ただ、常に acme-dns を動かしておかないといけない状況はあまりないと思うので好みで
    • 更新の時に Nginx を止めているとかなり長い時間ダウンタイムがある事になるので 8080 番ポートで受ける形にしている
config.cfg
[general]
# DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53
# In this case acme-dns will error out and you will need to define the listening interface
# for example: listen = "127.0.0.1:53"
#listen = ":53"
listen = "203.0.113.1:53" # グローバルip 203.0.113.1 を設定する

[api]
# 0.0.0.0 を設定して直接公開
# Nginx などでリバースプロキシを使うなら 127.0.0.1 でもいい
ip = "0.0.0.0"

# 直接公開するので 443 番ポートを選択
# リバースプロキシを使うなら HTTP alternative の 8008 番とか 8080 番とか
#port = "443"
port = "8080" # Nginx を止めない形にしたので alternative で受ける

# すでに取得済みの証明書を使うので "cert"
# "letsencrypt" の挙動は確かめてない
# リバースプロキシ使うなら "none" で
tls = "cert"

# only used if tls = "cert"
# 既存の証明書を指定
tls_cert_privkey = "/etc/letsencrypt/live/auth.example.org/privkey.pem"
tls_cert_fullchain = "/etc/letsencrypt/live/auth.example.org/fullchain.pem"

フックの設定

renewal-hooks/{pre,post} に acme-dns と nginx の起動停止の処理を書く

/etc/letsencrypt/renewal-hooks/pre/acme-dns-start.sh
#!/bin/sh
#systemctl stop nginx
systemctl start acme-dns
/etc/letsencrypt/renewal-hooks/post/acme-dns-finish.sh
#!/bin/sh
systemctl stop acme-dns
#systemctl start nginx
systemctl reload nginx # restart しなくても reload で証明書を読んでくれる

acme-dns に TXT レコードを設定するフックは --manual-auth-hook で指定する。取得/更新する の項を参照

DNSサーバーの設定

acme-dns を置くサーバーを例えば auth.example.org の NS レコードに設定する。 NS レコードは IP アドレスを受け付けないので適当に ns.example.org などで acme-dns を置いたサーバーの IP アドレスを A/AAAA レコードに入れる

ns.example.org A 203.0.113.1
auth.example.org NS ns.example.org

こうすることで任意のドメインで _acme-challenge に CNAME レコードで <uuid>.auth.example.org とした時に acme-dns の TXT レコードを取りに来る

取得/更新する

acme-dns 用の認証スクリプトは joohoi/acme-dns-certbot-joohoikoesie10/acme-dns-certbot-hook などがある。acme-dns-certbot-joohoi は acme-dns に未登録のドメインだった時に自動でアカウントを作って _acme_challange.example.org. CNAME <uuid>.auth.example.org. をプリントしてくれるので、事前に curl -sX POST https://auth.example.org/register する手間が省ける

スクリプトを適当な場所にインストールして --manual-auth-hook で指定する

取得
#!/bin/bash

sudo systemctl start acme-dns

sudo certbot certonly --manual \
  --manual-auth-hook /etc/letsencrypt/renewal-hooks/acme-dns-auth.py \
  --manual-public-ip-logging-ok \
  --agree-tos -m <メールアドレス> \
  -d example.org -d \*.example.org \
  --preferred-challenges dns-01 \
  --server https://acme-v2.api.letsencrypt.org/directory

sudo systemctl stop acme-dns
sudo systemctl reload nginx

--force-renewal オプションをつけて全てのドメインの更新するときのオプションを変更させる

更新
#!/bin/bash

sudo certbot renew --force-renewal --manual \
  --manual-auth-hook /etc/letsencrypt/renewal-hooks/acme-dns-auth.py \
  --manual-public-ip-logging-ok

終わり

Cloudflare に DNS のためだけにネームサーバーを向けていたので、是非はともかくこれで Google Domains DNS に戻せる

もし不明なところ等あればコメントどうぞ


  1. symlink を /etc/systemd/system に作ると systemctl disable acme-dns した時に消えてしまうので /lib/systemd/system/usr/lib/systemd/system で管理するのが望ましいと思う 

  2. joohoi/acme-dns Issue #118 

  3. example use a reverse Nginx proxy and set up basic authentication https://community.letsencrypt.org/t/help-me-understand-acme-dns/58892/22 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
10
Help us understand the problem. What are the problem?