本章概述

本章将深入介绍OpenVPN的PKI(公钥基础设施)体系、证书管理、基础配置文件编写和安全参数设置,为构建安全可靠的VPN连接奠定基础。

graph TD
    A[基础配置与证书管理] --> B[PKI体系概述]
    A --> C[CA根证书管理]
    A --> D[服务端证书配置]
    A --> E[客户端证书管理]
    A --> F[配置文件编写]
    A --> G[安全参数设置]
    
    B --> B1[PKI原理]
    B --> B2[证书链结构]
    B --> B3[密钥管理]
    
    C --> C1[CA证书创建]
    C --> C2[CA密钥保护]
    C --> C3[证书撤销列表]
    
    D --> D1[服务端证书生成]
    D --> D2[DH参数生成]
    D --> D3[TLS认证密钥]
    
    E --> E1[客户端证书生成]
    E --> E2[证书分发]
    E --> E3[证书更新]
    
    F --> F1[服务端配置]
    F --> F2[客户端配置]
    F --> F3[配置验证]
    
    G --> G1[加密算法选择]
    G --> G2[认证方式配置]
    G --> G3[安全策略设置]

3.1 PKI体系概述

3.1.1 PKI基本概念

# PKI体系结构说明
class PKIStructure:
    """
    PKI(Public Key Infrastructure)公钥基础设施
    """
    def __init__(self):
        self.components = {
            'ca': 'Certificate Authority - 证书颁发机构',
            'certificate': 'Digital Certificate - 数字证书',
            'private_key': 'Private Key - 私钥',
            'public_key': 'Public Key - 公钥',
            'crl': 'Certificate Revocation List - 证书撤销列表',
            'csr': 'Certificate Signing Request - 证书签名请求'
        }
    
    def explain_certificate_chain(self):
        """
        证书链结构说明
        """
        chain_structure = {
            'root_ca': {
                'description': '根CA证书',
                'purpose': '签发中间CA或直接签发终端证书',
                'security_level': '最高',
                'storage': '离线存储,高度保护'
            },
            'intermediate_ca': {
                'description': '中间CA证书',
                'purpose': '签发终端用户证书',
                'security_level': '高',
                'storage': '在线存储,适度保护'
            },
            'end_entity': {
                'description': '终端实体证书',
                'purpose': '服务端或客户端身份认证',
                'security_level': '中等',
                'storage': '设备本地存储'
            }
        }
        return chain_structure
    
    def certificate_lifecycle(self):
        """
        证书生命周期管理
        """
        lifecycle = {
            'generation': '证书生成',
            'distribution': '证书分发',
            'validation': '证书验证',
            'renewal': '证书更新',
            'revocation': '证书撤销',
            'archival': '证书归档'
        }
        return lifecycle

3.1.2 OpenVPN中的PKI应用

# OpenVPN PKI配置结构
openvpn_pki:
  ca_certificate:
    file: "ca.crt"
    purpose: "验证所有证书的根信任锚点"
    validity: "10年"
    
  server_certificate:
    file: "server.crt"
    key_file: "server.key"
    purpose: "服务端身份认证和加密"
    validity: "1年"
    
  client_certificates:
    files: ["client1.crt", "client2.crt"]
    key_files: ["client1.key", "client2.key"]
    purpose: "客户端身份认证"
    validity: "1年"
    
  dh_parameters:
    file: "dh2048.pem"
    purpose: "Diffie-Hellman密钥交换"
    key_size: 2048
    
  tls_auth:
    file: "ta.key"
    purpose: "TLS握手认证,防止DoS攻击"
    
  crl:
    file: "crl.pem"
    purpose: "证书撤销列表"
    update_frequency: "定期更新"

3.2 CA根证书管理

3.2.1 初始化PKI环境

#!/bin/bash
# PKI环境初始化脚本

# 设置工作目录
PKI_DIR="/etc/openvpn/easy-rsa"
cd $PKI_DIR

# 清理旧的PKI环境(如果存在)
if [ -d "pki" ]; then
    echo "警告: PKI目录已存在,是否要重新初始化?(y/N)"
    read -r response
    if [[ "$response" =~ ^[Yy]$ ]]; then
        rm -rf pki
        echo "已清理旧的PKI环境"
    else
        echo "保留现有PKI环境"
        exit 0
    fi
fi

# 配置Easy-RSA变量
cat > vars << 'EOF'
# Easy-RSA 3.x 变量配置文件

# 证书信息
set_var EASYRSA_REQ_COUNTRY     "CN"
set_var EASYRSA_REQ_PROVINCE    "Beijing"
set_var EASYRSA_REQ_CITY        "Beijing"
set_var EASYRSA_REQ_ORG         "MyCompany Ltd"
set_var EASYRSA_REQ_EMAIL       "admin@mycompany.com"
set_var EASYRSA_REQ_OU          "IT Department"

# 密钥和证书配置
set_var EASYRSA_KEY_SIZE        2048
set_var EASYRSA_ALGO            rsa
set_var EASYRSA_CA_EXPIRE       3650    # CA证书有效期10年
set_var EASYRSA_CERT_EXPIRE     365     # 普通证书有效期1年
set_var EASYRSA_CRL_DAYS        30      # CRL更新周期30天

# 安全配置
set_var EASYRSA_DIGEST          "sha256"
set_var EASYRSA_BATCH           "1"     # 批处理模式
EOF

