目 录CONTENT

文章目录

多台Nginx服务器使用同份SSL证书,实现证书分发+自动重载

过客
2026-01-20 / 0 评论 / 1 点赞 / 3 阅读 / 0 字

一、需求

多台服务器的Nginx服务器共用一套SSL证书,不想每台服务器配置一个acme.sh 或 Let's Encrypt 签发。只要一台服务器配置自动申请/更新证书,然后用脚本同步到其他服务器上去。

二、方案

    1. 主服务器: 负责申请/更新证书(如运行 acme.sh);
    1. 其他从服务器:通过安全方式(如 rsync + SSH)定期或触发式同步证书;
    1. 更新后使用 nginx -s reload 重新加载证书;
    1. 使用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脚本分发。

1
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区