使用動態 DNS 申請及自動更新 Wildcard Let’s Encrypt 憑證

Let’s Encrypt 的 Wildcard 憑證 (*.ishm.idv.tw) 申請,除了手動 --manual 參數申請外,也可以使用 DynamicDNS (rfc2136)  來申請及自動更新。

Let’s Encrypt 的「說明文件」。

首先,要在 DNS Server 設定動態 DNS 機制。

DDNS (Dynamic DNS) 可以在 named.conf 中指定只允許信任的來源 IP 做紀錄更新,也可使用 TSIG 密鑰的方式來安全控管,我們要實作的是使用 TSIG 金鑰的管道來做 DNS 紀錄動態更新。

先使用 dnssec-keygen 來產生 TSIG 密鑰:

# dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST keyname.
(-a 指定演算法,-b 加密長度,-n 金鑰類型,這裡指定類型為 HOST,大小寫不拘)
(最後 keyname. 為金鑰名稱,請自取,後面有沒有加點在實作上沒有影響結果)

指令會產生兩個金鑰檔案:
Kkeyname.+165+10340.key
Kkeyname.+165+10340.private

金鑰檔案格式為:
K{檔案名稱}.+{演算法}+{亂數代碼}.key
K{檔案名稱}.+{演算法}+{亂數代碼}.private

金鑰檔案內容 (當然底下的金鑰密文只是範例,不是真實環境中用的):

# cat Kkeyname.+165+10340.key
keyname. IN KEY 512 3 165 o4mIcqvdrqA9xcmfkyiRAfGGo5hSW9gStp8Cp3pSXLkfNAJbcJ0QdtaZ PaPjUH/1RAUR0cuF+pqTtW5Y4ncGMg==

# cat Kkeyname.+165+10340.private
Private-key-format: v1.3
Algorithm: 165 (HMAC_SHA512)
Key: o4mIcqvdrqA9xcmfkyiRAfGGo5hSW9gStp8Cp3pSXLkfNAJbcJ0QdtaZPaPjUH/1RAUR0cuF+pqTtW5Y4ncGMg==
Bits: AAA=
Created: 20190526015818
Publish: 20190526015818
Activate: 20190526015818

接下來將金鑰內容加到 named.conf 裡面,並在 zone 裡面設定 update policy:

key "keyname." {
algorithm hmac-sha512;
secret "o4mIcqvdrqA9xcmfkyiRAfGGo5hSW9gStp8Cp3pSXLkfNAJbcJ0QdtaZ PaPjUH/1RAUR0cuF+pqTtW5Y4ncGMg==";
};

zone "ishm.idv.tw." {
type master;
file "/var/named/named.ishm";
update-policy {
grant keyname. name _acme-challenge.ishm.idv.tw. txt;
};
};

上述 update-policy 裡面, grant 給 keyname. 的  nametype 是指定為 “name”,意即明確指定要改的 FQDN 為 _acme-challenge.ishm.idv.tw.,後面的 txt 指定只能更新 txt 紀錄。

如果不加 txt 的話,可以更新 _acme-challenge.ishm.idv.tw. 的所有類型紀錄,如 A, AAAA, MX 等,但 certbot 的申請與更新只要用到 TXT 做驗證,所以指定 txt 就夠了。

要注意的是,使用 DDNS 功能後,Zone File 的格式會整個被 named 改寫掉,我的 named.ishm 會被依字母排序重新改寫。如果不想發生這種狀況,其實也可以將 _acme-challenge.ishm.idv.tw 拉出來成一個獨立的 Zone,當然那個獨立出來的 Zone File 要記得建立:

zone "ishm.idv.tw." {
type master;
file "/var/named/named.ishm";
};

zone "_acme-challenge.ishm.idv.tw" {
type master;
file "/var/named/named.letsencrypt";
update-policy {
grant keyname. name _acme-challenge.ishm.idv.tw. txt;
};
};

