13.1 概述

在OpenVPN的日常运行中,故障排除和系统维护是确保服务稳定运行的关键环节。本章将介绍OpenVPN常见问题的诊断方法、系统维护策略以及最佳实践,帮助管理员快速定位和解决问题,保障VPN服务的可靠性。

graph TD
    A[故障排除与维护] --> B[常见问题诊断]
    A --> C[系统维护策略]
    A --> D[性能问题排查]
    A --> E[安全问题处理]
    A --> F[升级与迁移]
    A --> G[备份与恢复]
    A --> H[维护最佳实践]
    
    B --> B1[连接问题]
    B --> B2[认证问题]
    B --> B3[路由问题]
    B --> B4[DNS问题]
    
    C --> C1[日常维护计划]
    C --> C2[定期检查项目]
    C --> C3[维护窗口管理]
    
    D --> D1[带宽问题]
    D --> D2[延迟问题]
    D --> D3[资源瓶颈]
    
    E --> E1[证书问题]
    E --> E2[安全漏洞处理]
    E --> E3[入侵检测与响应]
    
    F --> F1[版本升级策略]
    F --> F2[平滑迁移方案]
    F --> F3[回滚计划]
    
    G --> G1[配置备份]
    G --> G2[证书备份]
    G --> G3[灾难恢复]
    
    H --> H1[文档管理]
    H --> H2[变更控制]
    H --> H3[监控与预警]

13.2 常见问题诊断

13.2.1 连接问题

连接问题是OpenVPN最常见的故障类型,以下是诊断和解决连接问题的系统方法:

13.2.1.1 连接建立失败

当客户端无法与服务器建立连接时,可以使用以下步骤进行诊断:

#!/bin/bash
# openvpn_connection_diagnosis.sh

# 设置变量
VPN_SERVER="your-vpn-server.com"
VPN_PORT="1194"
VPN_PROTO="udp"
CLIENT_CONFIG="/path/to/client.ovpn"

echo "===== OpenVPN 连接诊断工具 ====="
echo "正在诊断与 $VPN_SERVER:$VPN_PORT 的连接问题..."

# 1. 检查网络连通性
echo "\n[1] 检查基本网络连通性..."
ping -c 4 $VPN_SERVER
if [ $? -ne 0 ]; then
    echo "[错误] 无法 ping 通 VPN 服务器。可能的原因:"
    echo "  - 网络连接问题"
    echo "  - 服务器名称解析失败"
    echo "  - 服务器防火墙阻止 ICMP"
    echo "建议:检查您的网络连接和 DNS 设置"
else
    echo "[成功] 可以 ping 通 VPN 服务器"
fi

# 2. 检查端口可达性
echo "\n[2] 检查 VPN 端口可达性..."
if [ "$VPN_PROTO" = "udp" ]; then
    nc -zu -w 5 $VPN_SERVER $VPN_PORT
    if [ $? -ne 0 ]; then
        echo "[错误] UDP 端口 $VPN_PORT 不可达。可能的原因:"
        echo "  - 服务器防火墙阻止该端口"
        echo "  - OpenVPN 服务未在该端口运行"
        echo "  - 中间网络设备阻止 UDP 流量"
        echo "建议:检查服务器防火墙设置和 OpenVPN 服务状态"
    else
        echo "[成功] UDP 端口 $VPN_PORT 可达"
    fi
else
    nc -z -w 5 $VPN_SERVER $VPN_PORT
    if [ $? -ne 0 ]; then
        echo "[错误] TCP 端口 $VPN_PORT 不可达。可能的原因:"
        echo "  - 服务器防火墙阻止该端口"
        echo "  - OpenVPN 服务未在该端口运行"
        echo "建议:检查服务器防火墙设置和 OpenVPN 服务状态"
    else
        echo "[成功] TCP 端口 $VPN_PORT 可达"
    fi
fi

# 3. 检查客户端配置
echo "\n[3] 检查客户端配置..."
if [ -f "$CLIENT_CONFIG" ]; then
    echo "分析客户端配置文件..."
    
    # 检查基本配置项
    grep -q "^remote $VPN_SERVER $VPN_PORT" "$CLIENT_CONFIG"
    if [ $? -ne 0 ]; then
        echo "[警告] 配置文件中的服务器地址或端口可能不正确"
        echo "当前配置:"
        grep "^remote" "$CLIENT_CONFIG"
    fi
    
    # 检查协议设置
    grep -q "^proto $VPN_PROTO" "$CLIENT_CONFIG"
    if [ $? -ne 0 ]; then
        echo "[警告] 配置文件中的协议设置可能不正确"
        echo "当前配置:"
        grep "^proto" "$CLIENT_CONFIG"
    fi
    
    # 检查证书和密钥
    if ! grep -q "<ca>" "$CLIENT_CONFIG" || ! grep -q "</ca>" "$CLIENT_CONFIG"; then
        echo "[错误] 配置文件中缺少 CA 证书"
    fi
    
    if ! grep -q "<cert>" "$CLIENT_CONFIG" || ! grep -q "</cert>" "$CLIENT_CONFIG"; then
        echo "[错误] 配置文件中缺少客户端证书"
    fi
    
    if ! grep -q "<key>" "$CLIENT_CONFIG" || ! grep -q "</key>" "$CLIENT_CONFIG"; then
        echo "[错误] 配置文件中缺少客户端密钥"
    fi
else
    echo "[错误] 找不到客户端配置文件: $CLIENT_CONFIG"
fi

# 4. 尝试使用详细模式连接
echo "\n[4] 尝试使用详细模式连接 (5秒后自动取消)..."
echo "运行: openvpn --config $CLIENT_CONFIG --verb 5"
echo "按 Ctrl+C 取消测试连接"
sleep 3
timeout 5 openvpn --config "$CLIENT_CONFIG" --verb 5

echo "\n===== 诊断完成 ====="
echo "如果问题仍然存在,请检查服务器日志以获取更多信息。"
echo "服务器日志通常位于: /var/log/openvpn/"

13.2.1.2 连接不稳定

对于连接经常断开或不稳定的情况,可以使用以下脚本进行监控和诊断:

#!/bin/bash
# openvpn_stability_monitor.sh

# 设置变量
VPN_SERVER="your-vpn-server.com"
MONITOR_DURATION=3600  # 监控时长(秒)
PING_INTERVAL=5        # Ping 间隔(秒)
LOG_FILE="vpn_stability.log"

echo "===== OpenVPN 连接稳定性监控 ====="
echo "监控 VPN 连接到 $VPN_SERVER"
echo "监控时长: $MONITOR_DURATION 秒"
echo "Ping 间隔: $PING_INTERVAL 秒"
echo "日志文件: $LOG_FILE"
echo ""

# 初始化日志文件
echo "时间戳,状态,延迟(ms),丢包率(%)" > "$LOG_FILE"

# 获取 VPN 接口名称
VPN_INTERFACE=$(ip addr | grep tun | awk '{print $2}' | cut -d':' -f1)
if [ -z "$VPN_INTERFACE" ]; then
    echo "[错误] 未检测到 VPN 接口 (tun)。请确保 VPN 已连接。"
    exit 1
fi
echo "检测到 VPN 接口: $VPN_INTERFACE"

# 获取 VPN 网关
VPN_GATEWAY=$(ip route | grep "$VPN_INTERFACE" | grep default | awk '{print $3}')
if [ -z "$VPN_GATEWAY" ]; then
    # 尝试获取服务器端 IP
    VPN_GATEWAY=$(ip addr show dev "$VPN_INTERFACE" | grep inet | awk '{print $4}')
    if [ -z "$VPN_GATEWAY" ]; then
        echo "[警告] 无法确定 VPN 网关。将使用 VPN 服务器地址进行监控。"
        VPN_GATEWAY=$VPN_SERVER
    fi
fi
echo "使用 VPN 网关/目标: $VPN_GATEWAY"

# 监控函数
monitor_vpn() {
    local start_time=$(date +%s)
    local end_time=$((start_time + MONITOR_DURATION))
    local disconnects=0
    local total_checks=0
    local high_latency_count=0
    
    echo "开始监控... 按 Ctrl+C 停止"
    
    while [ $(date +%s) -lt $end_time ]; do
        timestamp=$(date +"%Y-%m-%d %H:%M:%S")
        total_checks=$((total_checks + 1))
        
        # 检查 VPN 接口是否存在
        if ! ip addr show dev "$VPN_INTERFACE" &>/dev/null; then
            status="断开"
            latency=0
            packet_loss=100
            disconnects=$((disconnects + 1))
            echo "$timestamp,$status,$latency,$packet_loss" >> "$LOG_FILE"
            echo "[$timestamp] VPN 连接断开 (接口 $VPN_INTERFACE 不存在)"
            
            # 等待 VPN 重新连接
            while ! ip addr show dev "$VPN_INTERFACE" &>/dev/null; do
                sleep 1
                if [ $(date +%s) -ge $end_time ]; then
                    break 2
                fi
            done
            continue
        fi
        
        # 通过 VPN 执行 ping 测试
        ping_result=$(ping -c 3 -W 2 -I "$VPN_INTERFACE" "$VPN_GATEWAY" 2>/dev/null)
        if [ $? -ne 0 ]; then
            status="不可达"
            latency=0
            packet_loss=100
            disconnects=$((disconnects + 1))
            echo "$timestamp,$status,$latency,$packet_loss" >> "$LOG_FILE"
            echo "[$timestamp] VPN 连接不可达 (无法 ping 通 $VPN_GATEWAY)"
        else
            status="正常"
            latency=$(echo "$ping_result" | grep rtt | cut -d'/' -f5)
            packet_loss=$(echo "$ping_result" | grep -oP '\d+(?=% packet loss)')
            echo "$timestamp,$status,$latency,$packet_loss" >> "$LOG_FILE"
            
            # 检查高延迟
            if (( $(echo "$latency > 100" | bc -l) )); then
                high_latency_count=$((high_latency_count + 1))
                echo "[$timestamp] 检测到高延迟: ${latency}ms"
            else
                echo -ne "[$timestamp] 连接正常: ${latency}ms, 丢包率: ${packet_loss}%\r"
            fi
        fi
        
        sleep "$PING_INTERVAL"
    done
    
    echo -e "\n\n===== 监控结果摘要 ====="
    echo "总检查次数: $total_checks"
    echo "连接断开次数: $disconnects"
    echo "高延迟事件 (>100ms): $high_latency_count"
    echo "连接可用率: $(( (total_checks - disconnects) * 100 / total_checks ))%"
    echo "详细日志已保存到: $LOG_FILE"
    
    # 生成简单的 CSV 报告
    echo "\n生成图表分析报告..."
    echo "可以使用 Excel 或其他工具打开 $LOG_FILE 进行可视化分析"
}

# 捕获 Ctrl+C
trap 'echo -e "\n\n监控已停止"; exit 0' INT

# 开始监控
monitor_vpn

13.2.2 认证问题

认证问题通常与证书、密钥或用户凭据有关。以下是一个用于诊断认证问题的脚本:

#!/bin/bash
# openvpn_auth_diagnosis.sh

# 设置变量
CERT_DIR="/etc/openvpn/easy-rsa/pki"
SERVER_CONF="/etc/openvpn/server.conf"
CLIENT_CONF="/path/to/client.ovpn"

echo "===== OpenVPN 认证问题诊断 ====="

# 1. 检查服务器证书
echo "\n[1] 检查服务器证书..."
if [ -d "$CERT_DIR" ]; then
    # 检查 CA 证书
    if [ -f "$CERT_DIR/ca.crt" ]; then
        echo "CA 证书存在"
        # 检查 CA 证书有效期
        ca_expiry=$(openssl x509 -enddate -noout -in "$CERT_DIR/ca.crt" | cut -d= -f2)
        ca_expiry_epoch=$(date -d "$ca_expiry" +%s)
        current_epoch=$(date +%s)
        days_left=$(( (ca_expiry_epoch - current_epoch) / 86400 ))
        
        echo "CA 证书有效期至: $ca_expiry ($days_left 天)"
        if [ $days_left -lt 30 ]; then
            echo "[警告] CA 证书即将过期!请考虑续期。"
        fi
    else
        echo "[错误] CA 证书不存在: $CERT_DIR/ca.crt"
    fi
    
    # 检查服务器证书
    server_cert=$(grep -A 1 "^cert" "$SERVER_CONF" | tail -1 | tr -d ' ')
    if [ -f "$server_cert" ]; then
        echo "服务器证书存在: $server_cert"
        # 检查服务器证书有效期
        server_expiry=$(openssl x509 -enddate -noout -in "$server_cert" | cut -d= -f2)
        server_expiry_epoch=$(date -d "$server_expiry" +%s)
        days_left=$(( (server_expiry_epoch - current_epoch) / 86400 ))
        
        echo "服务器证书有效期至: $server_expiry ($days_left 天)"
        if [ $days_left -lt 30 ]; then
            echo "[警告] 服务器证书即将过期!请考虑续期。"
        fi
        
        # 验证服务器证书是否由 CA 签发
        echo "验证服务器证书..."
        openssl verify -CAfile "$CERT_DIR/ca.crt" "$server_cert"
    else
        echo "[错误] 服务器证书不存在: $server_cert"
    fi
    
    # 检查 CRL
    if [ -f "$CERT_DIR/crl.pem" ]; then
        echo "证书吊销列表 (CRL) 存在"
        # 检查 CRL 是否最新
        crl_last_update=$(openssl crl -in "$CERT_DIR/crl.pem" -noout -lastupdate | cut -d= -f2)
        crl_next_update=$(openssl crl -in "$CERT_DIR/crl.pem" -noout -nextupdate | cut -d= -f2)
        echo "CRL 上次更新: $crl_last_update"
        echo "CRL 下次更新: $crl_next_update"
        
        # 检查 CRL 中的吊销证书
        revoked_count=$(openssl crl -in "$CERT_DIR/crl.pem" -noout -text | grep -c "Serial Number:")
        echo "CRL 中包含 $revoked_count 个吊销证书"
    else
        echo "[警告] 未找到证书吊销列表 (CRL)"
    fi
else
    echo "[错误] 证书目录不存在: $CERT_DIR"
fi