# 初始化PKI
echo "初始化PKI环境..."
./easyrsa init-pki

echo "PKI环境初始化完成"
echo "配置文件: $PKI_DIR/vars"
echo "PKI目录: $PKI_DIR/pki"

3.2.2 创建CA根证书

#!/bin/bash
# 创建CA根证书

PKI_DIR="/etc/openvpn/easy-rsa"
cd $PKI_DIR

# 加载配置变量
source ./vars

# 创建CA根证书和私钥
echo "创建CA根证书..."
echo "请输入CA证书的通用名称(CN),建议使用: MyCompany-CA"
./easyrsa build-ca

# 验证CA证书
echo "\n=== CA证书信息 ==="
openssl x509 -in pki/ca.crt -text -noout | grep -A 5 "Subject:"
openssl x509 -in pki/ca.crt -text -noout | grep -A 2 "Validity"

# 设置CA私钥权限
chmod 600 pki/private/ca.key
echo "CA私钥权限已设置为600"

# 备份CA证书和私钥
echo "\n创建CA证书备份..."
mkdir -p /root/ca-backup
cp pki/ca.crt /root/ca-backup/
cp pki/private/ca.key /root/ca-backup/
echo "CA证书已备份到 /root/ca-backup/"

echo "\nCA根证书创建完成!"
echo "CA证书位置: $PKI_DIR/pki/ca.crt"
echo "CA私钥位置: $PKI_DIR/pki/private/ca.key"
echo "\n重要提醒: 请妥善保管CA私钥,建议离线存储!"

3.2.3 CA证书安全管理

#!/usr/bin/env python3
# CA证书安全管理工具

import os
import shutil
import hashlib
import datetime
from pathlib import Path

class CASecurityManager:
    def __init__(self, pki_dir="/etc/openvpn/easy-rsa/pki"):
        self.pki_dir = Path(pki_dir)
        self.backup_dir = Path("/root/ca-backup")
        self.ca_cert = self.pki_dir / "ca.crt"
        self.ca_key = self.pki_dir / "private" / "ca.key"
    
    def verify_ca_integrity(self):
        """
        验证CA证书完整性
        """
        if not self.ca_cert.exists():
            return False, "CA证书文件不存在"
        
        if not self.ca_key.exists():
            return False, "CA私钥文件不存在"
        
        # 验证证书和私钥匹配
        try:
            import subprocess
            
            # 获取证书公钥指纹
            cert_cmd = f"openssl x509 -in {self.ca_cert} -pubkey -noout | openssl rsa -pubin -outform DER | openssl dgst -sha256"
            cert_result = subprocess.run(cert_cmd, shell=True, capture_output=True, text=True)
            
            # 获取私钥公钥指纹
            key_cmd = f"openssl rsa -in {self.ca_key} -pubout -outform DER | openssl dgst -sha256"
            key_result = subprocess.run(key_cmd, shell=True, capture_output=True, text=True)
            
            if cert_result.stdout == key_result.stdout:
                return True, "CA证书和私钥匹配"
            else:
                return False, "CA证书和私钥不匹配"
                
        except Exception as e:
            return False, f"验证过程出错: {e}"
    
    def backup_ca_files(self):
        """
        备份CA文件
        """
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_subdir = self.backup_dir / f"backup_{timestamp}"
        backup_subdir.mkdir(parents=True, exist_ok=True)
        
        # 备份CA证书
        if self.ca_cert.exists():
            shutil.copy2(self.ca_cert, backup_subdir / "ca.crt")
        
        # 备份CA私钥
        if self.ca_key.exists():
            shutil.copy2(self.ca_key, backup_subdir / "ca.key")
            # 设置严格权限
            os.chmod(backup_subdir / "ca.key", 0o600)
        
        # 创建校验和文件
        self._create_checksums(backup_subdir)
        
        return backup_subdir
    
    def _create_checksums(self, backup_dir):
        """
        创建文件校验和
        """
        checksum_file = backup_dir / "checksums.txt"
        
        with open(checksum_file, 'w') as f:
            for file_path in backup_dir.glob("*.crt"):
                checksum = self._calculate_sha256(file_path)
                f.write(f"{checksum}  {file_path.name}\n")
            
            for file_path in backup_dir.glob("*.key"):
                checksum = self._calculate_sha256(file_path)
                f.write(f"{checksum}  {file_path.name}\n")
    
    def _calculate_sha256(self, file_path):
        """
        计算文件SHA256校验和
        """
        sha256_hash = hashlib.sha256()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                sha256_hash.update(chunk)
        return sha256_hash.hexdigest()
    
    def secure_ca_key(self):
        """
        加强CA私钥安全
        """
        if self.ca_key.exists():
            # 设置严格权限
            os.chmod(self.ca_key, 0o600)
            
            # 设置文件属性(仅限Linux)
            try:
                import subprocess
                subprocess.run(["chattr", "+i", str(self.ca_key)], check=True)
                return True, "CA私钥已设置为不可修改"
            except (subprocess.CalledProcessError, FileNotFoundError):
                return False, "无法设置文件不可修改属性"
        
        return False, "CA私钥文件不存在"
    
    def generate_security_report(self):
        """
        生成安全报告
        """
        report = {
            'timestamp': datetime.datetime.now().isoformat(),
            'ca_cert_exists': self.ca_cert.exists(),
            'ca_key_exists': self.ca_key.exists(),
            'ca_cert_permissions': oct(os.stat(self.ca_cert).st_mode)[-3:] if self.ca_cert.exists() else None,
            'ca_key_permissions': oct(os.stat(self.ca_key).st_mode)[-3:] if self.ca_key.exists() else None,
        }
        
        # 验证完整性
        integrity_ok, integrity_msg = self.verify_ca_integrity()
        report['integrity_check'] = {
            'status': integrity_ok,
            'message': integrity_msg
        }
        
        return report

