Certbot
在安裝之前
Ubuntu 有可能使用 snap 安裝 certbot
如果同時使用 apt 安裝 certbot,可能會造成干擾
檢查 Certbot
# 查看 certbot 安裝的路徑(是否有安裝)
which certbot
# 檢查 certbot 版本
certbot --version
安裝 Certbot 與 Google DNS Plugin
sudo apt update
# 安裝 Certbot 與 Google Cloud DNS Plugin
sudo apt install -y certbot python3-certbot-dns-google
Directory Structure
letsencrypt
├── accounts
│ ├── acme-staging-v02.api.letsencrypt.org
│ │ └── directory
│ │ └── 180d32a90fcecd7620396273e9c6fd95
│ │ ├── meta.json
│ │ ├── private_key.json
│ │ └── regr.json
│ └── acme-v02.api.letsencrypt.org
│ └── directory
│ └── 515be3a8137b3c8af7b6696dfc3915ad
│ ├── meta.json
│ ├── private_key.json
│ └── regr.json
├── archive (存放憑證的目錄)
│ └── ${main_fqdn}
│ ├── cert1.pem
│ ├── chain1.pem
│ ├── fullchain.pfx
│ ├── fullchain1.pem
│ └── privkey1.pem
├── cli.ini
├── csr (Certbot 自動產生)
│ ├── 0000_csr-certbot.pem
│ └── 0001_csr-certbot.pem
├── keys (Certbot 自動產生)
│ ├── 0000_key-certbot.pem
│ └── 0001_key-certbot.pem
├── live (連結到 archive 目錄下的憑證)
│ ├── README
│ └── ${main_fqdn}
│ ├── cert.pem -> ../../archive/${main_fqdn}/cert1.pem
│ ├── chain.pem -> ../../archive/${main_fqdn}/chain1.pem
│ ├── fullchain.pem -> ../../archive/${main_fqdn}/fullchain1.pem
│ └── privkey.pem -> ../../archive/${main_fqdn}/privkey1.pem
├── options-ssl-nginx.conf
├── renewal
│ └── ${main_fqdn}.conf
├── renewal-hooks
│ ├── deploy
│ ├── post (憑證更新後會執行 Script 的目錄)
│ │ └── post_script.sh
│ └── pre (憑證更新前會執行 Script 的目錄)
└── ssl-dhparams.pem
建立目錄結構的指令
mkdir -p /etc/letsencrypt/accounts/acme-staging-v02.api.letsencrypt.org/directory/
mkdir -p /etc/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory/
mkdir -p /etc/letsencrypt/archive/${main_fqdn}
mkdir -p /etc/letsencrypt/csr
mkdir -p /etc/letsencrypt/keys
mkdir -p /etc/letsencrypt/live/${main_fqdn}
mkdir -p /etc/letsencrypt/renewal
mkdir -p /etc/letsencrypt/renewal-hooks/deploy
mkdir -p /etc/letsencrypt/renewal-hooks/post
mkdir -p /etc/letsencrypt/renewal-hooks/pre
檔案介紹
- /etc/letsencrypt/renewal/${main_fqdn}.conf
存放申請憑證的帳號、向誰驗證、驗證方式、金鑰,以及相關憑證存放路徑
# 過期前 30天才能更新憑證
# renew_before_expiry = 30 days
version = 1.21.0
# 憑證存放路徑
archive_dir = /etc/letsencrypt/archive/${main_fqdn}
# 憑證連結路徑名稱
cert = /etc/letsencrypt/live/${main_fqdn}/cert.pem
privkey = /etc/letsencrypt/live/${main_fqdn}/privkey.pem
chain = /etc/letsencrypt/live/${main_fqdn}/chain.pem
fullchain = /etc/letsencrypt/live/${main_fqdn}/fullchain.pem
# Options used in the renewal process
[renewalparams]
# 申請憑證的帳號
account = 515be3a8696dfc3915ad
# 向誰驗證網域
authenticator = dns-google
dns_google_propagation_seconds = 60
# Google Cloud DNS 的金鑰
dns_google_credentials = /root/ssl_bak/myproject-123456.json
server = https://acme-v02.api.letsencrypt.org/directory
測試申請憑證
# Test Drive
certbot certonly \
--dns-google \
--dns-google-credentials=/root/ssl_bak/myproject-123456.json \
--dns-google-propagation-seconds 60 \
-d "${main_fqdn}" \
-d "*.${main_fqdn}" \
--email mis@${main_fqdn} \
--agree-tos \
--non-interactive \
--force-renewal
--dry-run
如果要正式申請憑證,請拿掉 --dry-run 參數
因為 Let’s Encrypt 會限制正式申請憑證測次數
建議先用 --dry-run 測試沒有錯誤,再執行正式申請
測試更新憑證
certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/${main_fqdn}.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simulating renewal of an existing certificate for ${main_fqdn} and 3 more domains Waiting 60 seconds for DNS changes to propagate
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/${main_fqdn}/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
檢查 Certbot 更新憑證的排程服務
- 排程執行,可以使用 cron job 或是 systemd 去管理,certbot 使用 systemd 去管理
systemctl list-timers | grep certbot
Wed 2025-04-16 04:51:00 CST 2h 12min left
Tue 2025-04-15 23:02:12 CST 3h 36min ago snap.certbot.renew.timer snap.certbot.renew.service
Wed 2025-04-16 21:43:00 CST 19h left n/a n/a certbot.timer certbot.service
從上面的輸出訊息來看,系統也有 ubuntu snap 套件管理程式安裝的 certbot
停用 snap certbot 排程
systemctl stop snap.certbot.renew.timer
systemctl disable snap.certbot.renew.timer
如果要使用 cron job 排程
(crontab -l 2>/dev/null; echo "0 */12 * * * certbot renew --quiet") | crontab -
0 */12 * * * certbot renew --quiet
檢查 certbot.timer 排程設定內容
systemctl cat certbot.timer
# /lib/systemd/system/certbot.timer
[Unit]
Description=Run certbot twice daily
[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true
[Install]
WantedBy=timers.target
OnCalendar=--* 00,12:00:00
RandomizedDelaySec=43200
每天 2 次,大約在凌晨與中午之間,會隨機延遲最多 12 小時執行續期
systemctl cat certbot.service
# /lib/systemd/system/certbot.service
[Unit]
Description=Certbot
Documentation=file:///usr/share/doc/python-certbot-doc/html/index.html
Documentation=https://certbot.eff.org/docs
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
PrivateTmp=true
usr/bin/certbot -q renew 表示靜默執行
檢查申請憑證資訊
certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
Certificate Name: ${main_fqdn}
Serial Number: 625834c79a1b0fbf5
Key Type: RSA
Domains: ${main_fqdn} *.${main_fqdn}
Expiry Date: 2025-07-14 16:28:48+00:00 (VALID: 88 days)
Certificate Path: /etc/letsencrypt/live/${main_fqdn}/fullchain.pem
Private Key Path: /etc/letsencrypt/live/${main_fqdn}/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Renew-Hooks/post
#!/bin/bash
DOMAIN="${main_fqdn}"
PASS="1qaz2wsx"
REMOTE_HOST="10.40.2.15"
REMOTE_USER="root"
ARCHIVE_PATH="/etc/letsencrypt/archive/${DOMAIN}"
LIVE_PATH="/etc/letsencrypt/live/${DOMAIN}"
PFX_FILE="${ARCHIVE_PATH}/fullchain.pfx"
# 1. 產出 PFX
openssl pkcs12 -export \
-out "${PFX_FILE}" \
-inkey "${ARCHIVE_PATH}/privkey1.pem" \
-in "${ARCHIVE_PATH}/fullchain1.pem" \
-certfile "${ARCHIVE_PATH}/chain1.pem" \
-password pass:"${PASS}" \
-leagcy
# 2. 傳送所有檔案到遠端
scp ${ARCHIVE_PATH}/* ${REMOTE_USER}@${REMOTE_HOST}:${ARCHIVE_PATH}/
# 3. 在遠端建立 symlinks
ssh ${REMOTE_USER}@${REMOTE_HOST} bash <<EOF
mkdir -p ${LIVE_PATH}
ln -sf ${ARCHIVE_PATH}/cert1.pem ${LIVE_PATH}/cert.pem
ln -sf ${ARCHIVE_PATH}/chain1.pem ${LIVE_PATH}/chain.pem
ln -sf ${ARCHIVE_PATH}/fullchain1.pem ${LIVE_PATH}/fullchain.pem
ln -sf ${ARCHIVE_PATH}/privkey1.pem ${LIVE_PATH}/privkey.pem
EOF
# 4. 測試並重啟 nginx
nginx -t && systemctl reload nginx
將 憑證轉換成 pkcs12
IIS 使用 PKCS12 格式,它將 Private Key, Full Chain 憑證都包裝成一個檔案
還可以設定密碼(匯入/匯出憑證時會詢問)
很早期的系統只要提供 Private Key & Endpoint Certificate 兩個憑證檔案,
但是科技日新月異,安全性要求越來越高,
所以必須提供中繼憑證 Intermediate Certificate,甚至是 Root Certificate
傳統使用憑證需要 Private Key, Cert, Chain 三個憑證檔案
新一點的系統會需要 Private Key, Full Chain 兩個憑證檔案
Full Chain 通常就是包含 Root, Chain, Cert 三個憑證
openssl pkcs12 -export \
-out /etc/letsencrypt/archive/${main_fqdn}/fullchain.pfx \
-inkey /etc/letsencrypt/archive/${main_fqdn}/privkey1.pem \
-in /etc/letsencrypt/archive/${main_fqdn}/fullchain1.pem \
-certfile /etc/letsencrypt/archive/${main_fqdn}/chain1.pem \
-password pass:1qaz2wsx
-legacy
使用 OpenSSL 檢驗 PKCS12 憑證
openssl pkcs12 -info -in fullchain.pfx -noout
提取 PKCS12 當中的憑證與私鑰
openssl pkcs12 -in fullchain.pfx -clcerts -nokeys -out cert.pem
提取 PKCS12 當中的私鑰
openssl pkcs12 -in fullchain.pfx -nocerts -out privkey.pem
提取 PKCS12 當中的Chain
openssl pkcs12 -in fullchain.pfx -cacerts -nokeys -out chain.pem
檢查憑證檔案
openssl x509 -in cert.pem -text -noout
檢查私鑰
openssl rsa -in privkey.pem -check
驗證憑證與私鑰是否匹配
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in privkey.pem | openssl md5