1. 安装 OpenVPN (主节点与子节点):

apt update
apt install openvpn easy-rsa

2. 配置OpenVPN服务器 (主节点):

  • 初始化 PKI 并生成 CA、服务器证书和密钥
    make-cadir ~/openvpn-ca
    cd ~/openvpn-ca
    # 初始化 pki 目录
    ./easyrsa init-pki
    # 生成ca证书
    ./easyrsa build-ca
    # 创建连接秘钥
    ./easyrsa gen-req server nopass
    ./easyrsa sign-req server server
  • 创建 OpenVPN 服务器配置文件 (/etc/openvpn/server.conf)
    # 指定服务器监听的端口号
    port 1194
    # 指定使用的协议(TCP 或 UDP),这里使用 TCP
    proto tcp
    # 指定使用的设备类型,tun 表示隧道设备
    dev tun
    # 指定 CA 证书文件路径,用于验证客户端和服务器的身份
    ca ca.crt
    # 指定服务器的证书文件路径
    cert server.crt
    # 指定服务器的私钥文件路径
    key server.key
    # 指定 Diffie-Hellman 参数文件路径,用于密钥交换
    dh dh2048.pem
    # 定义 VPN 服务器的 IP 地址池和子网掩码,分配给客户端
    server 10.25.0.0 255.255.192.0
    # 指定客户端配置目录,用于为特定客户端设置自定义配置
    client-config-dir /etc/openvpn/ccd
    # 推送路由到客户端,使客户端能够访问指定网段
    push "route 10.25.0.0 255.255.192.0"
    # 设置保活机制,10 秒发送一次 ping,120 秒未收到回应则认为连接断开
    keepalive 10 120
    # 保持密钥在重启时不重新生成
    persist-key
    # 保持隧道设备在重启时不关闭
    persist-tun
    # 设置日志的详细级别,4 表示较详细的日志
    verb 4
    # 指定日志文件路径,日志会追加到该文件
    log-append /var/log/openvpn.log
  • 创建 /etc/openvpn/ccd 目录,为客户端指定固定ip
    mkdir /etc/openvpn/ccd
    # node1 文件名为 gen-req 时设置的节点的名称
    vim /etc/openvpn/ccd/node1

    /etc/openvpn/ccd/node1

ifconfig-push 10.25.0.3 255.255.192.0
  • 复制证书和密钥到 /etc/openvpn/
    cp pki/ca.crt pki/issued/server.crt pki/private/server.key /etc/openvpn/
    openssl dhparam -out /etc/openvpn/dh2048.pem 2048

3. 配置 OpenVPN 客户端(主节点):

  • 为每个节点生成客户端证书:

    # client1 为节点配置文件名称,可修改
    ./easyrsa gen-req client1 nopass
    ./easyrsa sign-req client client1
  • 创建客户端配置文件(client1.ovpn)

    client
    dev tun
    proto tcp
    remote <master-public-ip> 1194
    resolv-retry infinite
    nobind
    persist-key
    persist-tun
    ca /etc/openvpn/client/ca.crt
    cert /etc/openvpn/client/client1.crt
    key /etc/openvpn/client/client1.key
    remote-cert-tls server
  • 将证书和配置文件分发到客户端节点,保存到 /etc/openvpn/client 目录。

4. 启动服务端(主节点)

sudo systemctl enable openvpn@server
sudo systemctl start openvpn@server

5. 配置客户端并启动(子节点)

ca.crt client1.crt client1.key 三个文件放入 /etc/openvpn/client 目录后

  • 创建 systemd 服务,执行 vim /etc/systemd/system/openvpn-client@client1.service
[Unit]
Description=OpenVPN Client for %i
After=network.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage

[Service]
Type=forking
ExecStart=/usr/sbin/openvpn --daemon --config /etc/openvpn/client/%i.ovpn
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
ProtectSystem=full
PrivateTmp=true

[Install]
WantedBy=multi-user.target
systemctl enable openvpn-client@client1.service
systemctl start openvpn-client@client1.service

附:自动化客户端证书创建脚本

  • mkClient.sh 自动创建证书并且打包为zip

bash mkClient.sh 节点名称 ca证书密码 节点IP

#!/bin/bash

# 检查是否提供了足够的参数
if [ "$#" -lt 2 ]; then
    echo "用法: $0 <NODE_NAME> <CA_PASS>"
    echo "示例: $0 node2 mysecretpass"
    exit 1
fi

# 从参数获取变量
NODE_NAME="$1"  # 节点名称
CA_PASS="$2"    # CA 密钥的密码短语
NODE_IP="$3"   # 节点使用ip
IP_MASK="255.255.192.0"
EASYRSA_DIR="/root/openvpn-ca"  # easy-rsa 目录
MASTER_PUBLIC_IP="server_a_00026.hostidc.net"  # 主服务器ip或者域名

# 切换到 easy-rsa 目录
cd "$EASYRSA_DIR" || { echo "无法进入 $EASYRSA_DIR 目录"; exit 1; }

# 1. 生成证书请求(gen-req),自动确认 'yes' 和节点名称
echo -e "$NODE_NAME" | ./easyrsa gen-req "$NODE_NAME" nopass
if [ $? -ne 0 ]; then
    echo "生成证书请求失败"
    exit 1
fi

# 2. 签名证书请求(sign-req),自动确认 'yes' 并提供 CA 密码
expect << EOF
    spawn ./easyrsa sign-req client "$NODE_NAME"
    expect "Confirm request details:"
    send "yes\r"
    expect "Enter pass phrase for"
    send "$CA_PASS\r"
    expect eof
EOF