if __name__ == "__main__":
    manager = CASecurityManager()
    
    # 验证CA完整性
    integrity_ok, msg = manager.verify_ca_integrity()
    print(f"CA完整性检查: {msg}")
    
    # 备份CA文件
    if integrity_ok:
        backup_dir = manager.backup_ca_files()
        print(f"CA文件已备份到: {backup_dir}")
    
    # 生成安全报告
    report = manager.generate_security_report()
    print("\n=== CA安全报告 ===")
    for key, value in report.items():
        print(f"{key}: {value}")

3.3 服务端证书配置

3.3.1 生成服务端证书

#!/bin/bash
# 生成OpenVPN服务端证书

PKI_DIR="/etc/openvpn/easy-rsa"
SERVER_NAME="openvpn-server"

cd $PKI_DIR

# 加载配置变量
source ./vars

echo "=== 生成OpenVPN服务端证书 ==="

# 1. 生成服务端证书请求和私钥
echo "1. 生成服务端证书请求..."
./easyrsa gen-req $SERVER_NAME nopass

# 2. 签发服务端证书
echo "2. 签发服务端证书..."
./easyrsa sign-req server $SERVER_NAME

# 3. 生成Diffie-Hellman参数
echo "3. 生成DH参数(这可能需要几分钟)..."
./easyrsa gen-dh

# 4. 生成TLS认证密钥
echo "4. 生成TLS认证密钥..."
openvpn --genkey --secret pki/ta.key

# 5. 复制证书到OpenVPN目录
echo "5. 复制证书文件..."
mkdir -p /etc/openvpn/server
cp pki/ca.crt /etc/openvpn/server/
cp pki/issued/$SERVER_NAME.crt /etc/openvpn/server/
cp pki/private/$SERVER_NAME.key /etc/openvpn/server/
cp pki/dh.pem /etc/openvpn/server/
cp pki/ta.key /etc/openvpn/server/

# 6. 设置文件权限
echo "6. 设置文件权限..."
chmod 644 /etc/openvpn/server/ca.crt
chmod 644 /etc/openvpn/server/$SERVER_NAME.crt
chmod 600 /etc/openvpn/server/$SERVER_NAME.key
chmod 644 /etc/openvpn/server/dh.pem
chmod 600 /etc/openvpn/server/ta.key

# 7. 验证证书
echo "\n=== 服务端证书验证 ==="
echo "证书主题信息:"
openssl x509 -in /etc/openvpn/server/$SERVER_NAME.crt -subject -noout

echo "\n证书有效期:"
openssl x509 -in /etc/openvpn/server/$SERVER_NAME.crt -dates -noout

echo "\n证书指纹:"
openssl x509 -in /etc/openvpn/server/$SERVER_NAME.crt -fingerprint -noout

echo "\n=== 服务端证书生成完成 ==="
echo "证书文件位置:"
echo "  CA证书: /etc/openvpn/server/ca.crt"
echo "  服务端证书: /etc/openvpn/server/$SERVER_NAME.crt"
echo "  服务端私钥: /etc/openvpn/server/$SERVER_NAME.key"
echo "  DH参数: /etc/openvpn/server/dh.pem"
echo "  TLS认证密钥: /etc/openvpn/server/ta.key"

3.3.2 服务端配置文件

#!/bin/bash
# 创建OpenVPN服务端配置文件

SERVER_CONFIG="/etc/openvpn/server/server.conf"
SERVER_NAME="openvpn-server"

cat > $SERVER_CONFIG << EOF
# OpenVPN服务端配置文件
# 生成时间: $(date)

#################################################
# 基本配置
#################################################

# 监听端口和协议
port 1194
proto udp

# 设备类型(tun为路由模式,tap为桥接模式)
dev tun

# 证书和密钥文件
ca ca.crt
cert $SERVER_NAME.crt
key $SERVER_NAME.key
dh dh.pem

# TLS认证(防止DoS攻击)
tls-auth ta.key 0

#################################################
# 网络配置
#################################################

# VPN子网配置(服务端IP池)
server 10.8.0.0 255.255.255.0

# 维持客户端和虚拟IP地址之间的关联
ifconfig-pool-persist /var/log/openvpn/ipp.txt

# 推送路由到客户端
# 推送默认网关,使所有流量通过VPN
push "redirect-gateway def1 bypass-dhcp"

# 推送DNS服务器
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"

# 允许客户端之间通信
client-to-client

#################################################
# 安全配置
#################################################

# 加密算法
cipher AES-256-GCM
auth SHA256

# 密钥协商算法
tls-version-min 1.2
tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384

# 完美前向保密
tls-crypt ta.key

# 用户权限降级
user nobody
group nogroup

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

#################################################
# 日志配置
#################################################

# 日志级别(0-9,9为最详细)
verb 3

