はじめに
無料で SSL 証明書を発行してくれる Let's Encrypt では、証明書の発行・更新の際にドメインの所有者であることを証明する必要があるが、その方法として次の2つがよく使用される。
- HTTP-01 チャレンジ
- DNS-01 チャレンジ
(参考: チャレンジのタイプ - Let's Encrypt)
ワイルドカード証明書 (「*.example.com」のようなすべてのサブドメインで有効な証明書) を発行してもらう場合は、DNS-01 チャレンジを使用する必要がある。
DNS-01 チャレンジとは
自身が所有するドメインの権威 DNS サーバにおいて、Let's Encrypt から指定された特定の値を TXT レコードに設定することにより、ドメインの所有権を証明する。
_acme-challenge.example.com. IN TXT 82QiAOKu66D70Jm-Q3nAijh29d2587F3HiuhEgWGFf8
Let's Encrypt の証明書の有効期限は90日なので、約3ヶ月ごとに DNS サーバのレコードを変更する必要があり、手間がかかる。そこで acme-dns という仕組みを利用する。
acme-dns とは
acme-dns とは、DNS-01 チャレンジ認証を実施するための
- 簡易 DNS サーバ
- Web API サーバ
がセットになった、Go 言語で書かれたオープンソースのソフトウェアである。
用意するもの
acme-dns 用サーバ
今回は Ubuntu Server 18.04 をインストールした適当な VPS を使う。グローバル IP アドレスが必要。
Let's Encrypt 用クライアント
適当な Linux 環境。今回は発行した SSL 証明書を使用する Web サーバを想定。
文中のパラメータ
文中の下記の値は、環境に応じて適宜読み替えること。
key | value |
---|---|
acme-dns サーバのホスト名 | acme-dns.example.net |
acme-dns 用のゾーン | acme-dns.example.net |
acme-dns サーバの IP アドレス | 203.0.113.10 |
SSL 証明書を発行するドメイン | example.com |
構築手順
acme-dns サーバ用の DNS レコードの登録
acme-dns で使用するドメイン (例: example.net) の権威 DNS に、次のレコードを登録する (SSL 証明書の発行は、このドメインに限られないのでご安心を)。
- acme-dns サーバの A レコード
- DNS-01 チャレンジで使用する NS レコード
acme-dns.example.net. IN A 203.0.113.10
acme-dns.example.net. IN NS acme-dns.example.net.
なお今回は前記「文中のパラメータ」の通り、「acme-dns サーバのホスト名」と「acme-dns 用のゾーン」で同じ名前を使うので、A レコードはグルーレコードの役割も果たす。このゾーンの権威サーバも acme-dns が担う。
acme-dns サーバ
今回は Docker で acme-dns サーバを動かすことにする。
まず用意した Ubuntu Server 18.04 に
- Docker 環境
- docker-compose コマンド
をインストールしておく。
Ubuntu で systemd-resolved が動いている場合、acme-dns が Port 53 を listen するのに邪魔になるので無効化する (下記「1.1.1.1」等の部分は、適当な DNS リゾルバを指定)。
$ sudo systemctl stop systemd-resolved
$ sudo systemctl disable systemd-resolved
$ cat << EOM | sudo tee /etc/resolv.conf
nameserver 1.1.1.1
nameserver 1.0.0.1
EOM
acme-dns のソースを GitHub から持ってくる。
$ git clone https://github.com/joohoi/acme-dns
設定ファイルやデータベース用のディレクトリを作成する。
$ cd acme-dns
$ mkdir data db
設定ファイルを作成する。
$ cp config.cfg config/
[general]
listen = "0.0.0.0:53"
protocol = "both"
domain = "acme-dns.example.net"
nsname = "acme-dns.example.net"
nsadmin = "admin.example.net"
records = [
"acme-dns.example.net. A 203.0.113.10",
"acme-dns.example.net. NS acme-dns.example.net.",
]
debug = true
[database]
engine = "sqlite3"
connection = "/var/lib/acme-dns/acme-dns.db"
[api]
ip = "0.0.0.0"
disable_registration = false
port = "443"
tls = "letsencrypt"
acme_cache_dir = "api-certs"
corsorigins = [
"*"
]
use_header = false
header_name = "X-Forwarded-For"
[logconfig]
loglevel = "debug"
logtype = "stdout"
logformat = "text"
なお acme-dns の API は HTTPS なので、自身にも SSL 証明書が必要になるが、上記の tls
オプションで letsencrypt
を指定することで、自動的に Let's Encrypt で証明書を発行して適用してくれる。
設定ができたので、docker-compose コマンドで acme-dns を起動する (-d
はバックグラウンドで起動するオプション)。
$ docker-compose build
$ docker-compose up -d
以上で acme-dns サーバが構築できた。
SSL 証明書の発行
以後は、適当な Linux 環境 (例: 証明書を使用する Web サーバ) で作業する。
あらかじめ以下のコマンドをインストールしておく。
- curl
- jq
acme-dns のユーザ登録 (初回のみ)
構築した acme-dns に対して、証明書を発行するドメイン (ゾーン) ごとにユーザ登録が必要になる。
ユーザ登録は Web API で行える。なお下記の 203.0.113.0/24
の部分は、このユーザが acme-dns を使用できる IP アドレス (つまり Web サーバ等) の範囲を指定する。必須パラメータではないが、セキュリティ上指定しておいた方が良い。
$ curl \
-s \
-X POST \
-H "Content-Type: application/json" \
-d '{
"allowfrom": [
"203.0.113.0/24"
]
}' https://acme-dns.example.com/register | jq .
すると以下のような結果が返ってくるので、これをメモしておく。
{
"username": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz",
"password": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"fulldomain": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.acme-dns.example.net",
"subdomain": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"allowfrom": [
"203.0.113.0/24"
]
}
DNS レコードの登録 (初回のみ)
SSL 証明書を発行するドメイン (例: example.com) の権威 DNS サーバに、acme-dns を使用するための CNAME レコードを登録する。下記の xxx...
の部分は、上記結果の fulldomain
の値に置き換えること。
_acme-challenge.example.com. IN CNAME xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.acme-dns.example.net.
SSL 証明書の発行
いよいよ acme-dns を使って Let's Encrypt の SSL 証明書を発行する。
acme-dns に対応したクライアントの実装はいくつかある。また、よく Let's Encrypt で使われる certbot コマンドの hook スクリプト機能を使用する方法もあるが、今回は acme.sh というとても便利なクライアントを見つけたので、これを使用する。
まず acme.sh をインストールする。
$ curl https://get.acme.sh | sh
$ . ~/.bashrc
インストールできたら、-h
オプションでヘルプを見てみる。
$ acme.sh -h | head -20
https://github.com/acmesh-official/acme.sh
v2.8.6
Usage: acme.sh command ...[parameters]....
Commands:
--help, -h Show this help message.
--version, -v Show version info.
--install Install acme.sh to your system.
--uninstall Uninstall acme.sh, and uninstall the cron job.
--upgrade Upgrade acme.sh to the latest code from https://github.com/acmesh-official/acme.sh.
--issue Issue a cert.
--signcsr Issue a cert from an existing csr.
--deploy Deploy the cert to your server.
--install-cert Install the issued cert to apache/nginx or any other server.
--renew, -r Renew a cert.
--renew-all Renew all the certs.
--revoke Revoke a cert.
--remove Remove the cert from list of certs known to acme.sh.
--list List all the certs.
--showcsr Show the content of a csr.
--install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
証明書を発行する用のシェルスクリプト (例: letsencrypt.sh
) を作成する。
#!/usr/bin/env bash
OPT=$1
DOMAIN="example.com"
export ACMEDNS_BASE_URL="https://acme-dns.example.net"
export ACMEDNS_USERNAME="zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"
export ACMEDNS_PASSWORD="yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
export ACMEDNS_SUBDOMAIN="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
~/.acme.sh/acme.sh \
$OPT \
--debug \
--dns dns_acmedns \
-d "$DOMAIN" \
-d "*.$DOMAIN"
シェルスクリプトを実行する。
$ chmod +x letsencrypt.sh
$ ./letsencrypt.sh --issue
うまくいくと、$HOME/.acme.sh/example.com/
ディレクトリ以下に、SSL 証明書や秘密鍵が発行されるので、これらのファイルを Web サーバ等から参照する。
$ ls -1 $HOME/.acme.sh/example.com
ca.cer
fullchain.cer
example.com.cer
example.com.conf
example.com.csr
example.com.csr.conf
example.com.key
dig コマンドを使用すると、acme-dns が TXT レコードを応答していることが確認できる。
$ dig _acme-challenge.example.com. txt +short
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.acme-dns.example.net.
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
なお acme.sh の --list
オプションで、発行済証明書の一覧が確認できる。
$ acme.sh --list
Main_Domain KeyLength SAN_Domains Created Renew
example.com "" *.example.com Wed Jun 17 11:02:23 UTC 2020 Sun Aug 16 11:02:23 UTC 2020
発行した証明書を更新するには、以下のコマンドを実行すれば良い。
$ ./letsencrypt.sh --renew
acme.sh には、cron による証明書の自動更新など便利な機能がいろいろあるので、研究してみよう。