# 2. 检查客户端证书
echo "\n[2] 检查客户端证书..."
if [ -f "$CLIENT_CONF" ]; then
    # 提取客户端证书
    echo "从客户端配置中提取证书..."
    awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/' "$CLIENT_CONF" > client_cert.temp
    
    if [ -s client_cert.temp ]; then
        echo "客户端证书已提取"
        
        # 检查客户端证书有效期
        client_expiry=$(openssl x509 -enddate -noout -in client_cert.temp | cut -d= -f2)
        client_expiry_epoch=$(date -d "$client_expiry" +%s)
        days_left=$(( (client_expiry_epoch - current_epoch) / 86400 ))
        
        echo "客户端证书有效期至: $client_expiry ($days_left 天)"
        if [ $days_left -lt 30 ]; then
            echo "[警告] 客户端证书即将过期!请考虑续期。"
        fi
        
        # 验证客户端证书是否由 CA 签发
        if [ -f "$CERT_DIR/ca.crt" ]; then
            echo "验证客户端证书..."
            openssl verify -CAfile "$CERT_DIR/ca.crt" client_cert.temp
            
            # 检查客户端证书是否已吊销
            if [ -f "$CERT_DIR/crl.pem" ]; then
                echo "检查客户端证书是否已吊销..."
                client_serial=$(openssl x509 -in client_cert.temp -noout -serial | cut -d= -f2)
                if openssl crl -in "$CERT_DIR/crl.pem" -noout -text | grep -q "$client_serial"; then
                    echo "[错误] 客户端证书已被吊销!这是认证失败的原因。"
                else
                    echo "客户端证书未被吊销"
                fi
            fi
        fi
        
        # 清理临时文件
        rm client_cert.temp
    else
        echo "[错误] 无法从客户端配置中提取证书"
    fi
else
    echo "[警告] 客户端配置文件不存在: $CLIENT_CONF"
fi

# 3. 检查 TLS 认证密钥
echo "\n[3] 检查 TLS 认证..."
if grep -q "^tls-auth" "$SERVER_CONF"; then
    echo "服务器配置使用 TLS 认证"
    tls_key=$(grep -A 1 "^tls-auth" "$SERVER_CONF" | tail -1 | tr -d ' ' | cut -d' ' -f1)
    if [ -f "$tls_key" ]; then
        echo "TLS 密钥存在: $tls_key"
    else
        echo "[错误] TLS 密钥不存在: $tls_key"
    fi
    
    # 检查客户端是否也配置了 TLS 认证
    if [ -f "$CLIENT_CONF" ] && ! grep -q "^tls-auth" "$CLIENT_CONF" && ! grep -q "<tls-auth>" "$CLIENT_CONF"; then
        echo "[错误] 客户端配置中缺少 TLS 认证设置"
    fi
elif grep -q "^tls-crypt" "$SERVER_CONF"; then
    echo "服务器配置使用 TLS 加密"
    tls_key=$(grep -A 1 "^tls-crypt" "$SERVER_CONF" | tail -1 | tr -d ' ')
    if [ -f "$tls_key" ]; then
        echo "TLS 加密密钥存在: $tls_key"
    else
        echo "[错误] TLS 加密密钥不存在: $tls_key"
    fi
    
    # 检查客户端是否也配置了 TLS 加密
    if [ -f "$CLIENT_CONF" ] && ! grep -q "^tls-crypt" "$CLIENT_CONF" && ! grep -q "<tls-crypt>" "$CLIENT_CONF"; then
        echo "[错误] 客户端配置中缺少 TLS 加密设置"
    fi
fi

# 4. 检查用户名/密码认证
echo "\n[4] 检查用户名/密码认证..."
if grep -q "^auth-user-pass-verify" "$SERVER_CONF"; then
    echo "服务器配置使用用户名/密码认证"
    auth_script=$(grep "^auth-user-pass-verify" "$SERVER_CONF" | awk '{print $2}')
    if [ -f "$auth_script" ]; then
        echo "认证脚本存在: $auth_script"
        if [ -x "$auth_script" ]; then
            echo "认证脚本具有执行权限"
        else
            echo "[错误] 认证脚本缺少执行权限: $auth_script"
            echo "建议运行: chmod +x $auth_script"
        fi
    else
        echo "[错误] 认证脚本不存在: $auth_script"
    fi
    
    # 检查客户端是否配置了用户名/密码认证
    if [ -f "$CLIENT_CONF" ] && ! grep -q "^auth-user-pass" "$CLIENT_CONF"; then
        echo "[错误] 客户端配置中缺少用户名/密码认证设置"
    fi
fi

echo "\n===== 认证诊断完成 ====="
echo "如果问题仍然存在,请检查服务器日志以获取更多信息。"
echo "认证相关的日志通常包含 'TLS Error', 'AUTH', 'VERIFY' 等关键词。"

13.2.3 路由问题

路由问题会导致VPN连接成功但无法访问特定网络。以下是一个用于诊断路由问题的脚本:

#!/bin/bash
# openvpn_routing_diagnosis.sh

# 设置变量
VPN_INTERFACE="tun0"
VPN_NETWORK="10.8.0.0/24"  # VPN 内部网络
TARGET_NETWORK="192.168.1.0/24"  # 目标网络

echo "===== OpenVPN 路由问题诊断 ====="

# 1. 检查 VPN 接口状态
echo "\n[1] 检查 VPN 接口状态..."
if ip addr show dev "$VPN_INTERFACE" &>/dev/null; then
    echo "VPN 接口 $VPN_INTERFACE 存在"
    ip addr show dev "$VPN_INTERFACE"
    
    # 获取 VPN 接口 IP
    VPN_IP=$(ip addr show dev "$VPN_INTERFACE" | grep inet | awk '{print $2}' | cut -d/ -f1)
    echo "VPN IP 地址: $VPN_IP"
else
    echo "[错误] VPN 接口 $VPN_INTERFACE 不存在"
    echo "请确保 OpenVPN 连接已建立"
    exit 1
fi

# 2. 检查路由表
echo "\n[2] 检查路由表..."
echo "当前路由表:"
ip route

# 检查 VPN 网络路由
if ip route | grep -q "$VPN_NETWORK dev $VPN_INTERFACE"; then
    echo "VPN 网络路由正确配置"
else
    echo "[警告] 未找到 VPN 网络路由"
    echo "预期路由: $VPN_NETWORK dev $VPN_INTERFACE"
fi

# 检查目标网络路由
if ip route | grep -q "$TARGET_NETWORK dev $VPN_INTERFACE"; then
    echo "目标网络路由正确配置"
else
    echo "[警告] 未找到目标网络路由"
    echo "预期路由: $TARGET_NETWORK dev $VPN_INTERFACE"
    echo "这可能是无法访问目标网络的原因"
fi

# 3. 检查 IP 转发
echo "\n[3] 检查 IP 转发..."
IP_FORWARD=$(cat /proc/sys/net/ipv4/ip_forward)
if [ "$IP_FORWARD" = "1" ]; then
    echo "IP 转发已启用"
else
    echo "[错误] IP 转发未启用"
    echo "这会阻止 VPN 流量的路由"
    echo "建议: 运行以下命令启用 IP 转发"
    echo "  echo 1 > /proc/sys/net/ipv4/ip_forward"
    echo "  echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf"
    echo "  sysctl -p"
fi

# 4. 检查 iptables 规则
echo "\n[4] 检查 iptables 规则..."
echo "NAT 表规则:"
iptables -t nat -L -v -n

# 检查 MASQUERADE 规则
if iptables -t nat -L -v -n | grep -q "MASQUERADE.*$VPN_NETWORK"; then
    echo "NAT MASQUERADE 规则正确配置"
else
    echo "[警告] 未找到 NAT MASQUERADE 规则"
    echo "这可能导致 VPN 客户端无法访问外部网络"
    echo "建议: 添加以下 iptables 规则"
    echo "  iptables -t nat -A POSTROUTING -s $VPN_NETWORK -o eth0 -j MASQUERADE"
fi

# 检查 FORWARD 规则
echo "\nFORWARD 链规则:"
iptables -L FORWARD -v -n

if iptables -L FORWARD -v -n | grep -q "ACCEPT.*$VPN_INTERFACE"; then
    echo "FORWARD 规则正确配置"
else
    echo "[警告] 未找到允许 VPN 流量的 FORWARD 规则"
    echo "这可能阻止 VPN 流量通过"
    echo "建议: 添加以下 iptables 规则"
    echo "  iptables -A FORWARD -i $VPN_INTERFACE -j ACCEPT"
    echo "  iptables -A FORWARD -o $VPN_INTERFACE -j ACCEPT"
fi

# 5. 测试连通性
echo "\n[5] 测试连通性..."

# 获取目标网络的一个 IP 地址进行测试
TARGET_IP=$(echo $TARGET_NETWORK | sed 's/0\/24/1/')

echo "尝试 ping 目标网络 ($TARGET_IP)..."
ping -c 3 -W 2 $TARGET_IP
if [ $? -eq 0 ]; then
    echo "可以成功 ping 通目标网络"
else
    echo "[错误] 无法 ping 通目标网络"
    
    # 尝试跟踪路由
    echo "\n执行路由跟踪..."
    traceroute -n $TARGET_IP
fi

# 6. 检查服务器配置
echo "\n[6] 检查 OpenVPN 服务器配置..."
SERVER_CONF="/etc/openvpn/server.conf"
if [ -f "$SERVER_CONF" ]; then
    # 检查 push 路由指令
    echo "检查推送路由配置..."
    if grep -q "^push.*route $TARGET_NETWORK" "$SERVER_CONF"; then
        echo "服务器正确推送目标网络路由"
        grep "^push.*route" "$SERVER_CONF"
    else
        echo "[警告] 服务器未推送目标网络路由"
        echo "建议: 在服务器配置中添加以下行"
        echo "  push \"route $TARGET_NETWORK\""
    fi
    
    # 检查客户端到客户端路由
    if grep -q "^client-to-client" "$SERVER_CONF"; then
        echo "客户端到客户端路由已启用"
    else
        echo "[信息] 客户端到客户端路由未启用"
        echo "如果需要 VPN 客户端之间互相通信,请添加 'client-to-client' 指令"
    fi
else
    echo "[警告] 无法访问服务器配置文件: $SERVER_CONF"
fi

echo "\n===== 路由诊断完成 ====="
echo "如果问题仍然存在,请考虑以下可能的原因:"
echo "1. 目标网络的防火墙阻止了 VPN 流量"
echo "2. 路由冲突 (本地网络与 VPN 网络使用相同的子网)"
echo "3. 服务器端的路由配置问题"
echo "4. 客户端操作系统的防火墙阻止了 VPN 流量"

13.2.4 DNS问题

DNS问题是VPN用户常见的困扰,以下是一个用于诊断DNS问题的脚本:

#!/bin/bash
# openvpn_dns_diagnosis.sh

# 设置变量
VPN_INTERFACE="tun0"
DNS_SERVERS="8.8.8.8 8.8.4.4"  # 预期的 DNS 服务器
TEST_DOMAINS="google.com example.com github.com"

echo "===== OpenVPN DNS 问题诊断 ====="

# 1. 检查 VPN 接口状态
echo "\n[1] 检查 VPN 接口状态..."
if ip addr show dev "$VPN_INTERFACE" &>/dev/null; then
    echo "VPN 接口 $VPN_INTERFACE 存在"
    ip addr show dev "$VPN_INTERFACE"
else
    echo "[错误] VPN 接口 $VPN_INTERFACE 不存在"
    echo "请确保 OpenVPN 连接已建立"
    exit 1
fi

# 2. 检查当前 DNS 配置
echo "\n[2] 检查当前 DNS 配置..."
echo "当前 DNS 服务器:"
cat /etc/resolv.conf | grep nameserver

# 检查是否包含预期的 DNS 服务器
for dns in $DNS_SERVERS; do
    if grep -q "nameserver $dns" /etc/resolv.conf; then
        echo "找到预期的 DNS 服务器: $dns"
    else
        echo "[警告] 未找到预期的 DNS 服务器: $dns"
    fi
done

# 3. 测试 DNS 解析
echo "\n[3] 测试 DNS 解析..."
for domain in $TEST_DOMAINS; do
    echo "解析 $domain..."
    host $domain
    if [ $? -ne 0 ]; then
        echo "[错误] 无法解析 $domain"
    fi
done

# 4. 检查 DNS 泄漏
echo "\n[4] 检查 DNS 泄漏..."
echo "执行 DNS 泄漏测试..."

# 获取当前使用的 DNS 服务器
echo "通过 VPN 接口查询 DNS 服务器信息..."
dig +short whoami.akamai.net @ns1.akamai.net

# 5. 检查服务器 DNS 配置
echo "\n[5] 检查 OpenVPN 服务器 DNS 配置..."
SERVER_CONF="/etc/openvpn/server.conf"
if [ -f "$SERVER_CONF" ]; then
    # 检查 push DNS 指令
    echo "检查 DNS 推送配置..."
    if grep -q "^push.*dhcp-option DNS" "$SERVER_CONF"; then
        echo "服务器正确推送 DNS 设置"
        grep "^push.*dhcp-option DNS" "$SERVER_CONF"
    else
        echo "[警告] 服务器未推送 DNS 设置"
        echo "建议: 在服务器配置中添加以下行"
        for dns in $DNS_SERVERS; do
            echo "  push \"dhcp-option DNS $dns\""
        done
    fi
else
    echo "[警告] 无法访问服务器配置文件: $SERVER_CONF"
fi

# 6. 检查客户端 DNS 处理
echo "\n[6] 检查客户端 DNS 处理..."

# 检查 systemd-resolved (适用于 Ubuntu 18.04+)
if command -v systemd-resolve &>/dev/null; then
    echo "系统使用 systemd-resolved 管理 DNS"
    echo "systemd-resolved 状态:"
    systemd-resolve --status | grep -A 10 "$VPN_INTERFACE"
    
    # 检查 OpenVPN 是否配置了 update-resolv-conf
    CLIENT_CONF="/etc/openvpn/client/client.conf"
    if [ -f "$CLIENT_CONF" ]; then
        if grep -q "^script-security 2" "$CLIENT_CONF" && grep -q "^up /etc/openvpn/update-resolv-conf" "$CLIENT_CONF"; then
            echo "客户端配置了 update-resolv-conf 脚本"
        else
            echo "[警告] 客户端未配置 DNS 更新脚本"
            echo "建议: 在客户端配置中添加以下行"
            echo "  script-security 2"
            echo "  up /etc/openvpn/update-resolv-conf"
            echo "  down /etc/openvpn/update-resolv-conf"
        fi
    fi
