SSL/TLS証明書の有効期限短縮に備えて脱・手動更新②

概要

前回の記事「SSL/TLS証明書の有効期限短縮に備えて脱・手動更新①」の続きとなります。 本記事では、Certbotサーバがどのように認証局(CA)から証明書を取得するのか、 そのための手段としてACME-DNSというソフトウェアがどのように関わるのかを説明します。

CertBOTを用いた証明書発行と設定の流れ

Certbotが認証局(CA)から証明書を取得しする過程はこちらの図の通りですが、 これは大まかな流れとなります。 CAがCertBOTサーバに対して行うドメイン所有確認の手法には、 HTTP-01チャレンジとDNS-01チャレンジがあります。 HTTP-01 チャレンジは、 Web サーバーを使ってドメインの所有を証明する方式です。 Let’s Encryptなど認証局 は、「このドメインの管理者であるなら、Web サーバー上の特定の場所に指定した内容のファイルを置けるはずだ」という考え方で確認を行います。 ただし、認証局が対象ドメインに対して HTTP(ポート80)でアクセスするため、インターネットから対象ドメインの80番ポートへアクセスできる必要があります。
Webサーバーを公開していない環境や、80番ポートを外部公開できない環境では利用できないという制約があります。 本記事ではそのような制約を受けないDNS-01チャレンジを採用します。

DNS-01チャレンジ

DNS-01チャレンジでは、下記のような流れで証明書取得が行われていきます。 HTTP-01チャレンジが「Web サーバーに置いたファイル(トークン)を見に来る」方式なのに対し、DNS チャレンジは「DNS に登録された情報を見に来る」方式です。 HTTP-01チャレンジとDNS-01チャレンジ、それぞれの特徴を比較すると表のようになります。 DNS-01チャレンジは80 番ポートの公開が不要であるため、メールサーバーや LDAPサーバーなど80 番ポートを使わないサーバーを対象にドメインの所有確認ができ、証明書自動取得を行えます。 一方で、DNS-01チャレンジには注意点もあります。一部のDNSサービスでは、そもそもAPIが公開されておらず、Certbotから自動で書き換える手段がありません。 すべての DNS プロバイダがAPI を提供しているわけではないため、APIがない場合はDNS-01 チャレンジの自動化はできず、更新のたびに手作業が必要になります。 その課題に対する対処法として、acme-dns というオープンソースソフトウェアが挙げられます。

ACME-DNSとは

ACME-DNSは、DNS-01 チャレンジ認証を実施するための簡易 DNS サーバ、Web API サーバ機能を有するソフトウェアです。 https://github.com/joohoi/acme-dns acme-dns を使う場合、実際の DNS には最初に一度だけ設定を行います。 まず、ドメインを管理するDNS側で、サブドメイン(_acme-challenge)を、CNAMEを使ってACME-DNSサーバーへ向くように設定(委任)します。 これにより、認証局からのドメイン所有確認のクエリは、自動的にACME-DNSサーバーへと転送されるようになります。 また、ACME-DNSサーバーへの問い合わせを委任するための情報として、Aレコード、acme.example.comのNSレコードもメインのDNSに登録しています。 委任に必要なUUIDとはACME-DNSがFQDNごとに発行するもので、 ACME-DNSサーバーのエンドポイントを叩いた際に発行される情報ですが、 それを取得するコマンドはこの後説明させていただきます。
CertbotとACME-DNSを用いて証明書取得を行う具体的な流れは6つのステップです。 (なお、今回構築する環境では、CertbotとACME-DNSの機能が1つのサーバに同居している構成としています)
  1. まず、CertbotがACMEサーバに証明書を要求し、それに対してACMEサーバがドメイン所有を確認しようとDNSチャレンジを要求します。
  2. 次に、CertbotはACME-DNSのAPIを叩き、取得する証明書のFQDNに対応するUUIDのレコードに検証用トークンを書き込みます。
  3. ACMEサーバがDNS検証を開始します。
  4. メインDNSはCNAME設定に従い、問い合わせをACME-DNSサーバへ転送します。
  5. ACME-DNSがUUIDに基づいた適切なトークンを回答することで検証が成功し、
  6. 証明書が発行されます。
結果、DNSのプロバイダにAPI がなくても、DNS チャレンジの自動更新が可能になります。CNAMEレコードさえ設定すればDNSのレコードを手でいじる手間がなくなり自動更新されるものとなります。

ACME-DNSの構築手順

まずは、ACME-DNSのソースコードを取得します。
# ACME-DNSのソースをGithubから取得
$ git clone https://github.com/joohoi/acme-dns


# 設定ファイルやデータベース用のディレクトリを作成
$ cd acme-dns
$ mkdir data db
次に、オリジナルをコピーした上で、設定ファイルを編集します。
$ cp config.cfg config.org
$ vim 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 = "127.0.0.1:53"
listen = "0.0.0.0:53"
# protocol, "both", "both4", "both6", "udp", "udp4", "udp6" or "tcp", "tcp4", "tcp6"
protocol = "both"
# domain name to serve the requests off of
domain = "auth.example.org"     # acme-dns が管理する DNS ゾーン名
# zone name server
nsname = "auth.example.org"     # domain で指定したゾーンのネームサーバ名
# admin email address, where @ is substituted with .
nsadmin = "admin.example.org"   # SOAレコード内の管理者メールアドレス
# predefined records served in addition to the TXT
records = [                     # acme-dns サーバが固定で返す DNS レコードを定義
# domain pointing to the public IP of your acme-dns server
auth.example.org. A 198.51.100.1,
# specify that auth.example.org will resolve any *.auth.example.org records
auth.example.org. NS auth.example.org.,
]
# debug messages from CORS etc
debug = true                   # デバッグ用の詳細ログを出力するかどうかを指定する項目