# 静默重复消息
mute 20

# 状态日志文件
status /var/log/openvpn/openvpn-status.log

# 日志文件
log-append /var/log/openvpn/openvpn.log

#################################################
# 性能优化
#################################################

# 启用压缩
comp-lzo

# 最大客户端连接数
max-clients 100

# 客户端超时设置
keepalive 10 120

# 快速TLS模式
fast-io

#################################################
# 管理接口(可选)
#################################################

# 管理接口(用于监控和管理)
# management localhost 7505

EOF

echo "服务端配置文件已创建: $SERVER_CONFIG"

# 创建日志目录
mkdir -p /var/log/openvpn
chown openvpn:openvpn /var/log/openvpn 2>/dev/null || true

# 验证配置文件语法
echo "\n验证配置文件语法..."
openvpn --config $SERVER_CONFIG --test-crypto

if [ $? -eq 0 ]; then
    echo "✓ 配置文件语法正确"
else
    echo "✗ 配置文件语法错误,请检查"
fi

3.3.3 高级服务端配置

#!/bin/bash
# 高级服务端配置模板

cat > /etc/openvpn/server/server-advanced.conf << 'EOF'
# OpenVPN高级服务端配置
# 适用于生产环境

#################################################
# 网络接口配置
#################################################

# 多端口监听
port 1194
port 443
proto udp

# 多协议支持
proto-force udp

# 设备配置
dev tun0
dev-type tun

# 拓扑模式
topology subnet

#################################################
# 证书和加密配置
#################################################

# 证书文件
ca ca.crt
cert openvpn-server.crt
key openvpn-server.key
dh dh.pem

# 高级TLS配置
tls-auth ta.key 0
tls-version-min 1.2
tls-version-max 1.3

# 强加密套件
cipher AES-256-GCM
auth SHA512
ncp-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC

# ECDH曲线
ecdh-curve secp384r1

#################################################
# 网络和路由配置
#################################################

# 服务端网络
server 10.8.0.0 255.255.255.0

# 客户端配置目录
client-config-dir /etc/openvpn/ccd

# 路由推送
push "route 192.168.1.0 255.255.255.0"
push "route 192.168.2.0 255.255.255.0"

# DNS推送
push "dhcp-option DNS 192.168.1.1"
push "dhcp-option DNS 8.8.8.8"

# 域名推送
push "dhcp-option DOMAIN company.local"

#################################################
# 安全策略
#################################################

# 客户端证书验证
verify-client-cert require

# 证书撤销列表
crl-verify /etc/openvpn/server/crl.pem

# 远程证书类型验证
remote-cert-tls client

# 用户名密码验证(可选)
# auth-user-pass-verify /etc/openvpn/scripts/auth.sh via-env

# 客户端连接脚本
client-connect /etc/openvpn/scripts/client-connect.sh
client-disconnect /etc/openvpn/scripts/client-disconnect.sh

#################################################
# 性能和稳定性
#################################################

# 连接保持
keepalive 10 60

# 重连设置
ping-timer-rem

# 压缩
comp-lzo adaptive

# 并发连接
max-clients 500

# 连接速率限制
connect-freq 1 10

# 内存优化
mlock

#################################################
# 日志和监控
#################################################

# 详细日志
verb 4
mute 10

# 状态文件
status /var/log/openvpn/status.log 30

# 日志文件
log /var/log/openvpn/server.log

# 管理接口
management 127.0.0.1 7505 /etc/openvpn/server/mgmt-password

#################################################
# 系统集成
#################################################

# 权限降级
user openvpn
group openvpn

# 持久化
persist-key
persist-tun
persist-local-ip
persist-remote-ip

# 脚本安全
script-security 2

EOF

echo "高级服务端配置文件已创建"

3.4 客户端证书管理

3.4.1 生成客户端证书

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

PKI_DIR="/etc/openvpn/easy-rsa"
CLIENT_DIR="/etc/openvpn/clients"

# 函数:生成单个客户端证书
generate_client_cert() {
    local client_name=$1
    
    if [ -z "$client_name" ]; then
        echo "错误: 请提供客户端名称"
        echo "用法: $0 <客户端名称>"
        exit 1
    fi
    
    cd $PKI_DIR
    source ./vars
    
    echo "=== 生成客户端证书: $client_name ==="
    
    # 检查客户端是否已存在
    if [ -f "pki/issued/$client_name.crt" ]; then
        echo "警告: 客户端 $client_name 的证书已存在"
        echo "是否要重新生成?(y/N)"
        read -r response
        if [[ ! "$response" =~ ^[Yy]$ ]]; then
            echo "操作取消"
            exit 0
        fi
        
        # 撤销现有证书
        ./easyrsa revoke $client_name
        ./easyrsa gen-crl
    fi
    
    # 生成客户端证书请求和私钥
    echo "1. 生成客户端证书请求..."
    ./easyrsa gen-req $client_name nopass
    
    # 签发客户端证书
    echo "2. 签发客户端证书..."
    ./easyrsa sign-req client $client_name
    
    # 创建客户端目录
    mkdir -p $CLIENT_DIR/$client_name
    
    # 复制证书文件
    echo "3. 复制证书文件..."
    cp pki/ca.crt $CLIENT_DIR/$client_name/
    cp pki/issued/$client_name.crt $CLIENT_DIR/$client_name/
    cp pki/private/$client_name.key $CLIENT_DIR/$client_name/
    cp pki/ta.key $CLIENT_DIR/$client_name/
    
    # 设置权限
    chmod 644 $CLIENT_DIR/$client_name/ca.crt
    chmod 644 $CLIENT_DIR/$client_name/$client_name.crt
    chmod 600 $CLIENT_DIR/$client_name/$client_name.key
    chmod 600 $CLIENT_DIR/$client_name/ta.key
    
    echo "\n=== 客户端证书生成完成 ==="
    echo "证书目录: $CLIENT_DIR/$client_name"
    
    # 验证证书
    echo "\n证书验证:"
    openssl x509 -in $CLIENT_DIR/$client_name/$client_name.crt -subject -noout
    openssl x509 -in $CLIENT_DIR/$client_name/$client_name.crt -dates -noout
}