# 检查 NetworkManager (适用于大多数 Linux 桌面发行版)
elif command -v nmcli &>/dev/null; then
    echo "系统使用 NetworkManager 管理网络"
    echo "NetworkManager DNS 配置:"
    nmcli dev show "$VPN_INTERFACE" | grep DNS
    
    # 检查 OpenVPN NetworkManager 插件
    if [ -f "/etc/NetworkManager/dispatcher.d/01-openvpn" ]; then
        echo "NetworkManager OpenVPN 插件已安装"
    else
        echo "[警告] NetworkManager OpenVPN 插件可能未安装"
        echo "建议: 安装 network-manager-openvpn 软件包"
    fi
fi

# 7. 提供常见解决方案
echo "\n[7] 常见 DNS 问题解决方案:"

echo "1. 确保服务器配置推送正确的 DNS 设置:"
echo "   push \"dhcp-option DNS 8.8.8.8\""
echo "   push \"dhcp-option DNS 8.8.4.4\""

echo "\n2. 对于 Linux 客户端,确保安装并配置了适当的 DNS 更新脚本:"
echo "   - 对于使用 systemd-resolved 的系统:"
echo "     script-security 2"
echo "     up /etc/openvpn/update-systemd-resolved"
echo "     down /etc/openvpn/update-systemd-resolved"
echo "     dhcp-option DOMAIN-ROUTE ."

echo "\n3. 对于 Windows 客户端,确保未勾选 'Use default gateway on remote network' 选项"
echo "   如果需要防止 DNS 泄漏"

echo "\n4. 考虑在服务器配置中添加以下选项以强制所有 DNS 流量通过 VPN:"
echo "   push \"redirect-gateway def1 bypass-dhcp\""
echo "   push \"dhcp-option DOMAIN-ROUTE .\""

echo "\n===== DNS 诊断完成 ====="

13.3 系统维护策略

13.3.1 日常维护计划

以下是一个OpenVPN服务器日常维护计划脚本,可以设置为定期执行:

#!/bin/bash
# openvpn_daily_maintenance.sh

# 设置变量
OPENVPN_DIR="/etc/openvpn"
LOG_DIR="/var/log/openvpn"
BACKUP_DIR="/var/backups/openvpn"
MAIL_RECIPIENT="admin@example.com"
MAINTENANCE_LOG="/var/log/openvpn_maintenance.log"

# 创建日志函数
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$MAINTENANCE_LOG"
}

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

# 开始维护
log_message "===== 开始 OpenVPN 日常维护 ====="

# 1. 检查 OpenVPN 服务状态
log_message "检查 OpenVPN 服务状态..."
systemctl status openvpn@server > /tmp/openvpn_status.tmp 2>&1
if grep -q "active (running)" /tmp/openvpn_status.tmp; then
    log_message "OpenVPN 服务正在运行"
else
    log_message "[警告] OpenVPN 服务未运行或状态异常"
    log_message "尝试重启服务..."
    systemctl restart openvpn@server
    sleep 5
    if systemctl is-active --quiet openvpn@server; then
        log_message "服务重启成功"
    else
        log_message "[错误] 服务重启失败,请手动检查"
    fi
fi

# 2. 备份配置文件
log_message "备份 OpenVPN 配置文件..."
BACKUP_FILE="$BACKUP_DIR/openvpn_config_$(date '+%Y%m%d').tar.gz"
tar -czf "$BACKUP_FILE" -C / "${OPENVPN_DIR#/}"
if [ $? -eq 0 ]; then
    log_message "配置备份成功: $BACKUP_FILE"
    
    # 删除超过30天的旧备份
    find "$BACKUP_DIR" -name "openvpn_config_*.tar.gz" -type f -mtime +30 -delete
    log_message "已清理超过30天的旧备份"
else
    log_message "[错误] 配置备份失败"
fi

# 3. 日志轮转和分析
log_message "处理 OpenVPN 日志..."

# 检查日志目录
if [ ! -d "$LOG_DIR" ]; then
    log_message "[警告] 日志目录不存在: $LOG_DIR"
    mkdir -p "$LOG_DIR"
fi

# 轮转日志
if [ -f "$LOG_DIR/openvpn.log" ]; then
    mv "$LOG_DIR/openvpn.log" "$LOG_DIR/openvpn.log.1"
    touch "$LOG_DIR/openvpn.log"
    chown nobody:nogroup "$LOG_DIR/openvpn.log"
    log_message "日志轮转完成"
    
    # 压缩旧日志
    gzip -f "$LOG_DIR/openvpn.log.1"
    
    # 分析日志中的错误
    zgrep -i "error\|warn\|fail" "$LOG_DIR/openvpn.log.1.gz" > /tmp/openvpn_errors.tmp
    if [ -s /tmp/openvpn_errors.tmp ]; then
        ERROR_COUNT=$(wc -l < /tmp/openvpn_errors.tmp)
        log_message "在日志中发现 $ERROR_COUNT 个错误/警告"
        
        # 分析常见错误模式
        TLS_ERRORS=$(grep -c "TLS Error" /tmp/openvpn_errors.tmp)
        AUTH_ERRORS=$(grep -c "AUTH" /tmp/openvpn_errors.tmp)
        ROUTE_ERRORS=$(grep -c "ROUTE" /tmp/openvpn_errors.tmp)
        
        log_message "错误分类统计:"
        log_message "- TLS 相关错误: $TLS_ERRORS"
        log_message "- 认证相关错误: $AUTH_ERRORS"
        log_message "- 路由相关错误: $ROUTE_ERRORS"
    else
        log_message "日志中未发现错误或警告"
    fi
fi

# 4. 检查证书状态
log_message "检查证书状态..."
CA_CERT="$OPENVPN_DIR/ca.crt"
SERVER_CERT="$OPENVPN_DIR/server.crt"

if [ -f "$CA_CERT" ]; then
    CA_EXPIRY=$(openssl x509 -enddate -noout -in "$CA_CERT" | cut -d= -f2)
    CA_EXPIRY_EPOCH=$(date -d "$CA_EXPIRY" +%s)
    CURRENT_EPOCH=$(date +%s)
    CA_DAYS_LEFT=$(( (CA_EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))
    
    log_message "CA 证书有效期剩余: $CA_DAYS_LEFT 天"
    if [ $CA_DAYS_LEFT -lt 30 ]; then
        log_message "[警告] CA 证书即将过期!请尽快续期。"
    fi
fi

if [ -f "$SERVER_CERT" ]; then
    SERVER_EXPIRY=$(openssl x509 -enddate -noout -in "$SERVER_CERT" | cut -d= -f2)
    SERVER_EXPIRY_EPOCH=$(date -d "$SERVER_EXPIRY" +%s)
    SERVER_DAYS_LEFT=$(( (SERVER_EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))
    
    log_message "服务器证书有效期剩余: $SERVER_DAYS_LEFT 天"
    if [ $SERVER_DAYS_LEFT -lt 30 ]; then
        log_message "[警告] 服务器证书即将过期!请尽快续期。"
    fi
fi

# 5. 检查系统资源
log_message "检查系统资源使用情况..."

# CPU 使用率
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
log_message "CPU 使用率: ${CPU_USAGE}%"

# 内存使用情况
MEM_TOTAL=$(free -m | awk '/Mem:/ {print $2}')
MEM_USED=$(free -m | awk '/Mem:/ {print $3}')
MEM_USAGE=$((MEM_USED * 100 / MEM_TOTAL))
log_message "内存使用率: ${MEM_USAGE}% (${MEM_USED}MB/${MEM_TOTAL}MB)"

# 磁盘使用情况
DISK_USAGE=$(df -h / | awk '/\// {print $5}')
log_message "磁盘使用率: $DISK_USAGE"

# 检查是否有资源警告
if (( $(echo "$CPU_USAGE > 80" | bc -l) )); then
    log_message "[警告] CPU 使用率过高"
fi

if [ $MEM_USAGE -gt 80 ]; then
    log_message "[警告] 内存使用率过高"
fi

if [ "${DISK_USAGE%\%}" -gt 80 ]; then
    log_message "[警告] 磁盘使用率过高"
fi

# 6. 检查连接状态
log_message "检查 OpenVPN 连接状态..."
if [ -f "$LOG_DIR/status.log" ]; then
    CONNECTED_CLIENTS=$(grep -c "^CLIENT_LIST" "$LOG_DIR/status.log")
    log_message "当前连接客户端数: $CONNECTED_CLIENTS"
    
    # 列出连接时间最长的客户端
    if [ $CONNECTED_CLIENTS -gt 0 ]; then
        log_message "连接时间最长的客户端:"
        grep "^CLIENT_LIST" "$LOG_DIR/status.log" | sort -k8 -n | head -3 | while read -r line; do
            CLIENT_NAME=$(echo "$line" | awk '{print $2}')
            CLIENT_IP=$(echo "$line" | awk '{print $3}')
            CONN_TIME=$(echo "$line" | awk '{print $8}')
            # 将连接时间转换为可读格式
            CONN_TIME_READABLE=$(date -d "@$CONN_TIME" -u +"%d天%H时%M分%S秒")
            log_message "- $CLIENT_NAME ($CLIENT_IP): 连接时长 $CONN_TIME_READABLE"
        done
    fi
else
    log_message "[警告] 状态日志文件不存在: $LOG_DIR/status.log"
fi

# 7. 检查网络性能
log_message "检查网络性能..."

# 检查网络接口状态
NETWORK_INTERFACE=$(ip route | grep default | awk '{print $5}')
log_message "主网络接口: $NETWORK_INTERFACE"

# 检查网络流量
RX_BYTES=$(cat /sys/class/net/$NETWORK_INTERFACE/statistics/rx_bytes)
TX_BYTES=$(cat /sys/class/net/$NETWORK_INTERFACE/statistics/tx_bytes)
RX_MB=$(echo "scale=2; $RX_BYTES/1048576" | bc)
TX_MB=$(echo "scale=2; $TX_BYTES/1048576" | bc)
log_message "网络流量统计 (自启动以来):"
log_message "- 接收: ${RX_MB}MB"
log_message "- 发送: ${TX_MB}MB"

# 检查 VPN 接口流量
if ip addr show dev "tun0" &>/dev/null; then
    VPN_RX_BYTES=$(cat /sys/class/net/tun0/statistics/rx_bytes)
    VPN_TX_BYTES=$(cat /sys/class/net/tun0/statistics/tx_bytes)
    VPN_RX_MB=$(echo "scale=2; $VPN_RX_BYTES/1048576" | bc)
    VPN_TX_MB=$(echo "scale=2; $VPN_TX_BYTES/1048576" | bc)
    log_message "VPN 流量统计 (自启动以来):"
    log_message "- 接收: ${VPN_RX_MB}MB"
    log_message "- 发送: ${VPN_TX_MB}MB"
fi

# 8. 生成维护报告
log_message "生成维护报告..."
REPORT_FILE="/tmp/openvpn_maintenance_report.txt"

cat > "$REPORT_FILE" << EOF
OpenVPN 服务器维护报告
$(date '+%Y-%m-%d %H:%M:%S')

1. 服务状态: $(systemctl is-active openvpn@server)

2. 证书状态:
   - CA 证书有效期剩余: $CA_DAYS_LEFT 天
   - 服务器证书有效期剩余: $SERVER_DAYS_LEFT 天

3. 系统资源:
   - CPU 使用率: ${CPU_USAGE}%
   - 内存使用率: ${MEM_USAGE}% (${MEM_USED}MB/${MEM_TOTAL}MB)
   - 磁盘使用率: $DISK_USAGE

4. 连接状态:
   - 当前连接客户端数: $CONNECTED_CLIENTS

5. 网络性能:
   - 主网络接口: $NETWORK_INTERFACE
   - 网络流量 (自启动以来):
     * 接收: ${RX_MB}MB
     * 发送: ${TX_MB}MB
   - VPN 流量 (自启动以来):
     * 接收: ${VPN_RX_MB}MB
     * 发送: ${VPN_TX_MB}MB

6. 日志分析:
   - 错误/警告总数: $ERROR_COUNT
   - TLS 相关错误: $TLS_ERRORS
   - 认证相关错误: $AUTH_ERRORS
   - 路由相关错误: $ROUTE_ERRORS

7. 维护操作:
   - 配置备份: $BACKUP_FILE
   - 日志轮转: 已完成

如有问题,请联系系统管理员。
EOF

# 发送报告邮件
if command -v mail &>/dev/null; then
    log_message "通过邮件发送维护报告..."
    mail -s "OpenVPN 服务器维护报告 $(date '+%Y-%m-%d')" "$MAIL_RECIPIENT" < "$REPORT_FILE"
    if [ $? -eq 0 ]; then
        log_message "维护报告已发送至 $MAIL_RECIPIENT"
    else
        log_message "[错误] 发送维护报告邮件失败"
    fi
else
    log_message "[警告] 未安装邮件客户端,无法发送报告"
fi

# 清理临时文件
rm -f /tmp/openvpn_status.tmp /tmp/openvpn_errors.tmp "$REPORT_FILE"

log_message "===== OpenVPN 日常维护完成 ====="

13.3.2 定期检查项目

以下是一个OpenVPN服务器定期检查清单脚本:

#!/bin/bash
# openvpn_periodic_checks.sh

# 设置变量
OPENVPN_DIR="/etc/openvpn"
LOG_DIR="/var/log/openvpn"
CHECK_LOG="/var/log/openvpn_checks.log"

# 创建日志函数
log_check() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$CHECK_LOG"
}

# 开始检查
log_check "===== 开始 OpenVPN 定期检查 ====="

# 1. 安全检查
log_check "\n[1] 执行安全检查..."

# 检查文件权限
log_check "检查关键文件权限..."
FILES_TO_CHECK="$OPENVPN_DIR/ca.key $OPENVPN_DIR/server.key $OPENVPN_DIR/ta.key"
for file in $FILES_TO_CHECK; do
    if [ -f "$file" ]; then
        PERMS=$(stat -c "%a" "$file")
        OWNER=$(stat -c "%U:%G" "$file")
        log_check "$file: 权限=$PERMS, 所有者=$OWNER"
        
        # 检查权限是否过于宽松
        if [ "$PERMS" != "600" ] && [ "$PERMS" != "400" ]; then
            log_check "[警告] $file 权限过于宽松 ($PERMS),建议设置为 600 或 400"
        fi
    else
        log_check "[信息] 文件不存在: $file"
    fi
done

# 检查 OpenVPN 进程权限
OPENVPN_USER=$(ps -ef | grep "[o]penvpn --config" | awk '{print $1}')
if [ "$OPENVPN_USER" != "nobody" ] && [ "$OPENVPN_USER" != "openvpn" ]; then
    log_check "[警告] OpenVPN 进程以 $OPENVPN_USER 用户运行,建议使用专用用户"
else
    log_check "OpenVPN 进程以适当的用户运行: $OPENVPN_USER"
fi

# 检查防火墙状态
log_check "检查防火墙状态..."
if command -v ufw &>/dev/null; then
    UFW_STATUS=$(ufw status | head -1)
    log_check "UFW 状态: $UFW_STATUS"
    
    # 检查 OpenVPN 端口是否开放
    if ufw status | grep -q "1194"; then
        log_check "OpenVPN 端口 1194 已在防火墙中开放"
    else
        log_check "[警告] OpenVPN 端口 1194 可能未在防火墙中开放"
    fi
elif command -v firewall-cmd &>/dev/null; then
    FIREWALL_STATUS=$(firewall-cmd --state 2>/dev/null)
    log_check "Firewalld 状态: $FIREWALL_STATUS"
    
    if firewall-cmd --list-services | grep -q "openvpn"; then
        log_check "OpenVPN 服务已在防火墙中允许"
    else
        log_check "[警告] OpenVPN 服务可能未在防火墙中允许"
    fi
else
    log_check "[信息] 未检测到常见的防火墙管理工具"
fi

# 2. 性能检查
log_check "\n[2] 执行性能检查..."

# 检查系统负载
LOAD_AVG=$(uptime | awk -F'load average:' '{print $2}')
log_check "系统负载: $LOAD_AVG"

# 检查内存使用
MEM_INFO=$(free -h | grep Mem)
log_check "内存使用情况: $MEM_INFO"

# 检查磁盘空间
DISK_USAGE=$(df -h / | tail -1)
log_check "根分区磁盘使用: $DISK_USAGE"

# 检查 OpenVPN 进程资源使用
if pgrep openvpn &>/dev/null; then
    OPENVPN_PID=$(pgrep openvpn)
    OPENVPN_CPU=$(ps -p $OPENVPN_PID -o %cpu --no-headers)
    OPENVPN_MEM=$(ps -p $OPENVPN_PID -o %mem --no-headers)
    log_check "OpenVPN 进程资源使用: CPU=${OPENVPN_CPU}%, 内存=${OPENVPN_MEM}%"
else
    log_check "[错误] OpenVPN 进程未运行"
fi

# 3. 连接检查
log_check "\n[3] 执行连接检查..."

# 检查监听端口
log_check "检查 OpenVPN 监听端口..."
LISTENING_PORTS=$(netstat -ulnp | grep openvpn)
if [ -n "$LISTENING_PORTS" ]; then
    log_check "OpenVPN 监听端口:"
    echo "$LISTENING_PORTS" | while read line; do
        log_check "  $line"
    done
else
    log_check "[警告] 未检测到 OpenVPN 监听端口"
fi

# 检查活动连接
if [ -f "$LOG_DIR/status.log" ]; then
    ACTIVE_CONNECTIONS=$(grep -c "^CLIENT_LIST" "$LOG_DIR/status.log")
    log_check "当前活动连接数: $ACTIVE_CONNECTIONS"
    
    if [ $ACTIVE_CONNECTIONS -gt 0 ]; then
        log_check "活动连接详情:"
        grep "^CLIENT_LIST" "$LOG_DIR/status.log" | head -5 | while read line; do
            CLIENT_NAME=$(echo "$line" | awk '{print $2}')
            CLIENT_IP=$(echo "$line" | awk '{print $3}')
            log_check "  客户端: $CLIENT_NAME, IP: $CLIENT_IP"
        done
    fi
else
    log_check "[警告] 状态日志文件不存在: $LOG_DIR/status.log"
fi

# 4. 配置检查
log_check "\n[4] 执行配置检查..."

# 检查服务器配置文件
SERVER_CONF="$OPENVPN_DIR/server.conf"
if [ -f "$SERVER_CONF" ]; then
    log_check "服务器配置文件存在: $SERVER_CONF"
    
    # 检查关键配置项
    if grep -q "^port" "$SERVER_CONF"; then
        PORT=$(grep "^port" "$SERVER_CONF" | awk '{print $2}')
        log_check "配置端口: $PORT"
    fi
    
    if grep -q "^proto" "$SERVER_CONF"; then
        PROTO=$(grep "^proto" "$SERVER_CONF" | awk '{print $2}')
        log_check "配置协议: $PROTO"
    fi
    
    if grep -q "^server" "$SERVER_CONF"; then
        SERVER_NET=$(grep "^server" "$SERVER_CONF" | awk '{print $2, $3}')
        log_check "VPN 网络: $SERVER_NET"
    fi
    
    # 检查日志配置
    if grep -q "^log" "$SERVER_CONF"; then
        LOG_FILE=$(grep "^log" "$SERVER_CONF" | awk '{print $2}')
        log_check "日志文件: $LOG_FILE"
        
        if [ -f "$LOG_FILE" ]; then
            LOG_SIZE=$(du -h "$LOG_FILE" | awk '{print $1}')
            log_check "日志文件大小: $LOG_SIZE"
        else
            log_check "[警告] 配置的日志文件不存在: $LOG_FILE"
        fi
    fi
else
    log_check "[错误] 服务器配置文件不存在: $SERVER_CONF"
fi

# 5. 证书检查
log_check "\n[5] 执行证书检查..."

# 检查证书文件
CERT_FILES="ca.crt server.crt server.key dh.pem ta.key"
for cert_file in $CERT_FILES; do
    CERT_PATH="$OPENVPN_DIR/$cert_file"
    if [ -f "$CERT_PATH" ]; then
        log_check "证书文件存在: $cert_file"
        
        # 检查证书有效期(仅对 .crt 文件)
        if [[ "$cert_file" == *.crt ]]; then
            EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_PATH" 2>/dev/null | cut -d= -f2)
            if [ -n "$EXPIRY" ]; then
                EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null)
                CURRENT_EPOCH=$(date +%s)
                DAYS_LEFT=$(( (EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))
                
                if [ $DAYS_LEFT -lt 30 ]; then
                    log_check "[警告] $cert_file 将在 $DAYS_LEFT 天后过期"
                else
                    log_check "$cert_file 有效期剩余: $DAYS_LEFT 天"
                fi
            fi
        fi
    else
        log_check "[警告] 证书文件不存在: $cert_file"
    fi
done

# 6. 网络检查
log_check "\n[6] 执行网络检查..."

# 检查 IP 转发
IP_FORWARD=$(cat /proc/sys/net/ipv4/ip_forward)
if [ "$IP_FORWARD" = "1" ]; then
    log_check "IP 转发已启用"
else
    log_check "[警告] IP 转发未启用"
fi

# 检查路由表
log_check "检查 VPN 相关路由..."
VPN_ROUTES=$(ip route | grep tun)
if [ -n "$VPN_ROUTES" ]; then
    log_check "VPN 路由:"
    echo "$VPN_ROUTES" | while read route; do
        log_check "  $route"
    done
else
    log_check "[信息] 未检测到 VPN 相关路由"
fi

# 检查 iptables 规则
log_check "检查 iptables NAT 规则..."
NAT_RULES=$(iptables -t nat -L POSTROUTING -n | grep MASQUERADE)
if [ -n "$NAT_RULES" ]; then
    log_check "NAT MASQUERADE 规则存在"
else
    log_check "[警告] 未找到 NAT MASQUERADE 规则"
fi

log_check "===== OpenVPN 定期检查完成 ====="

# 生成检查摘要
WARNING_COUNT=$(grep -c "\[警告\]" "$CHECK_LOG")
ERROR_COUNT=$(grep -c "\[错误\]" "$CHECK_LOG")

log_check "\n检查摘要:"
log_check "- 警告数量: $WARNING_COUNT"
log_check "- 错误数量: $ERROR_COUNT"

if [ $ERROR_COUNT -gt 0 ]; then
    log_check "\n发现的错误:"
    grep "\[错误\]" "$CHECK_LOG" | tail -10
fi

if [ $WARNING_COUNT -gt 0 ]; then
    log_check "\n发现的警告:"
    grep "\[警告\]" "$CHECK_LOG" | tail -10
fi

13.3.3 维护窗口管理

以下是一个用于管理OpenVPN维护窗口的脚本:

#!/bin/bash
# openvpn_maintenance_window.sh

# 设置变量
MAINTENANCE_LOG="/var/log/openvpn_maintenance_window.log"
NOTIFICATION_SCRIPT="/usr/local/bin/send_notification.sh"
OPENVPN_SERVICE="openvpn@server"
BACKUP_DIR="/var/backups/openvpn"

# 创建日志函数
log_maintenance() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$MAINTENANCE_LOG"
}

