締め切りより品質という記事が出ていますが、お疲れ様です。Let’s Encryptのワイルドカード証明書のサポートはとても便利になるので期待しながらDNSSECでdns-01を使った自動化の下準備をしてみます。(2018/3/14 ACME v2 and Wildcard Certificate Support is Liveということでcertbotにてワイルドカード証明書を取得しました。末尾に簡単に追記します)
tls-sni-01(とtls-sni-02)は多くのホスティング環境でなりすませるので新規発行用には消えてなくなり、いずれtls-sni-03になるそうです。それはともかくワイルドカード証明書の発行ではdns-01だけになるだろうということでdns-01、あとは長期に渡ってサポートされそうなcertbotを使おうと思います。もしワイルドカード証明書発行がproduction環境に登場する前のテストに参加される方は自力でAPI叩くか、ACME v2 Compatible Clientsを利用されると良いかと思います。Example hook script using Dynamic DNS update utility for dns-01 challengeはcertbot用の例ではないですが、末尾の例に書かれているようにdns-01でCNAMEは解決してくれるようなので既存のゾーンをダイナミックな更新をすることはせずに、DNS update(RFC2136, nsupdate)専用のゾーンを1個だけ作成するシンプルな作業をDNSSECを有効にしたISC BIND 9.11で行ってみます。ACMEのdraft-09を見ても特にDNS query回数の上限など書かれていないので良さそうです。ダイナミックではないDNSSECのsign周りは適宜ご利用のツールで行ってください。当たり前とは思いますが各種の権限の設定など重々ご注意を。鍵無しや不要なIP範囲から更新できてしまわないかなどのテストもどうぞお忘れなく。amazon.com.___________________.dynamic.example.comなどというRRを作成されちゃうかもしれません。なおmasterが公開権威サーバである必要はないので、ClouDNS(Equinix TY8 POP追加されました。かゆいところに手が届き、サポートに連絡して何度かやりとりしてるとCEOが登場して解決にむけて動きだすという身軽さも含めて、人生の中で本当に使ってよかったと思える数少ないお気に入りサービス。)のような外部のDNSSEC対応セカンダリサーバのサービスを利用して公開権威サーバにするという方法もあります。
まずは既存のexample.comの設定が次のようになっているとします。RRは適当です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
-- example.com.zone @ IN SOA ns100.example.com. postmaster.example.com. ( 1519698955; Serial ; 1w; Refresh (we are using notify) 1h; Retry ; 9d; Expire 15m; Negative cache TTL ) 7200 IN ns100.example.com. 7200 IN ns200.example.com. 7200 IN ns300.example.com. @ 1200 IN MX 0 . * 1200 IN MX 0 . @ 1200 IN TXT "v=spf1 -all" * 1200 IN TXT "v=spf1 -all" _adsp._domainkey 1200 IN TXT "dkim=discardable" _domainkey 1200 IN TXT "o=-" _dmarc 1200 IN TXT "v=DMARC1;p=reject;sp=reject;rua=mailto:[email protected];ruf=mailto:[email protected];fo=1;pct=100" |
ただでさえ複雑なsplit-horizon DNSで多数のviewなうえにDNSSECも有効である設定で、in-viewが実装された時には狂喜乱舞した、スクリプトによる処理無しではメンテが不可能な環境の再編成と抜粋で、viewはTSIGでsplit、IPアドレスでの制限はzone宣言で行っています。ゾーン転送においてはポートやIPアドレスを追加してsplitする方法よりシンプルな設定になります。ここではmasterとslaveが1viewに対して1キーを共有しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
--- named.conf (master) ... view "axfr" { match-clients { key "axfr-key"; }; match-destinations { ... }; allow-query { any; }; rate-limit {...}; server 2001:db8::1:53 { keys "axfr-key"; }; # slave server 2001:db8::2:53 { keys "axfr-key"; }; # slave server 198.51.100.153 { keys "axfr-key"; }; # slave server 198.51.100.253 { keys "axfr-key"; }; # slave notify explicit; allow-transfer { none; }; zone "example.com" { type master; file "/etc/namedb/master/example.com.zone.signed"; allow-transfer { 2001:db8::1:53; 2001:db8::2:53; 198.51.100.153; 198.51.100.253; }; also-notify { 2001:db8::1:53; 2001:db8::2:53; 198.51.100.153; 198.51.100.253; }; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; view "query" { match-clients { localhost; 127.0.0.1; ::1; ... }; match-destinations { ... }; allow-query { any; }; rate-limit {...}; zone "example.com" { in-view "axfr"; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
--- named.conf (slave) ... view "axfr" { match-clients { key "axfr-key"; }; match-destinations { ... }; allow-query { any; }; rate-limit { ... }; server 2001:db8::53 { keys "axfr-key"; }; # master server 198.51.100.53 { keys "axfr-key"; }; # master notify explicit; allow-transfer { none; }; zone "example.com" { type slave; file "/etc/namedb/slave/example.com.zone"; masters { 2001:db8::53 key "axfr-key"; 198.51.100.53 key "axfr-key"; }; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; view "query" { match-clients { localhost; 127.0.0.1; ::1; ... }; match-destinations { ... }; allow-query { any; }; rate-limit { ... }; zone "example.com" { in-view "axfr"; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; ... |
次にダイナミックに更新可能専用ゾーンをここではdynamic.example.comサブドメインで作ってみることにします。簡単のため親と同じDNSサーバを公開権威サーバにしています。negative cacheやdns-01とぶつかるワイルドカードなTXT RRのTTLは短めにしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-- dynamic.example.com.zone @ IN SOA ns100.example.com. postmaster.example.com. ( 1519698956; Serial ; 1w; Refresh (we are using notify) 1h; Retry ; 9d; Expire 30; Negative cache TTL ) 7200 IN NS ns100.example.com. 7200 IN NS ns200.example.com. 7200 IN NS ns300.example.com. @ 1200 IN MX 0 . * 1200 IN MX 0 . @ 1200 IN TXT "v=spf1 -all" * 30 IN TXT "v=spf1 -all" |
dynamic.example.com 用のKSKとZSKを作成し、上のゾーンファイルをsignしたものをdynamic.example.com.zone.signedとします。またKSKからDSレコードを表示させておきます。
1 |
dynamic.example.com. IN DS 60160 8 2 82092F....... |
DNSSECとは関係なく、今度はnsupdate用の鍵を作成します。
1 |
tsig-keygen -a HMAC-SHA512 dynamic-key |
tsig-keygenが無いバージョンではdnssec-keygenを流用します。
1 |
dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST dynamic-key |
出来上がったKdynamic-key*.privateのKey:の行から以下のようなファイルを作成します。Kdynamic-key*.keyファイルも同じような内容ですが途中にスペースが入っているのでご注意です。
1 2 3 4 5 |
--- acl/dynamic.key key "dynamic-key" { algorithm hmac-sha512; secret "ONj323..........Q=="; }; |
環境に合わせて適切な権限を設定しておきます。
1 2 3 4 |
(例) chown root:bind acl acl/dynamic.key chmod a-w,g-rw,o= acl chmod a-w,o= acl/dynamic.key |
このゾーン用の設定を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
--- named.conf (master) ... include "acl/dymamic.key"; ... view "axfr" { match-clients { key "axfr-key"; }; match-destinations { ... }; allow-query { any; }; rate-limit {...}; server 2001:db8::1:53 { keys "axfr-key"; }; # slave server 2001:db8::2:53 { keys "axfr-key"; }; # slave server 198.51.100.153 { keys "axfr-key"; }; # slave server 198.51.100.253 { keys "axfr-key"; }; # slave notify explicit; allow-transfer { none; }; zone "dynamic.example.com" { type master; file "/etc/namedb/dynamic/dynamic.example.com.zone.signed"; update-policy { grant "dynamic-key" subdomain _acme.dynamic.example.com. TXT; grant "dynamic-key" subdomain _tlsa.dynamic.example.com. TLSA; deny * subdomain .; }; allow-transfer { 2001:db8::1:53; 2001:db8::2:53; 198.51.100.153; 198.51.100.253; }; also-notify { 2001:db8::1:53; 2001:db8::2:53; 198.51.100.153; 198.51.100.253; }; }; zone "example.com" { type master; file "/etc/namedb/master/example.com.zone.signed"; allow-transfer { 2001:db8::1:53; 2001:db8::2:53; 198.51.100.153; 198.51.100.253; }; also-notify { 2001:db8::1:53; 2001:db8::2:53; 198.51.100.153; 198.51.100.253; }; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; view "query" { match-clients { localhost; 127.0.0.1; ::1; ... }; match-destinations { ... }; allow-query { any; }; rate-limit {...}; zone "dynamic.example.com" { in-view "axfr"; }; zone "example.com" { in-view "axfr"; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
--- named.conf (slave) ... view "axfr" { match-clients { key "axfr-key"; }; match-destinations { ... }; allow-query { any; }; rate-limit { ... }; server 2001:db8::53 { keys "axfr-key"; }; # master server 198.51.100.53 { keys "axfr-key"; }; # master notify explicit; allow-transfer { none; }; zone "dynamic.example.com" { type slave; file "/etc/namedb/slave/dynamic.example.com.zone"; masters { 2001:db8::53 key "axfr-key"; 198.51.100.53 key "axfr-key"; }; }; zone "example.com" { type slave; file "/etc/namedb/slave/example.com.zone"; masters { 2001:db8::53 key "axfr-key"; 198.51.100.53 key "axfr-key"; }; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; view "query" { match-clients { localhost; 127.0.0.1; ::1; ... }; match-destinations { ... }; allow-query { any; }; rate-limit { ... }; zone "dynamic.example.com" { in-view "axfr"; }; zone "example.com" { in-view "axfr"; }; zone "." { type hint; file "/etc/namedb/named.root"; }; }; ... |
BINDのreloadにより、ここまででDNSSEC環境下でnsupdate可能になりました。
1 2 3 4 5 6 |
nsupdate -k acl/dynamic.key > server 2001:db8::53 > update add hoge._acme.dynamic.example.com. 30 IN TXT "h3j2\"h32kj4" > send > update del hoge._acme.dynamic.example.com. > send |
などとやってみてサーバ共にエラーにならないことを確認します。ちゃんと鍵を指定しているにもかかわらず、クライアント側で
1 |
dns_request_getresponse: expected a TSIG or SIG(0) |
というエラーが出る場合は、server指定がTSIGを受け付けないキャッシュサーバなどを指している可能性があります。
クライアント側で
1 |
response to SOA query was unsuccessful |
というエラーが出る場合は、権威サーバの管轄外を更新しようとしていて、サーバ側ではREFUSEDとなっているはずです。
サーバ側で
1 |
found no active private keys, unable to generate any signatures |
というエラーが出る場合は、その前に以下のような警告が出ているはずなので修正します。
1 |
general: warning: dns_dnssec_findzonekeys2: error reading Kdynamic.example.com.+alg+123.private: file not found |
などと出る場合は、sign用の鍵が無いといっているので、named.confにて
1 2 3 |
options { directory "/etc/namedb/tmp"; ... |
のようにdirectoryで指定されているディレクトリに鍵を置きます。
1 2 |
cd /etc/namedb/tmp ln -s ../dnssec/work/keydir/Kdynamic.example.com.* . |
環境に合わせて適切な権限を設定しておきます。
1 2 3 |
(例) chown root:bind Kdynamic.example.com.* chmod o= Kdynamic.example.com.* |
1 |
general: warning: dns_dnssec_findzonekeys2: error reading Kdynamic.example.com.+alg+123.private: permission denied |
などと出る場合は、sign用の鍵が存在しているけど読む権限がないといっているので、symlinkの場合は元のファイルおよびそこから上の全てのディレクトリの権限を確認して、namedが読むのに適切かつ最小限の権限を追加するか、symlinkをやめてファイルを移動するなどの対応します。
1 |
request has invalid signature: TSIG dynamic-key: tsig verify failure (BADKEY) |
などと出る場合は、サーバの設定ファイルの確認およびnsupdateに直接鍵名と鍵を指定して確認すると何がおかしいのかわかるかもしれません。
1 |
nsupdate -y 'dynamic-key:ONj323..........Q==' |
ダイナミックに更新できるゾーンができたのは良いですが、ゾーンを作っただけなのでdynamic.example.comの権威サーバを自力で知っている人にしか内容は伝わりません。それではdns-01で評価してもらえないので今度はexample.com側でdelegationします。DNSSECのチェーンをつなぐために、先ほどdynamic.example.comのKSKより取得したDSレコードも追加しなればなりませんが、DSレコードをレジストラ経由でレジストリに登録するよりは素早くできる作業かもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
-- example.com.zone @ IN SOA ns100.example.com. postmaster.example.com. ( 1519698955; Serial ; 1w; Refresh (we are using notify) 1h; Retry ; 9d; Expire 15m; Negative cache TTL ) 7200 IN ns100.example.com. 7200 IN ns200.example.com. 7200 IN ns300.example.com. @ 1200 IN MX 0 . * 1200 IN MX 0 . @ 1200 IN TXT "v=spf1 -all" * 1200 IN TXT "v=spf1 -all" _adsp._domainkey 1200 IN TXT "dkim=discardable" _domainkey 1200 IN TXT "o=-" _dmarc 1200 IN TXT "v=DMARC1;p=reject;sp=reject;rua=mailto:[email protected];ruf=mailto:[email protected];fo=1;pct=100" ########## delegation ########## dynamic 7200 IN NS ns100.example.com. dynamic 7200 IN NS ns200.example.com. dynamic 7200 IN NS ns300.example.com. dynamic 86400 IN DS 60160 8 2 82092F....... |
lame delegationにならないようにNSおよびDSレコードはコピペ間違いなど十分注意します。example.comをsignしてBINDをreloadでdynamic.example.comの中のダイナミックなRRがインターネットから解決できるようになります。あとは Let’s Encrypt のACME challengeに対応するCNAMEを追加していきます。*.example1.jpというワイルドカード証明書を発行したければ恐らくACME v1でexample1.jpの証明書を発行する場合と同様の次のようなCNAMEをexample1.jpゾーンに追加しておいて、
1 |
_acme-challenge.example1.jp. IN CNAME example1.jp._acme.dynamic.example.com. |
_acme-challenge.example1.jpではなくexample1.jp._acme.dynamic.example.comのTXTを追加して成功後に削除すればよい、ということになるかと思います。example1.jp.dynamic.example.comのdns-01チャレンジのTXT RRとぶつからないように念のため丸ごと先頭につけるのではない別な規則のCNAMEにしておきます。またこれによってCNAME先を完全に信頼することになるので、この例ではexample1.jp._acme.dynamic.example.comを第三者が更新できてしまう場合(第三者が更新できるような設定にしてしまった場合)、第三者による成りすまし発行が可能になります。将来なにか事件でも起きればCNAMEは解決しなくなるかもしれません。TLSA用にもCNAMEを追加しておきます。
1 2 3 |
_443._tcp.www.example1.jp. IN CNAME example1.jp._tlsa.dynamic.example.com. _443._tcp.example1.jp. IN CNAME example1.jp._tlsa.dynamic.example.com. _25._tcp.mx.example1.jp. IN CNAME example1.jp._tlsa.dynamic.example.com. |
certbot用にちょいっとスクリプトを書きます。ドキュメント真面目に読んでないのがばれますが、ちゃんとしたプラグインcertbot-dns-rfc2136があるのに後で気付いたので書く必要なかったか。もっとも権威サーバに届いたことを確認したり、TLSA周りを自動化したので、結局自分でやらないといけない部分はあったのでまあ良いか。certbotを信用してますが、厳しくやるならば$CERTBOT_… のvalidationしたり利用時にエスケープしたりする必要があるかもしれません。sleepするだけだと芸がないのでPUBLIC_AUTH_SERVERSには、先ほどdynamic.example.comゾーンのNS RRで指定した公開権威サーバを並べて実際に問い合わせてゾーン転送(IXFR)されたことを確認します。TLSA RRの追加もしてみました。TXT RRはすぐ消せますが、これを使った環境では冗長サーバに証明書が行き渡るのに最大で24時間かかるので、TLSA RRは保存しておいて(テキストファイルでも何でも良いですが、簡単なのでSQLiteつかって)次回時間が経っていたら削除です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
(例なので各環境に合わせて調整の必要があります) #!/bin/sh # The MIT License (MIT) http://opensource.org/licenses/mit-license.php # Copyright (c) 2018 Abacus Technologies, Inc. umask 077 PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin var_auth () { # CERTBOT_DOMAIN: The domain being authenticated # CERTBOT_VALIDATION: The validation string (HTTP-01 and DNS-01 only) [ -z "$CERTBOT_DOMAIN" ] && { echo "CERTBOT_DOMAIN is not specified."; exit 1; } [ -z "$CERTBOT_VALIDATION" ] && { echo "CERTBOT_VALIDATION is not specified."; exit 1; } CHALLENGE=_acme-challenge."$CERTBOT_DOMAIN" TARGET="$CERTBOT_DOMAIN"._acme.dynamic.example.com. } var_deploy () { local DOM [ -z "$RENEWED_DOMAINS" ] && { echo "RENEWED_DOMAINS are not specified."; exit 1; } [ -z "$RENEWED_LINEAGE" ] && { echo "RENEWED_LINEAGE is not specified."; exit 1; } DOM=$(echo "$RENEWED_DOMAINS" | sed -E -e 's/^\*\.//' -e 's/^([^[:space:]]+)[[:space:]].*$/\1/') TARGET="$DOM"._tlsa.dynamic.example.com. TLSA_DB=/var/db/tlsa-rr # 1week TLSA_GRACE_PERIOD=604800 SQLITE=/usr/local/bin/sqlite3 } PUBLIC_AUTH_SERVERS='ns100.example.com ns200.example.com ns300.example.com' RFC2136SERVER=2001:db8::53 RFC2136PORT= TTL=120 WAIT=5 NSUPDATE=/usr/local/bin/nsupdate NSUPDATE_KEY=/etc/namedb/acl/dynamic.key BINDHOST=/usr/bin/host OPENSSL=/usr/local/bin/openssl MODE=$(basename "$0") rc=1 TMPD=$(mktemp -d "/tmp/.abacustech.$MODE.XXXXXXXXXXX") [ -z "$TMPD" ] && exit $rc cleanup () { rm -r "$TMPD" exit $rc } trap 'cleanup' EXIT TERM nsupd () { "$NSUPDATE" -k "$NSUPDATE_KEY" "$@" } host () { "$BINDHOST" "$@" } tlsa () { [ ! -r "$1" ] && return 1 "$OPENSSL" x509 -in "$1" -pubkey -noout | "$OPENSSL" pkey -pubin -outform DER | "$OPENSSL" dgst -sha256 | sed -E -e 's/^.*=[[:space:]]*([[:xdigit:]]+)$/\1/' } confirm () { local n FIN NS n=1 while [ $n -le 10 ]; do FIN=$(mktemp "$TMPD/XXXXXXXXXX") for NS in $PUBLIC_AUTH_SERVERS; do #Use 'host -t A $NS' instead on IPv4-only env, or 'host -t AAAA $NS' on IPv6-only env. host $NS | sed -n -E -e 's/^.* has (IPv6 )?address (.*)$/\2/p' | while read -r IP; do if [ -e "$FIN" ]; then host -t txt "$TARGET" $IP | fgrep " text \"$CERTBOT_VALIDATION\"" || rm "$FIN" fi done done [ -e "$FIN" ] && { rm "$FIN"; return 0; } n=$(expr $n + 1) sleep $WAIT done return 1 } dbinit () { [ -w "$TLSA_DB" -a -s "$TLSA_DB" ] && return 0 rm -f "$TLSA_DB" "$SQLITE" "$TLSA_DB" ' CREATE TABLE tlsa_rr( dom TEXT NOT NULL, tlsa TEXT NOT NULL, created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, obsoleted DATETIME, PRIMARY KEY(dom,tlsa) );' } dbselect () { local dom dom=$(echo "$1" | "$OPENSSL" enc -e -base64) "$SQLITE" "$TLSA_DB" "SELECT tlsa FROM tlsa_rr WHERE dom='$dom' AND $TLSA_GRACE_PERIOD"$tlsa_rrs" while read -r tlsa_rr; do nsupd <<AbacusTech && dbdelete "$TARGET" "$tlsa_rr" server $RFC2136SERVER $RFC2136PORT update del $TARGET IN TLSA 3 1 1 $tlsa_rr send AbacusTech done <"$tlsa_rrs" rm -f "$tlsa_rrs" } case "$MODE" in authenticator.sh) nsupd <<AbacusTech && confirm server $RFC2136SERVER $RFC2136PORT update add $TARGET $TTL IN TXT "$CERTBOT_VALIDATION" send AbacusTech rc=$? ;; cleanup.sh) nsupd <<AbacusTech server $RFC2136SERVER $RFC2136PORT update del $TARGET IN TXT send AbacusTech rc=$? ;; deploy.sh) var_deploy C="$RENEWED_LINEAGE/cert.pem" TLSA=$(tlsa "$C") if [ -n "$TLSA" ]; then nsupd <<AbacusTech && tlsa_updated "$TARGET" "$TLSA" server $RFC2136SERVER $RFC2136PORT update add $TARGET $TTL IN TLSA 3 1 1 $TLSA send AbacusTech fi rc=$? ;; *) ;; esac exit $rc # end of file |
簡単のためここではsymlinkでモードを変えます。
1 2 |
ln -s authenticator.sh cleanup.sh ln -s authenticator.sh deploy.sh |
certbotにスクリプトを渡して確認してみます。
1 |
/usr/local/bin/certbot ..... --dry-run --manual --manual-auth-hook /etc/letsencrypt/authenticator.sh --manual-cleanup-hook /etc/letsencrypt/cleanup.sh --deploy-hook /etc/letsencrypt/deploy.sh ..... |
まだACME v2未対応なcertbotなのでワイルドカード証明書の要求ではありませんがoutbound1.letsencrypt.orgおよびAWSから飛んでくるDNS queryを見ているとちゃんとCNAMEも解決され、CNAME元の権威サーバとCNAME先の権威サーバが異なっていても正常終了となりました。さて下準備もできたのでワイルドカード証明書対応がproduction環境に降臨するのをわくわくしながら待つことにします。やってきました。せっかくダイナミックなゾーンを作ったので、このままTLSAの自動化もしてしまいましょうか。やってみました。
2018/3/14 ACME v2 and Wildcard Certificate Support is Liveということでありがたいことでございます。
certbot 0.22初版ではconstants.pyのserverもSTAGING_URIもACME v1のサーバを指していますが、ACME v2 Compatible Clientsに0.22以降で追加されていて、実際ACME v2 Production Environment & Wildcardsに書かれているURLを指定することでワイルドカード証明書が発行されました。
1 2 3 4 5 6 |
/usr/local/bin/certbot certonly --server 'https://acme-v02.api.letsencrypt.org/directory' \ --manual-auth-hook /etc/letsencrypt/authenticator.sh \ --manual-cleanup-hook /etc/letsencrypt/cleanup.sh \ --deploy-hook /etc/letsencrypt/deploy.sh \ --domains '*.hoge.example1.jp,example.com,*.example.com' |
この例のように*.example.comとその親example.comをSAN(Subject Alternative Names)に指定した場合、dns-01は片方だけでは許してもらえずに両方のvalidationが同時に発生し、従ってexample.comのTXT RRは複数必要となるので、直前にTXT RRを削除してから追加、というやり方ではエラーになりました。authenticatorではひたすら追加、cleanupでお掃除ということになります。certbot-dns-rfc2136使ってればきっと何も気にしないですんなりいってこれを気にすることもなかったのだろうななどと思ってます。