設定完成,重啟 named 服務後 (# systemctl restart named),可以測試一下 DDNS 的功能:

(先查詢一下 _acme-challenge.ishm.idv.tw,目前沒有紀錄)
# nslookup -q=txt _acme-challenge.ishm.idv.tw ::1
Server: ::1
Address: ::1#53

*** Can't find _acme-challenge.ishm.idv.tw: No answer

(現在測試一下動態更新功能新增紀錄,其中 60 是 TTL)
# nsupdate -k Kkeyname.+165+10340.key
> server ::1
> update add _acme-challenge.ishm.idv.tw 60 txt "This is TEXT content!!"
> send
> quit

(查詢動態更新結果)
# nslookup -q=txt _acme-challenge.ishm.idv.tw ::1
Server: ::1
Address: ::1#53

_acme-challenge.ishm.idv.tw text = "This is TEXT content!!"

(再測試一下動態刪除紀錄)
# nsupdate -k Kkeyname.+165+10340.key
> server ::1
> update del _acme-challenge.ishm.idv.tw txt
> send
> quit

(確定已刪除紀錄)
# nslookup -q=txt _acme-challenge.ishm.idv.tw ::1
Server: ::1
Address: ::1#53

*** Can't find _acme-challenge.ishm.idv.tw: No answer

接下來就申請 *.ishm.idv.tw 的 Wildcard 憑證,我們要使用 dns-rfc2136 的插件,所以安裝 certbot 時,要同時安裝 python2-certbot-dns-rfc2136:

# yum install certbot python2-certbot-dns-rfc2136

接下來要製作動態更新認證設定檔給 dns-rfc2136 插件使用,相關說明可參考certbot官方文件。我們依官方說明的位置及檔名,編輯認證設定檔 /root/.secrets/certbot/rfc2136.ini:

# Target DNS server
dns_rfc2136_server = 127.0.0.1
# Target DNS port
dns_rfc2136_port = 53
# TSIG key name
dns_rfc2136_name = keyname.
# TSIG key secret
dns_rfc2136_secret = o4mIcqvdrqA9xcmfkyiRAfGGo5hSW9gStp8Cp3pSXLkfNAJbcJ0QdtaZ PaPjUH/1RAUR0cuF+pqTtW5Y4ncGMg==
# TSIG key algorithm
dns_rfc2136_algorithm = HMAC-SHA512

DNS跟動態認證檔都準備好了,我們就可以申請憑證了,其中 –dns-rfc2136 表示使用動態DNS更新插件,–dns-rfc2136-credentials 指定動態更新認證設定檔的位置,–dns-rfc2136-propagation-seconds 是指定等候DNS動態更新同步到其他台DNS伺服器的緩衝時間,預設是60秒,這個例子是指定等候 5 秒即可:

# certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /root/.secrets/certbot/rfc2136.ini --dns-rfc2136-propagation-seconds 5 -d *.ishm.idv.tw

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-rfc2136, Installer None
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for ishm.idv.tw
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/ishm.idv.tw/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/ishm.idv.tw/privkey.pem
Your cert will expire on xxxx-xx-xx. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
- If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

取得憑證後,將憑證路徑寫入該有的設定檔中,如 /etc/httpd/conf.d/ssl.conf 或 /etc/vsftpd/vsftpd.conf 中,再將服務重啟,即可套用新申請的萬用憑證。

補充說明一下,*.ishm.idv.tw 的憑證可以給 www.ishm.idv.twftp.ishm.idv.twxxx.ishm.idv.tw 等網址使用,但無法提供 ishm.idv.tw 用喔。

編輯 crontab 以啟動排程更新:

50 12 * * 3 root /usr/bin/certbot renew
55 12 * * 3 root /usr/bin/systemctl restart vsftpd
56 12 * * 3 root /usr/bin/systemctl restart httpd

更新完 crontab 後,記得 systemctl reload crond,這個例子是每週三的 12:50 檢查憑證更新,檢查完會重啟 vsftpd 及 httpd,如果 renew 有取得新的憑證,後續的 restart 服務就會讀取並套用新憑證。