# 发送通知函数
send_notification() {
    local message="$1"
    local priority="$2"
    
    log_maintenance "发送通知: $message"
    
    # 如果存在通知脚本,则使用它
    if [ -x "$NOTIFICATION_SCRIPT" ]; then
        "$NOTIFICATION_SCRIPT" "$message" "$priority"
    fi
    
    # 也可以发送邮件
    if command -v mail &>/dev/null; then
        echo "$message" | mail -s "OpenVPN 维护通知" admin@example.com
    fi
}

# 检查是否为维护时间
is_maintenance_time() {
    local current_hour=$(date +%H)
    local current_day=$(date +%u)  # 1=Monday, 7=Sunday
    
    # 维护窗口:每周日凌晨 2-4 点
    if [ "$current_day" = "7" ] && [ "$current_hour" -ge "2" ] && [ "$current_hour" -lt "4" ]; then
        return 0
    fi
    
    return 1
}

# 预维护检查
pre_maintenance_check() {
    log_maintenance "===== 开始预维护检查 ====="
    
    # 检查服务状态
    if ! systemctl is-active --quiet "$OPENVPN_SERVICE"; then
        log_maintenance "[错误] OpenVPN 服务未运行,取消维护"
        return 1
    fi
    
    # 检查连接数
    if [ -f "/var/log/openvpn/status.log" ]; then
        ACTIVE_CONNECTIONS=$(grep -c "^CLIENT_LIST" "/var/log/openvpn/status.log")
        log_maintenance "当前活动连接数: $ACTIVE_CONNECTIONS"
        
        if [ $ACTIVE_CONNECTIONS -gt 10 ]; then
            log_maintenance "[警告] 活动连接数较多,建议推迟维护"
            return 1
        fi
    fi
    
    # 检查系统负载
    LOAD_1MIN=$(uptime | awk -F'load average:' '{print $2}' | awk -F',' '{print $1}' | tr -d ' ')
    if (( $(echo "$LOAD_1MIN > 2.0" | bc -l) )); then
        log_maintenance "[警告] 系统负载过高 ($LOAD_1MIN),建议推迟维护"
        return 1
    fi
    
    log_maintenance "预维护检查通过"
    return 0
}

# 执行维护
perform_maintenance() {
    log_maintenance "===== 开始执行维护 ====="
    
    # 1. 通知用户维护开始
    send_notification "OpenVPN 服务器维护即将开始,预计持续 30 分钟" "high"
    
    # 等待 5 分钟让用户准备
    log_maintenance "等待 5 分钟让用户准备..."
    sleep 300
    
    # 2. 备份配置
    log_maintenance "备份当前配置..."
    BACKUP_FILE="$BACKUP_DIR/maintenance_backup_$(date '+%Y%m%d_%H%M%S').tar.gz"
    mkdir -p "$BACKUP_DIR"
    tar -czf "$BACKUP_FILE" -C / etc/openvpn var/log/openvpn
    
    if [ $? -eq 0 ]; then
        log_maintenance "配置备份成功: $BACKUP_FILE"
    else
        log_maintenance "[错误] 配置备份失败"
        send_notification "OpenVPN 维护失败:配置备份失败" "critical"
        return 1
    fi
    
    # 3. 停止服务
    log_maintenance "停止 OpenVPN 服务..."
    systemctl stop "$OPENVPN_SERVICE"
    sleep 5
    
    if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
        log_maintenance "[错误] 无法停止 OpenVPN 服务"
        send_notification "OpenVPN 维护失败:无法停止服务" "critical"
        return 1
    fi
    
    # 4. 执行维护任务
    log_maintenance "执行维护任务..."
    
    # 清理旧日志
    log_maintenance "清理旧日志文件..."
    find /var/log/openvpn -name "*.log.*" -type f -mtime +30 -delete
    
    # 轮转当前日志
    if [ -f "/var/log/openvpn/openvpn.log" ]; then
        mv "/var/log/openvpn/openvpn.log" "/var/log/openvpn/openvpn.log.$(date '+%Y%m%d')"
        touch "/var/log/openvpn/openvpn.log"
        chown nobody:nogroup "/var/log/openvpn/openvpn.log"
    fi
    
    # 更新 CRL(如果存在)
    if [ -f "/etc/openvpn/easy-rsa/pki/crl.pem" ]; then
        log_maintenance "更新证书吊销列表..."
        cd /etc/openvpn/easy-rsa
        ./easyrsa gen-crl
        cp pki/crl.pem /etc/openvpn/
    fi
    
    # 检查证书有效期
    log_maintenance "检查证书有效期..."
    for cert in /etc/openvpn/ca.crt /etc/openvpn/server.crt; do
        if [ -f "$cert" ]; then
            EXPIRY=$(openssl x509 -enddate -noout -in "$cert" | cut -d= -f2)
            EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
            CURRENT_EPOCH=$(date +%s)
            DAYS_LEFT=$(( (EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))
            
            log_maintenance "$(basename $cert) 有效期剩余: $DAYS_LEFT 天"
            
            if [ $DAYS_LEFT -lt 60 ]; then
                send_notification "警告:$(basename $cert) 将在 $DAYS_LEFT 天后过期" "high"
            fi
        fi
    done
    
    # 优化配置文件
    log_maintenance "优化配置文件..."
    # 这里可以添加配置优化逻辑
    
    # 5. 重启服务
    log_maintenance "重启 OpenVPN 服务..."
    systemctl start "$OPENVPN_SERVICE"
    sleep 10
    
    if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
        log_maintenance "OpenVPN 服务重启成功"
    else
        log_maintenance "[错误] OpenVPN 服务重启失败"
        
        # 尝试恢复备份
        log_maintenance "尝试恢复备份配置..."
        tar -xzf "$BACKUP_FILE" -C /
        systemctl start "$OPENVPN_SERVICE"
        
        if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
            log_maintenance "服务恢复成功"
            send_notification "OpenVPN 维护遇到问题,已恢复到维护前状态" "high"
        else
            log_maintenance "[严重错误] 服务恢复失败"
            send_notification "OpenVPN 维护失败,服务无法启动,需要手动干预" "critical"
        fi
        return 1
    fi
    
    # 6. 验证服务
    log_maintenance "验证服务状态..."
    sleep 30  # 等待服务完全启动
    
    # 检查监听端口
    if netstat -ulnp | grep -q openvpn; then
        log_maintenance "服务监听端口正常"
    else
        log_maintenance "[警告] 服务监听端口异常"
    fi
    
    # 7. 通知维护完成
    send_notification "OpenVPN 服务器维护完成,服务已恢复正常" "normal"
    
    log_maintenance "===== 维护执行完成 ====="
    return 0
}

# 主函数
main() {
    case "$1" in
        "check")
            if is_maintenance_time; then
                echo "当前时间在维护窗口内"
                exit 0
            else
                echo "当前时间不在维护窗口内"
                exit 1
            fi
            ;;
        "force")
            log_maintenance "强制执行维护"
            if pre_maintenance_check; then
                perform_maintenance
            else
                log_maintenance "预维护检查失败,取消维护"
                exit 1
            fi
            ;;
        "auto")
            if is_maintenance_time; then
                log_maintenance "自动维护:当前时间在维护窗口内"
                if pre_maintenance_check; then
                    perform_maintenance
                else
                    log_maintenance "预维护检查失败,跳过本次维护"
                    send_notification "OpenVPN 自动维护跳过:预维护检查失败" "normal"
                fi
            else
                log_maintenance "自动维护:当前时间不在维护窗口内"
            fi
            ;;
        *)
            echo "用法: $0 {check|force|auto}"
            echo "  check - 检查是否在维护时间窗口内"
            echo "  force - 强制执行维护(忽略时间窗口)"
            echo "  auto  - 自动维护(仅在维护时间窗口内执行)"
            exit 1
            ;;
    esac
}

