1. 首先,查看 acme.sh 的配置信息
root@VM-24-16-debian:/etc/profile.d# acme.sh --info
LE_WORKING_DIR=/root/.acme.sh
LE_CONFIG_HOME=/root/.acme.sh
#LOG_FILE="/root/.acme.sh/acme.sh.log"
#LOG_LEVEL=1
AUTO_UPGRADE='1'
#NO_TIMESTAMP=1
2. 接着,查看 acme.sh 已配置的任务
root@VM-24-16-debian:~/.acme.sh# acme.sh --list
Main_Domain KeyLength SAN_Domains CA Created Renew
kzwr.com "ec-256" *.kzwr.com ZeroSSL.com 2024-07-11T09:15:22Z 2024-09-08T09:15:22Z
可以看见我们为 kzwr.com 配置了一个证书任务
3. 根据以上信息,我们定位到 acme.sh 的 LE_WORKING_DIR 工作目录
root@VM-24-16-debian:~/.acme.sh# cd /root/.acme.sh
root@VM-24-16-debian:~/.acme.sh# ls
account.conf acme.sh acme.sh.env ca deploy dnsapi http.header kzwr.com_ecc notify
root@VM-24-16-debian:~/.acme.sh# cd kzwr.com_ecc/
root@VM-24-16-debian:~/.acme.sh/kzwr.com_ecc# ls
backup ca.cer fullchain.cer kzwr.com.cer kzwr.com.conf kzwr.com.csr kzwr.com.csr.conf kzwr.com.key
kzwr.com_ecc 是证书保存的位置
- kzwr.com.cer:服务器证书
- ca.cer:中间证书链(CA 证书)
- kzwr.com.key:私钥
- fullchain.cer:完整证书链(包括服务器证书和中间证书)
4. 查看 HAProxy 的配置信息
root@VM-24-16-debian:~/.acme.sh/kzwr.com_ecc# cat /etc/haproxy/
block/ errors/ haproxy.cfg kzwr.com.pem
root@VM-24-16-debian:~/.acme.sh/kzwr.com_ecc# cat /etc/haproxy/haproxy.cfg
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
# https://discourse.haproxy.org/t/how-to-redirect-non-www-or-www-without-ssl-to-www-ssl-prefix-in-haproxy/2439
# Frontend for HTTP (port 80)
frontend http_in
bind *:80
# Redirect http://kzwr.com to https://www.kzwr.com
redirect scheme https code 301 if { hdr(host) -i kzwr.com }
# Redirect http://www.kzwr.com to https://www.kzwr.com
redirect location https://www.kzwr.com%[capture.req.uri] code 301 if { hdr(host) -i www.kzwr.com }
frontend https_in
bind *:443 ssl crt /etc/haproxy/kzwr.com.pem alpn h2,http/1.1
由上可见,/etc/haproxy/kzwr.com.pem 是 HAProxy 读取的证书的位置
5. 在 /etc/haproxy/ 路径创建更新脚本
root@VM-24-16-debian:~/.acme.sh# nano renew_haproxy_cert.sh
#!/bin/bash
# Paths
ACME_CERT_DIR="/root/.acme.sh/kzwr.com_ecc"
HAPROXY_CERT_FILE="/etc/haproxy/kzwr.com.pem"
TEMP_CERT_FILE="/tmp/haproxy_new_cert.pem"
# acme.sh certificate and key files
FULLCHAIN="$ACME_CERT_DIR/fullchain.cer"
PRIVATE_KEY="$ACME_CERT_DIR/kzwr.com.key"
# Check if certificate and private key files exist
if [[ -f "$FULLCHAIN" && -f "$PRIVATE_KEY" ]]; then
echo "Merging new certificate and private key into a temporary file..."
cat "$FULLCHAIN" "$PRIVATE_KEY" > "$TEMP_CERT_FILE"
# Compare the new certificate with the current one
if ! diff "$TEMP_CERT_FILE" "$HAPROXY_CERT_FILE" > /dev/null; then
echo "New certificate detected, updating and reloading HAProxy..."
# Copy the new certificate to HAProxy directory
cp "$TEMP_CERT_FILE" "$HAPROXY_CERT_FILE"
# Check if the copy operation was successful
if [[ $? -eq 0 ]]; then
echo "Certificate successfully updated, reloading HAProxy..."
systemctl reload haproxy
if [[ $? -eq 0 ]]; then
echo "HAProxy successfully reloaded."
else
echo "Failed to reload HAProxy."
fi
else
echo "Failed to update the certificate."
fi
else
echo "No changes in the certificate, no update needed."
fi
# Clean up the temporary file
rm -f "$TEMP_CERT_FILE"
else
echo "Certificate or private key file not found, please check $ACME_CERT_DIR"
fi
关键步骤解释:
- 临时合并文件:将新的证书和私钥合并到一个临时文件 $TEMP_CERT_FILE。
- 将新的证书和私钥合并到一个临时文件 $TEMP_CERT_FILE。
- 使用 diff 比较证书文件:通过 diff "$TEMP_CERT_FILE" "$HAPROXY_CERT_FILE" 来检查当前使用的证书和新生成的证书是否相同。如果有差异,表示证书已更新。
- 通过 diff "$TEMP_CERT_FILE" "$HAPROXY_CERT_FILE" 来检查当前使用的证书和新生成的证书是否相同。
- 如果有差异,表示证书已更新。
- 证书变更时的处理:当检测到新证书时,复制新的证书文件到 HAProxy 目录,覆盖旧证书。之后执行 systemctl reload haproxy 以重新加载 HAProxy 配置。
- 当检测到新证书时,复制新的证书文件到 HAProxy 目录,覆盖旧证书。
- 之后执行 systemctl reload haproxy 以重新加载 HAProxy 配置。
- 临时文件清理:最后删除临时文件 $TEMP_CERT_FILE。
- 最后删除临时文件 $TEMP_CERT_FILE。
6. 赋予执行权限
chmod +x renew_haproxy_cert.sh
7. 配置定时任务 (cronjob)
编辑 crontab:
crontab -e
然后添加以下条目来每天凌晨 3 点执行一次脚本:
0 3 * * * /root/.acme.sh/renew_haproxy_cert.sh >> /var/log/haproxy_cert_renew.log 2>&1