[database]
# Database engine to use, sqlite3 or postgres
engine = "sqlite3"             # acme-dns が TXT レコードや登録情報を保存するデータベースの種類を指定
# Connection string, filename for sqlite3 and postgres://$username:$password@$host/$db_name for postgres
# Please note that the default Docker image uses path /var/lib/acme-dns/acme-dns.db for sqlite3
connection = "/var/lib/acme-dns/acme-dns.db"  # データベースへの接続情報(保存先)を指定
# connection = "postgres://user:password@localhost/acmedns_db"
[api]
# listen ip eg. 127.0.0.1
#ip = "0.0.0.0"
ip = "127.0.0.1"               # API サーバが待ち受ける IP アドレスを指定。ACME-DNSサーバー内でAPI叩く構成であれば 127.0.0.1 を指定。
# disable registration endpoint
disable_registration = false   # /register エンドポイントを無効化するか指定する項目。false を設定することで、新しいサブドメインを ACME-DNS に登録することが可能。
# listen port, eg. 443 for default HTTPS
port = "8080"                  # APIサーバの待受ポート
# possible values: "letsencrypt", "letsencryptstaging", "cert", "none"
tls = "none"                   # APIとの通信を HTTPS (TLS) で暗号化するかどうかを指定
# only used if tls = "cert"    # tls = "cert" を選んだ際に使用する項目。ACME-DNS の API との通信を暗号化する際に使用する SSL/TLS 証明書のパスを指定。
tls_cert_privkey = "/etc/tls/example.org/privkey.pem"
tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem"
# only used if tls = "letsencrypt"
acme_cache_dir = "api-certs"   # tls = "letsencrypt" を選んだ際、API との通信に使用する証明書を保存するディレクトリ
# optional e-mail address to which Let's Encrypt will send expiration notices for the API's cert
notification_email = ""        # tls = "letsencrypt" に設定している場合に使用。ACME-DNS の API との通信を暗号化する際に使用する証明書の期限を通知する先のメールアドレスを指定。
# CORS AllowOrigins, wildcards can be used
corsorigins = [                # ブラウザから API を呼び出せる Web ページのドメインを指定する設定。
*
]
# use HTTP header to get the client ip
use_header = false             # 接続してきたクライアントのIPアドレスを特定する際に、HTTPヘッダーの情報を使用するかどうかを指定する項目。
# header name to pull the ip address / list of ip addresses from
header_name = "X-Forwarded-For"  # use_header = true に設定した場合に、どの名前のHTTPヘッダにIPアドレスが入っているかを指定。

[logconfig]
# logging level: "error", "warning", "info" or "debug"
loglevel = "debug"             # ログの出力レベルを指定
# possible values: stdout, TODO file & integrations
logtype = "stdout"             # ログの出力先を指定
# file path for logfile TODO
# logfile = "./acme-dns.log"
# format, either "json" or "text"
logformat = "text"             # 記録されるログのフォーマットを指定
設定ファイルを編集したら、諸々の準備を行います。
# Go言語の開発環境(コンパイラ)をインストール
$ sudo dnf install -y golang
# ソースコードから実行可能なプログラムを生成
$ go build

# ビルドしたプログラムを、システム全体の実行用フォルダへ配置
$ sudo cp acme-dns /usr/local/bin/
# 配置されたことを確認
$ which acme-dns

# 設定ファイル用のディレクトリを root 権限で作成
$ sudo install -d -m 755 -o root -g root /etc/acme-dns
# 専用ユーザーの作成(この手順ではacmednsというユーザでACME-DNSを実行させることとしています)
getent passwd acmedns || sudo useradd -r -s /sbin/nologin -d /var/lib/acme-dns -M acmedns
# 設定ファイルをコピーしつつ、所有者を root、グループを acmedns に設定
# /path/to は git clone を実行ユーザーの home ディレクトリに置き換えます
$ sudo rsync -a --chown=root:acmedns /path/to/acme-dns/config.cfg /etc/acme-dns/
# データディレクトリの作成と権限設定
sudo install -d -m 700 -o acmedns -g acmedns /var/lib/acme-dns
# データベースファイルの空作成
sudo test -f /var/lib/acme-dns/acme-dns.db || sudo touch /var/lib/acme-dns/acme-dns.db
# ファイルの所有権変更
sudo chown acmedns:acmedns /var/lib/acme-dns/acme-dns.db
# ファイルのアクセス権制限
sudo chmod 600 /var/lib/acme-dns/acme-dns.db
# 一般ユーザー(acmedns)に、特権が必要なポート(53番)を使うための権限を付与
$ sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/acme-dns
次に、ACME-DNSをSystemd化させて常駐化します。
$ sudo vim /etc/systemd/system/acme-dns.service
こちらのファイルを下記のように編集します。
[Unit]
Description=acme-dns authoritative DNS server for ACME DNS-01
Wants=network-online.target
After=network-online.target
[Service]
User=acmedns
Group=acmedns
ExecStart=/usr/local/bin/acme-dns -c /etc/acme-dns/config.cfg
WorkingDirectory=/var/lib/acme-dns
Restart=on-failure
RestartSec=2s
# 低権限でも 53 を開ける(setcapコマンドを実行しているので不要ですが予備措置として記載)
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
# ログは journal に流す
StandardOutput=append:/var/log/acme-dns/acme-dns.log
[Install]
WantedBy=multi-user.target
編集後、サービスを読み込んで起動。
$ sudo systemctl daemon-reload
$ systemctl restart acme-dns
ここまで実行すれば、ACME-DNSの初期設定・構築は完了です。
次に、「3. 認証スクリプトの用意」のステップへ進みますが、次回の記事にて解説させていただきます。
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。
エンジニア募集中!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です