跳转至

部署指南

R-VPN 服务器生产环境部署最佳实践。

架构

单服务器部署

基础部署架构:

互联网 ──► 服务器 (端口 443) ──► NAT ──► 互联网
            - TLS 终止
            - VPN 处理
            - 伪装网站

高可用部署

生产环境冗余部署:

              ┌─────────────┐
互联网 ────►│   负载      │
              │  均衡器     │
              └──────┬──────┘
         ┌───────────┴───────────┐
         ▼                       ▼
   ┌──────────┐            ┌──────────┐
   │ 服务器 1 │            │ 服务器 2 │
   └──────────┘            └──────────┘
         │                       │
         └───────────┬───────────┘
              ┌──────────┐
              │  数据库  │
              │ (Redis)  │
              └──────────┘

生产环境检查清单

安全

  • 来自可信 CA 的 TLS 证书(推荐 Let's Encrypt)
  • 防火墙配置(仅开放 443/tcp 和可选的 80/tcp)
  • 安装并配置 Fail2ban
  • 定期安全更新(apt update && apt upgrade
  • SSH 密钥认证(禁用密码认证)
  • 禁用 Root 登录(使用 sudo)
  • 服务器身份密钥权限设置为 600
  • 私钥文件安全备份
  • 适当配置速率限制
  • 部署伪装网站以提高隐蔽性

性能

  • 足够的带宽(每个并发用户至少 1 Mbps)
  • 充足的内存(建议 2GB+,高负载建议 4GB+)
  • 用于日志和临时数据的 SSD 存储
  • 到目标用户的网络延迟低于 100ms
  • 增加文件描述符限制(65536+)
  • 优化 TCP 缓冲区以实现高吞吐量
  • 支持 AES-NI 的 CPU 用于 TLS 加速

可靠性

  • 配置 journalctl 监控
  • 配置日志轮转
  • 配置自动备份
  • 配置健康检查
  • 故障自动重启(systemd)
  • 配置 TLS 自动续期
  • 测试恢复脚本
  • 记录灾难恢复计划

DNS 配置

主域名

vpn.example.com.    A       1.2.3.4
vpn.example.com.    AAAA    2001:db8::1    # 可选 IPv6

客户端配置

客户端连接地址: - 服务器:wss://vpn.example.com/connect - 预密钥包:https://vpn.example.com/prekey-bundle.json

DNS TTL 建议

生产环境部署: - A/AAAA 记录:300-600 秒(5-10 分钟)以便快速故障转移 - 正常运营期间:3600 秒(1 小时)以获得更好的缓存效果

使用 Let's Encrypt 自动续期 TLS

初始证书设置

# 安装 certbot
sudo apt update
sudo apt install certbot

# 获取证书(独立模式 - 会停止 80 端口上的任何服务)
sudo certbot certonly --standalone -d vpn.example.com

# 复制证书到 R-VPN 目录
sudo cp /etc/letsencrypt/live/vpn.example.com/fullchain.pem /etc/rvpn/cert.pem
sudo cp /etc/letsencrypt/live/vpn.example.com/privkey.pem /etc/rvpn/key.pem

# 设置安全权限
sudo chmod 600 /etc/rvpn/key.pem
sudo chmod 644 /etc/rvpn/cert.pem

使用钩子自动续期

创建续期钩子脚本:

# 创建部署钩子
sudo tee /etc/letsencrypt/renewal-hooks/deploy/rvpn-server << 'EOF'
#!/bin/bash
# R-VPN 服务器证书续期钩子

set -e

DOMAIN="vpn.example.com"
RVPN_CERT_DIR="/etc/rvpn"
LE_DIR="/etc/letsencrypt/live/${DOMAIN}"

# 复制新证书
cp "${LE_DIR}/fullchain.pem" "${RVPN_CERT_DIR}/cert.pem"
cp "${LE_DIR}/privkey.pem" "${RVPN_CERT_DIR}/key.pem"

# 设置权限
chmod 600 "${RVPN_CERT_DIR}/key.pem"
chmod 644 "${RVPN_CERT_DIR}/cert.pem"

# 重新加载服务器(优雅地重新加载证书)
systemctl reload rvpn-server 2>/dev/null || systemctl restart rvpn-server

echo "$(date): R-VPN 证书已续期,服务器已重新加载" >> /var/log/rvpn/cert-renewal.log
EOF

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/rvpn-server

测试自动续期

# 测试续期过程(模拟运行)
sudo certbot renew --dry-run

# 强制续期进行测试
sudo certbot renew --force-renewal

# 检查续期定时器状态
sudo systemctl status certbot.timer
sudo certbot certificates

证书路径

[server]
tls_cert_file = "/etc/letsencrypt/live/vpn.example.com/fullchain.pem"
tls_key_file = "/etc/letsencrypt/live/vpn.example.com/privkey.pem"

或使用复制的文件:

[server]
tls_cert_file = "/etc/rvpn/cert.pem"
tls_key_file = "/etc/rvpn/key.pem"

使用 journalctl 监控

基础日志查看

# 实时查看日志
sudo journalctl -u rvpn-server -f

# 查看最后 100 行
sudo journalctl -u rvpn-server -n 100

# 查看自上次启动以来的日志
sudo journalctl -u rvpn-server --since today

# 查看特定时间段的日志
sudo journalctl -u rvpn-server --since "2024-01-01 10:00:00" --until "2024-01-01 12:00:00"

按日志级别过滤

# 仅错误
sudo journalctl -u rvpn-server -p err

# 警告及以上级别
sudo journalctl -u rvpn-server -p warning

# 所有日志及其优先级
sudo journalctl -u rvpn-server -o verbose | grep PRIORITY

持久化日志配置

确保启用持久化日志:

# 检查是否启用持久化日志
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal

# 重启 journald
sudo systemctl restart systemd-journald

# 配置日志保留
sudo tee /etc/systemd/journald.conf.d/rvpn.conf << 'EOF'
[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxFileSec=7day
EOF

sudo systemctl restart systemd-journald

日志导出和分析

# 导出日志到文件
sudo journalctl -u rvpn-server --since "24 hours ago" > /tmp/rvpn-logs.txt

# 以 JSON 格式导出以便分析
sudo journalctl -u rvpn-server -o json --since today > /tmp/rvpn-logs.json

# 按小时统计错误数
sudo journalctl -u rvpn-server --since today -p err -o short | awk '{print $1, $2, $3}' | uniq -c

日志轮转

Systemd Journal 轮转

Journald 自动处理轮转。配置保留策略:

# 创建自定义 journal 配置
sudo tee /etc/systemd/journald.conf.d/99-rvpn.conf << 'EOF'
[Journal]
# Journal 最大磁盘空间
SystemMaxUse=1G
# 单个 journal 文件最大大小
SystemMaxFileSize=100M
# 保留日志 30 天
MaxRetentionSec=30day
EOF

sudo systemctl restart systemd-journald

应用日志轮转(如果启用了文件日志)

创建 /etc/logrotate.d/rvpn

/var/log/rvpn/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 root adm
    sharedscripts
    postrotate
        systemctl reload rvpn-server 2>/dev/null || true
    endscript
}

测试日志轮转

# 测试 logrotate 配置
sudo logrotate -d /etc/logrotate.d/rvpn

# 强制轮转
sudo logrotate -f /etc/logrotate.d/rvpn

# 检查轮转后的日志
ls -la /var/log/rvpn/

备份和恢复脚本

备份脚本

创建 /usr/local/bin/rvpn-backup.sh

#!/bin/bash
# R-VPN 服务器备份脚本

set -e

BACKUP_DIR="/backup/rvpn"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30

# 创建备份目录
mkdir -p "${BACKUP_DIR}"

# 备份密钥和配置
tar -czf "${BACKUP_DIR}/rvpn-backup-${DATE}.tar.gz" \
    -C /etc/rvpn \
    --exclude='*.log' \
    . 2>/dev/null || true

# 备份预密钥包
tar -czf "${BACKUP_DIR}/rvpn-data-${DATE}.tar.gz" \
    -C /var/lib/rvpn \
    . 2>/dev/null || true

# 如果自定义了 systemd 服务,一并备份
if [ -f /etc/systemd/system/rvpn-server.service ]; then
    cp /etc/systemd/system/rvpn-server.service "${BACKUP_DIR}/rvpn-server.service-${DATE}"
fi

# 创建备份清单
cat > "${BACKUP_DIR}/manifest-${DATE}.txt" << EOF
R-VPN 备份清单
=====================
日期: $(date)
主机名: $(hostname)
版本: $(rvpn-server --version 2>/dev/null || echo "unknown")

备份内容:
- 配置文件 (/etc/rvpn)
- 数据文件 (/var/lib/rvpn)
- Systemd 服务(如果自定义)

恢复方法:
1. 停止 rvpn-server: systemctl stop rvpn-server
2. 解压配置: tar -xzf rvpn-backup-${DATE}.tar.gz -C /etc/rvpn
3. 解压数据: tar -xzf rvpn-data-${DATE}.tar.gz -C /var/lib/rvpn
4. 恢复服务: cp rvpn-server.service-${DATE} /etc/systemd/system/rvpn-server.service
5. 重新加载 systemd: systemctl daemon-reload
6. 启动服务器: systemctl start rvpn-server
EOF

# 清理旧备份
find "${BACKUP_DIR}" -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete
find "${BACKUP_DIR}" -name "*.service-*" -mtime +${RETENTION_DAYS} -delete
find "${BACKUP_DIR}" -name "manifest-*.txt" -mtime +${RETENTION_DAYS} -delete

echo "备份完成: ${BACKUP_DIR}/rvpn-backup-${DATE}.tar.gz"

设置可执行并定时运行:

sudo chmod +x /usr/local/bin/rvpn-backup.sh

# 添加到 crontab(每天凌晨 3 点)
echo "0 3 * * * root /usr/local/bin/rvpn-backup.sh >> /var/log/rvpn/backup.log 2>&1" | sudo tee /etc/cron.d/rvpn-backup

恢复脚本

创建 /usr/local/bin/rvpn-restore.sh

#!/bin/bash
# R-VPN 服务器恢复脚本

set -e

if [ $# -lt 1 ]; then
    echo "用法: $0 <备份日期> [备份目录]"
    echo "示例: $0 20240115_030000"
    exit 1
fi

BACKUP_DATE="$1"
BACKUP_DIR="${2:-/backup/rvpn}"
CONFIG_BACKUP="${BACKUP_DIR}/rvpn-backup-${BACKUP_DATE}.tar.gz"
DATA_BACKUP="${BACKUP_DIR}/rvpn-data-${BACKUP_DATE}.tar.gz"

if [ ! -f "${CONFIG_BACKUP}" ]; then
    echo "错误: 未找到配置备份: ${CONFIG_BACKUP}"
    exit 1
fi

echo "=== R-VPN 服务器恢复 ==="
echo "备份日期: ${BACKUP_DATE}"
echo ""

# 确认
read -p "这将覆盖当前配置。是否继续? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
    echo "已中止。"
    exit 0
fi

# 停止服务器
echo "停止 rvpn-server..."
systemctl stop rvpn-server

# 备份当前配置(以防万一)
echo "创建当前配置的安全备份..."
tar -czf "${BACKUP_DIR}/rvpn-pre-restore-$(date +%Y%m%d_%H%M%S).tar.gz" -C /etc/rvpn . 2>/dev/null || true

# 恢复配置
echo "恢复配置..."
mkdir -p /etc/rvpn
tar -xzf "${CONFIG_BACKUP}" -C /etc/rvpn

# 恢复数据
echo "恢复数据..."
if [ -f "${DATA_BACKUP}" ]; then
    mkdir -p /var/lib/rvpn
    tar -xzf "${DATA_BACKUP}" -C /var/lib/rvpn
fi

# 如果存在则恢复 systemd 服务
SERVICE_BACKUP="${BACKUP_DIR}/rvpn-server.service-${BACKUP_DATE}"
if [ -f "${SERVICE_BACKUP}" ]; then
    echo "恢复 systemd 服务..."
    cp "${SERVICE_BACKUP}" /etc/systemd/system/rvpn-server.service
    systemctl daemon-reload
fi

# 设置权限
echo "设置权限..."
chmod 600 /etc/rvpn/*.key 2>/dev/null || true
chmod 644 /etc/rvpn/*.pem 2>/dev/null || true
chmod 755 /var/lib/rvpn

# 启动服务器
echo "启动 rvpn-server..."
systemctl start rvpn-server

# 检查状态
sleep 2
if systemctl is-active --quiet rvpn-server; then
    echo ""
    echo "=== 恢复成功完成 ==="
    systemctl status rvpn-server --no-pager
else
    echo ""
    echo "=== 警告: 服务器启动失败 ==="
    echo "检查日志: journalctl -u rvpn-server -n 50"
    exit 1
fi

设置可执行:

sudo chmod +x /usr/local/bin/rvpn-restore.sh

远程备份(可选)

# 在备份脚本中添加远程同步
# rsync -avz --delete /backup/rvpn/ backup-server:/backups/rvpn-$(hostname)/

性能调优

文件描述符

增加系统范围的文件描述符限制:

# 编辑 limits.conf
sudo tee -a /etc/security/limits.conf << 'EOF'
# R-VPN 服务器
* soft nofile 65536
* hard nofile 65536
rvpn soft nofile 65536
rvpn hard nofile 65536
EOF

# 编辑 systemd 服务限制
sudo mkdir -p /etc/systemd/system/rvpn-server.service.d/
sudo tee /etc/systemd/system/rvpn-server.service.d/limits.conf << 'EOF'
[Service]
LimitNOFILE=65536
LimitNOFILESoft=65536
EOF

sudo systemctl daemon-reload
sudo systemctl restart rvpn-server

# 验证
ulimit -n
cat /proc/$(pgrep rvpn-server)/limits | grep "Max open files"

TCP 设置

为高性能 VPN 优化 TCP:

# 创建 sysctl 配置
sudo tee /etc/sysctl.d/99-rvpn.conf << 'EOF'
# R-VPN 性能调优

# 增加 TCP 缓冲区大小
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216

# TCP 内存调优
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 启用 TCP 窗口缩放
net.ipv4.tcp_window_scaling = 1

# 增加连接跟踪
net.netfilter.nf_conntrack_max = 524288
net.netfilter.nf_conntrack_tcp_timeout_established = 600

# 减少 TCP keepalive 时间
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3

# 启用 TCP fast open
net.ipv4.tcp_fastopen = 3

# 增加本地端口范围
net.ipv4.ip_local_port_range = 1024 65535

# 启用 BBR 拥塞控制(如果可用)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF

# 应用设置
sudo sysctl --system

# 验证 BBR 可用并已启用
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control

VPN 内核参数

# 添加到 /etc/sysctl.d/99-rvpn.conf

# 启用 IP 转发
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

# 禁用 ICMP 重定向
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

# 启用 SYN cookies
net.ipv4.tcp_syncookies = 1

# 减少 SYN 积压
net.ipv4.tcp_max_syn_backlog = 65536

# 为高数据包速率增加 netdev 预算
net.core.netdev_budget = 50000
net.core.netdev_budget_usecs = 5000

# 增加 SYN 队列中的最大连接数
net.ipv4.tcp_max_syn_backlog = 65536
net.core.somaxconn = 65535
EOF

网络接口调优

# 调优网络接口(将 eth0 替换为您的接口)
sudo tee /etc/networkd-dispatcher/routable.d/50-rvpn-tune << 'EOF'
#!/bin/bash
# R-VPN 网络调优

IFACE="eth0"

# 增加环形缓冲区大小
ethtool -G ${IFACE} rx 4096 tx 4096 2>/dev/null || true

# 启用卸载功能
ethtool -K ${IFACE} gro on 2>/dev/null || true
ethtool -K ${IFACE} gso on 2>/dev/null || true
EOF

sudo chmod +x /etc/networkd-dispatcher/routable.d/50-rvpn-tune

性能监控

# 检查当前连接
ss -s

# 监控带宽使用
iftop -i eth0

# 检查进程 CPU 使用
top -p $(pgrep rvpn-server)

# 监控网络吞吐量
sar -n DEV 1

# 检查丢包
ethtool -S eth0 | grep -i drop

Docker 部署

Dockerfile

# 构建阶段
FROM rust:1.75-slim-bookworm as builder

WORKDIR /app
COPY . .

# 安装依赖
RUN apt-get update && apt-get install -y \
    pkg-config \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# 构建发布二进制文件
RUN cargo build --release -p rvpn-server

# 运行阶段
FROM debian:bookworm-slim

# 安装运行时依赖
RUN apt-get update && apt-get install -y \
    ca-certificates \
    iptables \
    iproute2 \
    && rm -rf /var/lib/apt/lists/*

# 创建非 root 用户
RUN useradd -r -s /sbin/nologin -M rvpn

# 复制二进制文件
COPY --from=builder /app/target/release/rvpn-server /usr/local/bin/

# 创建目录
RUN mkdir -p /etc/rvpn /var/lib/rvpn /var/log/rvpn && \
    chown -R rvpn:rvpn /etc/rvpn /var/lib/rvpn /var/log/rvpn

# 暴露端口
EXPOSE 443/tcp
EXPOSE 80/tcp

# 切换到非 root 用户
USER rvpn

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f -k https://localhost:443/ || exit 1

ENTRYPOINT ["rvpn-server"]
CMD ["--config", "/etc/rvpn/server.toml"]

Docker Compose

创建 docker-compose.yml

version: '3.8'

services:
  rvpn-server:
    build: .
    image: rvpn-server:latest
    container_name: rvpn-server
    restart: unless-stopped

    # VPN 功能使用网络模式
    network_mode: host

    # 或使用端口映射(功能受限)
    # ports:
    #   - "443:443/tcp"
    #   - "80:80/tcp"

    volumes:
      # 配置
      - ./config:/etc/rvpn:ro
      # 数据持久化
      - rvpn-data:/var/lib/rvpn
      # 日志
      - rvpn-logs:/var/log/rvpn
      # TLS 证书
      - /etc/letsencrypt:/etc/letsencrypt:ro

    # VPN 所需能力
    cap_add:
      - NET_ADMIN
      - NET_RAW

    # 网络 sysctl
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv6.conf.all.forwarding=1

    environment:
      - RUST_LOG=info

    healthcheck:
      test: ["CMD", "curl", "-f", "-k", "https://localhost:443/"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 10s

volumes:
  rvpn-data:
  rvpn-logs:

构建和运行

# 构建镜像
docker build -t rvpn-server:latest .

# 使用 docker 运行
docker run -d \
  --name rvpn-server \
  --restart unless-stopped \
  --network host \
  --cap-add NET_ADMIN \
  --cap-add NET_RAW \
  -v /etc/rvpn:/etc/rvpn:ro \
  -v /var/lib/rvpn:/var/lib/rvpn \
  -v /etc/letsencrypt:/etc/letsencrypt:ro \
  rvpn-server:latest

# 或使用 docker-compose
docker-compose up -d

# 查看日志
docker logs -f rvpn-server

# 停止
docker-compose down

Docker Swarm 部署

version: '3.8'

services:
  rvpn-server:
    image: rvpn-server:latest
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
      resources:
        limits:
          cpus: '2.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 256M
    networks:
      - rvpn-network
    volumes:
      - /etc/rvpn:/etc/rvpn:ro
      - rvpn-data:/var/lib/rvpn
    configs:
      - source: rvpn_config
        target: /etc/rvpn/server.toml

networks:
  rvpn-network:
    driver: overlay
    attachable: true

volumes:
  rvpn-data:

configs:
  rvpn_config:
    external: true

故障排除

高连接数

检查活动连接:

# 连接摘要
ss -s

# 每个 IP 的连接数(流量大户)
ss -tn | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20

# 连接状态
ss -tan | awk '{print $1}' | sort | uniq -c

# 检查 SYN 洪水
ss -tan state syn-recv | wc -l

# 监控连接速率
watch -n 1 'ss -tan | wc -l'

缓解措施:

# 添加连接速率限制
sudo iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 100 -j DROP

# 启用 SYN cookies
sudo sysctl -w net.ipv4.tcp_syncookies=1

内存问题

检查内存使用:

# 进程内存
ps aux | grep rvpn-server

# 详细内存信息
cat /proc/$(pgrep rvpn-server)/status | grep -i vm

# 系统内存
free -h

# 内存随时间变化
vmstat 1 10

# OOM killer 日志
dmesg | grep -i "out of memory"

# 检查内存泄漏
pmap $(pgrep rvpn-server)

解决方案:

# 重启服务
sudo systemctl restart rvpn-server

# 如果需要,添加交换空间
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

网络问题

检查防火墙:

# 列出所有规则
sudo iptables -L -n -v

# 检查特定端口
sudo ss -tlnp | grep 443

# 从外部测试端口
nc -zv vpn.example.com 443

# 检查路由
ip route

# 检查接口统计
ip -s link show eth0

# 追踪路径
mtr vpn.example.com

常见修复:

# 重置 iptables(小心!)
sudo iptables -F
sudo iptables -X
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

# 重新添加基本规则
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -j DROP

TLS 证书问题

# 验证证书
openssl x509 -in /etc/rvpn/cert.pem -text -noout

# 检查证书日期
openssl x509 -in /etc/rvpn/cert.pem -noout -dates

# 测试 TLS 连接
openssl s_client -connect vpn.example.com:443 -tls1_3 -servername vpn.example.com

# 检查证书链
openssl s_client -connect vpn.example.com:443 -showcerts </dev/null

# 验证 Let's Encrypt 续期
sudo certbot certificates

服务无法启动

# 检查服务状态
sudo systemctl status rvpn-server

# 检查错误
sudo journalctl -u rvpn-server -n 50

# 测试配置语法
cat /etc/rvpn/server.toml

# 检查文件权限
ls -la /etc/rvpn/
ls -la /var/lib/rvpn/

# 直接测试二进制文件
sudo rvpn-server --config /etc/rvpn/server.toml -vv

# 检查端口冲突
sudo ss -tlnp | grep 443

性能问题

# CPU 分析
perf top -p $(pgrep rvpn-server)

# 网络延迟
ping vpn.example.com

# 带宽测试
iperf3 -c vpn.example.com

# 检查丢包
netstat -i

# DNS 解析时间
dig vpn.example.com

下一步