申請並部署免費的 SSL/TLS 證書

莱布尼茨發表於2024-05-28

對於囊中羞澀的我們來說,只要能白嫖,就絕不亂花錢。慣常申請免費 SSL/TLS 證書的途徑有:

  • 各大雲服務平臺限量提供。比如阿里雲會給每個賬號每年 20 個證書的申請額度。缺點是不支援泛域名,一年(目前已縮短至三個月)後須重新申請並部署;
  • 一些網路服務商如Cloudflare會提供自動化的證書服務,只要在它們的官網作簡單配置即可。缺點是流量都要經其中轉,在某些未很好覆蓋的區域網站效能會受限;
  • 直接從免費證書頒發機構(CA)那裡申請。透過指令碼可使得獲取、安裝和續期 SSL/TLS 證書完全自動化。缺點是有一定的操作門檻。

本文介紹最後這種方式。

免費 CA 選擇

免費 CA,常考慮的有 Let’s EncryptZeroSSL。兩者差異可自行網搜,基本上都能滿足我們普通使用者的需求,選擇哪一個更依賴於申請過程是否順利。

以 Let’s Encrypt 作一簡單介紹。它是由 Internet Security Research Group (ISRG) 發起的公共利益專案,於2016年推出,由 Mozilla, Cisco, Akamai, Linux Foundation, Google, Microsoft, OVH 和其它一些組織支援。這個專案的主要目標是促進 HTTPS 協議的普及。Let’s Encrypt 使用 ACME 協議來驗證您對給定域名的控制權並向您頒發證書,也就是說,要獲得 Let’s Encrypt 證書,需要選擇一個 ACME 客戶端。

這裡以 acme.sh 為例。acme.sh 實現了 ACME 協議, 預設 CA 是 ZeroSSL。然而筆者在申請時一直報Order status is processing, lets sleep and retry,retry 多次後報 504,不知為何。所以筆者將預設 CA 設定為 Let’s Encrypt:acme.sh --set-default-ca --server letsencrypt

申請並部署證書

安裝 acme.sh

不清楚 docker 模式安裝會否自動續期,所以直接安裝到宿主機 curl https://get.acme.sh | sh -s email=xxx@xxx。直接安裝會自動建立 cronjob, 每天自動檢測所有的證書, 如果快過期了, 則會自動更新證書。

如前所述,設定預設 CA:acme.sh --set-default-ca --server letsencrypt

申請

在證書申請過程中,acme.sh 需要確認域名屬於你本人,有兩種驗證方式:http 和 dns。

  • http mode: 支援自動更新證書;不支援萬用字元即泛域名。
  • dns mode:支援泛域名;但如果不同時配置 Automatic DNS API,將無法自動更新證書,每次都需要手動再次重新解析驗證域名所有權。所以,如果你有多個二級域名需要繫結證書,要麼就使用下述 http mode 的指令,要麼就去 dns 服務商處拿到 API Access(可能會有一些要求)。 參看acme.sh 支援的 dns 服務商列表

這裡我們以 http mode 為例。

先設定好 DNS 解析記錄,將待申請證書的域名 A 記錄到伺服器 IP。然後如下指令碼申請證書,acme.sh 會同時進行域名驗證。

acme.sh --issue -d demo.com -d www.demo.com -d admin.demo.com --webroot /srv/www/html/

只需要指定域名,並指定對應的網站根目錄,acme.sh 會自動生成驗證檔案,並放到網站根目錄,然後自動完成驗證,最後會刪除驗證檔案。

如果你用的是直接在宿主機安裝的 nginx 伺服器,甚至不需要指定網站根目錄。acme.sh 可以智慧地依據 nginx 配置自動完成驗證:

acme.sh --issue -d demo.com ... --nginx

上述指令碼的前提是 acme.sh 可以呼叫 nginx 指令,否則會報 nginx command is not found 錯誤。所以如果 nginx 是以 docker 方式安裝,這種方式將無法成功;還是得使用 --webroot 的方式,只要將 nginx 根目錄對映出來即可,如下:

docker run --name ngix -v /srv/www/html:/usr/share/nginx/html:ro -v /srv/www/nginx.conf:/etc/nginx/nginx.conf:ro -p 80:80 -p 443:443 --network inner_net --restart=always -d --label=sh.acme.autoload.domain=anything nginx:stable-alpine

注意指令碼中的 label,用於後續部署證書時 acme.sh 定位該容器。這裡的 anything 可以替換為任意字元。

域名驗證無誤的話,acme.sh 會去 CA 那裡獲取證書,並下載到本地。一個主域名及其多個子域名同時申請的話,CA 只為它們簽發一張證書。

部署

還是以 nginx docker 容器為例。

# The label value to find the container
# 多個不同域名申請證書時可重複使用該容器
export DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=anything

# The target file path in the container.
# The files will be copied to the position in the container.
export DEPLOY_DOCKER_CONTAINER_KEY_FILE="/etc/nginx/ssl/demo.com/key.pem"
export DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/ssl/demo.com/cert.pem"
export DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/ssl/demo.com/ca.pem"
export DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/ssl/demo.com/full.pem"

# The command to reload the service in the container.
export DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="nginx -s reload"

# -d 指定 acme.sh --issue 時第一個[二級]域名或其執行結果產生的目錄名
acme.sh --deploy --deploy-hook docker -d demo.com

筆者尚不清楚自動更新證書時是否會將新證書自動複製至容器內,推測應該是會的,這些臨時環境變數應該會以當前申請的域名配置項的形式固化到磁碟中。

配置 nginx.conf

在 http 節中加入如下配置

server {
     listen 443 ssl;
     # 二級域名配置相同,本文略過
     server_name demo.com;
     root /usr/share/nginx/html;
     # 上一步證書複製的地址
     ssl_certificate /etc/nginx/ssl/demo.com/cert.pem;
     ssl_certificate_key /etc/nginx/ssl/demo.com/key.pem;
 
     ssl_session_cache shared:SSL:1m;
     ssl_session_timeout 5m;
 
     #預設加密套件
     ssl_ciphers HIGH:!aNULL:!MD5;
     #表示優先使用服務端加密套件。預設開啟
     ssl_prefer_server_ciphers on; 
 
    location / {
           index index.html index.htm;
    }
}

吊銷證書

假如因某種原因需要吊銷證書的話,可執行如下指令碼:

acme.sh --revoke -d demo.com -d www.demo.com -d admin.demo.com

筆者尚不清楚在一個主域名及其多個子域名共用一張證書的情況下,是否可只給部分域名吊銷證書。

相關文章