# 函数:批量生成客户端证书
batch_generate_clients() {
    local client_list_file=$1
    
    if [ ! -f "$client_list_file" ]; then
        echo "错误: 客户端列表文件不存在: $client_list_file"
        exit 1
    fi
    
    echo "=== 批量生成客户端证书 ==="
    
    while IFS= read -r client_name; do
        # 跳过空行和注释行
        if [[ -z "$client_name" || "$client_name" =~ ^#.* ]]; then
            continue
        fi
        
        echo "\n处理客户端: $client_name"
        generate_client_cert "$client_name"
        
        echo "等待2秒后处理下一个客户端..."
        sleep 2
    done < "$client_list_file"
    
    echo "\n=== 批量生成完成 ==="
}

# 主程序
if [ "$1" = "--batch" ]; then
    batch_generate_clients "$2"
else
    generate_client_cert "$1"
fi

3.4.2 客户端配置文件生成

#!/usr/bin/env python3
# 客户端配置文件生成工具

import os
import sys
import argparse
from pathlib import Path
import ipaddress

class OpenVPNClientConfigGenerator:
    def __init__(self, server_ip, server_port=1194, protocol="udp"):
        self.server_ip = server_ip
        self.server_port = server_port
        self.protocol = protocol
        self.client_dir = Path("/etc/openvpn/clients")
        
    def generate_config(self, client_name, config_type="separate"):
        """
        生成客户端配置文件
        config_type: 'separate' 或 'inline'
        """
        client_path = self.client_dir / client_name
        
        if not client_path.exists():
            raise FileNotFoundError(f"客户端目录不存在: {client_path}")
        
        if config_type == "inline":
            return self._generate_inline_config(client_name, client_path)
        else:
            return self._generate_separate_config(client_name, client_path)
    
    def _generate_separate_config(self, client_name, client_path):
        """
        生成分离式配置文件(证书文件分开)
        """
        config_content = f"""# OpenVPN客户端配置文件
# 客户端: {client_name}
# 生成时间: {self._get_timestamp()}

##############################################
# 客户端配置
##############################################

# 指定这是客户端
client

# 使用tun设备
dev tun

# 协议类型
proto {self.protocol}

# 服务器地址和端口
remote {self.server_ip} {self.server_port}

# 保持尝试连接
resolv-retry infinite

# 不绑定本地端口
nobind

# 权限降级
user nobody
group nogroup

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

##############################################
# 证书和密钥文件
##############################################

# CA证书
ca ca.crt

# 客户端证书
cert {client_name}.crt

# 客户端私钥
key {client_name}.key

# TLS认证密钥
tls-auth ta.key 1

##############################################
# 安全配置
##############################################

# 加密算法
cipher AES-256-GCM
auth SHA256

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

# TLS版本
tls-version-min 1.2

##############################################
# 连接配置
##############################################

# 压缩
comp-lzo

# 日志级别
verb 3

# 静默重复消息
mute 20

# 连接保持
keepalive 10 120

##############################################
# 路由配置
##############################################

# 忽略服务端推送的路由(如果需要)
# route-nopull

# 自定义路由(如果需要)
# route 192.168.1.0 255.255.255.0
"""
        
        config_file = client_path / f"{client_name}.ovpn"
        with open(config_file, 'w') as f:
            f.write(config_content)
        
        return config_file
    
    def _generate_inline_config(self, client_name, client_path):
        """
        生成内联式配置文件(证书内容嵌入)
        """
        # 读取证书文件内容
        ca_content = self._read_file_content(client_path / "ca.crt")
        cert_content = self._read_file_content(client_path / f"{client_name}.crt")
        key_content = self._read_file_content(client_path / f"{client_name}.key")
        ta_content = self._read_file_content(client_path / "ta.key")
        
        config_content = f"""# OpenVPN客户端内联配置文件
# 客户端: {client_name}
# 生成时间: {self._get_timestamp()}

client
dev tun
proto {self.protocol}
remote {self.server_ip} {self.server_port}
resolv-retry infinite
nobind
user nobody
group nogroup
persist-key
persist-tun
cipher AES-256-GCM
auth SHA256
remote-cert-tls server
tls-version-min 1.2
comp-lzo
verb 3
mute 20
keepalive 10 120

<ca>
{ca_content}
</ca>

<cert>
{cert_content}
</cert>

<key>
{key_content}
</key>

<tls-auth>
{ta_content}
</tls-auth>
key-direction 1
"""
        
        config_file = client_path / f"{client_name}-inline.ovpn"
        with open(config_file, 'w') as f:
            f.write(config_content)
        
        return config_file
    
    def _read_file_content(self, file_path):
        """
        读取文件内容
        """
        try:
            with open(file_path, 'r') as f:
                return f.read().strip()
        except FileNotFoundError:
            raise FileNotFoundError(f"证书文件不存在: {file_path}")
    
    def _get_timestamp(self):
        """
        获取当前时间戳
        """
        import datetime
        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    def generate_mobile_config(self, client_name):
        """
        生成移动设备优化配置
        """
        client_path = self.client_dir / client_name
        
        # 读取证书内容
        ca_content = self._read_file_content(client_path / "ca.crt")
        cert_content = self._read_file_content(client_path / f"{client_name}.crt")
        key_content = self._read_file_content(client_path / f"{client_name}.key")
        ta_content = self._read_file_content(client_path / "ta.key")
        
        config_content = f"""# OpenVPN移动设备配置
# 客户端: {client_name}
# 优化移动网络连接

client
dev tun
proto udp
remote {self.server_ip} {self.server_port}
remote {self.server_ip} 443 tcp-client
resolv-retry infinite
nobind
persist-key
persist-tun

# 移动网络优化
connect-retry-max 3
connect-timeout 10
server-poll-timeout 4

# 快速重连
fast-io
sndbuf 524288
rcvbuf 524288

# 安全配置
cipher AES-256-GCM
auth SHA256
remote-cert-tls server
tls-version-min 1.2

# 压缩(移动设备建议关闭以节省CPU)
# comp-lzo

# 日志
verb 3
mute 20

# 连接保持(移动网络优化)
keepalive 10 60
ping-timer-rem

<ca>
{ca_content}
</ca>

<cert>
{cert_content}
</cert>

<key>
{key_content}
</key>

<tls-auth>
{ta_content}
</tls-auth>
key-direction 1
"""
        
        config_file = client_path / f"{client_name}-mobile.ovpn"
        with open(config_file, 'w') as f:
            f.write(config_content)
        
        return config_file
    
    def list_clients(self):
        """
        列出所有客户端
        """
        if not self.client_dir.exists():
            return []
        
        clients = []
        for item in self.client_dir.iterdir():
            if item.is_dir():
                clients.append(item.name)
        
        return sorted(clients)

def main():
    parser = argparse.ArgumentParser(description='OpenVPN客户端配置生成工具')
    parser.add_argument('--server', required=True, help='服务器IP地址')
    parser.add_argument('--port', type=int, default=1194, help='服务器端口')
    parser.add_argument('--protocol', choices=['udp', 'tcp'], default='udp', help='协议类型')
    parser.add_argument('--client', help='客户端名称')
    parser.add_argument('--type', choices=['separate', 'inline', 'mobile'], default='separate', help='配置类型')
    parser.add_argument('--list', action='store_true', help='列出所有客户端')
    
    args = parser.parse_args()
    
    generator = OpenVPNClientConfigGenerator(args.server, args.port, args.protocol)
    
    if args.list:
        clients = generator.list_clients()
        print("可用的客户端:")
        for client in clients:
            print(f"  - {client}")
        return
    
    if not args.client:
        print("错误: 请指定客户端名称")
        sys.exit(1)
    
    try:
        if args.type == 'mobile':
            config_file = generator.generate_mobile_config(args.client)
        else:
            config_file = generator.generate_config(args.client, args.type)
        
        print(f"配置文件已生成: {config_file}")
        
    except Exception as e:
        print(f"错误: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

3.4.3 证书撤销管理

#!/bin/bash
# 证书撤销管理脚本

PKI_DIR="/etc/openvpn/easy-rsa"
CLIENT_DIR="/etc/openvpn/clients"
SERVER_DIR="/etc/openvpn/server"

# 函数:撤销客户端证书
revoke_client_cert() {
    local client_name=$1
    
    if [ -z "$client_name" ]; then
        echo "错误: 请提供客户端名称"
        echo "用法: revoke_client_cert <客户端名称>"
        return 1
    fi
    
    cd $PKI_DIR
    
    # 检查证书是否存在
    if [ ! -f "pki/issued/$client_name.crt" ]; then
        echo "错误: 客户端 $client_name 的证书不存在"
        return 1
    fi
    
    echo "=== 撤销客户端证书: $client_name ==="
    
    # 确认操作
    echo "警告: 此操作将撤销客户端 $client_name 的证书"
    echo "撤销后该客户端将无法连接到VPN服务器"
    echo "是否确认撤销?(yes/no)"
    read -r confirmation
    
    if [ "$confirmation" != "yes" ]; then
        echo "操作取消"
        return 0
    fi
    
    # 撤销证书
    echo "1. 撤销证书..."
    ./easyrsa revoke $client_name
    
    if [ $? -ne 0 ]; then
        echo "错误: 证书撤销失败"
        return 1
    fi
    
    # 生成新的CRL
    echo "2. 生成证书撤销列表..."
    ./easyrsa gen-crl
    
    # 复制CRL到服务器目录
    echo "3. 更新服务器CRL..."
    cp pki/crl.pem $SERVER_DIR/
    
    # 备份并移除客户端文件
    echo "4. 备份客户端文件..."
    if [ -d "$CLIENT_DIR/$client_name" ]; then
        mkdir -p "$CLIENT_DIR/revoked"
        mv "$CLIENT_DIR/$client_name" "$CLIENT_DIR/revoked/$client_name-$(date +%Y%m%d_%H%M%S)"
    fi
    
    # 重启OpenVPN服务以加载新的CRL
    echo "5. 重启OpenVPN服务..."
    systemctl restart openvpn@server
    
    echo "\n=== 证书撤销完成 ==="
    echo "客户端 $client_name 的证书已被撤销"
    echo "CRL已更新: $SERVER_DIR/crl.pem"
    echo "客户端文件已移动到: $CLIENT_DIR/revoked/"
}

# 函数:列出撤销的证书
list_revoked_certs() {
    cd $PKI_DIR
    
    echo "=== 已撤销的证书列表 ==="
    
    if [ -f "pki/index.txt" ]; then
        echo "状态  过期时间        撤销时间        序列号                    主题"
        echo "--------------------------------------------------------------------"
        grep "^R" pki/index.txt | while IFS=$'\t' read -r status expiry revoke_time serial subject; do
            echo "撤销  $expiry  $revoke_time  $serial  $subject"
        done
    else
        echo "未找到证书索引文件"
    fi
}

# 函数:检查CRL状态
check_crl_status() {
    echo "=== CRL状态检查 ==="
    
    local crl_file="$PKI_DIR/pki/crl.pem"
    
    if [ -f "$crl_file" ]; then
        echo "CRL文件存在: $crl_file"
        
        # 显示CRL信息
        echo "\nCRL详细信息:"
        openssl crl -in "$crl_file" -text -noout | head -20
        
        # 检查CRL有效期
        echo "\nCRL有效期:"
        openssl crl -in "$crl_file" -nextupdate -noout
        
        # 统计撤销证书数量
        local revoked_count=$(openssl crl -in "$crl_file" -text -noout | grep -c "Serial Number:")
        echo "撤销证书数量: $revoked_count"
        
    else
        echo "CRL文件不存在"
    fi
    
    # 检查服务器CRL
    local server_crl="$SERVER_DIR/crl.pem"
    if [ -f "$server_crl" ]; then
        echo "\n服务器CRL文件存在: $server_crl"
        local server_crl_date=$(stat -c %Y "$server_crl")
        local pki_crl_date=$(stat -c %Y "$crl_file" 2>/dev/null || echo 0)
        
        if [ $server_crl_date -lt $pki_crl_date ]; then
            echo "警告: 服务器CRL文件过期,需要更新"
        else
            echo "服务器CRL文件是最新的"
        fi
    else
        echo "\n警告: 服务器CRL文件不存在"
    fi
}

# 函数:更新CRL
update_crl() {
    echo "=== 更新CRL ==="
    
    cd $PKI_DIR
    
    # 生成新的CRL
    echo "生成新的CRL..."
    ./easyrsa gen-crl
    
    # 复制到服务器目录
    echo "复制CRL到服务器目录..."
    cp pki/crl.pem $SERVER_DIR/
    
    # 重启服务
    echo "重启OpenVPN服务..."
    systemctl restart openvpn@server
    
    echo "CRL更新完成"
}

# 主程序
case "$1" in
    "revoke")
        revoke_client_cert "$2"
        ;;
    "list")
        list_revoked_certs
        ;;
    "status")
        check_crl_status
        ;;
    "update")
        update_crl
        ;;
    *)
        echo "OpenVPN证书撤销管理工具"
        echo "用法: $0 {revoke|list|status|update} [客户端名称]"
        echo ""
        echo "命令说明:"
        echo "  revoke <客户端名称>  - 撤销指定客户端证书"
        echo "  list                - 列出所有撤销的证书"
        echo "  status              - 检查CRL状态"
        echo "  update              - 更新CRL"
        exit 1
        ;;