# 执行主函数
main "$@"

13.4 性能问题排查

13.4.1 带宽问题诊断

当OpenVPN连接速度较慢时,可以使用以下脚本进行带宽问题诊断:

#!/bin/bash
# openvpn_bandwidth_diagnosis.sh

# 设置变量
VPN_INTERFACE="tun0"
TEST_SERVER="speedtest.net"
TEST_DURATION=30
LOG_FILE="/var/log/openvpn_bandwidth_test.log"

echo "===== OpenVPN 带宽问题诊断 ====="

# 创建日志函数
log_bandwidth() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 1. 检查 VPN 接口状态
log_bandwidth "检查 VPN 接口状态..."
if ip addr show dev "$VPN_INTERFACE" &>/dev/null; then
    VPN_IP=$(ip addr show dev "$VPN_INTERFACE" | grep inet | awk '{print $2}' | cut -d/ -f1)
    log_bandwidth "VPN 接口 $VPN_INTERFACE 存在,IP: $VPN_IP"
else
    log_bandwidth "[错误] VPN 接口 $VPN_INTERFACE 不存在"
    exit 1
fi

# 2. 检查 MTU 设置
log_bandwidth "检查 MTU 设置..."
VPN_MTU=$(ip addr show dev "$VPN_INTERFACE" | grep mtu | awk '{print $5}')
ETH_MTU=$(ip addr show dev eth0 | grep mtu | awk '{print $5}' 2>/dev/null || echo "1500")
log_bandwidth "VPN MTU: $VPN_MTU, 物理接口 MTU: $ETH_MTU"

if [ "$VPN_MTU" -gt 1400 ]; then
    log_bandwidth "[警告] VPN MTU 可能过大,建议设置为 1400 或更小"
fi

# 3. 测试不同数据包大小的延迟
log_bandwidth "测试不同数据包大小的延迟..."
for size in 64 128 256 512 1024 1400; do
    if ping -c 5 -s $size -I "$VPN_INTERFACE" 8.8.8.8 &>/dev/null; then
        avg_time=$(ping -c 5 -s $size -I "$VPN_INTERFACE" 8.8.8.8 | grep rtt | cut -d'/' -f5)
        log_bandwidth "数据包大小 ${size}B: 平均延迟 ${avg_time}ms"
    else
        log_bandwidth "数据包大小 ${size}B: 测试失败"
    fi
done

# 4. 检查 TCP 窗口大小
log_bandwidth "检查 TCP 窗口大小设置..."
TCP_RMEM=$(cat /proc/sys/net/ipv4/tcp_rmem)
TCP_WMEM=$(cat /proc/sys/net/ipv4/tcp_wmem)
log_bandwidth "TCP 接收缓冲区: $TCP_RMEM"
log_bandwidth "TCP 发送缓冲区: $TCP_WMEM"

# 5. 检查网络拥塞控制算法
log_bandwidth "检查网络拥塞控制算法..."
CONGESTION_CONTROL=$(cat /proc/sys/net/ipv4/tcp_congestion_control)
log_bandwidth "当前拥塞控制算法: $CONGESTION_CONTROL"

# 6. 使用 iperf3 进行带宽测试(如果可用)
if command -v iperf3 &>/dev/null; then
    log_bandwidth "使用 iperf3 进行带宽测试..."
    
    # 启动 iperf3 服务器(后台运行)
    iperf3 -s -D -p 5201
    sleep 2
    
    # 通过 VPN 接口进行测试
    log_bandwidth "通过 VPN 接口测试带宽..."
    VPN_BANDWIDTH=$(iperf3 -c 127.0.0.1 -p 5201 -B "$VPN_IP" -t 10 -f M | grep receiver | awk '{print $7, $8}')
    log_bandwidth "VPN 带宽测试结果: $VPN_BANDWIDTH"
    
    # 停止 iperf3 服务器
    pkill iperf3
else
    log_bandwidth "[信息] iperf3 未安装,跳过带宽测试"
fi

# 7. 检查 OpenVPN 配置中的性能相关设置
log_bandwidth "检查 OpenVPN 性能配置..."
SERVER_CONF="/etc/openvpn/server.conf"
if [ -f "$SERVER_CONF" ]; then
    # 检查压缩设置
    if grep -q "^comp-lzo" "$SERVER_CONF" || grep -q "^compress" "$SERVER_CONF"; then
        COMPRESSION=$(grep -E "^(comp-lzo|compress)" "$SERVER_CONF")
        log_bandwidth "压缩设置: $COMPRESSION"
    else
        log_bandwidth "[信息] 未启用压缩"
    fi
    
    # 检查协议设置
    if grep -q "^proto" "$SERVER_CONF"; then
        PROTOCOL=$(grep "^proto" "$SERVER_CONF" | awk '{print $2}')
        log_bandwidth "协议设置: $PROTOCOL"
        
        if [ "$PROTOCOL" = "tcp" ]; then
            log_bandwidth "[建议] TCP 协议可能影响性能,考虑使用 UDP"
        fi
    fi
    
    # 检查加密算法
    if grep -q "^cipher" "$SERVER_CONF"; then
        CIPHER=$(grep "^cipher" "$SERVER_CONF" | awk '{print $2}')
        log_bandwidth "加密算法: $CIPHER"
        
        case "$CIPHER" in
            "AES-256-CBC")
                log_bandwidth "[建议] 考虑使用 AES-128-GCM 以提高性能"
                ;;
            "AES-128-GCM")
                log_bandwidth "[良好] 使用了高性能的加密算法"
                ;;
        esac
    fi
fi

# 8. 生成性能优化建议
log_bandwidth "\n===== 性能优化建议 ====="
log_bandwidth "1. MTU 优化:如果 VPN MTU 大于 1400,建议降低到 1400"
log_bandwidth "2. 协议选择:UDP 通常比 TCP 性能更好"
log_bandwidth "3. 加密算法:AES-128-GCM 比 AES-256-CBC 性能更好"
log_bandwidth "4. 压缩:在高延迟网络中启用压缩可能有帮助"
log_bandwidth "5. TCP 缓冲区:增大 TCP 缓冲区可以提高吞吐量"
log_bandwidth "6. 拥塞控制:BBR 算法在某些情况下性能更好"

log_bandwidth "===== 带宽诊断完成 ====="

13.4.2 延迟问题分析

以下脚本用于分析和诊断OpenVPN连接的延迟问题:

#!/bin/bash
# openvpn_latency_analysis.sh

# 设置变量
VPN_INTERFACE="tun0"
TEST_TARGETS="8.8.8.8 1.1.1.1 google.com"
TEST_COUNT=20
LOG_FILE="/var/log/openvpn_latency_analysis.log"

echo "===== OpenVPN 延迟问题分析 ====="

# 创建日志函数
log_latency() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 1. 基础延迟测试
log_latency "执行基础延迟测试..."

# 测试直接连接延迟
log_latency "测试直接连接延迟(不通过VPN)..."
for target in $TEST_TARGETS; do
    if ping -c $TEST_COUNT "$target" &>/dev/null; then
        direct_stats=$(ping -c $TEST_COUNT "$target" | grep rtt)
        log_latency "直接连接到 $target: $direct_stats"
    else
        log_latency "[错误] 无法直接连接到 $target"
    fi
done

# 测试通过VPN的延迟
if ip addr show dev "$VPN_INTERFACE" &>/dev/null; then
    log_latency "\n测试通过VPN的延迟..."
    for target in $TEST_TARGETS; do
        if ping -c $TEST_COUNT -I "$VPN_INTERFACE" "$target" &>/dev/null; then
            vpn_stats=$(ping -c $TEST_COUNT -I "$VPN_INTERFACE" "$target" | grep rtt)
            log_latency "通过VPN连接到 $target: $vpn_stats"
        else
            log_latency "[错误] 无法通过VPN连接到 $target"
        fi
    done
else
    log_latency "[错误] VPN接口 $VPN_INTERFACE 不存在"
    exit 1
fi

# 2. 路由跳数分析
log_latency "\n执行路由跳数分析..."
for target in google.com; do
    log_latency "直接路由到 $target:"
    traceroute -n "$target" 2>/dev/null | head -10 | while read line; do
        log_latency "  $line"
    done
    
    log_latency "\n通过VPN路由到 $target:"
    traceroute -i "$VPN_INTERFACE" -n "$target" 2>/dev/null | head -10 | while read line; do
        log_latency "  $line"
    done
done

# 3. DNS 解析延迟测试
log_latency "\n测试DNS解析延迟..."
for domain in google.com github.com stackoverflow.com; do
    # 直接DNS解析
    direct_dns_time=$(time (nslookup "$domain" 8.8.8.8 >/dev/null) 2>&1 | grep real | awk '{print $2}')
    log_latency "直接DNS解析 $domain: $direct_dns_time"
    
    # 通过VPN的DNS解析
    if [ -f "/etc/resolv.conf" ]; then
        vpn_dns_server=$(grep nameserver /etc/resolv.conf | head -1 | awk '{print $2}')
        vpn_dns_time=$(time (nslookup "$domain" "$vpn_dns_server" >/dev/null) 2>&1 | grep real | awk '{print $2}')
        log_latency "通过VPN DNS解析 $domain: $vpn_dns_time"
    fi
done

# 4. 检查系统负载对延迟的影响
log_latency "\n检查系统负载..."
LOAD_AVG=$(uptime | awk -F'load average:' '{print $2}')
log_latency "系统负载: $LOAD_AVG"

# CPU 使用率
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
log_latency "CPU 使用率: ${CPU_USAGE}%"

# 内存使用率
MEM_USAGE=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}')
log_latency "内存使用率: ${MEM_USAGE}%"

# 5. 检查网络接口统计
log_latency "\n检查网络接口统计..."
for interface in eth0 "$VPN_INTERFACE"; do
    if [ -d "/sys/class/net/$interface" ]; then
        rx_errors=$(cat "/sys/class/net/$interface/statistics/rx_errors")
        tx_errors=$(cat "/sys/class/net/$interface/statistics/tx_errors")
        rx_dropped=$(cat "/sys/class/net/$interface/statistics/rx_dropped")
        tx_dropped=$(cat "/sys/class/net/$interface/statistics/tx_dropped")
        
        log_latency "接口 $interface 统计:"
        log_latency "  接收错误: $rx_errors"
        log_latency "  发送错误: $tx_errors"
        log_latency "  接收丢包: $rx_dropped"
        log_latency "  发送丢包: $tx_dropped"
    fi
done

# 6. 检查 OpenVPN 进程优先级
log_latency "\n检查 OpenVPN 进程优先级..."
if pgrep openvpn &>/dev/null; then
    OPENVPN_PID=$(pgrep openvpn)
    OPENVPN_NICE=$(ps -o ni -p "$OPENVPN_PID" --no-headers)
    log_latency "OpenVPN 进程优先级 (nice): $OPENVPN_NICE"
    
    if [ "$OPENVPN_NICE" -gt 0 ]; then
        log_latency "[建议] 考虑降低 OpenVPN 进程的 nice 值以提高优先级"
    fi
fi

# 7. 生成延迟优化建议
log_latency "\n===== 延迟优化建议 ====="
log_latency "1. 选择地理位置更近的VPN服务器"
log_latency "2. 使用UDP协议而不是TCP"
log_latency "3. 优化MTU大小,避免数据包分片"
log_latency "4. 考虑使用更快的加密算法"
log_latency "5. 确保系统负载不过高"
log_latency "6. 检查网络接口是否有错误或丢包"
log_latency "7. 考虑调整OpenVPN进程优先级"
log_latency "8. 使用本地DNS缓存减少DNS解析延迟"

log_latency "===== 延迟分析完成 ====="

13.5 升级与迁移

13.5.1 版本升级策略

以下是一个OpenVPN版本升级脚本:

#!/bin/bash
# openvpn_upgrade.sh

# 设置变量
CURRENT_VERSION=$(openvpn --version | head -1 | awk '{print $2}')
BACKUP_DIR="/var/backups/openvpn/upgrade_$(date '+%Y%m%d_%H%M%S')"
LOG_FILE="/var/log/openvpn_upgrade.log"
OPENVPN_SERVICE="openvpn@server"

echo "===== OpenVPN 版本升级工具 ====="

# 创建日志函数
log_upgrade() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 检查当前版本
log_upgrade "当前 OpenVPN 版本: $CURRENT_VERSION"

# 1. 预升级检查
log_upgrade "===== 开始预升级检查 ====="

# 检查服务状态
if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
    log_upgrade "OpenVPN 服务正在运行"
else
    log_upgrade "[警告] OpenVPN 服务未运行"
fi

# 检查配置文件
if [ -f "/etc/openvpn/server.conf" ]; then
    log_upgrade "服务器配置文件存在"
else
    log_upgrade "[错误] 服务器配置文件不存在"
    exit 1
fi

# 检查证书文件
CERT_FILES="/etc/openvpn/ca.crt /etc/openvpn/server.crt /etc/openvpn/server.key"
for cert in $CERT_FILES; do
    if [ -f "$cert" ]; then
        log_upgrade "证书文件存在: $(basename $cert)"
    else
        log_upgrade "[错误] 证书文件不存在: $cert"
        exit 1
    fi
done

# 检查磁盘空间
AVAILABLE_SPACE=$(df /var | tail -1 | awk '{print $4}')
if [ "$AVAILABLE_SPACE" -lt 1048576 ]; then  # 1GB
    log_upgrade "[警告] 可用磁盘空间不足 1GB"
else
    log_upgrade "磁盘空间充足"
fi

# 2. 创建备份
log_upgrade "===== 创建升级前备份 ====="
mkdir -p "$BACKUP_DIR"

# 备份配置文件
log_upgrade "备份配置文件..."
cp -r /etc/openvpn "$BACKUP_DIR/"
if [ $? -eq 0 ]; then
    log_upgrade "配置文件备份成功"
else
    log_upgrade "[错误] 配置文件备份失败"
    exit 1
fi

# 备份日志文件
log_upgrade "备份日志文件..."
cp -r /var/log/openvpn "$BACKUP_DIR/" 2>/dev/null

# 备份当前二进制文件
log_upgrade "备份当前二进制文件..."
cp $(which openvpn) "$BACKUP_DIR/openvpn.old"

# 记录当前包信息
log_upgrade "记录当前包信息..."
if command -v dpkg &>/dev/null; then
    dpkg -l | grep openvpn > "$BACKUP_DIR/package_info.txt"
elif command -v rpm &>/dev/null; then
    rpm -qa | grep openvpn > "$BACKUP_DIR/package_info.txt"
fi

log_upgrade "备份完成,备份目录: $BACKUP_DIR"

# 3. 停止服务
log_upgrade "===== 停止 OpenVPN 服务 ====="
systemctl stop "$OPENVPN_SERVICE"
sleep 5

if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
    log_upgrade "[错误] 无法停止 OpenVPN 服务"
    exit 1
else
    log_upgrade "OpenVPN 服务已停止"
fi

# 4. 执行升级
log_upgrade "===== 执行升级 ====="

# 检测包管理器并执行升级
if command -v apt &>/dev/null; then
    log_upgrade "使用 APT 包管理器升级..."
    apt update
    apt upgrade -y openvpn
    UPGRADE_RESULT=$?
elif command -v yum &>/dev/null; then
    log_upgrade "使用 YUM 包管理器升级..."
    yum update -y openvpn
    UPGRADE_RESULT=$?
elif command -v dnf &>/dev/null; then
    log_upgrade "使用 DNF 包管理器升级..."
    dnf update -y openvpn
    UPGRADE_RESULT=$?
else
    log_upgrade "[错误] 未检测到支持的包管理器"
    exit 1
fi

if [ $UPGRADE_RESULT -eq 0 ]; then
    log_upgrade "包升级成功"
else
    log_upgrade "[错误] 包升级失败"
    # 尝试恢复
    log_upgrade "尝试恢复原始配置..."
    cp -r "$BACKUP_DIR/openvpn" /etc/
    exit 1
fi

# 检查新版本
NEW_VERSION=$(openvpn --version | head -1 | awk '{print $2}')
log_upgrade "升级后版本: $NEW_VERSION"

# 5. 验证配置兼容性
log_upgrade "===== 验证配置兼容性 ====="

# 测试配置文件语法
log_upgrade "测试配置文件语法..."
openvpn --config /etc/openvpn/server.conf --test-crypto
if [ $? -eq 0 ]; then
    log_upgrade "配置文件语法正确"
else
    log_upgrade "[警告] 配置文件可能存在兼容性问题"
fi

# 6. 重启服务
log_upgrade "===== 重启 OpenVPN 服务 ====="
systemctl start "$OPENVPN_SERVICE"
sleep 10

if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
    log_upgrade "OpenVPN 服务启动成功"
else
    log_upgrade "[错误] OpenVPN 服务启动失败"
    
    # 查看错误日志
    log_upgrade "查看服务错误信息:"
    systemctl status "$OPENVPN_SERVICE" --no-pager | while read line; do
        log_upgrade "  $line"
    done
    
    # 尝试回滚
    log_upgrade "尝试回滚到升级前版本..."
    if [ -f "$BACKUP_DIR/openvpn.old" ]; then
        cp "$BACKUP_DIR/openvpn.old" $(which openvpn)
        cp -r "$BACKUP_DIR/openvpn" /etc/
        systemctl start "$OPENVPN_SERVICE"
        
        if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
            log_upgrade "回滚成功,服务已恢复"
        else
            log_upgrade "[严重错误] 回滚失败,需要手动干预"
        fi
    fi
    exit 1
fi

# 7. 升级后验证
log_upgrade "===== 升级后验证 ====="

# 检查监听端口
if netstat -ulnp | grep -q openvpn; then
    log_upgrade "服务监听端口正常"
else
    log_upgrade "[警告] 服务监听端口异常"
fi

# 等待并检查客户端连接
log_upgrade "等待客户端重新连接..."
sleep 30

if [ -f "/var/log/openvpn/status.log" ]; then
    CONNECTED_CLIENTS=$(grep -c "^CLIENT_LIST" "/var/log/openvpn/status.log")
    log_upgrade "当前连接客户端数: $CONNECTED_CLIENTS"
fi

# 8. 生成升级报告
log_upgrade "===== 生成升级报告 ====="
REPORT_FILE="$BACKUP_DIR/upgrade_report.txt"

cat > "$REPORT_FILE" << EOF
OpenVPN 升级报告
$(date '+%Y-%m-%d %H:%M:%S')

升级信息:
- 原版本: $CURRENT_VERSION
- 新版本: $NEW_VERSION
- 升级时间: $(date '+%Y-%m-%d %H:%M:%S')

备份信息:
- 备份目录: $BACKUP_DIR
- 配置备份: 已完成
- 二进制备份: 已完成

升级结果:
- 包升级: 成功
- 服务启动: 成功
- 配置兼容性: 正常

验证结果:
- 监听端口: 正常
- 当前连接数: $CONNECTED_CLIENTS

注意事项:
1. 备份文件保存在 $BACKUP_DIR
2. 如有问题,可使用备份文件回滚
3. 建议监控服务运行状态
EOF

log_upgrade "升级报告已生成: $REPORT_FILE"
log_upgrade "===== OpenVPN 升级完成 ====="

echo "升级成功!"
echo "原版本: $CURRENT_VERSION"
echo "新版本: $NEW_VERSION"
echo "备份目录: $BACKUP_DIR"
echo "升级报告: $REPORT_FILE"

13.5.2 服务器迁移

以下脚本用于OpenVPN服务器的迁移:

#!/bin/bash
# openvpn_migration.sh

# 设置变量
SOURCE_SERVER="$1"
TARGET_SERVER="$2"
SSH_USER="root"
MIGRATION_DIR="/tmp/openvpn_migration_$(date '+%Y%m%d_%H%M%S')"
LOG_FILE="/var/log/openvpn_migration.log"

echo "===== OpenVPN 服务器迁移工具 ====="

