本章概述

本章将详细介绍OpenVPN客户端的配置与连接方法,涵盖不同操作系统平台的客户端安装、配置文件生成、连接脚本编写以及常见连接问题的排查。通过本章的学习,您将能够为各种客户端设备配置OpenVPN连接。

flowchart TD
    A[客户端配置] --> B[Windows客户端]
    A --> C[Linux客户端]
    A --> D[macOS客户端]
    A --> E[移动端客户端]
    
    B --> B1[GUI客户端]
    B --> B2[命令行客户端]
    
    C --> C1[NetworkManager]
    C --> C2[命令行配置]
    
    D --> D1[Tunnelblick]
    D --> D2[官方客户端]
    
    E --> E1[Android]
    E --> E2[iOS]
    
    F[配置管理] --> F1[证书分发]
    F --> F2[配置生成]
    F --> F3[批量部署]

5.1 客户端证书生成

5.1.1 批量证书生成脚本

#!/bin/bash
# OpenVPN客户端证书批量生成脚本

# 配置变量
EASY_RSA_DIR="/etc/openvpn/easy-rsa"
CLIENT_CONFIG_DIR="/etc/openvpn/client-configs"
BASE_CONFIG="/etc/openvpn/client-configs/base.conf"
KEY_DIR="$EASY_RSA_DIR/pki"

# 创建客户端配置目录
create_client_config_dir() {
    echo "创建客户端配置目录..."
    
    mkdir -p "$CLIENT_CONFIG_DIR/files"
    mkdir -p "$CLIENT_CONFIG_DIR/keys"
    
    echo "客户端配置目录创建完成"
}

# 创建基础配置模板
create_base_config() {
    echo "创建基础配置模板..."
    
    cat > "$BASE_CONFIG" << 'EOF'
##############################################
# OpenVPN客户端配置模板
##############################################

# 指定这是客户端
client

# 使用tun设备
dev tun

# 协议类型(与服务端保持一致)
proto udp

# 服务端地址和端口
remote YOUR_SERVER_IP 1194

# 保持尝试连接
resolv-retry infinite

# 不绑定本地端口
nobind

# 持久化选项
persist-key
persist-tun

# 验证服务端证书
remote-cert-tls server

# 加密算法(与服务端保持一致)
cipher AES-256-GCM

# 认证算法
auth SHA256

# 启用数据压缩
comp-lzo

# 日志级别
verb 3

# 静默重复消息
mute 20

EOF

    echo "基础配置模板创建完成"
}

# 生成客户端证书
generate_client_cert() {
    local client_name="$1"
    
    if [ -z "$client_name" ]; then
        echo "错误:请提供客户端名称"
        return 1
    fi
    
    echo "为客户端 $client_name 生成证书..."
    
    cd "$EASY_RSA_DIR"
    
    # 生成客户端私钥和证书请求
    ./easyrsa gen-req "$client_name" nopass
    
    # 签署客户端证书
    ./easyrsa sign-req client "$client_name"
    
    if [ $? -eq 0 ]; then
        echo "✓ 客户端 $client_name 证书生成成功"
        
        # 复制证书到客户端配置目录
        cp "$KEY_DIR/issued/${client_name}.crt" "$CLIENT_CONFIG_DIR/keys/"
        cp "$KEY_DIR/private/${client_name}.key" "$CLIENT_CONFIG_DIR/keys/"
        
        return 0
    else
        echo "✗ 客户端 $client_name 证书生成失败"
        return 1
    fi
}

# 生成客户端配置文件
generate_client_config() {
    local client_name="$1"
    local server_ip="$2"
    
    if [ -z "$client_name" ] || [ -z "$server_ip" ]; then
        echo "错误:请提供客户端名称和服务端IP地址"
        echo "用法: generate_client_config <客户端名称> <服务端IP>"
        return 1
    fi
    
    local output_file="$CLIENT_CONFIG_DIR/files/${client_name}.ovpn"
    
    echo "为客户端 $client_name 生成配置文件..."
    
    # 复制基础配置
    cp "$BASE_CONFIG" "$output_file"
    
    # 替换服务端IP
    sed -i "s/YOUR_SERVER_IP/$server_ip/g" "$output_file"
    
    # 添加证书内容
    {
        echo ""
        echo "<ca>"
        cat "$KEY_DIR/ca.crt"
        echo "</ca>"
        
        echo ""
        echo "<cert>"
        cat "$KEY_DIR/issued/${client_name}.crt"
        echo "</cert>"
        
        echo ""
        echo "<key>"
        cat "$KEY_DIR/private/${client_name}.key"
        echo "</key>"
        
        echo ""
        echo "<tls-auth>"
        cat "$KEY_DIR/ta.key"
        echo "</tls-auth>"
        
        echo "key-direction 1"
        
    } >> "$output_file"
    
    echo "✓ 客户端配置文件生成完成: $output_file"
}

# 批量生成客户端
batch_generate_clients() {
    local server_ip="$1"
    local client_list="$2"
    
    if [ -z "$server_ip" ] || [ -z "$client_list" ]; then
        echo "错误:请提供服务端IP和客户端列表文件"
        echo "用法: batch_generate_clients <服务端IP> <客户端列表文件>"
        return 1
    fi
    
    if [ ! -f "$client_list" ]; then
        echo "错误:客户端列表文件不存在: $client_list"
        return 1
    fi
    
    echo "开始批量生成客户端配置..."
    
    local success_count=0
    local total_count=0
    
    while IFS= read -r client_name; do
        # 跳过空行和注释行
        if [[ -z "$client_name" || "$client_name" =~ ^#.* ]]; then
            continue
        fi
        
        total_count=$((total_count + 1))
        
        echo "\n处理客户端: $client_name"
        
        # 生成证书
        if generate_client_cert "$client_name"; then
            # 生成配置文件
            if generate_client_config "$client_name" "$server_ip"; then
                success_count=$((success_count + 1))
                echo "✓ 客户端 $client_name 配置完成"
            else
                echo "✗ 客户端 $client_name 配置文件生成失败"
            fi
        else
            echo "✗ 客户端 $client_name 证书生成失败"
        fi
        
    done < "$client_list"
    
    echo "\n=== 批量生成完成 ==="
    echo "总计: $total_count 个客户端"
    echo "成功: $success_count 个客户端"
    echo "失败: $((total_count - success_count)) 个客户端"
}

# 撤销客户端证书
revoke_client_cert() {
    local client_name="$1"
    
    if [ -z "$client_name" ]; then
        echo "错误:请提供客户端名称"
        return 1
    fi
    
    echo "撤销客户端 $client_name 的证书..."
    
    cd "$EASY_RSA_DIR"
    
    # 撤销证书
    ./easyrsa revoke "$client_name"
    
    # 生成CRL
    ./easyrsa gen-crl
    
    # 复制CRL到OpenVPN目录
    cp "$KEY_DIR/crl.pem" /etc/openvpn/server/
    
    # 删除客户端配置文件
    rm -f "$CLIENT_CONFIG_DIR/files/${client_name}.ovpn"
    rm -f "$CLIENT_CONFIG_DIR/keys/${client_name}.crt"
    rm -f "$CLIENT_CONFIG_DIR/keys/${client_name}.key"
    
    echo "✓ 客户端 $client_name 证书已撤销"
    echo "请重启OpenVPN服务以应用CRL更新"
}

# 列出所有客户端
list_clients() {
    echo "=== 已生成的客户端配置 ==="
    
    if [ -d "$CLIENT_CONFIG_DIR/files" ]; then
        local count=0
        for config_file in "$CLIENT_CONFIG_DIR/files"/*.ovpn; do
            if [ -f "$config_file" ]; then
                local client_name=$(basename "$config_file" .ovpn)
                local file_size=$(du -h "$config_file" | cut -f1)
                local mod_time=$(stat -c %y "$config_file" | cut -d' ' -f1)
                
                echo "$client_name (大小: $file_size, 修改时间: $mod_time)"
                count=$((count + 1))
            fi
        done
        
        echo "\n总计: $count 个客户端配置"
    else
        echo "客户端配置目录不存在"
    fi
}

# 创建客户端列表示例
create_client_list_example() {
    local example_file="$CLIENT_CONFIG_DIR/client_list_example.txt"
    
    cat > "$example_file" << 'EOF'
# OpenVPN客户端列表示例
# 每行一个客户端名称,支持注释

# 管理员客户端
admin-laptop
admin-phone

# 开发团队
dev-john
dev-jane
dev-bob

# 销售团队
sales-alice
sales-charlie

# 远程办公
remote-worker1
remote-worker2
EOF

    echo "客户端列表示例已创建: $example_file"
}

# 主函数
main() {
    case "$1" in
        "init")
            create_client_config_dir
            create_base_config
            create_client_list_example
            echo "客户端配置环境初始化完成"
            ;;
        "generate")
            if [ -n "$3" ]; then
                generate_client_cert "$2" && generate_client_config "$2" "$3"
            else
                echo "用法: $0 generate <客户端名称> <服务端IP>"
            fi
            ;;
        "batch")
            if [ -n "$3" ]; then
                batch_generate_clients "$2" "$3"
            else
                echo "用法: $0 batch <服务端IP> <客户端列表文件>"
            fi
            ;;
        "revoke")
            if [ -n "$2" ]; then
                revoke_client_cert "$2"
            else
                echo "用法: $0 revoke <客户端名称>"
            fi
            ;;
        "list")
            list_clients
            ;;
        *)
            echo "OpenVPN客户端管理脚本"
            echo "用法: $0 {init|generate|batch|revoke|list}"
            echo "  init                              - 初始化客户端配置环境"
            echo "  generate <客户端名称> <服务端IP>    - 生成单个客户端配置"
            echo "  batch <服务端IP> <客户端列表文件>   - 批量生成客户端配置"
            echo "  revoke <客户端名称>               - 撤销客户端证书"
            echo "  list                             - 列出所有客户端配置"
            exit 1
            ;;
    esac
}

# 执行主函数
main "$@"

5.1.2 客户端配置管理器

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OpenVPN客户端配置管理器
提供客户端配置的生成、管理和分发功能
"""

import os
import json
import shutil
import zipfile
from datetime import datetime
from typing import Dict, List, Optional
from pathlib import Path

class ClientConfigManager:
    """客户端配置管理器"""
    
    def __init__(self, 
                 config_dir: str = "/etc/openvpn/client-configs",
                 easy_rsa_dir: str = "/etc/openvpn/easy-rsa"):
        self.config_dir = Path(config_dir)
        self.easy_rsa_dir = Path(easy_rsa_dir)
        self.files_dir = self.config_dir / "files"
        self.keys_dir = self.config_dir / "keys"
        self.templates_dir = self.config_dir / "templates"
        
        # 创建必要的目录
        self._create_directories()
    
    def _create_directories(self):
        """创建必要的目录"""
        for directory in [self.config_dir, self.files_dir, self.keys_dir, self.templates_dir]:
            directory.mkdir(parents=True, exist_ok=True)
    
    def create_config_template(self, template_name: str, config: Dict[str, any]) -> bool:
        """创建配置模板"""
        try:
            template_file = self.templates_dir / f"{template_name}.json"
            
            template_data = {
                'name': template_name,
                'description': config.get('description', ''),
                'created_at': datetime.now().isoformat(),
                'config': config
            }
            
            with open(template_file, 'w', encoding='utf-8') as f:
                json.dump(template_data, f, indent=2, ensure_ascii=False)
            
            return True
        except Exception as e:
            print(f"创建配置模板失败: {e}")
            return False
    
    def get_config_templates(self) -> List[Dict[str, any]]:
        """获取所有配置模板"""
        templates = []
        
        for template_file in self.templates_dir.glob("*.json"):
            try:
                with open(template_file, 'r', encoding='utf-8') as f:
                    template_data = json.load(f)
                    templates.append(template_data)
            except Exception as e:
                print(f"读取模板文件失败 {template_file}: {e}")
        
        return templates
    
    def generate_client_config(self, 
                             client_name: str,
                             server_ip: str,
                             template_name: str = 'default',
                             custom_options: Optional[Dict[str, any]] = None) -> bool:
        """生成客户端配置文件"""
        try:
            # 加载模板
            template = self._load_template(template_name)
            if not template:
                print(f"模板 {template_name} 不存在,使用默认配置")
                template = self._get_default_template()
            
            # 合并自定义选项
            if custom_options:
                template['config'].update(custom_options)
            
            # 生成配置文件内容
            config_content = self._build_config_content(
                client_name, server_ip, template['config']
            )
            
            # 保存配置文件
            output_file = self.files_dir / f"{client_name}.ovpn"
            with open(output_file, 'w', encoding='utf-8') as f:
                f.write(config_content)
            
            print(f"✓ 客户端配置文件生成成功: {output_file}")
            return True
            
        except Exception as e:
            print(f"生成客户端配置失败: {e}")
            return False
    
    def _load_template(self, template_name: str) -> Optional[Dict[str, any]]:
        """加载配置模板"""
        template_file = self.templates_dir / f"{template_name}.json"
        
        if not template_file.exists():
            return None
        
        try:
            with open(template_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception:
            return None
    
    def _get_default_template(self) -> Dict[str, any]:
        """获取默认模板"""
        return {
            'name': 'default',
            'description': '默认客户端配置',
            'config': {
                'port': 1194,
                'proto': 'udp',
                'dev': 'tun',
                'cipher': 'AES-256-GCM',
                'auth': 'SHA256',
                'comp_lzo': True,
                'persist_key': True,
                'persist_tun': True,
                'nobind': True,
                'resolv_retry': 'infinite',
                'remote_cert_tls': 'server',
                'verb': 3,
                'mute': 20
            }
        }
    
    def _build_config_content(self, client_name: str, server_ip: str, config: Dict[str, any]) -> str:
        """构建配置文件内容"""
        lines = [
            "##############################################",
            f"# OpenVPN客户端配置 - {client_name}",
            f"# 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
            "##############################################",
            "",
            "# 指定这是客户端",
            "client",
            ""
        ]
        
        # 基本配置
        lines.extend([
            f"# 设备类型",
            f"dev {config.get('dev', 'tun')}",
            "",
            f"# 协议和端口",
            f"proto {config.get('proto', 'udp')}",
            "",
            f"# 服务端地址",
            f"remote {server_ip} {config.get('port', 1194)}",
            ""
        ])
        
        # 连接选项
        if config.get('resolv_retry'):
            lines.append(f"resolv-retry {config['resolv_retry']}")
        
        if config.get('nobind'):
            lines.append("nobind")
        
        if config.get('persist_key'):
            lines.append("persist-key")
        
        if config.get('persist_tun'):
            lines.append("persist-tun")
        
        lines.append("")
        
        # 安全配置
        lines.extend([
            "# 安全配置",
            f"cipher {config.get('cipher', 'AES-256-GCM')}",
            f"auth {config.get('auth', 'SHA256')}"
        ])
        
        if config.get('remote_cert_tls'):
            lines.append(f"remote-cert-tls {config['remote_cert_tls']}")
        
        lines.append("")
        
        # 其他选项
        if config.get('comp_lzo'):
            lines.append("comp-lzo")
        
        lines.extend([
            f"verb {config.get('verb', 3)}",
            f"mute {config.get('mute', 20)}",
            ""
        ])
        
        # 添加证书内容
        cert_content = self._get_certificate_content(client_name)
        if cert_content:
            lines.extend(cert_content)
        
        return "\n".join(lines)
    
    def _get_certificate_content(self, client_name: str) -> List[str]:
        """获取证书内容"""
        lines = []
        
        try:
            # CA证书
            ca_file = self.easy_rsa_dir / "pki" / "ca.crt"
            if ca_file.exists():
                lines.extend(["<ca>", ca_file.read_text().strip(), "</ca>", ""])
            
            # 客户端证书
            cert_file = self.easy_rsa_dir / "pki" / "issued" / f"{client_name}.crt"
            if cert_file.exists():
                lines.extend(["<cert>", cert_file.read_text().strip(), "</cert>", ""])
            
            # 客户端私钥
            key_file = self.easy_rsa_dir / "pki" / "private" / f"{client_name}.key"
            if key_file.exists():
                lines.extend(["<key>", key_file.read_text().strip(), "</key>", ""])
            
            # TLS认证密钥
            ta_file = self.easy_rsa_dir / "pki" / "ta.key"
            if ta_file.exists():
                lines.extend(["<tls-auth>", ta_file.read_text().strip(), "</tls-auth>"])
                lines.append("key-direction 1")
            
        except Exception as e:
            print(f"读取证书文件失败: {e}")
        
        return lines
    
    def create_client_package(self, client_name: str, include_docs: bool = True) -> Optional[str]:
        """创建客户端安装包"""
        try:
            package_dir = self.config_dir / "packages"
            package_dir.mkdir(exist_ok=True)
            
            package_file = package_dir / f"{client_name}_openvpn_config.zip"
            
            with zipfile.ZipFile(package_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
                # 添加配置文件
                config_file = self.files_dir / f"{client_name}.ovpn"
                if config_file.exists():
                    zipf.write(config_file, f"{client_name}.ovpn")
                
                # 添加说明文档
                if include_docs:
                    readme_content = self._generate_readme(client_name)
                    zipf.writestr("README.txt", readme_content)
                    
                    install_guide = self._generate_install_guide()
                    zipf.writestr("安装指南.txt", install_guide)
            
            print(f"✓ 客户端安装包创建成功: {package_file}")
            return str(package_file)
            
        except Exception as e:
            print(f"创建客户端安装包失败: {e}")
            return None
    
    def _generate_readme(self, client_name: str) -> str:
        """生成README文件"""
        return f"""OpenVPN客户端配置包

客户端名称: {client_name}
生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

文件说明:
- {client_name}.ovpn: OpenVPN客户端配置文件
- 安装指南.txt: 详细的安装和使用说明

使用方法:
1. 安装OpenVPN客户端软件
2. 导入{client_name}.ovpn配置文件
3. 连接到VPN服务器

注意事项:
- 请妥善保管此配置文件,不要泄露给他人
- 如遇连接问题,请联系系统管理员
- 定期更新客户端软件以确保安全性
"""
    
    def _generate_install_guide(self) -> str:
        """生成安装指南"""
        return """OpenVPN客户端安装指南

=== Windows系统 ===
1. 下载并安装OpenVPN客户端:https://openvpn.net/community-downloads/
2. 右键点击系统托盘中的OpenVPN图标
3. 选择"Import file..."导入.ovpn配置文件
4. 右键点击配置文件名称,选择"Connect"

=== macOS系统 ===
1. 下载并安装Tunnelblick:https://tunnelblick.net/
2. 双击.ovpn文件,Tunnelblick会自动导入配置
3. 在Tunnelblick中选择配置并点击"Connect"

=== Linux系统 ===
1. 安装OpenVPN:sudo apt install openvpn (Ubuntu/Debian)
2. 复制配置文件到/etc/openvpn/client/
3. 启动连接:sudo openvpn --config /path/to/config.ovpn

=== Android系统 ===
1. 安装OpenVPN Connect应用
2. 导入.ovpn配置文件
3. 点击连接按钮

=== iOS系统 ===
1. 安装OpenVPN Connect应用
2. 通过邮件或其他方式导入配置文件
3. 在应用中连接

=== 常见问题 ===
Q: 连接失败怎么办?
A: 检查网络连接,确认服务器地址和端口正确

Q: 证书过期怎么办?
A: 联系管理员重新生成配置文件

Q: 如何验证连接成功?
A: 访问 https://whatismyipaddress.com/ 查看IP地址是否为VPN服务器IP
"""
    
    def get_client_list(self) -> List[Dict[str, any]]:
        """获取客户端列表"""
        clients = []
        
        for config_file in self.files_dir.glob("*.ovpn"):
            try:
                stat = config_file.stat()
                clients.append({
                    'name': config_file.stem,
                    'file_path': str(config_file),
                    'size': stat.st_size,
                    'created_at': datetime.fromtimestamp(stat.st_ctime).isoformat(),
                    'modified_at': datetime.fromtimestamp(stat.st_mtime).isoformat()
                })
            except Exception as e:
                print(f"读取客户端信息失败 {config_file}: {e}")
        
        return sorted(clients, key=lambda x: x['name'])
    
    def delete_client_config(self, client_name: str) -> bool:
        """删除客户端配置"""
        try:
            config_file = self.files_dir / f"{client_name}.ovpn"
            if config_file.exists():
                config_file.unlink()
            
            # 删除相关的密钥文件
            key_files = [
                self.keys_dir / f"{client_name}.crt",
                self.keys_dir / f"{client_name}.key"
            ]
            
            for key_file in key_files:
                if key_file.exists():
                    key_file.unlink()
            
            print(f"✓ 客户端配置 {client_name} 已删除")
            return True
            
        except Exception as e:
            print(f"删除客户端配置失败: {e}")
            return False

# 使用示例
if __name__ == "__main__":
    manager = ClientConfigManager()
    
    # 创建默认模板
    default_template = {
        'description': '标准客户端配置模板',
        'port': 1194,
        'proto': 'udp',
        'dev': 'tun',
        'cipher': 'AES-256-GCM',
        'auth': 'SHA256',
        'comp_lzo': True,
        'persist_key': True,
        'persist_tun': True,
        'nobind': True,
        'resolv_retry': 'infinite',
        'remote_cert_tls': 'server',
        'verb': 3,
        'mute': 20
    }
    
    manager.create_config_template('default', default_template)
    
    # 创建移动端模板
    mobile_template = default_template.copy()
    mobile_template.update({
        'description': '移动端优化配置模板',
        'proto': 'tcp',  # 移动网络更稳定
        'port': 443,     # 更容易穿透防火墙
        'comp_lzo': False,  # 减少CPU使用
        'verb': 1        # 减少日志输出
    })
    
    manager.create_config_template('mobile', mobile_template)
    
    print("配置模板创建完成")
    
    # 列出所有模板
    templates = manager.get_config_templates()
    print(f"\n可用模板: {[t['name'] for t in templates]}")
    
    # 列出客户端
    clients = manager.get_client_list()
    print(f"\n现有客户端: {[c['name'] for c in clients]}")

5.2 不同平台客户端配置

5.2.1 Windows客户端配置

# Windows OpenVPN客户端配置脚本
# PowerShell脚本用于自动化Windows客户端配置

# 配置变量
$OpenVPNPath = "C:\Program Files\OpenVPN"
$ConfigPath = "$OpenVPNPath\config"
$LogPath = "$env:USERPROFILE\OpenVPN\logs"

# 检查管理员权限
function Test-Administrator {
    $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
    return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

# 下载并安装OpenVPN客户端
function Install-OpenVPNClient {
    Write-Host "检查OpenVPN客户端安装状态..." -ForegroundColor Yellow
    
    if (Test-Path $OpenVPNPath) {
        Write-Host "✓ OpenVPN客户端已安装" -ForegroundColor Green
        return $true
    }
    
    Write-Host "OpenVPN客户端未安装,开始下载..." -ForegroundColor Yellow
    
    $downloadUrl = "https://swupdate.openvpn.org/community/releases/OpenVPN-2.6.8-I001-amd64.msi"
    $installerPath = "$env:TEMP\OpenVPN-installer.msi"
    
    try {
        # 下载安装程序
        Invoke-WebRequest -Uri $downloadUrl -OutFile $installerPath -UseBasicParsing
        
        # 静默安装
        Write-Host "开始安装OpenVPN客户端..." -ForegroundColor Yellow
        Start-Process -FilePath "msiexec.exe" -ArgumentList "/i", $installerPath, "/quiet", "/norestart" -Wait
        
        # 清理安装文件
        Remove-Item $installerPath -Force
        
        if (Test-Path $OpenVPNPath) {
            Write-Host "✓ OpenVPN客户端安装成功" -ForegroundColor Green
            return $true
        } else {
            Write-Host "✗ OpenVPN客户端安装失败" -ForegroundColor Red
            return $false
        }
    }
    catch {
        Write-Host "✗ 下载或安装过程中出错: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

# 配置OpenVPN客户端
function Configure-OpenVPNClient {
    param(
        [string]$ConfigFile,
        [string]$ClientName
    )
    
    Write-Host "配置OpenVPN客户端..." -ForegroundColor Yellow
    
    # 创建配置目录
    if (!(Test-Path $ConfigPath)) {
        New-Item -ItemType Directory -Path $ConfigPath -Force | Out-Null
    }
    
    # 创建日志目录
    if (!(Test-Path $LogPath)) {
        New-Item -ItemType Directory -Path $LogPath -Force | Out-Null
    }
    
    # 复制配置文件
    if (Test-Path $ConfigFile) {
        $targetConfig = "$ConfigPath\$ClientName.ovpn"
        Copy-Item $ConfigFile $targetConfig -Force
        Write-Host "✓ 配置文件已复制到: $targetConfig" -ForegroundColor Green
        
        # 创建连接脚本
        Create-ConnectionScript -ClientName $ClientName
        
        return $true
    } else {
        Write-Host "✗ 配置文件不存在: $ConfigFile" -ForegroundColor Red
        return $false
    }
}

# 创建连接脚本
function Create-ConnectionScript {
    param([string]$ClientName)
    
    $scriptPath = "$env:USERPROFILE\Desktop\Connect-$ClientName.bat"
    
    $scriptContent = @"
@echo off
echo 正在连接到OpenVPN服务器...
echo 客户端: $ClientName
echo.

cd /d "$OpenVPNPath\bin"
openvpn.exe --config "$ConfigPath\$ClientName.ovpn" --log "$LogPath\$ClientName.log"

pause
"@
    
    $scriptContent | Out-File -FilePath $scriptPath -Encoding ASCII
    Write-Host "✓ 连接脚本已创建: $scriptPath" -ForegroundColor Green
}

# 创建断开连接脚本
function Create-DisconnectScript {
    param([string]$ClientName)
    
    $scriptPath = "$env:USERPROFILE\Desktop\Disconnect-$ClientName.bat"
    
    $scriptContent = @"
@echo off
echo 正在断开OpenVPN连接...
echo 客户端: $ClientName
echo.

taskkill /f /im openvpn.exe 2>nul
if %errorlevel% == 0 (
    echo ✓ OpenVPN连接已断开
) else (
    echo ! 没有找到活动的OpenVPN连接
)

pause
"@
    
    $scriptContent | Out-File -FilePath $scriptPath -Encoding ASCII
    Write-Host "✓ 断开连接脚本已创建: $scriptPath" -ForegroundColor Green
}

# 测试连接
function Test-OpenVPNConnection {
    param([string]$ClientName)
    
    Write-Host "测试OpenVPN连接..." -ForegroundColor Yellow
    
    $configFile = "$ConfigPath\$ClientName.ovpn"
    if (!(Test-Path $configFile)) {
        Write-Host "✗ 配置文件不存在: $configFile" -ForegroundColor Red
        return $false
    }
    
    # 检查配置文件语法
    try {
        $testResult = & "$OpenVPNPath\bin\openvpn.exe" --config $configFile --verb 1 --test-crypto
        
        if ($LASTEXITCODE -eq 0) {
            Write-Host "✓ 配置文件语法正确" -ForegroundColor Green
            return $true
        } else {
            Write-Host "✗ 配置文件语法错误" -ForegroundColor Red
            return $false
        }
    }
    catch {
        Write-Host "✗ 测试过程中出错: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

# 创建Windows服务
function Install-OpenVPNService {
    param([string]$ClientName)
    
    if (!(Test-Administrator)) {
        Write-Host "✗ 需要管理员权限来安装服务" -ForegroundColor Red
        return $false
    }
    
    Write-Host "安装OpenVPN Windows服务..." -ForegroundColor Yellow
    
    $serviceName = "OpenVPN-$ClientName"
    $configFile = "$ConfigPath\$ClientName.ovpn"
    $logFile = "$LogPath\$ClientName-service.log"
    
    # 创建服务
    $serviceParams = @{
        Name = $serviceName
        BinaryPathName = "\"$OpenVPNPath\bin\openvpn.exe\" --config \"$configFile\" --log \"$logFile\" --service"
        DisplayName = "OpenVPN Client - $ClientName"
        Description = "OpenVPN客户端服务 - $ClientName"
        StartupType = "Manual"
    }
    
    try {
        New-Service @serviceParams
        Write-Host "✓ OpenVPN服务安装成功: $serviceName" -ForegroundColor Green
        return $true
    }
    catch {
        Write-Host "✗ 服务安装失败: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

# 主函数
function Main {
    param(
        [string]$Action,
        [string]$ConfigFile,
        [string]$ClientName
    )
    
    Write-Host "=== OpenVPN Windows客户端配置工具 ===" -ForegroundColor Cyan
    Write-Host "操作: $Action" -ForegroundColor Cyan
    
    switch ($Action.ToLower()) {
        "install" {
            if (Install-OpenVPNClient) {
                Write-Host "\n✓ OpenVPN客户端准备就绪" -ForegroundColor Green
            } else {
                Write-Host "\n✗ OpenVPN客户端安装失败" -ForegroundColor Red
                exit 1
            }
        }
        
        "configure" {
            if ([string]::IsNullOrEmpty($ConfigFile) -or [string]::IsNullOrEmpty($ClientName)) {
                Write-Host "✗ 请提供配置文件路径和客户端名称" -ForegroundColor Red
                Write-Host "用法: .\script.ps1 configure <配置文件路径> <客户端名称>" -ForegroundColor Yellow
                exit 1
            }
            
            if (Configure-OpenVPNClient -ConfigFile $ConfigFile -ClientName $ClientName) {
                Create-DisconnectScript -ClientName $ClientName
                Write-Host "\n✓ 客户端配置完成" -ForegroundColor Green
            } else {
                Write-Host "\n✗ 客户端配置失败" -ForegroundColor Red
                exit 1
            }
        }
        
        "test" {
            if ([string]::IsNullOrEmpty($ClientName)) {
                Write-Host "✗ 请提供客户端名称" -ForegroundColor Red
                exit 1
            }
            
            if (Test-OpenVPNConnection -ClientName $ClientName) {
                Write-Host "\n✓ 连接测试通过" -ForegroundColor Green
            } else {
                Write-Host "\n✗ 连接测试失败" -ForegroundColor Red
                exit 1
            }
        }
        
        "service" {
            if ([string]::IsNullOrEmpty($ClientName)) {
                Write-Host "✗ 请提供客户端名称" -ForegroundColor Red
                exit 1
            }
            
            if (Install-OpenVPNService -ClientName $ClientName) {
                Write-Host "\n✓ Windows服务安装完成" -ForegroundColor Green
            } else {
                Write-Host "\n✗ Windows服务安装失败" -ForegroundColor Red
                exit 1
            }
        }
        
        default {
            Write-Host "OpenVPN Windows客户端配置工具" -ForegroundColor Yellow
            Write-Host "用法: .\script.ps1 <操作> [参数]" -ForegroundColor Yellow
            Write-Host "\n可用操作:" -ForegroundColor Yellow
            Write-Host "  install                           - 安装OpenVPN客户端" -ForegroundColor White
            Write-Host "  configure <配置文件> <客户端名称>   - 配置客户端" -ForegroundColor White
            Write-Host "  test <客户端名称>                 - 测试连接" -ForegroundColor White
            Write-Host "  service <客户端名称>              - 安装为Windows服务" -ForegroundColor White
            Write-Host "\n示例:" -ForegroundColor Yellow
            Write-Host "  .\script.ps1 install" -ForegroundColor Gray
            Write-Host "  .\script.ps1 configure C:\client.ovpn myclient" -ForegroundColor Gray
            Write-Host "  .\script.ps1 test myclient" -ForegroundColor Gray
        }
    }
}

# 执行主函数
Main -Action $args[0] -ConfigFile $args[1] -ClientName $args[2]

5.2.2 Linux客户端配置

#!/bin/bash
# Linux OpenVPN客户端配置脚本

# 配置变量
OPENVPN_CONFIG_DIR="/etc/openvpn/client"
USER_CONFIG_DIR="$HOME/.openvpn"
LOG_DIR="/var/log/openvpn"
SYSTEMD_DIR="/etc/systemd/system"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 日志函数
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 检查root权限
check_root() {
    if [ "$EUID" -ne 0 ]; then
        log_error "此操作需要root权限,请使用sudo运行"
        return 1
    fi
    return 0
}

# 检测Linux发行版
detect_distro() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        echo "$ID"
    elif [ -f /etc/redhat-release ]; then
        echo "centos"
    elif [ -f /etc/debian_version ]; then
        echo "debian"
    else
        echo "unknown"
    fi
}

# 安装OpenVPN客户端
install_openvpn_client() {
    log_info "检测系统类型并安装OpenVPN客户端..."
    
    local distro=$(detect_distro)
    
    case "$distro" in
        "ubuntu"|"debian")
            log_info "检测到Debian/Ubuntu系统"
            apt update
            apt install -y openvpn resolvconf
            ;;
        "centos"|"rhel"|"fedora")
            log_info "检测到CentOS/RHEL/Fedora系统"
            if command -v dnf > /dev/null; then
                dnf install -y openvpn
            else
                yum install -y openvpn
            fi
            ;;
        "arch")
            log_info "检测到Arch Linux系统"
            pacman -S --noconfirm openvpn
            ;;
        "opensuse")
            log_info "检测到openSUSE系统"
            zypper install -y openvpn
            ;;
        *)
            log_error "不支持的Linux发行版: $distro"
            return 1
            ;;
    esac
    
    if command -v openvpn > /dev/null; then
        log_success "OpenVPN客户端安装成功"
        openvpn --version | head -n 1
        return 0
    else
        log_error "OpenVPN客户端安装失败"
        return 1
    fi
}

# 配置NetworkManager集成
configure_networkmanager() {
    local client_name="$1"
    local config_file="$2"
    
    log_info "配置NetworkManager集成..."
    
    # 检查NetworkManager是否安装
    if ! command -v nmcli > /dev/null; then
        log_warning "NetworkManager未安装,跳过集成配置"
        return 0
    fi
    
    # 安装NetworkManager OpenVPN插件
    local distro=$(detect_distro)
    case "$distro" in
        "ubuntu"|"debian")
            apt install -y network-manager-openvpn network-manager-openvpn-gnome
            ;;
        "centos"|"rhel"|"fedora")
            if command -v dnf > /dev/null; then
                dnf install -y NetworkManager-openvpn NetworkManager-openvpn-gnome
            else
                yum install -y NetworkManager-openvpn NetworkManager-openvpn-gnome
            fi
            ;;
    esac
    
    # 重启NetworkManager
    systemctl restart NetworkManager
    
    log_success "NetworkManager集成配置完成"
    log_info "您现在可以通过图形界面管理VPN连接"
}

# 创建客户端配置
configure_client() {
    local config_file="$1"
    local client_name="$2"
    local use_systemd="$3"
    
    if [ -z "$config_file" ] || [ -z "$client_name" ]; then
        log_error "请提供配置文件路径和客户端名称"
        return 1
    fi
    
    if [ ! -f "$config_file" ]; then
        log_error "配置文件不存在: $config_file"
        return 1
    fi
    
    log_info "配置OpenVPN客户端: $client_name"
    
    # 创建配置目录
    mkdir -p "$OPENVPN_CONFIG_DIR"
    mkdir -p "$USER_CONFIG_DIR"
    mkdir -p "$LOG_DIR"
    
    # 复制配置文件
    local target_config="$OPENVPN_CONFIG_DIR/${client_name}.conf"
    cp "$config_file" "$target_config"
    chmod 600 "$target_config"
    
    log_success "配置文件已复制到: $target_config"
    
    # 创建用户配置副本
    local user_config="$USER_CONFIG_DIR/${client_name}.conf"
    cp "$config_file" "$user_config"
    chown "$SUDO_USER:$SUDO_USER" "$user_config" 2>/dev/null || true
    
    # 配置systemd服务
    if [ "$use_systemd" = "true" ]; then
        create_systemd_service "$client_name"
    fi
    
    # 创建连接脚本
    create_connection_scripts "$client_name"
    
    log_success "客户端配置完成"
}

# 创建systemd服务
create_systemd_service() {
    local client_name="$1"
    
    log_info "创建systemd服务: openvpn-client@${client_name}"
    
    # 创建服务文件
    cat > "$SYSTEMD_DIR/openvpn-client@${client_name}.service" << EOF
[Unit]
Description=OpenVPN Client - %i
After=network-online.target
Wants=network-online.target
Documentation=man:openvpn(8)

[Service]
Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn/client
ExecStart=/usr/sbin/openvpn --suppress-timestamps --nobind --config %i.conf
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
LimitNPROC=100
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true
KillMode=process
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF
    
    # 重新加载systemd
    systemctl daemon-reload
    
    log_success "systemd服务创建完成"
    log_info "使用以下命令管理服务:"
    log_info "  启动: systemctl start openvpn-client@${client_name}"
    log_info "  停止: systemctl stop openvpn-client@${client_name}"
    log_info "  开机自启: systemctl enable openvpn-client@${client_name}"
}

# 创建连接脚本
create_connection_scripts() {
    local client_name="$1"
    
    log_info "创建连接脚本..."
    
    # 创建连接脚本
    cat > "/usr/local/bin/openvpn-connect-${client_name}" << EOF
#!/bin/bash
# OpenVPN连接脚本 - $client_name

echo "正在连接到OpenVPN服务器..."
echo "客户端: $client_name"
echo "配置文件: $OPENVPN_CONFIG_DIR/${client_name}.conf"
echo ""

# 检查是否已经连接
if pgrep -f "openvpn.*${client_name}.conf" > /dev/null; then
    echo "警告: OpenVPN连接已存在"
    echo "使用 openvpn-disconnect-${client_name} 断开现有连接"
    exit 1
fi

# 启动OpenVPN
sudo openvpn --config "$OPENVPN_CONFIG_DIR/${client_name}.conf" --log "/var/log/openvpn/${client_name}.log" --daemon

# 等待连接建立
sleep 3

if pgrep -f "openvpn.*${client_name}.conf" > /dev/null; then
    echo "✓ OpenVPN连接已建立"
    echo "日志文件: /var/log/openvpn/${client_name}.log"
else
    echo "✗ OpenVPN连接失败"
    echo "请检查日志文件: /var/log/openvpn/${client_name}.log"
    exit 1
fi
EOF
    
    # 创建断开连接脚本
    cat > "/usr/local/bin/openvpn-disconnect-${client_name}" << EOF
#!/bin/bash
# OpenVPN断开连接脚本 - $client_name

echo "正在断开OpenVPN连接..."
echo "客户端: $client_name"
echo ""

# 查找并终止OpenVPN进程
PID=\$(pgrep -f "openvpn.*${client_name}.conf")

if [ -n "\$PID" ]; then
    sudo kill \$PID
    sleep 2
    
    # 强制终止(如果需要)
    if pgrep -f "openvpn.*${client_name}.conf" > /dev/null; then
        sudo kill -9 \$PID
    fi
    
    echo "✓ OpenVPN连接已断开"
else
    echo "! 没有找到活动的OpenVPN连接"
fi
EOF
    
    # 创建状态检查脚本
    cat > "/usr/local/bin/openvpn-status-${client_name}" << EOF
#!/bin/bash
# OpenVPN状态检查脚本 - $client_name

echo "=== OpenVPN连接状态 - $client_name ==="
echo ""

# 检查进程状态
PID=\$(pgrep -f "openvpn.*${client_name}.conf")

if [ -n "\$PID" ]; then
    echo "状态: 已连接"
    echo "进程ID: \$PID"
    echo "运行时间: \$(ps -o etime= -p \$PID | tr -d ' ')"
else
    echo "状态: 未连接"
fi

echo ""

# 显示网络接口信息
echo "=== 网络接口信息 ==="
ip addr show tun0 2>/dev/null || echo "tun0接口不存在"

echo ""

# 显示路由信息
echo "=== 路由信息 ==="
ip route | grep tun0 || echo "没有找到tun0相关路由"

echo ""

# 显示最近的日志
echo "=== 最近的日志 ==="
echo "最近10行日志:"
tail -n 10 "/var/log/openvpn/${client_name}.log" 2>/dev/null || echo "日志文件不存在"
EOF
    
    # 设置执行权限
    chmod +x "/usr/local/bin/openvpn-connect-${client_name}"
    chmod +x "/usr/local/bin/openvpn-disconnect-${client_name}"
    chmod +x "/usr/local/bin/openvpn-status-${client_name}"
    
    log_success "连接脚本创建完成:"
    log_info "  连接: openvpn-connect-${client_name}"
    log_info "  断开: openvpn-disconnect-${client_name}"
    log_info "  状态: openvpn-status-${client_name}"
}

# 测试连接
test_connection() {
    local client_name="$1"
    
    if [ -z "$client_name" ]; then
        log_error "请提供客户端名称"
        return 1
    fi
    
    local config_file="$OPENVPN_CONFIG_DIR/${client_name}.conf"
    
    if [ ! -f "$config_file" ]; then
        log_error "配置文件不存在: $config_file"
        return 1
    fi
    
    log_info "测试OpenVPN配置: $client_name"
    
    # 检查配置文件语法
    if openvpn --config "$config_file" --verb 1 --test-crypto; then
        log_success "配置文件语法正确"
    else
        log_error "配置文件语法错误"
        return 1
    fi
    
    # 检查证书有效性
    log_info "检查证书有效性..."
    
    # 提取证书信息
    local cert_info=$(openssl x509 -in <(grep -A 100 '<cert>' "$config_file" | grep -B 100 '</cert>' | grep -v '<cert>' | grep -v '</cert>') -text -noout 2>/dev/null)
    
    if [ $? -eq 0 ]; then
        log_success "客户端证书有效"
        echo "证书信息:"
        echo "$cert_info" | grep -E "Subject:|Not Before:|Not After:"
    else
        log_warning "无法验证客户端证书"
    fi
    
    return 0
}

# 主函数
main() {
    case "$1" in
        "install")
            if check_root; then
                install_openvpn_client
            fi
            ;;
        "configure")
            if check_root; then
                configure_client "$2" "$3" "$4"
            fi
            ;;
        "networkmanager")
            if check_root; then
                configure_networkmanager "$2" "$3"
            fi
            ;;
        "test")
            test_connection "$2"
            ;;
        "connect")
            if [ -n "$2" ]; then
                "/usr/local/bin/openvpn-connect-$2"
            else
                log_error "请提供客户端名称"
            fi
            ;;
        "disconnect")
            if [ -n "$2" ]; then
                "/usr/local/bin/openvpn-disconnect-$2"
            else
                log_error "请提供客户端名称"
            fi
            ;;
        "status")
            if [ -n "$2" ]; then
                "/usr/local/bin/openvpn-status-$2"
            else
                log_error "请提供客户端名称"
            fi
            ;;
        *)
            echo "OpenVPN Linux客户端配置工具"
            echo "用法: $0 {install|configure|networkmanager|test|connect|disconnect|status}"
            echo ""
            echo "操作说明:"
            echo "  install                                    - 安装OpenVPN客户端"
            echo "  configure <配置文件> <客户端名称> [systemd] - 配置客户端"
            echo "  networkmanager <客户端名称> <配置文件>      - 配置NetworkManager集成"
            echo "  test <客户端名称>                          - 测试配置"
            echo "  connect <客户端名称>                       - 连接VPN"
            echo "  disconnect <客户端名称>                    - 断开VPN"
            echo "  status <客户端名称>                        - 查看状态"
            echo ""
            echo "示例:"
            echo "  $0 install"
            echo "  $0 configure /path/to/client.ovpn myclient true"
            echo "  $0 test myclient"
            echo "  $0 connect myclient"
            exit 1
            ;;
    esac
}

# 执行主函数
main "$@"

5.2.3 macOS客户端配置

#!/bin/bash
# macOS OpenVPN客户端配置脚本

# 配置变量
TUNNELBLICK_APP="/Applications/Tunnelblick.app"
OPENVPN_PATH="/usr/local/bin/openvpn"
CONFIG_DIR="$HOME/Library/Application Support/Tunnelblick/Configurations"
LOG_DIR="$HOME/Library/Logs/Tunnelblick"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# 日志函数
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 检查Homebrew
check_homebrew() {
    if ! command -v brew > /dev/null; then
        log_info "安装Homebrew..."
        /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
        
        if [ $? -eq 0 ]; then
            log_success "Homebrew安装成功"
        else
            log_error "Homebrew安装失败"
            return 1
        fi
    else
        log_success "Homebrew已安装"
    fi
    return 0
}

# 安装Tunnelblick
install_tunnelblick() {
    log_info "检查Tunnelblick安装状态..."
    
    if [ -d "$TUNNELBLICK_APP" ]; then
        log_success "Tunnelblick已安装"
        return 0
    fi
    
    log_info "下载并安装Tunnelblick..."
    
    # 使用Homebrew Cask安装
    if check_homebrew; then
        brew install --cask tunnelblick
        
        if [ -d "$TUNNELBLICK_APP" ]; then
            log_success "Tunnelblick安装成功"
            return 0
        else
            log_error "Tunnelblick安装失败"
            return 1
        fi
    else
        log_error "无法安装Homebrew,请手动下载Tunnelblick"
        log_info "下载地址: https://tunnelblick.net/"
        return 1
    fi
}

# 安装OpenVPN命令行工具
install_openvpn_cli() {
    log_info "安装OpenVPN命令行工具..."
    
    if command -v openvpn > /dev/null; then
        log_success "OpenVPN命令行工具已安装"
        return 0
    fi
    
    if check_homebrew; then
        brew install openvpn
        
        if command -v openvpn > /dev/null; then
            log_success "OpenVPN命令行工具安装成功"
            return 0
        else
            log_error "OpenVPN命令行工具安装失败"
            return 1
        fi
    else
        return 1
    fi
}

# 配置Tunnelblick
configure_tunnelblick() {
    local config_file="$1"
    local client_name="$2"
    
    if [ -z "$config_file" ] || [ -z "$client_name" ]; then
        log_error "请提供配置文件路径和客户端名称"
        return 1
    fi
    
    if [ ! -f "$config_file" ]; then
        log_error "配置文件不存在: $config_file"
        return 1
    fi
    
    log_info "配置Tunnelblick客户端: $client_name"
    
    # 创建配置目录
    mkdir -p "$CONFIG_DIR"
    
    # 创建.tblk包目录
    local tblk_dir="$CONFIG_DIR/${client_name}.tblk"
    mkdir -p "$tblk_dir"
    
    # 复制配置文件
    cp "$config_file" "$tblk_dir/config.ovpn"
    
    # 创建Info.plist文件
    cat > "$tblk_dir/Info.plist" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleIdentifier</key>
    <string>net.tunnelblick.tunnelblick.$client_name</string>
    <key>CFBundleVersion</key>
    <string>1.0</string>
    <key>TBPreferenceVersion</key>
    <string>1</string>
</dict>
</plist>
EOF
    
    log_success "Tunnelblick配置创建完成: $tblk_dir"
    log_info "请双击.tblk文件夹导入到Tunnelblick"
    
    # 自动打开配置目录
    open "$CONFIG_DIR"
    
    return 0
}

# 创建命令行连接脚本
create_cli_scripts() {
    local client_name="$1"
    local config_file="$2"
    
    log_info "创建命令行连接脚本..."
    
    # 创建脚本目录
    local script_dir="$HOME/bin"
    mkdir -p "$script_dir"
    
    # 创建连接脚本
    cat > "$script_dir/openvpn-connect-${client_name}" << EOF
#!/bin/bash
# macOS OpenVPN连接脚本 - $client_name

echo "正在连接到OpenVPN服务器..."
echo "客户端: $client_name"
echo "配置文件: $config_file"
echo ""

# 检查是否已经连接
if pgrep -f "openvpn.*$client_name" > /dev/null; then
    echo "警告: OpenVPN连接已存在"
    echo "使用 openvpn-disconnect-${client_name} 断开现有连接"
    exit 1
fi

# 创建日志目录
mkdir -p "$LOG_DIR"

# 启动OpenVPN(需要sudo权限)
sudo "$OPENVPN_PATH" --config "$config_file" --log "$LOG_DIR/${client_name}.log" --daemon

# 等待连接建立
sleep 3

if pgrep -f "openvpn.*$client_name" > /dev/null; then
    echo "✓ OpenVPN连接已建立"
    echo "日志文件: $LOG_DIR/${client_name}.log"
else
    echo "✗ OpenVPN连接失败"
    echo "请检查日志文件: $LOG_DIR/${client_name}.log"
    exit 1
fi
EOF
    
    # 创建断开连接脚本
    cat > "$script_dir/openvpn-disconnect-${client_name}" << EOF
#!/bin/bash
# macOS OpenVPN断开连接脚本 - $client_name

echo "正在断开OpenVPN连接..."
echo "客户端: $client_name"
echo ""

# 查找并终止OpenVPN进程
PID=\$(pgrep -f "openvpn.*$client_name")

if [ -n "\$PID" ]; then
    sudo kill \$PID
    sleep 2
    
    # 强制终止(如果需要)
    if pgrep -f "openvpn.*$client_name" > /dev/null; then
        sudo kill -9 \$PID
    fi
    
    echo "✓ OpenVPN连接已断开"
else
    echo "! 没有找到活动的OpenVPN连接"
fi
EOF
    
    # 设置执行权限
    chmod +x "$script_dir/openvpn-connect-${client_name}"
    chmod +x "$script_dir/openvpn-disconnect-${client_name}"
    
    log_success "命令行脚本创建完成:"
    log_info "  连接: $script_dir/openvpn-connect-${client_name}"
    log_info "  断开: $script_dir/openvpn-disconnect-${client_name}"
    
    # 添加到PATH(如果需要)
    if [[ ":$PATH:" != *":$script_dir:"* ]]; then
        echo "export PATH=\$PATH:$script_dir" >> "$HOME/.bash_profile"
        echo "export PATH=\$PATH:$script_dir" >> "$HOME/.zshrc"
        log_info "脚本目录已添加到PATH,请重新打开终端或运行 source ~/.bash_profile"
    fi
}

# 主函数
main() {
    case "$1" in
        "install-tunnelblick")
            install_tunnelblick
            ;;
        "install-cli")
            install_openvpn_cli
            ;;
        "configure-tunnelblick")
            configure_tunnelblick "$2" "$3"
            ;;
        "configure-cli")
            if [ -n "$2" ] && [ -n "$3" ]; then
                create_cli_scripts "$3" "$2"
            else
                log_error "请提供配置文件路径和客户端名称"
            fi
            ;;
        *)
            echo "macOS OpenVPN客户端配置工具"
            echo "用法: $0 {install-tunnelblick|install-cli|configure-tunnelblick|configure-cli}"
            echo ""
            echo "操作说明:"
            echo "  install-tunnelblick                        - 安装Tunnelblick"
            echo "  install-cli                                - 安装OpenVPN命令行工具"
            echo "  configure-tunnelblick <配置文件> <客户端名称> - 配置Tunnelblick"
            echo "  configure-cli <配置文件> <客户端名称>        - 配置命令行工具"
            echo ""
            echo "示例:"
            echo "  $0 install-tunnelblick"
            echo "  $0 configure-tunnelblick /path/to/client.ovpn myclient"
            exit 1
            ;;
    esac
}

# 执行主函数
main "$@"

5.3 移动端客户端配置

5.3.1 Android客户端配置指南

# Android OpenVPN客户端配置指南

## 应用选择

### 推荐应用
1. **OpenVPN Connect** (官方应用)
   - 下载地址: Google Play Store
   - 特点: 官方支持,功能完整
   - 适用: 一般用户

2. **OpenVPN for Android** (开源应用)
   - 下载地址: F-Droid / Google Play
   - 特点: 开源免费,功能丰富
   - 适用: 高级用户

## 配置步骤

### 方法一: 导入.ovpn文件
1. 将.ovpn配置文件传输到Android设备
2. 打开OpenVPN应用
3. 点击"导入"或"+"按钮
4. 选择.ovpn文件
5. 确认导入并连接

### 方法二: 手动配置
1. 打开OpenVPN应用
2. 创建新的配置文件
3. 填入服务器信息:
   - 服务器地址
   - 端口号
   - 协议类型
4. 导入证书文件
5. 保存配置

## 优化设置

### 电池优化
```xml
<!-- 在配置文件中添加 -->
<connection>
    <server>your-server.com</server>
    <port>1194</port>
    <proto>udp</proto>
</connection>

<!-- 移动网络优化 -->
<keepalive>10 60</keepalive>
<ping-timer-rem/>
<persist-tun/>

网络切换处理

<!-- 自动重连设置 -->
<resolv-retry>infinite</resolv-retry>
<connect-retry-max>3</connect-retry-max>
<connect-retry>5</connect-retry>

流量节省

<!-- 压缩设置 -->
<comp-lzo>yes</comp-lzo>
<!-- 或使用更新的压缩算法 -->
<compress>lz4</compress>

<!-- 减少心跳频率 -->
<keepalive>30 120</keepalive>

### 5.3.2 iOS客户端配置指南

```markdown
# iOS OpenVPN客户端配置指南

## 应用选择

### 推荐应用
1. **OpenVPN Connect** (官方应用)
   - 下载地址: App Store
   - 特点: 官方支持,稳定可靠
   - 适用: 所有用户

2. **OpenVPN Client** (第三方应用)
   - 下载地址: App Store
   - 特点: 功能丰富,界面友好
   - 适用: 高级用户

## 配置步骤

### 方法一: 通过邮件导入
1. 将.ovpn配置文件作为邮件附件发送到iOS设备
2. 在邮件中点击.ovpn附件
3. 选择"用OpenVPN打开"
4. 确认导入配置
5. 点击连接

### 方法二: 通过iTunes文件共享
1. 连接iOS设备到电脑
2. 打开iTunes,选择设备
3. 进入"文件共享"选项
4. 选择OpenVPN应用
5. 拖拽.ovpn文件到文档列表
6. 在应用中导入配置

### 方法三: 通过云存储
1. 将.ovpn文件上传到iCloud Drive或其他云存储
2. 在iOS设备上打开文件
3. 选择"用OpenVPN打开"
4. 完成导入和配置

## iOS特定优化

### 后台运行优化
```xml
<!-- iOS后台保持连接 -->
<keepalive>10 60</keepalive>
<ping-timer-rem/>
<persist-tun/>
<persist-key/>

网络切换优化

<!-- 处理WiFi/蜂窝网络切换 -->
<resolv-retry>infinite</resolv-retry>
<nobind/>
<float/>

## 5.4 连接测试与验证

### 5.4.1 连接测试脚本

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OpenVPN连接测试工具
用于验证VPN连接的有效性和性能
"""

import os
import sys
import time
import socket
import subprocess
import requests
from typing import Dict, List, Optional, Tuple
from datetime import datetime

class VPNConnectionTester:
    """VPN连接测试器"""
    
    def __init__(self):
        self.test_results = []
        self.start_time = None
        self.original_ip = None
        self.vpn_ip = None
    
    def get_public_ip(self) -> Optional[str]:
        """获取公网IP地址"""
        try:
            # 尝试多个IP查询服务
            services = [
                'https://api.ipify.org',
                'https://ipinfo.io/ip',
                'https://icanhazip.com',
                'https://ident.me'
            ]
            
            for service in services:
                try:
                    response = requests.get(service, timeout=10)
                    if response.status_code == 200:
                        return response.text.strip()
                except:
                    continue
            
            return None
        except Exception as e:
            print(f"获取公网IP失败: {e}")
            return None
    
    def test_dns_resolution(self) -> Dict[str, any]:
        """测试DNS解析"""
        test_domains = [
            'google.com',
            'cloudflare.com',
            'github.com',
            'stackoverflow.com'
        ]
        
        results = {
            'success_count': 0,
            'total_count': len(test_domains),
            'details': [],
            'average_time': 0
        }
        
        total_time = 0
        
        for domain in test_domains:
            start_time = time.time()
            try:
                socket.gethostbyname(domain)
                resolve_time = (time.time() - start_time) * 1000
                results['success_count'] += 1
                results['details'].append({
                    'domain': domain,
                    'status': 'success',
                    'time': f"{resolve_time:.2f}ms"
                })
                total_time += resolve_time
            except Exception as e:
                results['details'].append({
                    'domain': domain,
                    'status': 'failed',
                    'error': str(e)
                })
        
        if results['success_count'] > 0:
            results['average_time'] = f"{total_time / results['success_count']:.2f}ms"
        
        return results
    
    def test_connectivity(self) -> Dict[str, any]:
        """测试网络连通性"""
        test_hosts = [
            ('8.8.8.8', 53),      # Google DNS
            ('1.1.1.1', 53),      # Cloudflare DNS
            ('google.com', 80),   # HTTP
            ('google.com', 443),  # HTTPS
        ]
        
        results = {
            'success_count': 0,
            'total_count': len(test_hosts),
            'details': []
        }
        
        for host, port in test_hosts:
            try:
                start_time = time.time()
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(5)
                result = sock.connect_ex((host, port))
                sock.close()
                
                connect_time = (time.time() - start_time) * 1000
                
                if result == 0:
                    results['success_count'] += 1
                    results['details'].append({
                        'host': f"{host}:{port}",
                        'status': 'success',
                        'time': f"{connect_time:.2f}ms"
                    })
                else:
                    results['details'].append({
                        'host': f"{host}:{port}",
                        'status': 'failed',
                        'error': f"连接失败 (错误码: {result})"
                    })
            except Exception as e:
                results['details'].append({
                    'host': f"{host}:{port}",
                    'status': 'failed',
                    'error': str(e)
                })
        
        return results
    
    def test_speed(self) -> Dict[str, any]:
        """测试网络速度"""
        try:
            # 下载测试文件
            test_url = 'http://speedtest.ftp.otenet.gr/files/test1Mb.db'
            
            start_time = time.time()
            response = requests.get(test_url, timeout=30)
            download_time = time.time() - start_time
            
            if response.status_code == 200:
                file_size = len(response.content)
                speed_mbps = (file_size * 8) / (download_time * 1024 * 1024)
                
                return {
                    'status': 'success',
                    'file_size': f"{file_size / 1024:.2f} KB",
                    'download_time': f"{download_time:.2f}s",
                    'speed': f"{speed_mbps:.2f} Mbps"
                }
            else:
                return {
                    'status': 'failed',
                    'error': f"HTTP {response.status_code}"
                }
        except Exception as e:
            return {
                'status': 'failed',
                'error': str(e)
            }
    
    def test_geolocation(self) -> Dict[str, any]:
        """测试地理位置"""
        try:
            response = requests.get('https://ipinfo.io/json', timeout=10)
            if response.status_code == 200:
                data = response.json()
                return {
                    'status': 'success',
                    'ip': data.get('ip', 'Unknown'),
                    'city': data.get('city', 'Unknown'),
                    'region': data.get('region', 'Unknown'),
                    'country': data.get('country', 'Unknown'),
                    'org': data.get('org', 'Unknown')
                }
            else:
                return {
                    'status': 'failed',
                    'error': f"HTTP {response.status_code}"
                }
        except Exception as e:
            return {
                'status': 'failed',
                'error': str(e)
            }
    
    def check_vpn_interface(self) -> Dict[str, any]:
        """检查VPN网络接口"""
        try:
            if os.name == 'nt':  # Windows
                result = subprocess.run(['ipconfig'], capture_output=True, text=True)
                output = result.stdout
                
                # 查找TAP适配器
                vpn_interfaces = []
                lines = output.split('\n')
                current_adapter = None
                
                for line in lines:
                    if 'TAP-Windows' in line or 'OpenVPN' in line:
                        current_adapter = line.strip()
                    elif current_adapter and 'IPv4' in line:
                        ip = line.split(':')[-1].strip()
                        vpn_interfaces.append({
                            'adapter': current_adapter,
                            'ip': ip
                        })
                        current_adapter = None
            else:  # Linux/macOS
                result = subprocess.run(['ip', 'addr', 'show'], capture_output=True, text=True)
                if result.returncode != 0:
                    result = subprocess.run(['ifconfig'], capture_output=True, text=True)
                
                output = result.stdout
                vpn_interfaces = []
                
                # 查找tun接口
                for line in output.split('\n'):
                    if 'tun' in line and 'inet' in line:
                        parts = line.split()
                        for i, part in enumerate(parts):
                            if part == 'inet' and i + 1 < len(parts):
                                ip = parts[i + 1].split('/')[0]
                                vpn_interfaces.append({
                                    'interface': 'tun',
                                    'ip': ip
                                })
            
            return {
                'status': 'success',
                'interfaces': vpn_interfaces,
                'count': len(vpn_interfaces)
            }
        except Exception as e:
            return {
                'status': 'failed',
                'error': str(e)
            }
    
    def run_comprehensive_test(self) -> Dict[str, any]:
        """运行综合测试"""
        print("开始VPN连接综合测试...")
        print("=" * 50)
        
        self.start_time = datetime.now()
        
        # 获取原始IP
        print("1. 获取当前IP地址...")
        current_ip = self.get_public_ip()
        if current_ip:
            print(f"   当前IP: {current_ip}")
        else:
            print("   ✗ 无法获取当前IP地址")
        
        # 检查VPN接口
        print("\n2. 检查VPN网络接口...")
        interface_result = self.check_vpn_interface()
        if interface_result['status'] == 'success':
            if interface_result['count'] > 0:
                print(f"   ✓ 发现 {interface_result['count']} 个VPN接口")
                for iface in interface_result['interfaces']:
                    print(f"     - {iface}")
            else:
                print("   ✗ 未发现VPN接口")
        else:
            print(f"   ✗ 检查失败: {interface_result['error']}")
        
        # 测试DNS解析
        print("\n3. 测试DNS解析...")
        dns_result = self.test_dns_resolution()
        print(f"   成功率: {dns_result['success_count']}/{dns_result['total_count']}")
        if dns_result['average_time']:
            print(f"   平均响应时间: {dns_result['average_time']}")
        
        # 测试网络连通性
        print("\n4. 测试网络连通性...")
        connectivity_result = self.test_connectivity()
        print(f"   成功率: {connectivity_result['success_count']}/{connectivity_result['total_count']}")
        
        # 测试地理位置
        print("\n5. 测试地理位置...")
        geo_result = self.test_geolocation()
        if geo_result['status'] == 'success':
            print(f"   IP: {geo_result['ip']}")
            print(f"   位置: {geo_result['city']}, {geo_result['region']}, {geo_result['country']}")
            print(f"   ISP: {geo_result['org']}")
        else:
            print(f"   ✗ 获取失败: {geo_result['error']}")
        
        # 测试网络速度
        print("\n6. 测试网络速度...")
        speed_result = self.test_speed()
        if speed_result['status'] == 'success':
            print(f"   下载速度: {speed_result['speed']}")
            print(f"   测试文件: {speed_result['file_size']}")
            print(f"   下载时间: {speed_result['download_time']}")
        else:
            print(f"   ✗ 测试失败: {speed_result['error']}")
        
        # 生成测试报告
        end_time = datetime.now()
        test_duration = (end_time - self.start_time).total_seconds()
        
        report = {
            'timestamp': self.start_time.isoformat(),
            'duration': f"{test_duration:.2f}s",
            'current_ip': current_ip,
            'vpn_interfaces': interface_result,
            'dns_test': dns_result,
            'connectivity_test': connectivity_result,
            'geolocation': geo_result,
            'speed_test': speed_result
        }
        
        print("\n" + "=" * 50)
        print(f"测试完成,总耗时: {test_duration:.2f}秒")
        
        return report
    
    def save_report(self, report: Dict[str, any], filename: str = None):
        """保存测试报告"""
        if not filename:
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            filename = f"vpn_test_report_{timestamp}.json"
        
        try:
            import json
            with open(filename, 'w', encoding='utf-8') as f:
                json.dump(report, f, indent=2, ensure_ascii=False)
            print(f"\n测试报告已保存: {filename}")
        except Exception as e:
            print(f"\n保存报告失败: {e}")

# 使用示例
if __name__ == "__main__":
    tester = VPNConnectionTester()
    
    # 运行综合测试
    report = tester.run_comprehensive_test()
    
    # 保存报告
    tester.save_report(report)
    
    # 简单的连接状态判断
    vpn_active = (
        report['vpn_interfaces']['count'] > 0 and
        report['connectivity_test']['success_count'] > 0 and
        report['dns_test']['success_count'] > 0
    )
    
    if vpn_active:
        print("\n✓ VPN连接状态: 正常")
    else:
        print("\n✗ VPN连接状态: 异常")
        print("请检查VPN配置和网络连接")

5.5 常见问题与故障排除

5.5.1 连接问题排查

#!/bin/bash
# OpenVPN故障排查脚本

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# 日志函数
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 检查OpenVPN进程
check_openvpn_process() {
    log_info "检查OpenVPN进程..."
    
    local processes=$(pgrep -f openvpn)
    
    if [ -n "$processes" ]; then
        log_success "发现OpenVPN进程:"
        ps aux | grep openvpn | grep -v grep
    else
        log_warning "未发现OpenVPN进程"
    fi
}

# 检查网络接口
check_network_interfaces() {
    log_info "检查网络接口..."
    
    # 检查tun接口
    if ip link show | grep -q tun; then
        log_success "发现tun接口:"
        ip addr show | grep -A 3 tun
    else
        log_warning "未发现tun接口"
    fi
    
    # 检查路由表
    log_info "当前路由表:"
    ip route | head -10
}

# 检查防火墙设置
check_firewall() {
    log_info "检查防火墙设置..."
    
    # 检查iptables
    if command -v iptables > /dev/null; then
        log_info "iptables规则 (前10条):"
        iptables -L -n | head -10
    fi
    
    # 检查ufw
    if command -v ufw > /dev/null; then
        log_info "UFW状态:"
        ufw status
    fi
    
    # 检查firewalld
    if command -v firewall-cmd > /dev/null; then
        log_info "firewalld状态:"
        firewall-cmd --state
        firewall-cmd --list-all
    fi
}

# 检查DNS设置
check_dns() {
    log_info "检查DNS设置..."
    
    log_info "当前DNS服务器:"
    cat /etc/resolv.conf
    
    log_info "测试DNS解析:"
    for domain in google.com cloudflare.com; do
        if nslookup $domain > /dev/null 2>&1; then
            log_success "$domain 解析成功"
        else
            log_error "$domain 解析失败"
        fi
    done
}

# 检查证书
check_certificates() {
    local config_file="$1"
    
    if [ -z "$config_file" ] || [ ! -f "$config_file" ]; then
        log_warning "未提供配置文件或文件不存在"
        return 1
    fi
    
    log_info "检查证书有效性..."
    
    # 提取并检查CA证书
    if grep -q '<ca>' "$config_file"; then
        local ca_cert=$(mktemp)
        sed -n '/<ca>/,/<\/ca>/p' "$config_file" | sed '1d;$d' > "$ca_cert"
        
        if openssl x509 -in "$ca_cert" -text -noout > /dev/null 2>&1; then
            log_success "CA证书有效"
            openssl x509 -in "$ca_cert" -subject -dates -noout
        else
            log_error "CA证书无效"
        fi
        
        rm -f "$ca_cert"
    fi
    
    # 提取并检查客户端证书
    if grep -q '<cert>' "$config_file"; then
        local client_cert=$(mktemp)
        sed -n '/<cert>/,/<\/cert>/p' "$config_file" | sed '1d;$d' > "$client_cert"
        
        if openssl x509 -in "$client_cert" -text -noout > /dev/null 2>&1; then
            log_success "客户端证书有效"
            openssl x509 -in "$client_cert" -subject -dates -noout
        else
            log_error "客户端证书无效"
        fi
        
        rm -f "$client_cert"
    fi
}

# 测试服务器连通性
test_server_connectivity() {
    local server_ip="$1"
    local server_port="$2"
    
    if [ -z "$server_ip" ] || [ -z "$server_port" ]; then
        log_warning "未提供服务器地址或端口"
        return 1
    fi
    
    log_info "测试服务器连通性: $server_ip:$server_port"
    
    # 测试ping
    if ping -c 3 "$server_ip" > /dev/null 2>&1; then
        log_success "服务器ping测试成功"
    else
        log_warning "服务器ping测试失败"
    fi
    
    # 测试端口连通性
    if timeout 5 bash -c "</dev/tcp/$server_ip/$server_port"; then
        log_success "服务器端口 $server_port 可达"
    else
        log_error "服务器端口 $server_port 不可达"
    fi
}

# 分析日志文件
analyze_logs() {
    local log_file="$1"
    
    if [ -z "$log_file" ] || [ ! -f "$log_file" ]; then
        log_warning "未提供日志文件或文件不存在"
        return 1
    fi
    
    log_info "分析OpenVPN日志: $log_file"
    
    # 检查常见错误
    local errors=(
        "AUTH_FAILED"
        "TLS_ERROR"
        "RESOLVE"
        "Connection refused"
        "Connection timed out"
        "Certificate verification failed"
    )
    
    for error in "${errors[@]}"; do
        local count=$(grep -c "$error" "$log_file" 2>/dev/null || echo 0)
        if [ "$count" -gt 0 ]; then
            log_error "发现错误 '$error': $count 次"
            grep "$error" "$log_file" | tail -3
        fi
    done
    
    # 显示最近的日志
    log_info "最近的日志条目:"
    tail -10 "$log_file"
}

# 生成诊断报告
generate_diagnostic_report() {
    local config_file="$1"
    local log_file="$2"
    
    local report_file="openvpn_diagnostic_$(date +%Y%m%d_%H%M%S).txt"
    
    {
        echo "OpenVPN诊断报告"
        echo "生成时间: $(date)"
        echo "=" * 50
        
        echo "\n系统信息:"
        uname -a
        
        echo "\nOpenVPN版本:"
        openvpn --version 2>&1 | head -1
        
        echo "\n网络接口:"
        ip addr show
        
        echo "\n路由表:"
        ip route
        
        echo "\nDNS配置:"
        cat /etc/resolv.conf
        
        echo "\n进程信息:"
        ps aux | grep openvpn | grep -v grep
        
        if [ -n "$config_file" ] && [ -f "$config_file" ]; then
            echo "\n配置文件内容:"
            cat "$config_file"
        fi
        
        if [ -n "$log_file" ] && [ -f "$log_file" ]; then
            echo "\n最近日志:"
            tail -50 "$log_file"
        fi
        
    } > "$report_file"
    
    log_success "诊断报告已生成: $report_file"
}

# 主函数
main() {
    case "$1" in
        "process")
            check_openvpn_process
            ;;
        "network")
            check_network_interfaces
            ;;
        "firewall")
            check_firewall
            ;;
        "dns")
            check_dns
            ;;
        "cert")
            check_certificates "$2"
            ;;
        "server")
            test_server_connectivity "$2" "$3"
            ;;
        "logs")
            analyze_logs "$2"
            ;;
        "report")
            generate_diagnostic_report "$2" "$3"
            ;;
        "all")
            log_info "运行完整诊断..."
            check_openvpn_process
            echo
            check_network_interfaces
            echo
            check_firewall
            echo
            check_dns
            echo
            if [ -n "$2" ]; then
                check_certificates "$2"
                echo
            fi
            if [ -n "$3" ]; then
                analyze_logs "$3"
                echo
            fi
            generate_diagnostic_report "$2" "$3"
            ;;
        *)
            echo "OpenVPN故障排查工具"
            echo "用法: $0 {process|network|firewall|dns|cert|server|logs|report|all}"
            echo ""
            echo "操作说明:"
            echo "  process                    - 检查OpenVPN进程"
            echo "  network                    - 检查网络接口和路由"
            echo "  firewall                   - 检查防火墙设置"
            echo "  dns                        - 检查DNS配置"
            echo "  cert <配置文件>            - 检查证书有效性"
            echo "  server <IP> <端口>         - 测试服务器连通性"
            echo "  logs <日志文件>            - 分析日志文件"
            echo "  report [配置文件] [日志文件] - 生成诊断报告"
            echo "  all [配置文件] [日志文件]    - 运行完整诊断"
            echo ""
            echo "示例:"
            echo "  $0 all /etc/openvpn/client.conf /var/log/openvpn.log"
            echo "  $0 server 192.168.1.100 1194"
            exit 1
            ;;
    esac
}

# 执行主函数
main "$@"

5.6 本章小结

本章详细介绍了OpenVPN客户端的配置与连接方法,包括:

  1. 客户端证书生成:批量生成客户端证书和配置文件的脚本
  2. 多平台配置:Windows、Linux、macOS和移动端的配置方法
  3. 连接测试:全面的VPN连接测试和验证工具
  4. 故障排除:常见问题的诊断和解决方案

通过本章的学习,您应该能够: - 为不同平台的客户端生成和配置OpenVPN连接 - 使用自动化脚本简化客户端部署过程 - 进行全面的连接测试和性能评估 - 快速诊断和解决常见的连接问题

下一章我们将学习OpenVPN的高级配置,包括多实例部署、负载均衡和高可用性配置。