捐血一袋救人一命

2025年4月18日 星期五

Certbot

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

0 意見: