一、需求
多台服务器的Nginx服务器共用一套SSL证书,不想每台服务器配置一个acme.sh 或 Let's Encrypt 签发。只要一台服务器配置自动申请/更新证书,然后用脚本同步到其他服务器上去。
二、方案
-
- 主服务器: 负责申请/更新证书(如运行
acme.sh);
- 主服务器: 负责申请/更新证书(如运行
-
- 其他从服务器:通过安全方式(如
rsync + SSH)定期或触发式同步证书;
- 其他从服务器:通过安全方式(如
-
- 更新后使用
nginx -s reload重新加载证书;
- 更新后使用
-
- 使用SSH 密钥认证,无需密码,安全高效。
三、操作步骤
1. 创建新密钥
不要用root用户同步,创建专用最小权限用户,并用一组新的SSH密钥,在主服务器上生成新密钥
ssh-keygen -t ed25519 -f ~/.ssh/cert_sync_key -N ""
# 获取cert_sync_key公钥信息
cat ~/.ssh/cert_sync_key.pub
# 输出类似信息
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... root@master
2. 在所有从服务器上创建专用用户
假如用户为certsync,以最小权限方式运行,即该用户只支持 rsync上传和nginx -s reload重载命令。
# 创建 用户
sudo useradd -s /bin/bash -d /home/certsync certsync
sudo mkdir -p /home/certsync/.ssh
# 设置密钥限制, 最后的ed25519 AAAA替换为主服务器上获取的 ~/.ssh/cert_sync_key.pub内容
sudo tee /home/certsync/.ssh/authorized_keys <<'EOF'
command="case \"$SSH_ORIGINAL_COMMAND\" in \"sudo nginx -s reload\") exec sudo nginx -s reload;; rsync\ --server\ *) exec $SSH_ORIGINAL_COMMAND;; *) exit 1;; esac",no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... root@master
EOF
# 设置权限
sudo chown certsync:nogroup /home/certsync/.ssh
sudo chmod 700 /home/certsync/.ssh
sudo chown certsync:nogroup /home/certsync/.ssh/authorized_keys
sudo chmod 600 /home/certsync/.ssh/authorized_keys
3. 配置sudo免密执行reload
编辑 visudo
sudo visudo
添加
# 这里是nginx 的安装位置
certsync ALL=(ALL) NOPASSWD: /usr/sbin/nginx -s reload
4. 测试
- 假设从服务器ip:
192.168.1.108 - 主服务器证书位置:
/etc/nginx/conf.d/site.crt - 从服务器证书位置:
/etc/nginx/conf.d/site.crt,并设置/etc/nginx/conf.d/目录权限为777
然后在主服务器上输入以下命令测试:
# 测试 rsync
rsync -avz -e "ssh -i ~/.ssh/cert_sync_key" /etc/nginx/conf.d/site.crt certsync@192.168.1.108:/etc/nginx/conf.d/site.crt
# 测试 reload
ssh -i ~/.ssh/cert_sync_key certsync@192.168.1.108 "sudo nginx -s reload"
# 测试非法命令
ssh -i ~/.ssh/cert_sync_key certsync@192.168.1.108 "ls /"
# 决绝访问,无任何输出

5. 编写分发脚本
新建脚本,并赋执行权限
/home/deploy-cert.sh
chmod +x /home/deploy-cert.sh
脚本内容为:
#!/bin/bash
set -e
# ========================
# 配置区(按需修改)
# ========================
# 本地证书源文件(由 acme.sh 或其他工具生成)
LOCAL_CERT="/etc/nginx/conf.d/site.crt"
LOCAL_KEY="/etc/nginx/conf.d/site.key"
# SSH 密钥(用于免密登录目标服务器)
SSH_KEY="~/.ssh/cert_sync_key"
# 目标服务器列表:每行格式为 "user@host cert_path key_path"
# 注意:路径必须是绝对路径,不同服务器路径可以不一样。
# ["别名"]="用户名@IP 证书路径 私钥路径"
declare -A SERVERS=(
["web01"]="certsync@192.168.1.101 /etc/nginx/conf.d/site.crt /etc/nginx/conf.d/site.key"
["web02"]="certsync@192.168.1.102 /etc/nginx/conf.d/site.crt /etc/nginx/conf.d/site.key"
["web03"]="certsync@192.168.1.103 /etc/nginx/conf.d/site.crt /etc/nginx/conf.d/site.key"
)
# ========================
# 脚本逻辑
# ========================
if [[ ! -f "$LOCAL_CERT" ]] || [[ ! -f "$LOCAL_KEY" ]]; then
echo " 本地证书或私钥不存在!"
echo " CERT: $LOCAL_CERT"
echo " KEY: $LOCAL_KEY"
exit 1
fi
echo "开始分发 SSL 证书..."
for name in "${!SERVERS[@]}"; do
config="${SERVERS[$name]}"
# 拆分配置:user@host, cert_path, key_path
read -r host cert_path key_path <<< "$(echo $config | awk '{print $1, $2, $3}')"
echo "同步到 [$name] $host ..."
# 同步证书(644)和私钥(600)
rsync -avz --chmod=644 -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" "$LOCAL_CERT" "$host:$cert_path" || {
echo "[$name] 证书同步失败"
continue
}
rsync -avz --chmod=600 -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" "$LOCAL_KEY" "$host:$key_path" || {
echo "[$name] 私钥同步失败"
continue
}
# 重载 nginx (使用sudo nginx -s reload)
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$host" "sudo nginx -s reload" && \
echo "[$name] 证书更新并重载成功" || \
echo "[$name] 重载 nginx 失败!"
done
echo "所有服务器证书分发完成!"
6. 集成到 acme.sh 中
acme.sh --install-cert -d 你的域名 \
--key-file /etc/nginx/conf.d/site.key \
--fullchain-file /etc/nginx/conf.d/site.crt \
--reloadcmd "/home/deploy-cert.sh"
这样每次通过acme更新过证书后,就分发到其他服务器上去了。也可以手动执行/home/deploy-cert.sh脚本分发。

评论区