﻿# 如何使用 Certbot 建立免費的 TLS/SSL 網域憑證並自動產生 PFX 憑證

我之前有寫過一篇 [如何使用 Certbot 命令列工具建立免費的 TLS/SSL 頂層網域憑證](/post/2021/02/11/Create-SSL-TLS-certificates-from-LetsEncrypt-using-Certbot) 文章，當時的情境是我的域名是 **頂層網域** (naked domains)(Top-Level Domain)(TLD)，必須用「手動」方式申請。今天這篇文章，我要示範如何「全自動」申請與更新憑證，並自動產生 [Kestrel](https://learn.microsoft.com/zh-tw/aspnet/core/fundamentals/servers/kestrel) 所需的 `*.pfx` (PKCS#12) 憑證。

## 首次建立憑證與設定自動更新排程 (www.your-domain.com.tw)

> 本篇文章將會以 Ubuntu 18.04.1 LTS 為主進行示範，但其實使用的方式大同小異。

1.  使用 SSH 登入 Ubuntu 18.04.1 LTS 主機
    
2.  先確認 snapd 已升級到最新版本
    
    ```bash
    sudo snap install core; sudo snap refresh core
    ```
    
3.  確認現有已安裝的 Certbot 已確實移除
    
    ```bash
    sudo apt-get remove certbot
    ```
    
    > 因為安裝 Certbot 的過程中會自動註冊一些排程工作，所以為了避免舊版本的 Certbot 影響到新的安裝，建議先移除舊版。
    
4.  使用 `snap` 安裝 `certbot` 套件
    
    ```bash
    sudo snap install --classic certbot
    ```
    
    > 安裝過程中會自動安裝一個 `snap.certbot.renew.service` 系統服務，此服務專門用來自動更新憑證之用。你可以透過 `systemctl list-units --all --type=service --no-pager | grep certbot` 查詢該服務，或透過 `service snap.certbot.renew status` 查詢目前的執行狀態。剛安裝好的 certbot 並不會有服務執行。
    
5.  到網站建立 `/.well-known/acme-challenge/` 資料夾並測試網址可以正常連接
    
    ```bash
    cd $WWWROOT/
    
    mkdir -p .well-known/acme-challenge
    echo OK > .well-known/acme-challenge/abc
    curl https://www.your-domain.com.tw/.well-known/acme-challenge/abc
    rm -f .well-known/acme-challenge/abc
    ```
    
    > 這裡請將 `$WWWROOT` 替換成你目前的網站根目錄。
    
6.  建立憑證
    
    ```bash
    sudo certbot certonly --webroot
    ```
    
    > 注意：所有**偵錯訊息**都會放在 `/var/log/letsencrypt/letsencrypt.log` 檔案中。
    
    ```log
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Plugins selected: Authenticator webroot, Installer None
    Enter email address (used for urgent renewal and security notices)
    (Enter 'c' to cancel): YourName@example.com
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Please read the Terms of Service at
    https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
    agree in order to register with the ACME server. Do you agree?
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: Y
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Would you be willing, once your first certificate is successfully issued, to
    share your email address with the Electronic Frontier Foundation, a founding
    partner of the Let's Encrypt project and the non-profit organization that
    develops Certbot? We'd like to send you email about our work encrypting the web,
    EFF news, campaigns, and ways to support digital freedom.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: N
    Account registered.
    Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
    to cancel): www.your-domain.com.tw
    Requesting a certificate for www.your-domain.com.tw
    Performing the following challenges:
    http-01 challenge for www.your-domain.com.tw
    Input the webroot for www.your-domain.com.tw: (Enter 'c' to cancel): $WWWROOT/
    Waiting for verification...
    Cleaning up challenges
    
    IMPORTANT NOTES:
    - Congratulations! Your certificate and chain have been saved at:
      /etc/letsencrypt/live/www.your-domain.com.tw/fullchain.pem
      Your key file has been saved at:
      /etc/letsencrypt/live/www.your-domain.com.tw/privkey.pem
      Your certificate will expire on 2021-07-20. 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
    ```
    
7.  確認憑證輸出結果
    
    憑證輸出路徑
    
    ```log
    /etc/letsencrypt/live/www.your-domain.com.tw/
    ```
    
    主要 Certbot 設定檔
    
    ```log
    /etc/letsencrypt/renewal/www.your-domain.com.tw.conf
    ```
    
8.  憑證輸出路徑下的憑證檔案
    
    伺服器憑證 `fullchain.pem` (此檔案將包括完整的**憑證鏈**)
    
    私密金鑰檔 `privkey.pem` (此檔案是**未加密**的金鑰檔)
    
9.  修改 Apache 的 SSL 相關設定
    
    檔案路徑: `/etc/apache2/sites-enabled/www.your-domain.com.tw.conf`
    
    ```log
    SSLCertificateFile      /etc/ssl/certs/www.your-domain.com.tw.crt
    SSLCertificateKeyFile   /etc/ssl/private/www.your-domain.com.tw.key
    SSLCertificateChainFile /etc/ssl/certs/twca-chain.crt
    ```
    
    修改成
    
    ```log
    SSLCertificateFile      /etc/letsencrypt/live/www.your-domain.com.tw/cert.pem
    SSLCertificateKeyFile   /etc/letsencrypt/live/www.your-domain.com.tw/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/www.your-domain.com.tw/chain.pem
    ```
    
10.  重新啟動 Apache 服務
     
     ```bash
     sudo service apache2 reload
     ```
     
11.  驗證憑證自動更新 (Renew)
     
     ```bash
     sudo certbot renew --webroot --dry-run
     ```
     
     > 上述命令會自動設定一個系統排程，你可以透過 `systemctl list-timers --no-pager` 或 `systemctl list-unit-files --type timer` 查詢到一個名為 `snap.certbot.renew.timer` 的排程作業(UNIT)。
     
12.  調整 Systemd 的 Timer 排程
     
     請將 \[Timer\] 區段修改為 **每天早上 4:30 執行一次**：
     
     ```bash
     vi /etc/systemd/system/timers.target.wants/snap.certbot.renew.timer
     ```
     
     ```ini
     [Timer]
     Unit=snap.certbot.renew.service
     OnCalendar=*-*-* 04:30
     ```
     
     > 關於定時的格式可以參考 [systemd/Timers - ArchWiki](https://wiki.archlinux.org/title/Systemd/Timers) 文件說明。
     

## 取得憑證後自動產生 PFX 憑證

1.  產生 PFX (PKCS#12) 憑證檔
    
    ```bash
    cd /etc/letsencrypt/live/www.your-domain.com.tw/
    
    PASSWORD=SD9QT4bVq
    openssl pkcs12 -export -out your-domain.pfx -inkey "privkey.pem" -in "cert.pem" -certfile "fullchain.pem" -password "pass:$PASSWORD"
    
    cp your-domain.pfx $YourPfxLocation/your-domain.pfx
    ```
    
    > 注意: 請將 `$YourPfxLocation` 替換為你的 PFX 放置路徑！
    
2.  重新啟動網站
    
    ```bash
    service MyService reload
    ```
    
    > 注意: 我有將我的 Kestrel 網站伺服器封裝成一個服務，假設服務名稱為 `MyService`。
    
    如果網站可以正常瀏覽，才能確認你所產生的 PFX 檔案正確無誤，這時才能進行下一步。
    
3.  建立一個 `/etc/letsencrypt/renewal-hooks/post/certbot-post-hook.sh` 檔案
    
    這個檔案會在每次成功 Renew 憑證後自動執行，如此一來你就可以將自動產生 PFX 與重新啟動網站伺服器的動作，寫在這個 Shell Script 裡面，這樣就能完成自動化了！
    
    ```bash
    #!/bin/sh
    
    cd /etc/letsencrypt/live/www.your-domain.com.tw/
    
    PASSWORD=SD9QT4bVq
    openssl pkcs12 -export -out your-domain.pfx -inkey "privkey.pem" -in "cert.pem" -certfile "fullchain.pem" -password "pass:$PASSWORD"
    
    cp your-domain.pfx $YourPfxLocation/your-domain.pfx
    
    service MyService status
    ```
    
    變更執行權限
    
    ```bash
    chmod 700 /etc/letsencrypt/renewal-hooks/post/certbot-post-hook.sh
    ```
    
4.  驗證憑證自動更新 (Renew)
    
    ```bash
    sudo certbot renew --webroot --dry-run
    ```
    
    > 請記得加上 `--dry-run` 測試目前的腳本與設定可以正確的更新憑證並自動建立 PFX 憑證與重新啟動網站伺服器。
    

### 相關連結

-   [Certbot - Ubuntubionic Other](https://certbot.eff.org/instructions?ws=other&os=ubuntubionic)
-   [Systemd 定时器教程 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2018/03/systemd-timer.html)
-   [systemd/Timers - ArchWiki](https://wiki.archlinux.org/title/Systemd/Timers)
-   [如何使用 Certbot 命令列工具建立免費的 TLS/SSL 頂層網域憑證 | The Will Will Web](/post/2021/02/11/Create-SSL-TLS-certificates-from-LetsEncrypt-using-Certbot)
-   [How to list all services in Ubuntu 18.04?](https://maslosoft.com/kb/how-to-list-all-services-in-ubuntu-18-04/)
-   [Kestrel ASP.NET Core 中的 web 伺服器執行](https://learn.microsoft.com/zh-tw/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-5.0&WT.mc_id=DT-MVP-4015686)