esac

3.5 配置文件详解

3.5.1 服务端配置参数详解

# OpenVPN服务端配置参数详解
server_config_parameters:
  
  # 基础网络配置
  network:
    port: 
      description: "监听端口"
      default: 1194
      range: "1-65535"
      example: "port 1194"
      
    proto:
      description: "协议类型"
      options: ["udp", "tcp", "udp4", "udp6", "tcp4", "tcp6"]
      default: "udp"
      example: "proto udp"
      
    dev:
      description: "虚拟网络设备"
      options: ["tun", "tap"]
      default: "tun"
      example: "dev tun0"
      
    topology:
      description: "网络拓扑"
      options: ["net30", "p2p", "subnet"]
      default: "net30"
      example: "topology subnet"
  
  # 证书和加密
  security:
    ca:
      description: "CA证书文件"
      required: true
      example: "ca ca.crt"
      
    cert:
      description: "服务端证书文件"
      required: true
      example: "cert server.crt"
      
    key:
      description: "服务端私钥文件"
      required: true
      example: "key server.key"
      
    dh:
      description: "DH参数文件"
      required: true
      example: "dh dh2048.pem"
      
    cipher:
      description: "数据加密算法"
      options: ["AES-256-GCM", "AES-128-GCM", "AES-256-CBC"]
      default: "AES-256-GCM"
      example: "cipher AES-256-GCM"
      
    auth:
      description: "HMAC认证算法"
      options: ["SHA256", "SHA512", "SHA1"]
      default: "SHA256"
      example: "auth SHA256"
  
  # 客户端管理
  client_management:
    server:
      description: "VPN子网配置"
      format: "network netmask"
      example: "server 10.8.0.0 255.255.255.0"
      
    max_clients:
      description: "最大客户端连接数"
      default: 1024
      example: "max-clients 100"
      
    client_to_client:
      description: "允许客户端间通信"
      default: false
      example: "client-to-client"
      
    duplicate_cn:
      description: "允许重复通用名"
      default: false
      example: "duplicate-cn"
  
  # 路由配置
  routing:
    push_route:
      description: "推送路由到客户端"
      format: "route network netmask [gateway]"
      example: "push \"route 192.168.1.0 255.255.255.0\""
      
    push_redirect_gateway:
      description: "重定向默认网关"
      options: ["def1", "bypass-dhcp", "bypass-dns"]
      example: "push \"redirect-gateway def1 bypass-dhcp\""
      
    push_dhcp_option:
      description: "推送DHCP选项"
      common_options: ["DNS", "DOMAIN", "NTP"]
      example: "push \"dhcp-option DNS 8.8.8.8\""
  
  # 性能优化
  performance:
    sndbuf:
      description: "发送缓冲区大小"
      unit: "bytes"
      example: "sndbuf 524288"
      
    rcvbuf:
      description: "接收缓冲区大小"
      unit: "bytes"
      example: "rcvbuf 524288"
      
    comp_lzo:
      description: "LZO压缩"
      options: ["yes", "no", "adaptive"]
      example: "comp-lzo adaptive"
      
    fast_io:
      description: "快速I/O模式"
      example: "fast-io"
  
  # 日志和监控
  logging:
    verb:
      description: "日志详细级别"
      range: "0-11"
      default: 3
      example: "verb 4"
      
    mute:
      description: "静默重复消息数量"
      default: 20
      example: "mute 20"
      
    status:
      description: "状态日志文件"
      example: "status /var/log/openvpn/status.log 30"
      
    log:
      description: "日志文件"
      example: "log /var/log/openvpn/server.log"

