VPS间自动同步SSL证书配置指南
场景说明
两台VPS(vpsA和vpsB)在同一局域网内,需要实现vpsB每6天自动从vpsA同步x-ui的SSL证书文件。
环境信息:
- vpsA IP: 192.168.1.250(证书源服务器)
- vpsB IP: 192.168.1.251(证书目标服务器)
- 证书路径:
/etc/x-ui/server.crt和/etc/x-ui/server.key
一、在vpsA上的配置
1.1 创建同步专用用户
sudo useradd -m -s /bin/bash syncuser
sudo mkdir -p /home/syncuser/.ssh
sudo chmod 700 /home/syncuser/.ssh
sudo chown syncuser:syncuser /home/syncuser/.ssh
1.2 安装rsync工具
sudo apt update
sudo apt install rsync -y
which rsync
1.3 创建证书文件副本目录
sudo mkdir -p /home/syncuser/certs
sudo cp -L /etc/x-ui/server.crt /home/syncuser/certs/
sudo cp -L /etc/x-ui/server.key /home/syncuser/certs/
sudo chown syncuser:syncuser /home/syncuser/certs/*
sudo chmod 644 /home/syncuser/certs/server.crt
sudo chmod 644 /home/syncuser/certs/server.key
ls -la /home/syncuser/certs/
1.4 创建自动更新副本的脚本
sudo bash -c 'cat > /usr/local/bin/update-cert-copies.sh << "EOF"
#!/bin/bash
cp -L /etc/x-ui/server.crt /home/syncuser/certs/
cp -L /etc/x-ui/server.key /home/syncuser/certs/
chown syncuser:syncuser /home/syncuser/certs/server.*
chmod 644 /home/syncuser/certs/server.*
EOF'
sudo chmod +x /usr/local/bin/update-cert-copies.sh
sudo /usr/local/bin/update-cert-copies.sh
1.5 设置定时任务自动更新副本
(sudo crontab -l 2>/dev/null; echo "*/10 * * * * /usr/local/bin/update-cert-copies.sh") | sudo crontab -
sudo crontab -l
二、在vpsB上的配置
2.1 生成SSH密钥对
ssh-keygen -t ed25519 -f ~/.ssh/cert_sync_key -C "cert-sync-from-vpsB" -N ""
cat ~/.ssh/cert_sync_key.pub
2.2 配置SSH密钥认证
方法一(推荐):
sudo passwd syncuser
ssh-copy-id -i ~/.ssh/cert_sync_key.pub syncuser@192.168.1.250
方法二(手动配置):
cat ~/.ssh/cert_sync_key.pub
sudo bash -c 'cat > /home/syncuser/.ssh/authorized_keys << EOF
《密钥内容》
EOF'
sudo chown syncuser:syncuser /home/syncuser/.ssh/authorized_keys
sudo chmod 600 /home/syncuser/.ssh/authorized_keys
2.3 测试SSH连接
ssh -i ~/.ssh/cert_sync_key syncuser@192.168.1.250 "whoami"
ssh -i ~/.ssh/cert_sync_key syncuser@192.168.1.250 "ls -la /home/syncuser/certs/"
2.4 创建同步脚本
cat > ~/sync_certs.sh << 'EOF'
#!/bin/bash
# 配置变量
VPS_A_IP="192.168.1.250"
SSH_KEY="~/.ssh/cert_sync_key"
SYNC_USER="syncuser"
LOCAL_CERT_DIR="/etc/x-ui"
BACKUP_DIR="/backup/x-ui-certs"
LOG_FILE="/var/log/cert-sync.log"
# 展开波浪号
SSH_KEY="${SSH_KEY/#\\~/$HOME}"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 开始同步证书文件" | sudo tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 使用SSH密钥: $SSH_KEY" | sudo tee -a "$LOG_FILE"
# 检查SSH密钥文件是否存在
if [ ! -f "$SSH_KEY" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S'): 错误:SSH密钥文件不存在: $SSH_KEY" | sudo tee -a "$LOG_FILE"
exit 1
fi
# 创建必要目录
sudo mkdir -p "$BACKUP_DIR"
sudo mkdir -p "$(dirname $LOG_FILE)"
# 测试SSH连接
echo "$(date '+%Y-%m-%d %H:%M:%S'): 测试SSH连接..." | sudo tee -a "$LOG_FILE"
if ! ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$SYNC_USER@$VPS_A_IP" "echo 'SSH连接成功'" 2>&1 | sudo tee -a "$LOG_FILE"; then
echo "$(date '+%Y-%m-%d %H:%M:%S'): 错误:SSH连接失败" | sudo tee -a "$LOG_FILE"
exit 1
fi
# 备份当前证书(如果存在)
if [ -f "$LOCAL_CERT_DIR/server.crt" ] || [ -f "$LOCAL_CERT_DIR/server.key" ]; then
BACKUP_SUFFIX=$(date +%Y%m%d_%H%M%S)
if [ -f "$LOCAL_CERT_DIR/server.crt" ]; then
sudo cp "$LOCAL_CERT_DIR/server.crt" "$BACKUP_DIR/server.crt.$BACKUP_SUFFIX"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 已备份现有 server.crt" | sudo tee -a "$LOG_FILE"
fi
if [ -f "$LOCAL_CERT_DIR/server.key" ]; then
sudo cp "$LOCAL_CERT_DIR/server.key" "$BACKUP_DIR/server.key.$BACKUP_SUFFIX"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 已备份现有 server.key" | sudo tee -a "$LOG_FILE"
fi
# 删除旧的证书文件
echo "$(date '+%Y-%m-%d %H:%M:%S'): 删除旧的证书文件..." | sudo tee -a "$LOG_FILE"
sudo rm -f "$LOCAL_CERT_DIR/server.crt" "$LOCAL_CERT_DIR/server.key"
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S'): 旧证书文件已删除" | sudo tee -a "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): 警告:删除旧证书文件失败" | sudo tee -a "$LOG_FILE"
fi
fi
# 创建临时目录
TEMP_DIR="/tmp/cert_sync_$$"
mkdir -p "$TEMP_DIR"
# 同步证书文件到临时目录(使用 -L 参数跟随符号链接)
echo "$(date '+%Y-%m-%d %H:%M:%S'): 开始从 $VPS_A_IP 同步证书" | sudo tee -a "$LOG_FILE"
rsync -avzL --chmod=644 -e "ssh -i '$SSH_KEY' -o StrictHostKeyChecking=no" \\
"${SYNC_USER}@${VPS_A_IP}:/home/syncuser/certs/server.crt" \\
"${SYNC_USER}@${VPS_A_IP}:/home/syncuser/certs/server.key" \\
"$TEMP_DIR/" 2>&1 | sudo tee -a "$LOG_FILE"
# 检查同步结果
if [ ${PIPESTATUS[0]} -eq 0 ]; then
# 验证文件完整性(检查文件大小是否合理)
if [ -f "$TEMP_DIR/server.crt" ] && [ -f "$TEMP_DIR/server.key" ]; then
CRT_SIZE=$(stat -f%z "$TEMP_DIR/server.crt" 2>/dev/null || stat -c%s "$TEMP_DIR/server.crt")
KEY_SIZE=$(stat -f%z "$TEMP_DIR/server.key" 2>/dev/null || stat -c%s "$TEMP_DIR/server.key")
echo "$(date '+%Y-%m-%d %H:%M:%S'): 证书文件大小 - CRT: ${CRT_SIZE} bytes, KEY: ${KEY_SIZE} bytes" | sudo tee -a "$LOG_FILE"
# 检查文件大小是否合理(证书文件通常大于100字节)
if [ "$CRT_SIZE" -gt 100 ] && [ "$KEY_SIZE" -gt 100 ]; then
# 移动文件到目标位置
sudo cp "$TEMP_DIR/server.crt" "$LOCAL_CERT_DIR/"
sudo cp "$TEMP_DIR/server.key" "$LOCAL_CERT_DIR/"
sudo chmod 644 "$LOCAL_CERT_DIR/server.crt"
sudo chmod 600 "$LOCAL_CERT_DIR/server.key"
sudo chown root:root "$LOCAL_CERT_DIR/server.crt" "$LOCAL_CERT_DIR/server.key"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 证书同步成功" | sudo tee -a "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 新证书文件已部署到 $LOCAL_CERT_DIR" | sudo tee -a "$LOG_FILE"
# 验证新文件
sudo ls -la "$LOCAL_CERT_DIR/server."* 2>&1 | sudo tee -a "$LOG_FILE"
# 重启x-ui服务
if systemctl is-active --quiet x-ui; then
echo "$(date '+%Y-%m-%d %H:%M:%S'): 正在重启 x-ui 服务..." | sudo tee -a "$LOG_FILE"
sudo systemctl restart x-ui
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S'): x-ui服务已成功重启" | sudo tee -a "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): 警告:x-ui服务重启失败" | sudo tee -a "$LOG_FILE"
fi
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): x-ui服务未运行,跳过重启" | sudo tee -a "$LOG_FILE"
fi
# 清理旧备份(保留最近10个)
sudo find "$BACKUP_DIR" -name "server.crt.*" | sort -r | tail -n +11 | sudo xargs -r rm
sudo find "$BACKUP_DIR" -name "server.key.*" | sort -r | tail -n +11 | sudo xargs -r rm
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): 错误:同步的文件大小异常" | sudo tee -a "$LOG_FILE"
# 恢复备份
LATEST_BACKUP=$(ls -t "$BACKUP_DIR/server.crt."* 2>/dev/null | head -1)
if [ -n "$LATEST_BACKUP" ]; then
BACKUP_SUFFIX="${LATEST_BACKUP##*.crt.}"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 尝试恢复最近的备份: $BACKUP_SUFFIX" | sudo tee -a "$LOG_FILE"
sudo cp "$BACKUP_DIR/server.crt.$BACKUP_SUFFIX" "$LOCAL_CERT_DIR/server.crt"
sudo cp "$BACKUP_DIR/server.key.$BACKUP_SUFFIX" "$LOCAL_CERT_DIR/server.key"
sudo chmod 644 "$LOCAL_CERT_DIR/server.crt"
sudo chmod 600 "$LOCAL_CERT_DIR/server.key"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 已恢复备份" | sudo tee -a "$LOG_FILE"
fi
rm -rf "$TEMP_DIR"
exit 1
fi
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): 错误:同步的文件不完整" | sudo tee -a "$LOG_FILE"
# 恢复备份
LATEST_BACKUP=$(ls -t "$BACKUP_DIR/server.crt."* 2>/dev/null | head -1)
if [ -n "$LATEST_BACKUP" ]; then
BACKUP_SUFFIX="${LATEST_BACKUP##*.crt.}"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 尝试恢复最近的备份: $BACKUP_SUFFIX" | sudo tee -a "$LOG_FILE"
sudo cp "$BACKUP_DIR/server.crt.$BACKUP_SUFFIX" "$LOCAL_CERT_DIR/server.crt"
sudo cp "$BACKUP_DIR/server.key.$BACKUP_SUFFIX" "$LOCAL_CERT_DIR/server.key"
sudo chmod 644 "$LOCAL_CERT_DIR/server.crt"
sudo chmod 600 "$LOCAL_CERT_DIR/server.key"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 已恢复备份" | sudo tee -a "$LOG_FILE"
fi
rm -rf "$TEMP_DIR"
exit 1
fi
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): 证书同步失败,rsync返回错误" | sudo tee -a "$LOG_FILE"
# 恢复备份
LATEST_BACKUP=$(ls -t "$BACKUP_DIR/server.crt."* 2>/dev/null | head -1)
if [ -n "$LATEST_BACKUP" ]; then
BACKUP_SUFFIX="${LATEST_BACKUP##*.crt.}"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 尝试恢复最近的备份: $BACKUP_SUFFIX" | sudo tee -a "$LOG_FILE"
sudo cp "$BACKUP_DIR/server.crt.$BACKUP_SUFFIX" "$LOCAL_CERT_DIR/server.crt"
sudo cp "$BACKUP_DIR/server.key.$BACKUP_SUFFIX" "$LOCAL_CERT_DIR/server.key"
sudo chmod 644 "$LOCAL_CERT_DIR/server.crt"
sudo chmod 600 "$LOCAL_CERT_DIR/server.key"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 已恢复备份" | sudo tee -a "$LOG_FILE"
fi
rm -rf "$TEMP_DIR"
exit 1
fi
# 清理临时目录
rm -rf "$TEMP_DIR"
echo "$(date '+%Y-%m-%d %H:%M:%S'): 证书同步完成" | sudo tee -a "$LOG_FILE"
echo "================================================" | sudo tee -a "$LOG_FILE"
EOF
EOF chmod +x ~/sync_certs.sh
2.5 测试同步脚本
~/sync_certs.sh
sudo tail -50 /var/log/cert-sync.log
sudo ls -lh /etc/x-ui/server.*
sudo systemctl status x-ui
2.6 设置定时任务
(sudo crontab -l 2>/dev/null; echo "0 2 */6 * * /root/sync_certs.sh") | sudo crontab -
sudo crontab -l
三、验证和维护
3.1 验证证书内容一致性
diff <(ssh -i ~/.ssh/cert_sync_key syncuser@192.168.1.250 'cat /home/syncuser/certs/server.crt') <(sudo cat /etc/x-ui/server.crt)
3.2 查看备份文件
ls -lh /backup/x-ui-certs/
sudo systemctl restart x-ui
3.3 查看同步日志
sudo tail -100 /var/log/cert-sync.log
sudo grep "2025-10-21" /var/log/cert-sync.log
3.4 手动触发同步
~/sync_certs.sh
四、安全建议
- SSH密钥安全:定期轮换,权限600
- 限制局域网内访问
- syncuser 权限最小化
- 监控日志与告警
- 定期备份与恢复测试
五、故障排查
5.1 SSH连接失败
ssh -i ~/.ssh/cert_sync_key -v syncuser@192.168.1.250
5.2 权限问题
sudo ls -la /home/syncuser/certs/
sudo chmod 644 /home/syncuser/certs/*
5.3 rsync失败
ssh -i ~/.ssh/cert_sync_key syncuser@192.168.1.250 "which rsync"
sudo apt install rsync -y
5.4 服务重启失败
sudo systemctl status x-ui
sudo journalctl -u x-ui -n 50
六、工作流程总结
自动化流程:
- vpsA 每10分钟更新证书副本
- vpsB 每6天同步新证书并重启服务
文件路径说明:
- vpsA: /etc/x-ui/server.crt /home/syncuser/certs/
- vpsB: /etc/x-ui/server.crt /root/sync_certs.sh /backup/x-ui-certs/
七、常见问题
- 同步周期: vpsA每10分钟更新,vpsB每6天同步
- 同步失败: 自动恢复备份
- 修改频率: 调整crontab的
*/6 - 空间占用: 保留10份备份仅几十KB
- 扩展用途: 可同步其他配置文件
结语
本方案通过SSH密钥认证 + rsync实现自动化证书同步,具备以下优点:
- ✅ 安全可靠(SSH密钥 + 权限隔离)
- ✅ 自动运行(cron计划任务)
- ✅ 出错可恢复(备份机制)
- ✅ 可扩展性强(适配其他文件)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END







