acme.sh 自動化申請和更新 Let's Encrypt 萬用 SSL 憑證教學
Let's Encrypt是由多家公司與非營利組織共同創立的數位憑證認證機構,目標就是要讓網站可以免費、申請簡單與自動化流程的憑證服務,在2018年3月進一步提供了萬用SSL憑證(wildcard certificate)的支援。目前Let's Encrypt的萬用SSL憑證僅能經由DNS網域驗證來進行申請,在這篇教學中筆者將會使用acme.sh客戶端軟體提供的DNS驗證模式來申請萬用SSL憑證。筆者在之前有寫過Let's Encrypt申請憑證的教學,現在主要在寫這篇是因為想要申請萬用SSL憑證,參考了各項工具與比較之後,覺得acme.sh客戶端軟體還是最簡單的,除了能申請萬用憑證之外,還可以自動化更新萬用憑證,所以就決定選擇此工具來申請Let's Encrypt的SSL憑證了。
本文教學將指引使用者如何在Linux環境(筆者是使用Debian/Ubuntu)使用acme.sh客戶端軟體申請SSL憑證,以及如何在Nginx網頁伺服器的主機上配置SSL憑證。如果你使用其他的Linux發行版或者是其他的網頁伺服器,也是可以參考本教學,但在指令方面需要自行更改,以調整至你所使用的系統環境。
1)下載和安裝acme.sh客戶端軟體。
Step 1:在安裝acme.sh之前,將Debian/Ubuntu系統的套件資訊與版本更新到最新,及下載和安裝acme.sh會用到的相關套件。
sudo apt update && sudo apt upgrade -y
**下面的socat(SOcket CAT)套件可以不用裝,因為本篇教學是使用DNS驗證模式來驗證域名所有權,並不是使用standalone模式,所以不裝並不會影響到憑證的簽發。
sudo apt install git socat -y
Step 2:建議使用root權限來安裝和使用acme.sh。
**如果你已在root模式,請忽略此步驟。
sudo su - root
Step 3:acme.sh客戶端軟體是存放在GitHub上,所以可以使用Git下載acme.sh,下載完成之後直接安裝acme.sh。
**下面安裝acme.sh的『-m』的後面為電子郵件地址格式,請更換成你的Email信箱。
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh
./acme.sh --install -m email@example.com
source ~/.bashrc
cd
Step 4:acme.sh從2021年8月1日的v3.0版本開始會使用ZeroSSL來做預設的憑證頒發機構(CA),你可以使用以下指令來將acme.sh客戶端軟體預設CA更改回Let's Encrypt。
acme.sh --set-default-ca --server letsencrypt
Step 5:可查看所安裝好的acme.sh客戶端軟體版本。
acme.sh --version
2)從DNS服務提供商取得驗證時必要的DNS API金鑰。
**Let's Encrypt在簽發憑證時,有提供多種的驗證方式,目前還有支援的驗證方式,分別有HTTP、DNS和TLS-ALPN,在這三種驗證方式中,僅有DNS驗證方式提供萬用憑證的簽發。
**acme.sh客戶端有提供DNS驗證模式,而acme.sh也有整理目前可使用的DNS服務提供商,在這dnsapi文件中,可以知道你的DNS服務提供商在驗證時需輸入哪些格式和資訊。
**筆者以下僅以Cloudflare的DNS服務來做示範:
Cloudflare DNS
**Cloudflare取得DNS的API金鑰有兩種方式,一個是Global API Key,就是擁有你Cloudflare帳戶最大權限的金鑰,不建議使用者直接使用Global API Key來申請各項服務,因為假設你的某個伺服器被駭,那你的Cloudflare帳戶有可能會被駭客取得和篡改資料的風險,所以建議使用者以最小權限原則來申請服務,下面的教學筆者會用另一個方式,那就是API Tokens來產生一組DNS的API金鑰。
Step 1:連結至Cloudflare的API Tokens頁面,點選如下圖所示的『Create Token(建立Token)』。
Step 2:如下圖所示在『Edit zone DNS(編輯區域DNS)』點選『Use template(使用範本)』來產生一個DNS金鑰。
Step 3:如下圖所示可以為這個Token命一個你日後好辨識的名稱,然後Permissions(權限)區塊,分別新增兩筆『Zone(區域)』,一個Zone選『Zone(區域)』僅允許『Read(閱讀)』權限,另一個Zone選『DNS』僅允許『Edit(編輯)』權限;在Zone Resources(區域資源)區塊,新增一筆『Include(包含)』,然後選擇特定的Zone『Specific zone(特定區域)』,最後就選擇你要申請憑證的域名。權限設定完成之後就點選『Continue to summary(繼續至摘要)』。
Step 4:如下圖所示可以再次確定你本次產生API Token的服務和權限是否正確,無誤之後點選『Create Token(建立Token)』。
Step 5:可看到已產生了一組API Token,如下圖所示點選『Copy』按鈕將此API 金鑰貼在記事本等下會用到。
**請注意此API金鑰僅會顯示一次,若不見的話,僅能從API Tokens頁面,對需要重產的『API Tokens』點選『Roll(翻新)』重新產生新的API金鑰。
Step 6:回到Cloudflare帳戶首頁的『網站』頁籤選擇本次要申請SSL憑證的域名。
Step 7:如下圖所示在你的域名『Overview(概觀)』介面,滑鼠滾輪往下滑,可以在右側看到API區塊,在API區塊中可以看到『Zone ID(區域識別碼)』和『Account ID(帳戶識別碼)』,分別點擊『Click to copy(按一下即可複製)』貼在記事本等下一樣會用到。
Step 8 :請參考acme.sh提供的dnsapi文檔的格式,在CLI上各別輸入API金鑰,比如以Cloudflare的DNS來說,就將剛剛上面取得的金鑰分別填在對應的『CF_Token』、『CF_Account_ID』和『CF_Zone_ID』,API資訊輸入完成之後,就可以來申請SLL憑證了。
export CF_Token="vNqxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export CF_Account_ID="50exxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export CF_Zone_ID="db8xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
3)開始為域名申請SSL憑證。
Step 1:現在有些網頁伺服器有支援雙憑證的設定,比如說:Nginx,所以筆者會直接簽兩張憑證,分別為RSA和ECC兩種不同的加密演算法。
**RSA和ECC兩者不同的就是,RSA優點為相容性較好(因為有些舊系統不支援ECC);ECC優點為金鑰長度比RSA短很多(這表示系統可以使用更少的資源),在安全性方面ECC也比RSA來的高(ECC-256位元等同於RSA-3072位元)。
**如果瀏覽器有支援RSA和ECC憑證,那伺服器會優先讓瀏覽器使用ECC憑證(這跟密碼套件設定的優先順序也有關係),除非瀏覽器沒有支援ECC憑證,那才會使用RSA憑證。
**現代常見的作業系統和瀏覽器都有支援ECC憑證。
**還有要說一下萬用域名簽發的問題,如果你要讓域名『比如:example.com』(甚至『*.bar.example.com』等等更多層式的子域名)也要有憑證的話,那單純簽『*.example.com』是不行的喔,因為Wildcard憑證僅涵括一層的子域名,所以在申請萬用憑證時,不同層式的子域名都要分開簽。本文教學僅要『example.com』和『*.example.com』要有憑證,所以等下僅會示範『example.com』和『*.example.com』兩者憑證的簽發。
**Let's Encrypt在簽發憑證時是有一些限制的,例如:單個域名簽發數量等等或其他不同的限制,而Let's Encrypt官方也曾說過隨著時間未來會慢慢放寬這些限制,因為這些限制可能會隨著時間而改變,所以筆者這邊就不細說這些限制了,請自行至Let's Encrypt-Rate Limits頁面查看有哪些限制。
**下面的範例,請自行將『example.com』替換成你自己的域名。
#RSA #可用的金鑰長度分別為2048, 3072, 4096, 8192
acme.sh --issue --dns dns_cf --server letsencrypt -d example.com -d *.example.com -k 4096
#ECC/ECDSA #可用的金鑰長度分別為ec-256, ec-384, ec-521
acme.sh --issue --dns dns_cf --server letsencrypt -d example.com -d *.example.com -k ec-384
Step 2:憑證分別申請成功後,會出現如下面所示的訊息,而申請成功的憑證會暫時存放在『~/.acme.sh/』目錄中,請勿直接使用此目錄的憑證,因為該目錄可能會隨著acme.sh的更新而變動。
**下面範例筆者是使用『imkjie.com』域名,並使用RSA-4096加密演算法申請憑證成功訊息的過程:
root@kjnotes:~# acme.sh --issue --dns dns_cf -d imkjie.com -d *.imkjie.com -k 4096
[Sat 30 May 2020 01:10:43 PM CST] Multi domain='DNS:imkjie.com,DNS:*.imkjie.com'
[Sat 30 May 2020 01:10:43 PM CST] Getting domain auth token for each domain
[Sat 30 May 2020 01:10:46 PM CST] Getting webroot for domain='imkjie.com'
[Sat 30 May 2020 01:10:46 PM CST] Getting webroot for domain='*.imkjie.com'
##這邊可以看到acme.sh會在你的域名DNS管理介面添加兩筆TXT記錄,成功添加的話會有『Success』字眼。
[Sat 30 May 2020 01:10:46 PM CST] Adding txt value: Jl_egDr035xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx for domain: _acme-challenge.imkjie.com
[Sat 30 May 2020 01:10:47 PM CST] Adding record
[Sat 30 May 2020 01:10:47 PM CST] Added, OK
[Sat 30 May 2020 01:10:47 PM CST] The txt record is added: Success.
[Sat 30 May 2020 01:10:47 PM CST] Adding txt value: cwxWboP2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx for domain: _acme-challenge.imkjie.com
[Sat 30 May 2020 01:10:48 PM CST] Adding record
[Sat 30 May 2020 01:10:48 PM CST] Added, OK
[Sat 30 May 2020 01:10:48 PM CST] The txt record is added: Success.
##這邊會睡眠20秒,主要是要等待DNS記錄生效。
[Sat 30 May 2020 01:10:48 PM CST] Lets check each dns records now. Sleep 20 seconds first.
##acme.sh會去檢查DNS記錄是否生效及驗證域名,都成功的話,一樣會有『Success』字眼。
[Sat 30 May 2020 01:11:09 PM CST] Checking imkjie.com for _acme-challenge.imkjie.com
[Sat 30 May 2020 01:11:09 PM CST] Domain imkjie.com '_acme-challenge.imkjie.com' success.
[Sat 30 May 2020 01:11:09 PM CST] Checking imkjie.com for _acme-challenge.imkjie.com
[Sat 30 May 2020 01:11:10 PM CST] Domain imkjie.com '_acme-challenge.imkjie.com' success.
[Sat 30 May 2020 01:11:10 PM CST] All success, lets return
[Sat 30 May 2020 01:11:10 PM CST] Verifying: imkjie.com
[Sat 30 May 2020 01:11:13 PM CST] Success
[Sat 30 May 2020 01:11:13 PM CST] Verifying: *.imkjie.com
[Sat 30 May 2020 01:11:16 PM CST] Success
##驗證完成後,acme.sh也會協助將剛剛在DNS管理介面添加的兩筆TXT記錄刪除。
[Sat 30 May 2020 01:11:16 PM CST] Removing DNS records.
[Sat 30 May 2020 01:11:16 PM CST] Removing txt: Jl_egDr035xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx for domain: _acme-challenge.imkjie.com
[Sat 30 May 2020 01:11:17 PM CST] Removed: Success
[Sat 30 May 2020 01:11:17 PM CST] Removing txt: cwxWboP2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx for domain: _acme-challenge.imkjie.com
[Sat 30 May 2020 01:11:18 PM CST] Removed: Success
##開始簽發SSL憑證。
[Sat 30 May 2020 01:11:18 PM CST] Verify finished, start to sign.
[Sat 30 May 2020 01:11:18 PM CST] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/87483065/3559980973
[Sat 30 May 2020 01:11:19 PM CST] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/04a66ec9fdda767101fa1062d5de81b058c9
[Sat 30 May 2020 01:11:20 PM CST] Cert success.
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
##憑證暫時存放的目錄位置,請勿直接使用此目錄的憑證。
[Sat 30 May 2020 01:11:20 PM CST] Your cert is in /root/.acme.sh/imkjie.com/imkjie.com.cer
[Sat 30 May 2020 01:11:20 PM CST] Your cert key is in /root/.acme.sh/imkjie.com/imkjie.com.key
[Sat 30 May 2020 01:11:20 PM CST] The intermediate CA cert is in /root/.acme.sh/imkjie.com/ca.cer
[Sat 30 May 2020 01:11:20 PM CST] And the full chain certs is there: /root/.acme.sh/imkjie.com/fullchain.cer
**憑證申請成功時,你剛剛在第2部分Step 8所輸入的API資訊(比如筆者剛剛輸入的是『CF_Token』、『CF_Account_ID』和『CF_Zone_ID』),會被寫入在『~/.acme.sh/account.conf』的設定檔。
**acme.sh客戶端軟體在安裝完成後,acme.sh也已經自動新增好一個crontab排程了,你可以使用指令『sudo crontab -l』看到acme.sh新增的排程,如下面所示的排程會在每天的凌晨12點51分自動執行,若憑證少於30天,那acme.sh就會將要過期的憑證進行更新,也就不用擔心憑證會到期的問題了。
51 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
**如果要列出你申請的憑證,那可以使用以下的指令來查看:
acme.sh --list
Step 3:新增兩個目錄,來分別存放剛剛申請好的RSA和ECC憑證。
**下面的範例,請自行將『example.com』替換成你自己的域名。
#RSA
sudo mkdir -p /etc/letsencrypt/cert/example.com
#ECC/ECDSA
sudo mkdir -p /etc/letsencrypt/cert/example.com/ecc
Step 4:將申請好的RSA和ECC憑證複製到剛剛建立好的兩個目錄。
**下面的範例,請自行將『example.com』替換成你自己的域名。
#RSA
acme.sh --install-cert -d example.com \
--cert-file /etc/letsencrypt/cert/example.com/cert.pem \
--key-file /etc/letsencrypt/cert/example.com/private.key \
--fullchain-file /etc/letsencrypt/cert/example.com/fullchain.pem \
--ca-file /etc/letsencrypt/cert/example.com/chain.pem \
--reloadcmd "sudo systemctl reload nginx.service"
#ECC/ECDSA
acme.sh --install-cert -d example.com --ecc \
--cert-file /etc/letsencrypt/cert/example.com/ecc/cert.pem \
--key-file /etc/letsencrypt/cert/example.com/ecc/private.key \
--fullchain-file /etc/letsencrypt/cert/example.com/ecc/fullchain.pem \
--reloadcmd "sudo systemctl reload nginx.service"
Step 5:讓acme.sh日後能自行自動更新客戶端軟體。
acme.sh --upgrade --auto-upgrade
Step 6:憑證申請完,就可以退出root權限。
exit
4)設定Nginx伺服器。
Step 1:如下圖所示可以看到Chrome瀏覽器,因為還沒設定好Nginx config檔,所以在網址列上看到『不安全』及也沒看到鎖頭。
Step 2:編輯Nginx設定檔,這邊的config檔路徑和名稱會跟大家不一樣,請自行設定你主機原有的config檔。以下指令筆者用Vim編輯器來設定自己的config檔。
sudo vim /etc/nginx/sites-available/<nginx-config>
Step 3:設定Nginx HTTP自動轉址至HTTPS。
**下面的範例,請自行將『example.com』替換成你自己的域名。
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
Step 4:配置申請好的SSL憑證。
**下面的範例請自行將『root /var/www/html』替換至Nginx存放網頁的根目錄,及『example.com』替換成你自己的域名。
**下面的範例包含了RSA和ECC憑證的配置。
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
root /var/www/html;
#RSA
ssl_certificate /etc/letsencrypt/cert/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/cert/example.com/private.key;
#ECC/ECDSA
ssl_certificate /etc/letsencrypt/cert/example.com/ecc/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/cert/example.com/ecc/private.key;
ssl_ecdh_curve X25519:secp384r1;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/cert/example.com/chain.pem;
add_header Strict-Transport-Security "max-age=31536000; preload";
# ...
}
Step 5:測試Nginx設定檔,查看執行有沒有錯誤及重新載入Nginx的config檔。
sudo nginx -t
如看到以下的訊息,則代表此次設定是正確的:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
重新載入Nginx的config檔指令如下:
sudo systemctl reload nginx
Step 6:在瀏覽器網址列輸入你設定的域名,就可以看到網址列有小鎖頭,則表示你網站已成功配置好HTTPS加密協定了。
**若主機有啟用防火牆,不要忘記要允許443 Port。
Step 7:如果想要進一步知道憑證是否有配置好,或憑證的相關資訊,那可以使用第三方服務Qualys SSL Labs提供的SSL Server Test來檢測網站的HTTPS配置和評分級別,在Hostname欄位輸入你要檢測網站的域名,可以打勾『Do not show the results on the boards』不要將測試結果的網域顯示在欄位上,點擊『Submit』進行測試。
Step 8:需等待一些時間就可以看到測試結果了,如下圖所示可以看到筆者剛剛配置好的網站獲得A+級別的評分。
5)acme.sh客戶端軟體其他常用的指令。
Step 1:以下筆者列出acme.sh常用的指令:
**如果是使用root帳號安裝acme.sh客戶端軟體的話,那記得要將帳號切換成root權限,才能使用acme.sh指令。
手動強制更新憑證:
acme.sh --cron -f
停止續約某個域名或子域名的憑證:
**請將<domain>替換成要停止續約的域名或子域名。
acme.sh --remove -d <domain>
**若是ECC憑證,需帶入『--ecc』:
acme.sh --remove -d <domain> --ecc
禁用acme.sh客戶端軟體的自動更新:
acme.sh --upgrade --auto-upgrade 0
若在安裝acme.sh客戶端軟體忘記輸入電子郵件信箱,可使用以下指令來進行設定:
acme.sh --register-account -m email@example.com
acme.sh可用的指令及其各個指令的說明:
acme.sh --help
移除acme.sh客戶端軟體,建議先將acme.sh更新到最新再移除,因為網路上看到有人移除失敗:
acme.sh --upgrade
acme.sh --uninstall
rm -r ~/.acme.sh