3.5.2 客户端配置参数详解

# OpenVPN客户端配置参数详解
client_config_parameters:
  
  # 基础配置
  basic:
    client:
      description: "指定为客户端模式"
      required: true
      example: "client"
      
    dev:
      description: "虚拟网络设备类型"
      options: ["tun", "tap"]
      example: "dev tun"
      
    proto:
      description: "协议类型"
      options: ["udp", "tcp-client"]
      example: "proto udp"
  
  # 服务器连接
  connection:
    remote:
      description: "服务器地址和端口"
      format: "hostname/ip [port] [proto]"
      example: "remote vpn.example.com 1194"
      
    remote_random:
      description: "随机选择服务器"
      example: "remote-random"
      
    resolv_retry:
      description: "DNS解析重试"
      options: ["infinite", "number"]
      example: "resolv-retry infinite"
      
    nobind:
      description: "不绑定本地端口"
      example: "nobind"
      
    connect_retry:
      description: "连接重试间隔"
      unit: "seconds"
      example: "connect-retry 5"
  
  # 安全配置
  security:
    ca:
      description: "CA证书文件"
      required: true
      example: "ca ca.crt"
      
    cert:
      description: "客户端证书文件"
      required: true
      example: "cert client.crt"
      
    key:
      description: "客户端私钥文件"
      required: true
      example: "key client.key"
      
    tls_auth:
      description: "TLS认证密钥"
      format: "file direction"
      example: "tls-auth ta.key 1"
      
    remote_cert_tls:
      description: "验证远程证书类型"
      options: ["server", "client"]
      example: "remote-cert-tls server"
  
  # 系统配置
  system:
    user:
      description: "运行用户"
      example: "user nobody"
      
    group:
      description: "运行组"
      example: "group nogroup"
      
    persist_key:
      description: "持久化密钥"
      example: "persist-key"
      
    persist_tun:
      description: "持久化TUN设备"
      example: "persist-tun"