if [ $? -ne 0 ]; then
    echo "签名证书请求失败"
    exit 1
fi

# 配置节点ip
mkdir -p "/etc/openvpn/ccd"
cat > "/etc/openvpn/ccd/$NODE_NAME" << EOF
ifconfig-push $NODE_IP $IP_MASK
EOF

# 重启服务
systemctl restart openvpn@server

# 生成ovpn文件
cat << EOF > "${NODE_NAME}.ovpn"
client
dev tun
proto tcp
remote ${MASTER_PUBLIC_IP} 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca /etc/openvpn/client/ca.crt
cert /etc/openvpn/client/${NODE_NAME}.crt
key /etc/openvpn/client/${NODE_NAME}.key
remote-cert-tls server
EOF

# 3. 打包证书文件和 CA 证书到 ZIP
CERT_FILES=(
    "$EASYRSA_DIR/pki/reqs/$NODE_NAME.req"
    "$EASYRSA_DIR/pki/private/$NODE_NAME.key"
    "$EASYRSA_DIR/pki/issued/$NODE_NAME.crt"
    "$EASYRSA_DIR/pki/ca.crt"
    "$NODE_NAME.ovpn"
)

# 检查所有文件是否存在
for file in "${CERT_FILES[@]}"; do
    if [ ! -f "$file" ]; then
        echo "错误: 文件 $file 不存在"
        exit 1
    fi
done

# 创建 ZIP 文件
mkdir client
zip -j "client/$NODE_NAME.zip" "${CERT_FILES[@]}"
if [ $? -ne 0 ]; then
    echo "打包 ZIP 文件失败"
    exit 1
fi

echo "证书生成和打包完成:"
echo "请求文件: $EASYRSA_DIR/pki/reqs/$NODE_NAME.req"
echo "密钥文件: $EASYRSA_DIR/pki/private/$NODE_NAME.key"
echo "证书文件: $EASYRSA_DIR/pki/issued/$NODE_NAME.crt"
echo "CA 证书: $EASYRSA_DIR/pki/ca.crt"
echo "ZIP 文件: client/$NODE_NAME.zip"
  • clientStart.sh 节点自动解压部署脚本
#!/bin/bash

# 检查是否提供了节点名称参数
if [ "$#" -ne 1 ]; then
    echo "用法: $0 <NODE_NAME>"
    echo "示例: $0 node2"
    exit 1
fi

# 参数
NODE_NAME="$1"  # 节点名称
ZIP_FILE="$NODE_NAME.zip"  # ZIP 文件名
TARGET_DIR="/etc/openvpn/client"  # 目标解压目录
SERVICE_FILE="/etc/systemd/system/openvpn-client@.service"  # systemd 服务文件

# 检查 unzip 命令是否可用
if ! command -v unzip &> /dev/null; then
    echo "错误: unzip 命令未找到。正在安装 unzip(apt-get install unzip)"
    apt-get install unzip -y
fi

# 检查 openvpn 命令是否可用
if ! command -v openvpn &> /dev/null; then
    echo "错误: openvpn 命令未找到。正在安装 openvpn(apt-get install openvpn)"
    apt-get install openvpn -y
fi

# 检查 ZIP 文件是否存在
if [ ! -f "$ZIP_FILE" ]; then
    echo "错误: ZIP 文件 $ZIP_FILE 不存在"
    exit 1
fi

# 创建目标目录(如果不存在)
mkdir -p "$TARGET_DIR" || { echo "无法创建目录 $TARGET_DIR"; exit 1; }

# 解压 ZIP 文件到目标目录
unzip -o "$ZIP_FILE" -d "$TARGET_DIR"
if [ $? -ne 0 ]; then
    echo "解压 $ZIP_FILE 失败"
    exit 1
fi

# 检查必要的文件是否解压成功
REQUIRED_FILES=("$TARGET_DIR/$NODE_NAME.key" "$TARGET_DIR/$NODE_NAME.crt" "$TARGET_DIR/ca.crt" "$TARGET_DIR/$NODE_NAME.ovpn")
for file in "${REQUIRED_FILES[@]}"; do
    if [ ! -f "$file" ]; then
        echo "错误: 必要文件 $file 未找到"
        exit 1
    fi
done

# 创建 systemd 服务文件
cat > "$SERVICE_FILE" << 'EOF'
[Unit]
Description=OpenVPN Client for %i
After=network.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage

[Service]
Type=forking
ExecStart=/usr/sbin/openvpn --daemon --config /etc/openvpn/client/%i.ovpn
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
ProtectSystem=full
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

if [ $? -ne 0 ]; then
    echo "创建 systemd 服务文件失败"
    exit 1
fi

# 重新加载 systemd 配置
systemctl daemon-reload
if [ $? -ne 0 ]; then
    echo "重新加载 systemd 配置失败"
    exit 1
fi

# 启用 OpenVPN 客户端服务
systemctl enable "openvpn-client@$NODE_NAME.service"
if [ $? -ne 0 ]; then
    echo "启用 openvpn-client@$NODE_NAME.service 失败"
    exit 1
fi

# 启动 OpenVPN 客户端服务
systemctl start "openvpn-client@$NODE_NAME.service"
if [ $? -ne 0 ]; then
    echo "启动 openvpn-client@$NODE_NAME.service 失败"
    exit 1
fi

# 检查服务状态
echo "检查服务状态..."
systemctl status "openvpn-client@$NODE_NAME.service" --no-pager

echo "OpenVPN 客户端设置完成:"
echo "证书和配置文件已解压到: $TARGET_DIR"
echo "服务已启用并启动: openvpn-client@$NODE_NAME.service"
echo "ZIP 文件: $ZIP_FILE"