# 检查参数
if [ $# -ne 2 ]; then
    echo "用法: $0 <源服务器IP> <目标服务器IP>"
    exit 1
fi

# 创建日志函数
log_migration() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log_migration "开始迁移: $SOURCE_SERVER -> $TARGET_SERVER"

# 1. 预迁移检查
log_migration "===== 预迁移检查 ====="

# 检查源服务器连接
log_migration "检查源服务器连接..."
if ssh -o ConnectTimeout=10 "$SSH_USER@$SOURCE_SERVER" "echo 'SSH连接正常'" &>/dev/null; then
    log_migration "源服务器 SSH 连接正常"
else
    log_migration "[错误] 无法连接到源服务器"
    exit 1
fi

# 检查目标服务器连接
log_migration "检查目标服务器连接..."
if ssh -o ConnectTimeout=10 "$SSH_USER@$TARGET_SERVER" "echo 'SSH连接正常'" &>/dev/null; then
    log_migration "目标服务器 SSH 连接正常"
else
    log_migration "[错误] 无法连接到目标服务器"
    exit 1
fi

# 检查源服务器 OpenVPN 状态
log_migration "检查源服务器 OpenVPN 状态..."
SOURCE_OPENVPN_STATUS=$(ssh "$SSH_USER@$SOURCE_SERVER" "systemctl is-active openvpn@server 2>/dev/null || echo 'inactive'")
log_migration "源服务器 OpenVPN 状态: $SOURCE_OPENVPN_STATUS"

# 检查目标服务器是否已安装 OpenVPN
log_migration "检查目标服务器 OpenVPN 安装状态..."
TARGET_OPENVPN_INSTALLED=$(ssh "$SSH_USER@$TARGET_SERVER" "which openvpn >/dev/null 2>&1 && echo 'installed' || echo 'not_installed'")
log_migration "目标服务器 OpenVPN 安装状态: $TARGET_OPENVPN_INSTALLED"

# 2. 创建迁移目录
log_migration "===== 创建迁移目录 ====="
mkdir -p "$MIGRATION_DIR"
ssh "$SSH_USER@$TARGET_SERVER" "mkdir -p $MIGRATION_DIR"

# 3. 从源服务器导出配置和数据
log_migration "===== 从源服务器导出数据 ====="

# 导出 OpenVPN 配置
log_migration "导出 OpenVPN 配置..."
ssh "$SSH_USER@$SOURCE_SERVER" "tar -czf $MIGRATION_DIR/openvpn_config.tar.gz -C /etc openvpn"
if [ $? -eq 0 ]; then
    log_migration "配置导出成功"
else
    log_migration "[错误] 配置导出失败"
    exit 1
fi

# 导出证书和密钥
log_migration "导出证书和密钥..."
ssh "$SSH_USER@$SOURCE_SERVER" "tar -czf $MIGRATION_DIR/openvpn_pki.tar.gz -C /etc/openvpn easy-rsa pki 2>/dev/null || tar -czf $MIGRATION_DIR/openvpn_pki.tar.gz -C /etc/openvpn *.crt *.key *.pem dh*.pem 2>/dev/null"

# 导出日志文件
log_migration "导出日志文件..."
ssh "$SSH_USER@$SOURCE_SERVER" "tar -czf $MIGRATION_DIR/openvpn_logs.tar.gz -C /var/log openvpn 2>/dev/null || echo '日志目录不存在'"

# 导出系统配置
log_migration "导出系统配置..."
ssh "$SSH_USER@$SOURCE_SERVER" "cat > $MIGRATION_DIR/system_info.txt << EOF
# 系统信息
OS版本: \$(cat /etc/os-release | grep PRETTY_NAME)
OpenVPN版本: \$(openvpn --version | head -1)
内核版本: \$(uname -r)
IP转发: \$(cat /proc/sys/net/ipv4/ip_forward)

# 网络配置
\$(ip addr show)

# 防火墙规则
\$(iptables -L -n)
\$(iptables -t nat -L -n)

# 路由表
\$(ip route show)
EOF"

# 4. 传输数据到目标服务器
log_migration "===== 传输数据到目标服务器 ====="

# 传输配置文件
log_migration "传输配置文件..."
scp "$SSH_USER@$SOURCE_SERVER:$MIGRATION_DIR/openvpn_config.tar.gz" "$MIGRATION_DIR/"
scp "$MIGRATION_DIR/openvpn_config.tar.gz" "$SSH_USER@$TARGET_SERVER:$MIGRATION_DIR/"

# 传输证书文件
log_migration "传输证书文件..."
scp "$SSH_USER@$SOURCE_SERVER:$MIGRATION_DIR/openvpn_pki.tar.gz" "$MIGRATION_DIR/"
scp "$MIGRATION_DIR/openvpn_pki.tar.gz" "$SSH_USER@$TARGET_SERVER:$MIGRATION_DIR/"

# 传输系统信息
log_migration "传输系统信息..."
scp "$SSH_USER@$SOURCE_SERVER:$MIGRATION_DIR/system_info.txt" "$MIGRATION_DIR/"
scp "$MIGRATION_DIR/system_info.txt" "$SSH_USER@$TARGET_SERVER:$MIGRATION_DIR/"

# 5. 在目标服务器上安装和配置
log_migration "===== 在目标服务器上配置 ====="

# 安装 OpenVPN(如果未安装)
if [ "$TARGET_OPENVPN_INSTALLED" = "not_installed" ]; then
    log_migration "在目标服务器上安装 OpenVPN..."
    ssh "$SSH_USER@$TARGET_SERVER" "apt update && apt install -y openvpn easy-rsa || yum install -y openvpn easy-rsa"
fi

# 停止目标服务器上的 OpenVPN 服务(如果正在运行)
log_migration "停止目标服务器上的 OpenVPN 服务..."
ssh "$SSH_USER@$TARGET_SERVER" "systemctl stop openvpn@server 2>/dev/null || true"

# 备份目标服务器现有配置
log_migration "备份目标服务器现有配置..."
ssh "$SSH_USER@$TARGET_SERVER" "[ -d /etc/openvpn ] && mv /etc/openvpn /etc/openvpn.backup.\$(date '+%Y%m%d_%H%M%S') || true"

# 解压配置文件
log_migration "解压配置文件..."
ssh "$SSH_USER@$TARGET_SERVER" "cd / && tar -xzf $MIGRATION_DIR/openvpn_config.tar.gz"
ssh "$SSH_USER@$TARGET_SERVER" "cd /etc/openvpn && tar -xzf $MIGRATION_DIR/openvpn_pki.tar.gz"

# 设置正确的文件权限
log_migration "设置文件权限..."
ssh "$SSH_USER@$TARGET_SERVER" "chown -R root:root /etc/openvpn && chmod -R 600 /etc/openvpn/*.key"

# 6. 配置系统环境
log_migration "===== 配置系统环境 ====="

# 启用 IP 转发
log_migration "启用 IP 转发..."
ssh "$SSH_USER@$TARGET_SERVER" "echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf && sysctl -p"

# 配置防火墙规则
log_migration "配置防火墙规则..."
ssh "$SSH_USER@$TARGET_SERVER" "cat > /tmp/setup_firewall.sh << 'EOF'
#!/bin/bash
# 获取主网络接口
MAIN_INTERFACE=\$(ip route | grep default | awk '{print \$5}' | head -1)

# 清除现有规则
iptables -F
iptables -t nat -F

# 设置基本策略
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT

# 允许 OpenVPN 流量
iptables -A INPUT -i tun+ -j ACCEPT
iptables -A FORWARD -i tun+ -j ACCEPT
iptables -A FORWARD -i tun+ -o \$MAIN_INTERFACE -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i \$MAIN_INTERFACE -o tun+ -m state --state RELATED,ESTABLISHED -j ACCEPT

# NAT 规则
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o \$MAIN_INTERFACE -j MASQUERADE

# 允许 OpenVPN 端口
iptables -A INPUT -p udp --dport 1194 -j ACCEPT

# 保存规则
if command -v iptables-save >/dev/null; then
    iptables-save > /etc/iptables/rules.v4 2>/dev/null || iptables-save > /etc/iptables.rules 2>/dev/null
fi
EOF"

ssh "$SSH_USER@$TARGET_SERVER" "chmod +x /tmp/setup_firewall.sh && /tmp/setup_firewall.sh"

# 7. 启动服务并验证
log_migration "===== 启动服务并验证 ====="

# 启动 OpenVPN 服务
log_migration "启动 OpenVPN 服务..."
ssh "$SSH_USER@$TARGET_SERVER" "systemctl enable openvpn@server && systemctl start openvpn@server"

# 等待服务启动
sleep 10

# 检查服务状态
TARGET_SERVICE_STATUS=$(ssh "$SSH_USER@$TARGET_SERVER" "systemctl is-active openvpn@server 2>/dev/null || echo 'inactive'")
log_migration "目标服务器 OpenVPN 状态: $TARGET_SERVICE_STATUS"

if [ "$TARGET_SERVICE_STATUS" = "active" ]; then
    log_migration "OpenVPN 服务启动成功"
else
    log_migration "[错误] OpenVPN 服务启动失败"
    # 查看错误日志
    ssh "$SSH_USER@$TARGET_SERVER" "journalctl -u openvpn@server --no-pager -n 20"
    exit 1
fi

# 检查监听端口
log_migration "检查监听端口..."
TARGET_LISTENING=$(ssh "$SSH_USER@$TARGET_SERVER" "netstat -ulnp | grep openvpn || ss -ulnp | grep openvpn")
if [ -n "$TARGET_LISTENING" ]; then
    log_migration "服务监听端口正常: $TARGET_LISTENING"
else
    log_migration "[警告] 未检测到监听端口"
fi

# 8. 生成迁移报告
log_migration "===== 生成迁移报告 ====="
REPORT_FILE="$MIGRATION_DIR/migration_report.txt"

cat > "$REPORT_FILE" << EOF
OpenVPN 服务器迁移报告
$(date '+%Y-%m-%d %H:%M:%S')

迁移信息:
- 源服务器: $SOURCE_SERVER
- 目标服务器: $TARGET_SERVER
- 迁移时间: $(date '+%Y-%m-%d %H:%M:%S')

迁移内容:
- 配置文件: 已迁移
- 证书和密钥: 已迁移
- 系统配置: 已应用
- 防火墙规则: 已配置

服务状态:
- 目标服务器状态: $TARGET_SERVICE_STATUS
- 监听端口: 正常

后续步骤:
1. 更新 DNS 记录指向新服务器
2. 通知客户端更新服务器地址
3. 监控新服务器运行状态
4. 确认迁移成功后关闭源服务器

注意事项:
1. 客户端需要重新连接
2. 建议保留源服务器一段时间作为备份
3. 监控新服务器的性能和稳定性
EOF

log_migration "迁移报告已生成: $REPORT_FILE"
log_migration "===== 迁移完成 ====="

echo "迁移成功!"
echo "源服务器: $SOURCE_SERVER"
echo "目标服务器: $TARGET_SERVER"
echo "迁移报告: $REPORT_FILE"
echo ""
echo "后续步骤:"
echo "1. 更新 DNS 记录"
echo "2. 通知客户端"
echo "3. 监控新服务器"

13.6 备份与恢复

13.6.1 自动化备份脚本

以下是一个完整的OpenVPN自动化备份脚本:

#!/bin/bash
# openvpn_backup.sh

# 设置变量
BACKUP_DIR="/var/backups/openvpn"
DATE=$(date '+%Y%m%d_%H%M%S')
BACKUP_NAME="openvpn_backup_$DATE"
BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
LOG_FILE="/var/log/openvpn_backup.log"
RETENTION_DAYS=30
COMPRESSION_LEVEL=6
ENCRYPTION_KEY="/etc/openvpn/backup.key"

# 远程备份配置
REMOTE_BACKUP_ENABLED=false
REMOTE_HOST="backup.example.com"
REMOTE_USER="backup"
REMOTE_PATH="/backups/openvpn"

echo "===== OpenVPN 自动化备份工具 ====="

# 创建日志函数
log_backup() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 检查备份目录
if [ ! -d "$BACKUP_DIR" ]; then
    mkdir -p "$BACKUP_DIR"
    log_backup "创建备份目录: $BACKUP_DIR"
fi

log_backup "开始备份: $BACKUP_NAME"

# 1. 创建备份目录结构
log_backup "===== 创建备份目录结构 ====="
mkdir -p "$BACKUP_PATH"/{config,pki,logs,scripts,system}

# 2. 备份配置文件
log_backup "===== 备份配置文件 ====="
if [ -d "/etc/openvpn" ]; then
    cp -r /etc/openvpn/* "$BACKUP_PATH/config/"
    log_backup "配置文件备份完成"
else
    log_backup "[警告] OpenVPN 配置目录不存在"
fi

# 3. 备份 PKI 证书
log_backup "===== 备份 PKI 证书 ====="
if [ -d "/etc/openvpn/easy-rsa" ]; then
    cp -r /etc/openvpn/easy-rsa/* "$BACKUP_PATH/pki/"
    log_backup "Easy-RSA PKI 备份完成"
elif [ -d "/etc/openvpn/pki" ]; then
    cp -r /etc/openvpn/pki/* "$BACKUP_PATH/pki/"
    log_backup "PKI 目录备份完成"
else
    # 备份单独的证书文件
    for cert_file in /etc/openvpn/*.{crt,key,pem}; do
        if [ -f "$cert_file" ]; then
            cp "$cert_file" "$BACKUP_PATH/pki/"
        fi
    done
    log_backup "证书文件备份完成"
fi

# 4. 备份日志文件
log_backup "===== 备份日志文件 ====="
if [ -d "/var/log/openvpn" ]; then
    cp -r /var/log/openvpn/* "$BACKUP_PATH/logs/" 2>/dev/null
    log_backup "日志文件备份完成"
fi

# 备份系统日志中的 OpenVPN 相关条目
if [ -f "/var/log/syslog" ]; then
    grep -i openvpn /var/log/syslog > "$BACKUP_PATH/logs/syslog_openvpn.log" 2>/dev/null
elif [ -f "/var/log/messages" ]; then
    grep -i openvpn /var/log/messages > "$BACKUP_PATH/logs/messages_openvpn.log" 2>/dev/null
fi

# 5. 备份自定义脚本
log_backup "===== 备份自定义脚本 ====="
for script_dir in /etc/openvpn/scripts /usr/local/bin; do
    if [ -d "$script_dir" ]; then
        find "$script_dir" -name "*openvpn*" -type f -exec cp {} "$BACKUP_PATH/scripts/" \;
    fi
done
log_backup "自定义脚本备份完成"

# 6. 备份系统配置
log_backup "===== 备份系统配置 ====="

# 创建系统信息文件
cat > "$BACKUP_PATH/system/system_info.txt" << EOF
# 备份时间
$(date '+%Y-%m-%d %H:%M:%S')

# 系统信息
OS版本: $(cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2 | tr -d '"')
OpenVPN版本: $(openvpn --version | head -1)
内核版本: $(uname -r)
IP转发状态: $(cat /proc/sys/net/ipv4/ip_forward)

# 网络配置
$(ip addr show)

# 路由表
$(ip route show)

# 防火墙规则
$(iptables -L -n)

# NAT 规则
$(iptables -t nat -L -n)

# 系统服务状态
$(systemctl status openvpn@server --no-pager 2>/dev/null || echo "服务未运行")
EOF

# 备份网络配置文件
for net_file in /etc/sysctl.conf /etc/hosts /etc/resolv.conf; do
    if [ -f "$net_file" ]; then
        cp "$net_file" "$BACKUP_PATH/system/"
    fi
done

# 备份防火墙规则
if command -v iptables-save >/dev/null; then
    iptables-save > "$BACKUP_PATH/system/iptables.rules"
fi

log_backup "系统配置备份完成"

# 7. 创建备份清单
log_backup "===== 创建备份清单 ====="
find "$BACKUP_PATH" -type f -exec ls -la {} \; > "$BACKUP_PATH/backup_manifest.txt"
echo "备份创建时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$BACKUP_PATH/backup_manifest.txt"
echo "备份大小: $(du -sh $BACKUP_PATH | cut -f1)" >> "$BACKUP_PATH/backup_manifest.txt"

# 8. 压缩备份
log_backup "===== 压缩备份 ====="
cd "$BACKUP_DIR"
tar -czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME"
if [ $? -eq 0 ]; then
    rm -rf "$BACKUP_PATH"
    BACKUP_SIZE=$(du -sh "${BACKUP_NAME}.tar.gz" | cut -f1)
    log_backup "备份压缩完成,大小: $BACKUP_SIZE"
else
    log_backup "[错误] 备份压缩失败"
    exit 1
fi

# 9. 加密备份(可选)
if [ -f "$ENCRYPTION_KEY" ]; then
    log_backup "===== 加密备份 ====="
    gpg --cipher-algo AES256 --compress-algo 1 --symmetric --output "${BACKUP_NAME}.tar.gz.gpg" "${BACKUP_NAME}.tar.gz"
    if [ $? -eq 0 ]; then
        rm "${BACKUP_NAME}.tar.gz"
        log_backup "备份加密完成"
    else
        log_backup "[警告] 备份加密失败,保留未加密版本"
    fi
fi

# 10. 远程备份(可选)
if [ "$REMOTE_BACKUP_ENABLED" = "true" ]; then
    log_backup "===== 远程备份 ====="
    
    # 检查远程连接
    if ssh -o ConnectTimeout=10 "$REMOTE_USER@$REMOTE_HOST" "echo 'SSH连接正常'" &>/dev/null; then
        # 创建远程目录
        ssh "$REMOTE_USER@$REMOTE_HOST" "mkdir -p $REMOTE_PATH"
        
        # 传输备份文件
        BACKUP_FILE=$(ls ${BACKUP_NAME}.tar.gz* | head -1)
        scp "$BACKUP_FILE" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"
        
        if [ $? -eq 0 ]; then
            log_backup "远程备份完成: $REMOTE_HOST:$REMOTE_PATH/$BACKUP_FILE"
        else
            log_backup "[错误] 远程备份失败"
        fi
    else
        log_backup "[错误] 无法连接到远程备份服务器"
    fi
fi

# 11. 清理旧备份
log_backup "===== 清理旧备份 ====="
find "$BACKUP_DIR" -name "openvpn_backup_*.tar.gz*" -mtime +$RETENTION_DAYS -delete
CLEANED_COUNT=$(find "$BACKUP_DIR" -name "openvpn_backup_*.tar.gz*" -mtime +$RETENTION_DAYS | wc -l)
log_backup "清理了 $CLEANED_COUNT 个超过 $RETENTION_DAYS 天的旧备份"

# 12. 验证备份完整性
log_backup "===== 验证备份完整性 ====="
BACKUP_FILE=$(ls ${BACKUP_NAME}.tar.gz* | head -1)
if [ -f "$BACKUP_FILE" ]; then
    if [[ "$BACKUP_FILE" == *.gpg ]]; then
        # 验证加密文件
        gpg --list-packets "$BACKUP_FILE" >/dev/null 2>&1
        if [ $? -eq 0 ]; then
            log_backup "加密备份文件完整性验证通过"
        else
            log_backup "[错误] 加密备份文件损坏"
        fi
    else
        # 验证压缩文件
        tar -tzf "$BACKUP_FILE" >/dev/null 2>&1
        if [ $? -eq 0 ]; then
            log_backup "备份文件完整性验证通过"
        else
            log_backup "[错误] 备份文件损坏"
        fi
    fi
fi

# 13. 生成备份报告
log_backup "===== 生成备份报告 ====="
REPORT_FILE="$BACKUP_DIR/backup_report_$DATE.txt"

cat > "$REPORT_FILE" << EOF
OpenVPN 备份报告
$(date '+%Y-%m-%d %H:%M:%S')

备份信息:
- 备份名称: $BACKUP_NAME
- 备份文件: $BACKUP_FILE
- 备份大小: $(du -sh "$BACKUP_FILE" | cut -f1)
- 备份时间: $(date '+%Y-%m-%d %H:%M:%S')

备份内容:
- 配置文件: 已备份
- PKI 证书: 已备份
- 日志文件: 已备份
- 自定义脚本: 已备份
- 系统配置: 已备份

备份状态:
- 压缩: 成功
- 加密: $([ -f "$ENCRYPTION_KEY" ] && echo "已启用" || echo "未启用")
- 远程备份: $([ "$REMOTE_BACKUP_ENABLED" = "true" ] && echo "已启用" || echo "未启用")
- 完整性验证: 通过

保留策略:
- 保留天数: $RETENTION_DAYS 天
- 当前备份数: $(ls -1 $BACKUP_DIR/openvpn_backup_*.tar.gz* 2>/dev/null | wc -l)

注意事项:
1. 备份文件包含敏感信息,请妥善保管
2. 定期测试备份恢复流程
3. 监控备份存储空间
EOF

log_backup "备份报告已生成: $REPORT_FILE"
log_backup "===== 备份完成 ====="

echo "备份成功!"
echo "备份文件: $BACKUP_FILE"
echo "备份大小: $(du -sh "$BACKUP_FILE" | cut -f1)"
echo "备份报告: $REPORT_FILE"

13.6.2 恢复脚本

以下是OpenVPN系统恢复脚本:

#!/bin/bash
# openvpn_restore.sh

# 设置变量
BACKUP_FILE="$1"
RESTORE_DIR="/tmp/openvpn_restore_$(date '+%Y%m%d_%H%M%S')"
LOG_FILE="/var/log/openvpn_restore.log"
OPENVPN_SERVICE="openvpn@server"

echo "===== OpenVPN 系统恢复工具 ====="

# 检查参数
if [ $# -ne 1 ]; then
    echo "用法: $0 <备份文件路径>"
    echo "示例: $0 /var/backups/openvpn/openvpn_backup_20240101_120000.tar.gz"
    exit 1
fi

# 创建日志函数
log_restore() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log_restore "开始恢复: $BACKUP_FILE"

# 1. 预恢复检查
log_restore "===== 预恢复检查 ====="

# 检查备份文件是否存在
if [ ! -f "$BACKUP_FILE" ]; then
    log_restore "[错误] 备份文件不存在: $BACKUP_FILE"
    exit 1
fi

log_restore "备份文件存在: $BACKUP_FILE"
BACKUP_SIZE=$(du -sh "$BACKUP_FILE" | cut -f1)
log_restore "备份文件大小: $BACKUP_SIZE"

# 检查备份文件类型
if [[ "$BACKUP_FILE" == *.gpg ]]; then
    log_restore "检测到加密备份文件"
    ENCRYPTED=true
    
    # 验证加密文件
    if ! gpg --list-packets "$BACKUP_FILE" >/dev/null 2>&1; then
        log_restore "[错误] 加密备份文件损坏或无法读取"
        exit 1
    fi
else
    log_restore "检测到普通备份文件"
    ENCRYPTED=false
    
    # 验证压缩文件
    if ! tar -tzf "$BACKUP_FILE" >/dev/null 2>&1; then
        log_restore "[错误] 备份文件损坏或格式不正确"
        exit 1
    fi
fi

# 检查磁盘空间
AVAILABLE_SPACE=$(df /tmp | tail -1 | awk '{print $4}')
if [ "$AVAILABLE_SPACE" -lt 1048576 ]; then  # 1GB
    log_restore "[警告] /tmp 可用空间不足 1GB"
else
    log_restore "磁盘空间充足"
fi

# 2. 创建恢复目录
log_restore "===== 创建恢复目录 ====="
mkdir -p "$RESTORE_DIR"
log_restore "恢复目录: $RESTORE_DIR"

# 3. 解压备份文件
log_restore "===== 解压备份文件 ====="

if [ "$ENCRYPTED" = "true" ]; then
    log_restore "解密备份文件..."
    gpg --decrypt "$BACKUP_FILE" | tar -xzf - -C "$RESTORE_DIR"
    EXTRACT_RESULT=$?
else
    log_restore "解压备份文件..."
    tar -xzf "$BACKUP_FILE" -C "$RESTORE_DIR"
    EXTRACT_RESULT=$?
fi

if [ $EXTRACT_RESULT -eq 0 ]; then
    log_restore "备份文件解压成功"
else
    log_restore "[错误] 备份文件解压失败"
    exit 1
fi

# 查找解压后的目录
BACKUP_CONTENT_DIR=$(find "$RESTORE_DIR" -maxdepth 1 -type d -name "openvpn_backup_*" | head -1)
if [ -z "$BACKUP_CONTENT_DIR" ]; then
    log_restore "[错误] 未找到备份内容目录"
    exit 1
fi

log_restore "备份内容目录: $BACKUP_CONTENT_DIR"

# 4. 验证备份内容
log_restore "===== 验证备份内容 ====="

# 检查必要的目录
for dir in config pki system; do
    if [ -d "$BACKUP_CONTENT_DIR/$dir" ]; then
        log_restore "发现 $dir 目录"
    else
        log_restore "[警告] 缺少 $dir 目录"
    fi
done

# 检查关键文件
if [ -f "$BACKUP_CONTENT_DIR/config/server.conf" ]; then
    log_restore "发现服务器配置文件"
else
    log_restore "[警告] 未找到服务器配置文件"
fi

# 5. 停止当前服务
log_restore "===== 停止当前服务 ====="

if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
    log_restore "停止 OpenVPN 服务..."
    systemctl stop "$OPENVPN_SERVICE"
    sleep 5
    
    if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
        log_restore "[错误] 无法停止 OpenVPN 服务"
        exit 1
    else
        log_restore "OpenVPN 服务已停止"
    fi
else
    log_restore "OpenVPN 服务未运行"
fi

# 6. 备份当前配置
log_restore "===== 备份当前配置 ====="

CURRENT_BACKUP_DIR="/var/backups/openvpn/pre_restore_$(date '+%Y%m%d_%H%M%S')"
mkdir -p "$CURRENT_BACKUP_DIR"

if [ -d "/etc/openvpn" ]; then
    cp -r /etc/openvpn "$CURRENT_BACKUP_DIR/"
    log_restore "当前配置已备份到: $CURRENT_BACKUP_DIR"
fi

# 7. 恢复配置文件
log_restore "===== 恢复配置文件 ====="

# 删除现有配置
if [ -d "/etc/openvpn" ]; then
    rm -rf /etc/openvpn
    log_restore "删除现有配置目录"
fi

# 恢复配置文件
if [ -d "$BACKUP_CONTENT_DIR/config" ]; then
    mkdir -p /etc/openvpn
    cp -r "$BACKUP_CONTENT_DIR/config"/* /etc/openvpn/
    log_restore "配置文件恢复完成"
fi

# 恢复 PKI 证书
if [ -d "$BACKUP_CONTENT_DIR/pki" ]; then
    cp -r "$BACKUP_CONTENT_DIR/pki"/* /etc/openvpn/
    log_restore "PKI 证书恢复完成"
fi

# 设置正确的文件权限
chown -R root:root /etc/openvpn
chmod -R 600 /etc/openvpn/*.key 2>/dev/null
chmod 644 /etc/openvpn/*.conf 2>/dev/null
chmod 644 /etc/openvpn/*.crt 2>/dev/null
log_restore "文件权限设置完成"

# 8. 恢复系统配置
log_restore "===== 恢复系统配置 ====="

# 恢复网络配置
if [ -f "$BACKUP_CONTENT_DIR/system/sysctl.conf" ]; then
    # 备份当前 sysctl.conf
    cp /etc/sysctl.conf /etc/sysctl.conf.backup.$(date '+%Y%m%d_%H%M%S') 2>/dev/null
    
    # 确保 IP 转发启用
    if ! grep -q "net.ipv4.ip_forward = 1" /etc/sysctl.conf; then
        echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
    fi
    sysctl -p
    log_restore "网络配置恢复完成"
fi

# 恢复防火墙规则
if [ -f "$BACKUP_CONTENT_DIR/system/iptables.rules" ]; then
    log_restore "恢复防火墙规则..."
    iptables-restore < "$BACKUP_CONTENT_DIR/system/iptables.rules" 2>/dev/null
    log_restore "防火墙规则恢复完成"
else
    log_restore "设置基本防火墙规则..."
    # 设置基本防火墙规则
    MAIN_INTERFACE=$(ip route | grep default | awk '{print $5}' | head -1)
    
    iptables -A INPUT -i tun+ -j ACCEPT
    iptables -A FORWARD -i tun+ -j ACCEPT
    iptables -A FORWARD -i tun+ -o "$MAIN_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT
    iptables -A FORWARD -i "$MAIN_INTERFACE" -o tun+ -m state --state RELATED,ESTABLISHED -j ACCEPT
    iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o "$MAIN_INTERFACE" -j MASQUERADE
    iptables -A INPUT -p udp --dport 1194 -j ACCEPT
    
    log_restore "基本防火墙规则设置完成"
fi

# 9. 启动服务
log_restore "===== 启动服务 ====="

# 启用并启动 OpenVPN 服务
systemctl enable "$OPENVPN_SERVICE"
systemctl start "$OPENVPN_SERVICE"

# 等待服务启动
sleep 10

# 检查服务状态
if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
    log_restore "OpenVPN 服务启动成功"
else
    log_restore "[错误] OpenVPN 服务启动失败"
    
    # 查看错误日志
    log_restore "查看服务错误信息:"
    systemctl status "$OPENVPN_SERVICE" --no-pager | while read line; do
        log_restore "  $line"
    done
    
    # 尝试恢复原始配置
    log_restore "尝试恢复原始配置..."
    if [ -d "$CURRENT_BACKUP_DIR/openvpn" ]; then
        rm -rf /etc/openvpn
        cp -r "$CURRENT_BACKUP_DIR/openvpn" /etc/
        systemctl start "$OPENVPN_SERVICE"
        
        if systemctl is-active --quiet "$OPENVPN_SERVICE"; then
            log_restore "原始配置恢复成功"
        else
            log_restore "[严重错误] 原始配置恢复失败,需要手动干预"
        fi
    fi
    exit 1
fi

# 10. 验证恢复结果
log_restore "===== 验证恢复结果 ====="

# 检查监听端口
if netstat -ulnp | grep -q openvpn; then
    LISTENING_PORT=$(netstat -ulnp | grep openvpn | awk '{print $4}')
    log_restore "服务监听端口: $LISTENING_PORT"
else
    log_restore "[警告] 未检测到监听端口"
fi

# 检查 TUN 接口
sleep 5
if ip addr show | grep -q tun0; then
    TUN_IP=$(ip addr show tun0 | grep inet | awk '{print $2}' | head -1)
    log_restore "TUN 接口已创建: $TUN_IP"
else
    log_restore "[警告] TUN 接口未创建"
fi

# 检查日志
if [ -f "/var/log/openvpn/openvpn.log" ]; then
    RECENT_LOGS=$(tail -5 /var/log/openvpn/openvpn.log)
    log_restore "最近日志条目:"
    echo "$RECENT_LOGS" | while read line; do
        log_restore "  $line"
    done
fi

# 11. 清理临时文件
log_restore "===== 清理临时文件 ====="
rm -rf "$RESTORE_DIR"
log_restore "临时文件清理完成"

# 12. 生成恢复报告
log_restore "===== 生成恢复报告 ====="
REPORT_FILE="/var/log/openvpn_restore_report_$(date '+%Y%m%d_%H%M%S').txt"

cat > "$REPORT_FILE" << EOF
OpenVPN 系统恢复报告
$(date '+%Y-%m-%d %H:%M:%S')

恢复信息:
- 备份文件: $BACKUP_FILE
- 备份大小: $BACKUP_SIZE
- 恢复时间: $(date '+%Y-%m-%d %H:%M:%S')
- 加密状态: $([ "$ENCRYPTED" = "true" ] && echo "已加密" || echo "未加密")

恢复内容:
- 配置文件: 已恢复
- PKI 证书: 已恢复
- 系统配置: 已恢复
- 防火墙规则: 已恢复

服务状态:
- OpenVPN 服务: $(systemctl is-active $OPENVPN_SERVICE)
- 监听端口: $(netstat -ulnp | grep openvpn | awk '{print $4}' || echo "未检测到")
- TUN 接口: $(ip addr show tun0 >/dev/null 2>&1 && echo "已创建" || echo "未创建")

备份信息:
- 原配置备份: $CURRENT_BACKUP_DIR

注意事项:
1. 客户端可能需要重新连接
2. 监控服务运行状态
3. 验证客户端连接功能
4. 原配置已备份,如有问题可回滚
EOF

log_restore "恢复报告已生成: $REPORT_FILE"
log_restore "===== 恢复完成 ====="

echo "恢复成功!"
echo "备份文件: $BACKUP_FILE"
echo "服务状态: $(systemctl is-active $OPENVPN_SERVICE)"
echo "恢复报告: $REPORT_FILE"
echo ""
echo "后续步骤:"
echo "1. 验证客户端连接"
echo "2. 监控服务状态"
echo "3. 检查日志文件"

13.7 本章总结

本章详细介绍了OpenVPN的故障排除与维护,涵盖了以下主要内容:

13.7.1 故障排除体系

  1. 常见问题诊断

    • 连接问题:网络连通性、端口访问、防火墙配置
    • 认证问题:证书验证、用户认证、权限检查
    • 路由问题:路由表配置、网关设置、流量转发
    • DNS问题:域名解析、DNS服务器配置
  2. 系统维护策略

    • 日常维护计划:定期检查、日志轮转、性能监控
    • 定期检查项目:服务状态、证书有效期、系统资源
    • 维护窗口管理:计划维护、服务重启、配置更新
  3. 性能问题排查

    • 带宽问题诊断:MTU优化、协议选择、压缩设置
    • 延迟问题分析:网络路径、DNS解析、系统负载
    • 资源使用监控:CPU、内存、网络接口统计

13.7.2 升级与迁移

  1. 版本升级策略

    • 预升级检查:服务状态、配置文件、证书文件
    • 备份创建:配置备份、二进制备份、系统信息
    • 升级执行:包管理器升级、配置兼容性验证
    • 升级验证:服务启动、端口监听、客户端连接
  2. 服务器迁移

    • 迁移准备:连接检查、状态确认、目录创建
    • 数据传输:配置文件、证书密钥、系统信息
    • 环境配置:系统安装、网络配置、防火墙设置
    • 服务验证:启动测试、端口检查、功能验证

13.7.3 备份与恢复

  1. 自动化备份

    • 备份内容:配置文件、PKI证书、日志文件、系统配置
    • 备份策略:压缩存储、加密保护、远程备份
    • 保留管理:定期清理、空间监控、完整性验证
  2. 系统恢复

    • 恢复准备:文件验证、空间检查、服务停止
    • 配置恢复:文件解压、权限设置、系统配置
    • 服务启动:服务启用、状态检查、功能验证

13.7.4 最佳实践建议

  1. 预防性维护

    • 建立定期维护计划
    • 实施监控和告警机制
    • 保持系统和软件更新
    • 定期备份和恢复测试
  2. 故障响应流程

    • 建立故障分类和优先级
    • 制定标准化诊断流程
    • 准备应急响应预案
    • 记录和分析故障模式
  3. 文档和培训

    • 维护详细的操作文档
    • 建立知识库和FAQ
    • 定期进行技能培训
    • 分享最佳实践经验

通过本章的学习,读者应该能够: - 快速诊断和解决常见的OpenVPN问题 - 建立有效的维护和监控体系 - 安全地执行系统升级和迁移 - 实施可靠的备份和恢复策略 - 优化OpenVPN的性能和稳定性

这些技能对于维护生产环境中的OpenVPN服务至关重要,能够确保VPN服务的高可用性和可靠性。