3.6 安全参数设置

3.6.1 加密算法配置

#!/bin/bash
# 加密算法安全配置脚本

echo "=== OpenVPN加密算法安全配置 ==="

# 推荐的安全配置
cat > /etc/openvpn/security-template.conf << 'EOF'
# OpenVPN安全配置模板
# 基于当前最佳安全实践

#################################################
# 强加密配置
#################################################

# 数据通道加密(推荐AES-256-GCM)
cipher AES-256-GCM

# 备用加密算法
ncp-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC

# HMAC认证算法
auth SHA256

# TLS版本限制
tls-version-min 1.2
tls-version-max 1.3

# TLS加密套件
tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256

# ECDH曲线
ecdh-curve secp384r1

# 密钥大小
tls-ciphersuites TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256

#################################################
# 认证安全
#################################################

# TLS认证(防DoS)
tls-auth ta.key 0  # 服务端使用0,客户端使用1

# 或者使用tls-crypt(更安全)
# tls-crypt ta.key

# 远程证书验证
remote-cert-tls client  # 服务端配置
# remote-cert-tls server  # 客户端配置

# 证书撤销列表
crl-verify crl.pem

#################################################
# 连接安全
#################################################

# 重放攻击保护
replay-window 64 15

# 连接频率限制
connect-freq 1 10

# 脚本安全级别
script-security 2

EOF

echo "安全配置模板已创建: /etc/openvpn/security-template.conf"