avatar

甄天祥-Linux-个人小站

A text-focused Halo theme

  • 首页
  • 分类
  • 标签
  • 关于
Home Docker 快速部署 OpenVPN
文章

Docker 快速部署 OpenVPN

Posted 2025-07-22 Updated 5 days ago
By Administrator
100~129 min read

一、OpenVPN 简介

VPN 直译就是虚拟专用通道,是提供给企业之间或者个人与公司之间安全数据传输的隧道,OpenVPN 无疑是 Linux 下开源 VPN 的先锋,提供了良好的性能和友好的用户 GUI。

OpenVPN 是一个基于 OpenSSL 库的应用层 VPN 实现。和传统 VPN 相比,它的优点是简单易用。

OpenVPN 允许参与建立 VPN 的单点使用共享金钥,电子证书,或者用户名/密码来进行身份验证。它大量使用了 OpenSSL 加密库中的 SSLv3 /TLSv1 协议函式库。OpenVPN 能在 Solaris、Linux、OpenBSD、FreeBSD、NetBSD、Mac OS X 与 Windows 上运行,并包含了许多安全性的功能。它并不是一个基于 Web 的 VPN 软件,也不与 IPSec 及其他 VPN 软件包兼容。

虚拟私有网络(VPN)隧道是通过 Internet 隧道技术将两个不同地理位置的网络安全的连接起来的技术。当两个网络是使用私有 IP 地址的私有局域网络时,它们之间是不能相互访问的,这时使用隧道技术就可以使得两个子网内的主机进行通讯。例如,VPN 隧道技术经常被用于大型机构中不同办公区域子网的连接。有时,使用 VPN 隧道仅仅是因为它很安全。服务提供商与公司会使用这样一种方式架设网络,他们将重要的服务器(如,数据库,VoIP,银行服务器)放置到一个子网内,仅仅让有权限的用户通过 VPN 隧道进行访问。如果需要搭建一个安全的 VPN 隧道,通常会选用 IPSec,因为 IPSec VPN 隧道被多重安全层所保护。

VPN (虚拟专用网)发展至今已经不在是一个单纯的经过加密的访问隧道了,它已经融合了访问控制、传输管理、加密、路由选择、可用性管理等多种功能,并在全球的信息安全体系中发挥着重要的作用。也在网络上,有关各种 VPN 协议优缺点的比较是仁者见仁,智者见智,很多技术人员由于出于使用目的考虑,包括访问控制、 安全和用户简单易用,灵活扩展等各方面,权衡利弊,难以取舍;尤其在 VOIP 语音环境中,网络安全显得尤为重要,因此现在越来越多的网络电话和语音网关支持 VPN 协议。

二、开始部署服务端

1. 拉取镜像

[root@vpn openvpn]# docker pull zhentianxiang/openvpn:2.4.8

2. 创建挂载目录

[root@vpn openvpn]# mkdir -pv /etc/openvpn/conf
[root@vpn openvpn]# cd /etc/openvpn/

3. 生成配置文件

选择使用 OpenVPN 的 UDP 还是 TCP 取决于您的特定需求和情况。每个协议都有自己的优点和缺点,以下是一些考虑因素:

UDP:

  1. 速度快: UDP 通常比 TCP 更快,因为它不进行连接建立和错误恢复的复杂过程。这使得 UDP 成为处理实时流量(如音频和视频传输)的理想选择。

  2. 较少的开销: UDP 头部较小,因此它产生的额外开销较少,适用于网络连接较慢的情况。

  3. 适用于游戏和流媒体: 由于速度快且开销低,UDP 通常用于在线游戏、流媒体以及其他需要快速数据传输和低延迟的应用。

  4. 不稳定网络: 在不稳定的网络环境中,UDP 可能更可靠,因为它不会试图重传丢失的数据包,从而减少了延迟。

TCP:

  1. 可靠性: TCP 是一种可靠的协议,它确保数据包的有序传输和丢失包的重传。这使得它在不太可靠的网络环境中更适合,如公共 Wi-Fi 或有防火墙和代理的网络。

  2. 适用于文件传输: 由于可靠性,TCP 通常用于文件传输和下载,因为它可以确保文件完整性。

  3. 穿越防火墙: 由于通常的 HTTP 流量也使用 TCP,因此 TCP 通常可以更容易穿越防火墙和代理服务器,这使得它在某些网络环境中更可用。

  4. 保密性: 在某些情况下,TCP 可能更容易混淆和伪装,以保护数据的隐私。

总结来说,UDP 通常更适合需要高速传输和低延迟的应用,而 TCP 更适合需要可靠性和穿越防火墙的应用。选择哪种协议应取决于您的特定需求以及您所在网络环境的性质。有时,将两者结合使用也是一个可行的解决方案,以满足不同类型的流量需求。

# 47.120.62.2 是公网IP,根据实际需求切换自己的公网 IP
[root@vpn openvpn]# docker run -v $(pwd):/etc/openvpn --rm zhentianxiang/openvpn:2.4.8 ovpn_genconfig -u udp://47.120.62.2

使用以上命令会生成配置文件,但是我们还要进行修改,所以我这里提供了一个可用且可靠的配置文件

# 监听地址
local 0.0.0.0

# 启用服务器模式
# mode server

# tls-server

# 协议(明确为服务端模式)
proto udp

# 端口
port 1194

# 虚拟网卡设备
dev tun0

# 证书与密钥
key /etc/openvpn/pki/private/47.120.62.2.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/47.120.62.2.crt
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key 0
key-direction 0

# 表示禁用 OpenVPN 对缓冲区大小的显式设置
sndbuf 0 
rcvbuf 0

# 连接保持
persist-tun
persist-key

# 日志级别
verb 5
status /tmp/openvpn-status.log

# 运行用户权限
user nobody
group nogroup

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

# 允许客户端之间互相访问
client-to-client

# 限制最大客户端数量
max-clients 10

# 保持连接时间
keepalive 10 60

# 无限重连
resolv-retry infinite 

# 固定 MTU 值
tun-mtu 1500

# 修复 MSS 问题
mssfix 1450

# 允许多客户端使用相同证书
duplicate-cn

# 添加连接关闭通知
explicit-exit-notify 3

# 客户端分配 IP 的地址池
server 120.10.255.0 255.255.255.0

# 子网路由,也就是说 VPN 客户端除了可以访问到 VPN 服务端的局域网地址,还可以访问到服务端之外的局域网地址
push "route 120.10.255.0 255.255.255.0"  # openvpn
push "route 172.16.11.0 255.255.255.0" # 办公室1
push "route 172.16.12.0 255.255.255.0" # 办公室2
push "route 172.16.13.0 255.255.255.0" # 办公室3

# 明确禁用 IPv6
push "block-ipv6"

# DNS 配置,此配置是向 VPN 客户端进行 DNS 推送
#push "dhcp-option DNS 172.16.11.1"  # 比如办公室1的 DNS 服务器

4. 生成密钥文件

[root@vpn openvpn]# docker run -v $(pwd):/etc/openvpn --rm -it zhentianxiang/openvpn:2.4.8 ovpn_initpki

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /etc/openvpn/pki


Using SSL: openssl OpenSSL 1.1.1g  21 Apr 2020 (Library: OpenSSL 1.1.1d  10 Sep 2019)

Enter New CA Key Passphrase: 123456789  ########################### 定义 CA 跟证书密码 ###########################
Re-Enter New CA Key Passphrase: 123456789 ############################ 定义 CA 跟证书密码 ###########################
Generating RSA private key, 2048 bit long modulus (2 primes)
...............................................+++++
...................................+++++
e is 65537 (0x010001)
Can't load /etc/openvpn/pki/.rnd into RNG
140461893008712:error:2406F079:random number generator:RAND_load_file:Cannot open file:crypto/rand/randfile.c:98:Filename=/etc/openvpn/pki/.rnd
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]: ############################ 回车 ###########################

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/etc/openvpn/pki/ca.crt


Using SSL: openssl OpenSSL 1.1.1g  21 Apr 2020 (Library: OpenSSL 1.1.1d  10 Sep 2019)
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time


DH parameters of size 2048 created at /etc/openvpn/pki/dh.pem


Using SSL: openssl OpenSSL 1.1.1g  21 Apr 2020 (Library: OpenSSL 1.1.1d  10 Sep 2019)
Generating a RSA private key
.......................................................................................................................+++++
.....................................+++++
writing new private key to '/etc/openvpn/pki/private/47.120.62.2.key.XXXXoEPaiN'
-----
Using configuration from /etc/openvpn/pki/safessl-easyrsa.cnf
Enter pass phrase for /etc/openvpn/pki/private/ca.key: 123456789 ######################### 输入 CA 跟证书密码 #########################
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'1.1.1.1'
Certificate is to be certified until Jan  1 05:45:05 2028 GMT (1080 days)

Write out database with 1 new entries
Data Base Updated

Using SSL: openssl OpenSSL 1.1.1g  21 Apr 2020 (Library: OpenSSL 1.1.1d  10 Sep 2019)
Using configuration from /etc/openvpn/pki/safessl-easyrsa.cnf
Enter pass phrase for /etc/openvpn/pki/private/ca.key: 123456789   ######################### 输入 CA 跟证书密码 ######################

An updated CRL has been created.
CRL file: /etc/openvpn/pki/crl.pem

5. 生成客户端证书

vpn-client 就是客户端的名称

如果最后再加上一个 nopass 参数,表示客户端登陆不需要密码认证

[root@vpn openvpn]# docker run -v $(pwd):/etc/openvpn --rm -it zhentianxiang/openvpn:2.4.8 easyrsa build-client-full vpn-client

Using SSL: openssl OpenSSL 1.1.1g  21 Apr 2020 (Library: OpenSSL 1.1.1d  10 Sep 2019)
Generating a RSA private key
...............................................................+++++
...............+++++
writing new private key to '/etc/openvpn/pki/private/vpn-client.key.XXXXCphCkK'
Enter PEM pass phrase: 123123              # 定义客户端证书登陆密码
Verifying - Enter PEM pass phrase: 123123  # 定义客户端证书登陆密码
-----
Using configuration from /etc/openvpn/pki/safessl-easyrsa.cnf
Enter pass phrase for /etc/openvpn/pki/private/ca.key: 123456789   # 输入 ca 证书密码
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'vpn-client'
Certificate is to be certified until Jan  1 02:18:50 2028 GMT (1080 days)

Write out database with 1 new entries
Data Base Updated

6. 导出客户端配置

[root@vpn openvpn]# mkdir client
[root@vpn openvpn]# docker run -v $(pwd):/etc/openvpn --rm zhentianxiang/openvpn:2.4.8 ovpn_getclient vpn-client > $(pwd)/client/vpn-client.ovpn
[root@vpn openvpn]# ls -l client/
total 8
-rw-r--r-- 1 root root 5091 Jan 16 10:21 vpn-client.ovpn

7. 启动openvpn

  • docker-compose 启动

[root@vpn openvpn]# cat docker-compose.yml
version: '3.8'

services:
  openvpn:
    image: zhentianxiang/openvpn:2.4.8
    container_name: openvpn
    restart: always
    ports:
      - "1194:1194/udp"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./:/etc/openvpn
    cap_add:
      - NET_ADMIN
    stdin_open: true
    tty: true

三、脚本新增删除用户

1. 新增用户

[root@k8s-master1 .openvpn]# cat add-client.sh 
#!/bin/bash

# 创建客户端目录
mkdir -p client

# 提示用户输入用户名
read -p "请输入您的用户名: " NAME

# 询问是否修改端口(默认 1194)
read -p "默认端口为 1194,是否修改?(y/N): " CHANGE_PORT
if [[ "$CHANGE_PORT" =~ ^[Yy]$ ]]; then
    read -p "请输入新的端口号: " NEW_PORT
else
    NEW_PORT=1194
fi

# 询问是否修改协议(默认 TCP)
read -p "默认协议为 TCP,是否修改?(y/N): " CHANGE_PROTOCOL
if [[ "$CHANGE_PROTOCOL" =~ ^[Yy]$ ]]; then
    read -p "请输入新的协议 (tcp/udp): " NEW_PROTOCOL
else
    NEW_PROTOCOL="tcp"
fi

# 输出提示信息
echo "正在为 $NAME 构建客户端证书..."

# 构建客户端证书
docker run -v $(pwd):/etc/openvpn --rm -it zhentianxiang/openvpn:2.4.8 easyrsa build-client-full $NAME

# 输出提示信息
echo "正在为 $NAME 生成客户端配置文件..."

# 生成客户端配置文件
docker run -v $(pwd):/etc/openvpn --rm zhentianxiang/openvpn:2.4.8 ovpn_getclient $NAME > $(pwd)/client/"$NAME".ovpn

# 输出提示信息
echo "正在修改 $NAME 的客户端配置文件..."

# 修改端口号(如果有变化)
if [[ "$NEW_PORT" -ne 1194 ]]; then
    sed -i "s/1194/$NEW_PORT/g" client/"$NAME".ovpn
fi

# 修改协议(如果有变化)
if [[ "$NEW_PROTOCOL" != "tcp" ]]; then
    sed -i "s/tcp/$NEW_PROTOCOL/g" client/"$NAME".ovpn
fi

# 修改客户端配置文件,删除 redirect-gateway def1
sed -i "s/redirect-gateway def1//g" client/"$NAME".ovpn

# 在客户端配置文件中插入相关配置
#echo -e "\nroute 172.16.246.0 255.255.255.0 vpn_gateway" >> client/"$NAME".ovpn
#echo "route 192.168.0.0 255.255.0.0 172.16.246.171" >> client/"$NAME".ovpn

tee -a client/"$NAME".ovpn <<EOF
script-security 2
route-method exe
route-delay 2
block-ipv6
tun-mtu 1500
mssfix 1450
EOF

# 输出提示信息
echo "正在重启 OpenVPN 容器..."

# 重启 OpenVPN 容器
docker restart openvpn

docker exec -it openvpn /bin/sh -c "iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE"

# 输出完成提示信息
echo "$NAME 的客户端配置文件已成功生成并修改。"
echo "配置文件位于: $(pwd)/client/$NAME.ovpn"

操作如下:

[root@k8s-master1 .openvpn]# ./add-client.sh 
请输入您的用户名: 甄天祥
默认端口为 1194,是否修改?(y/N): y
请输入新的端口号: 31194
默认协议为 TCP,是否修改?(y/N): y
请输入新的协议 (tcp/udp): udp
正在为 甄天祥 构建客户端证书...

Using SSL: openssl OpenSSL 1.1.1g  21 Apr 2020 (Library: OpenSSL 1.1.1d  10 Sep 2019)
Generating a RSA private key
.......+++++
...........................................................................................+++++
writing new private key to '/etc/openvpn/pki/private/甄天祥.key.XXXXOmONFB'
Enter PEM pass phrase:           ########## 这里自定义客户端登陆 VPN 时的证书密码
Verifying - Enter PEM pass phrase:      ########## 这里自定义客户端登陆 VPN 时的证书密码
-----
Using configuration from /etc/openvpn/pki/safessl-easyrsa.cnf
Enter pass phrase for /etc/openvpn/pki/private/ca.key:     ########## 这里填写 CA 根证书密码
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'\0xFFFFFFE7\0xFFFFFF94\0xFFFFFF84\0xFFFFFFE5\0xFFFFFFA4\0xFFFFFFA9\0xFFFFFFE7\0xFFFFFFA5\0xFFFFFFA5'
Certificate is to be certified until Sep 13 03:00:38 2028 GMT (1080 days)

Write out database with 1 new entries
Data Base Updated
正在为 甄天祥 生成客户端配置文件...
正在修改 甄天祥 的客户端配置文件...
script-security 2
route-method exe
route-delay 2
block-ipv6
tun-mtu 1500
mssfix 1450
正在重启 OpenVPN 容器...
openvpn
甄天祥 的客户端配置文件已成功生成并修改。
配置文件位于: /root/.openvpn/client/甄天祥.ovpn

2. 删除用户

[root@k8s-master1 .openvpn]# cat del-client.sh 
#!/bin/bash

# 提示用户输入要删除的用户名
read -p "请输入要删除的用户名: " NAME

## 确保用户名不为空
#if [[ -z "$NAME" ]]; then
#    echo "错误: 用户名不能为空!"
#    exit 1
#fi
#
## 确认用户是否存在
#if [[ ! -f "pki/issued/$NAME.crt" ]]; then
#    echo "错误: 用户 $NAME 不存在或证书未签发!"
#    exit 1
#fi

# 输出提示信息
echo "正在撤销 $NAME 的客户端证书..."

# 撤销客户端证书
docker run -v $(pwd):/etc/openvpn --rm -it zhentianxiang/openvpn:2.4.8 easyrsa revoke $NAME

# 重新生成 CRL(证书吊销列表)
docker run -v $(pwd):/etc/openvpn --rm -it zhentianxiang/openvpn:2.4.8 easyrsa gen-crl

# 移动最新的 CRL 到 OpenVPN 目录
mv pki/crl.pem /etc/openvpn/crl.pem

# 输出提示信息
echo "正在删除 $NAME 的证书文件..."

# 删除证书和密钥
rm -f pki/issued/"$NAME".crt
rm -f pki/private/"$NAME".key
rm -f pki/reqs/"$NAME".req

# 删除客户端配置文件
rm -f client/"$NAME".ovpn

# 重启 OpenVPN 使更改生效
echo "正在重启 OpenVPN 容器..."
docker restart openvpn

docker exec -it openvpn /bin/sh -c "iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE"

# 输出完成提示信息
echo "用户 $NAME 已成功删除,并撤销其证书访问权限。"

四、关于网络环境配置

1. redirect-gateway def1

这个配置是客户端截取当前主机所有路由,并且全部转发到 openvpn 网关上,如果开启这个配置的话可能会造成本地网络环境不稳定,毕竟你的出口网关要全部走到 VPN 服务器端,这样一来就会使本地网络变得卡顿,延迟高

最后一行删除 redirect-gateway def1

[root@vpn openvpn]# vim client/vpn-client.ovpn

2. 静态路由配置

1. 客户端登陆 vpn 后访问网络不通问题

客户端连接登陆后无法 ping 通局域网地址,只能 ping 通 vpn 提供的 TUN0 (docker 容器内部) 网卡的地址,解决办法如下

开启 ipv4 net 转发

[root@vpn openvpn]# sysctl -w net.ipv4.ip_forward=1

动态 SNAT(源地址转换)

[root@vpn openvpn]# iptables -t nat -A POSTROUTING -s 120.10.255.0/24 -j MASQUERADE

部分

含义

iptables

Linux 内核的防火墙工具

-t nat

操作 nat 表(专门处理地址转换)

-A POSTROUTING

在 POSTROUTING 链追加规则(数据包离开主机前的最后处理阶段)

-s 120.10.255.0/24

匹配源(-s)IP 为 120.10.255.0~120.10.255.255 的数据包

-j MASQUERADE

对匹配的数据包执行 MASQUERADE 动作(动态源地址伪装)

核心功能:

  1. 动态 SNAT(源地址转换)

    • 将来自 120.10.255.0/24 的所有数据包的源 IP 替换为当前主机的出口 IP(如公网 IP、或主机 IP)。

    • 外部服务器看到的流量会“伪装”成来自当前主机,而非内网设备。

  2. 典型应用场景

    • VPN 服务器:让客户端(如 120.10.255.0/24)通过服务器访问互联网。

    • 网关/NAT 主机:内网设备共享一个公网 IP 上网。

对比 MASQUERADE vs SNAT

参数

特点

MASQUERADE

自动获取当前主机的出口 IP(适合动态 IP 环境,如拨号上网)

SNAT

需手动指定固定 IP(如 -j SNAT --to-source 1.2.3.4,适合静态 IP)


实际效果示例

  • 原始数据包:

    text

    源IP: 120.10.255.100 → 目标IP: 8.8.8.8
  • 经过规则后:

    text

    源IP: [服务器的公网IP或主机内网IP] → 目标IP: 8.8.8.8

增加一条静态路由

172.17.0.2 是 openvpn 容器地址

120.10.255.0/24 是 openvpn 容器内的虚拟网卡地址

由于 openvpn 是在容器中运行,所以要让源 IP 也就是 120.10.255.0/24 通过容器 IP 172.17.0.2 流出到宿主机

[root@vpn openvpn]# sudo route add -net 120.10.255.0/24 gw 172.18.0.2

2. openvpn 所在的宿主机无法 ping 通客户端地址解决

在容器内添加 NAT 规则

  • -t nat: 操作 nat 表(专门用于地址转换的 iptables 表)。

  • -A POSTROUTING: 在 POSTROUTING 链(数据包离开主机前的最后阶段)追加规则。

  • -o tun0: 仅匹配从 tun0 接口(通常是 OpenVPN 的虚拟网卡)发出的流量。

  • -j MASQUERADE: 对匹配的流量进行源地址伪装(SNAT),即隐藏容器内原始 IP,使流量看起来像是从 tun0 接口的 IP 发出的。

作用:

  • 允许容器内通过 OpenVPN 隧道(tun0)访问外部网络时,自动修改数据包的源 IP,确保返回的流量能正确路由回容器。

  • 典型场景:容器内其他服务需要通过 OpenVPN 访问外部资源时(如代理流量),此规则确保流量能正常通过 VPN 隧道并返回。

[root@vpn openvpn]# docker exec -it openvpn /bin/sh -c "iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE"

# 验证
[root@vpn openvpn]# ping 120.10.255.10
PING 120.10.255.10 (120.10.255.10) 56(84) bytes of data.
64 bytes from 120.10.255.10: icmp_seq=1 ttl=62 time=8.58 ms
64 bytes from 120.10.255.10: icmp_seq=2 ttl=62 time=6.17 ms

3. 开机自动配置 iptables 规则和静态路由

[root@vpn openvpn]# vim /usr/local/bin/openvpn-route.sh
#!/bin/bash
set -e

# 1) openvpn 使用的网段
OPENVPN_NET="120.10.255.0/24"
OPENVPN_GW=$(sudo docker inspect openvpn   | jq -r '.[0].NetworkSettings.Networks[].IPAddress')

# 2) 宿主机 NAT
iptables -t nat -C POSTROUTING -s "$OPENVPN_NET" -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s "$OPENVPN_NET" -j MASQUERADE

# 3) 静态路由(指向 openvpn 容器 IP 作为网关)
sudo ip route | grep -q "$NET via $OPENVPN_GW" || \
sudo route add -net $OPENVPN_NET gw $OPENVPN_GW

# 4) 容器内 NAT
docker exec openvpn /bin/sh -c \
  "iptables -t nat -C POSTROUTING -o tun0 -j MASQUERADE 2>/dev/null || \
   iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE"
[root@vpn openvpn]# chmod +x /usr/local/bin/openvpn-route.sh
[root@vpn openvpn]# vim /etc/systemd/system/openvpn-route.service
[Unit]
Description=Configure OpenVPN NAT and Routing
After=network.target docker.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/openvpn-route.sh
RemainAfterExit=true

[Install]
WantedBy=multi-user.target
[root@vpn openvpn]# systemctl daemon-reexec && systemctl daemon-reload && systemctl enable openvpn-route.service --now

4. vpn 服务端(局域网内)访问 vpn 客户端地址(120.10.255.0/24)

局域网内的其他机器,比如 web 机器、mysql 机器 k8s 机器等等,想要访问到远在异地已经登陆 vpn 客户端

192.168.100.10 是 openvpn 所在的宿主机本机 IP

[root@k8s-app-1 ~]# ip route add 120.10.255.0/24 via 192.168.100.10
[root@mysql-server ~]# ip route add 120.10.255.0/24 via 192.168.100.10
[root@web-server ~]# ip route add 120.10.255.0/24 via 192.168.100.10

# 验证
[root@k8s-app-1 ~]# ping 120.10.255.10
PING 120.10.255.10 (120.10.255.10) 56(84) bytes of data.
64 bytes from 120.10.255.10: icmp_seq=1 ttl=62 time=8.58 ms
64 bytes from 120.10.255.10: icmp_seq=2 ttl=62 time=6.17 ms

3. 配置指定客户端静态地址

1. 静态 IP 分配

为特定客户端分配固定的 IP 地址:

比如指定用户 client1 的静态地址为:120.10.255.10

# 在 ccd 目录下创建以客户端证书名命名的文件
tianxiang@tianxiang:~/docker-app/openvpn/conf/ccd$ cat client1
ifconfig-push 120.10.255.10 255.255.255.0

2. 推送特定路由

为特定客户端推送额外的路由:

# ccd/client1 文件内容
iroute 192.168.1.0 255.255.255.0
push "route 192.168.1.0 255.255.255.0"

五、监控工具

用来监控容器是否退出,如果退出则重新启动容器

1. 告警通知脚本

[root@vpn openvpn]# mkdir monitor
[root@vpn openvpn]# vim monitor/alert.py 
import smtplib
import os
import argparse
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header

def send_alert_email(subject, body):
    """发送告警邮件"""
    # 从环境变量获取配置
    sender_email = os.getenv("SENDER_EMAIL", "xiahediyijun@163.com")
    sender_name = os.getenv("SENDER_NAME", "OpenVPN 监控告警")
    receiver_email = os.getenv("RECEIVER_EMAIL", "2099637909@qq.com")
    password = os.getenv("EMAIL_PASSWORD", "xxxxxxxxx")
    smtp_server = os.getenv("SMTP_SERVER", "smtp.163.com")
    smtp_port = int(os.getenv("SMTP_PORT", "465"))

    # 构建邮件内容
    msg = MIMEMultipart()
    msg['From'] = Header(f"{sender_name} <{sender_email}>", 'utf-8')
    msg['To'] = receiver_email
    msg['Subject'] = Header(subject, 'utf-8')
    msg.attach(MIMEText(body, 'plain', 'utf-8'))

    try:
        # 连接 SMTP 服务器并发送
        with smtplib.SMTP_SSL(smtp_server, smtp_port) as server:
            server.login(sender_email, password)
            server.sendmail(sender_email, [receiver_email], msg.as_string())
            print(f"[ALERT] 告警邮件发送成功: {subject}")
            return True
    except Exception as e:
        print(f"[ALERT] 告警邮件发送失败: {e}")
        return False

def parse_arguments():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='发送告警邮件工具')
    parser.add_argument('--alert', nargs=2, metavar=('SUBJECT', 'BODY'), 
                       required=True, help='发送告警邮件的主题和内容')
    return parser.parse_args()

if __name__ == "__main__":
    args = parse_arguments()
    send_alert_email(args.alert[0], args.alert[1])

2. 监控主程序脚本

[root@vpn openvpn]# vim monitor/monitor_openvpn.sh 
#!/bin/bash

# 配置日志文件和检查间隔
LOG_FILE="/var/log/monitor_openvpn_container.log"
CHECK_INTERVAL=60
CONTAINER_NAME_PATTERN="openvpn"
PYTHON_SCRIPT="/etc/openvpn/monitor/alert.py"  # 替换为真实路径
ALERT_COUNT=0  # 告警触发次数

# 通过环境变量传递密码(更安全)
export EMAIL_PASSWORD="xxxxxxxxxxxx"

# 日志记录函数
function log_message {
    echo "$(date '+%Y-%m-%d %H:%M:%S'): $1" >> "$LOG_FILE"
}

# 告警发送函数
function send_alert {
    local subject="$1"
    local body="$2"
    log_message "触发告警: $subject - $body"
    python3.9 "$PYTHON_SCRIPT" --alert "$subject" "$body" >> "$LOG_FILE" 2>&1
}

# 获取容器的最后 20 行日志
function get_container_logs {
    local container_name=$1
    docker logs --tail 20 "$container_name"
}

# 获取容器的详细信息
function get_container_info {
    local container_name=$1
    CONTAINER_IMAGE=$(docker inspect --format '' "$container_name")
    CONTAINER_NAME=$(docker inspect --format '' "$container_name" | sed 's/\///')
}

# 主监控循环
while true; do
    # 获取容器的 ID
    CONTAINER_ID=$(docker ps -qf "name=$CONTAINER_NAME_PATTERN")

    if [ -z "$CONTAINER_ID" ]; then
        log_message "容器不存在,尝试重启..."

        # 尝试重启容器
        if ! docker restart "$CONTAINER_NAME_PATTERN"; then
            get_container_info "$CONTAINER_NAME_PATTERN"
            LOGS=$(get_container_logs "$CONTAINER_NAME_PATTERN")
            ALERT_COUNT=$((ALERT_COUNT + 1))
            send_alert "OpenVPN 容器重启失败" "
            时间: $(date '+%Y-%m-%d %H:%M:%S')
            触发次数: $ALERT_COUNT
            容器名称: $CONTAINER_NAME
            容器镜像: $CONTAINER_IMAGE
            容器日志: 
            $LOGS"
        else
            # 容器重启后等待 5 秒,检查容器是否成功启动
            sleep 5
            CONTAINER_ID=$(docker ps -qf "name=$CONTAINER_NAME_PATTERN")

            if [ -z "$CONTAINER_ID" ]; then
                get_container_info "$CONTAINER_NAME_PATTERN"
                LOGS=$(get_container_logs "$CONTAINER_NAME_PATTERN")
                ALERT_COUNT=$((ALERT_COUNT + 1))
                send_alert "OpenVPN 容器重启失败" "
                时间: $(date '+%Y-%m-%d %H:%M:%S')
                触发次数: $ALERT_COUNT
                容器名称: $CONTAINER_NAME
                容器镜像: $CONTAINER_IMAGE
                容器日志: 
                $LOGS"
            else
                get_container_info "$CONTAINER_NAME_PATTERN"
                LOGS=$(get_container_logs "$CONTAINER_NAME_PATTERN")
                ALERT_COUNT=$((ALERT_COUNT + 1))
                send_alert "OpenVPN 容器重启成功" "
                时间: $(date '+%Y-%m-%d %H:%M:%S')
                触发次数: $ALERT_COUNT
                容器名称: $CONTAINER_NAME
                容器镜像: $CONTAINER_IMAGE
                容器日志: 
                $LOGS"
            fi
        fi
    else
        # 获取容器状态信息
        CONTAINER_STATUS=$(docker inspect --format '' "$CONTAINER_ID")

        if [ "$CONTAINER_STATUS" == "restarting" ]; then
            get_container_info "$CONTAINER_NAME_PATTERN"
            LOGS=$(get_container_logs "$CONTAINER_NAME_PATTERN")
            ALERT_COUNT=$((ALERT_COUNT + 1))
            send_alert "OpenVPN 容器重启" "
            时间: $(date '+%Y-%m-%d %H:%M:%S')
            触发次数: $ALERT_COUNT
            容器名称: $CONTAINER_NAME
            容器镜像: $CONTAINER_IMAGE
            容器日志: 
            $LOGS"
        fi
    fi

    sleep $CHECK_INTERVAL
done

3. 守护进程

[root@vpn openvpn]# vim /etc/systemd/system/monitor_openvpn.service

[Unit]
Description=Monitor openvpn Docker container
After=docker.service

[Service]
Type=simple
Restart=always
User=root
ExecStart=/etc/openvpn/monitor/monitor_openvpn.sh
ExecStop=  

[Install]
WantedBy=default.target
[root@vpn openvpn]# systemctl daemon-reload
[root@vpn openvpn]# systemctl enable monitor_openvpn.service --now
[root@vpn openvpn]# systemctl status monitor_openvpn.service 
● monitor_openvpn.service - Monitor openvpn Docker container
     Loaded: loaded (/etc/systemd/system/monitor_openvpn.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2024-05-02 22:57:41 CST; 7s ago
   Main PID: 1373453 (monitor_openvpn)
      Tasks: 2 (limit: 9374)
     Memory: 1.1M
     CGroup: /system.slice/monitor_openvpn.service
             ├─1373453 /bin/bash /etc/openvpn/monitor_openvpn.sh
             └─1373484 sleep 10

手动触发测试

1-zpZV.png

2-AifA.png

3-FlyV.png

五、客户端使用

1. Windows

  1. 下载Windows OpenVPN客户端(64位):https://openvpn.net/client-connect-vpn-for-windows/

  2. 安装客户端并打开OpenVPN GUI

  3. 将.ovpn文件拖放到OpenVPN GUI窗口中

  4. 输入用户名和密码

  5. 点击”连接”,即可开始使用OpenVPN连接

2. MacOS

  1. 下载MacOS OpenVPN客户端:https://openvpn.net/client-connect-vpn-for-mac-os/

  2. 安装客户端并打开OpenVPN Connect

  3. 将.ovpn文件拖放到OpenVPN Connect窗口中

  4. 输入用户名和密码

  5. 点击”连接”,即可开始使用OpenVPN连接

3. iOS

  1. 下载iOS OpenVPN客户端:https://apps.apple.com/us/app/openvpn-connect/id590379981

  2. 安装客户端并打开OpenVPN Connect

  3. 将.ovpn文件发送到您的iOS设备

  4. 在iOS设备上选择“打开方式”为“OpenVPN Connect”

  5. 输入用户名和密码

  6. 点击”连接”,即可开始使用OpenVPN连接

4. Linux Ubuntu 使用

  1. 安装 openvpn 命令程序

sudo apt-get install openvpn
  1. 把上面生成的 ovpn 复制到 Linux 中

root@big-server:/etc/openvpn/client# ls
世纪互联.ovpn  password.txt
root@big-server:/etc/openvpn/client# cat 世纪互联.ovpn 
client
nobind
dev tun
remote-cert-tls server

remote 47.120.62.2 1194 udp

script-security 2
route-method exe
route-delay 2
tun-mtu 1500
mssfix 1450

# 显式禁用IPv6
pull-filter ignore "route-ipv6"
pull-filter ignore "ifconfig-ipv6" 
pull-filter ignore "block-ipv6"

# 如果服务器推送了不兼容的选项,忽略它们
pull-filter ignore "dhcp-option"

<key>
-----BEGIN ENCRYPTED PRIVATE KEY-----
.......................................................
-----END ENCRYPTED PRIVATE KEY-----
</key>
<cert>
-----BEGIN CERTIFICATE-----

................................................................
-----END CERTIFICATE-----
</cert>
<ca>
-----BEGIN CERTIFICATE-----
...........................
-----END CERTIFICATE-----
</ca>
key-direction 1
<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
................................
-----END OpenVPN Static key V1-----
</tls-auth>
  1. 启动客户端

# 创建私钥登陆密码
root@big-server:/etc/openvpn/client# echo "123456" > password.txt

# 创建 systemd 守护进程
root@big-server:/etc/openvpn/client# cat /etc/systemd/system/openvpn-client.service
[Unit]
Description=OpenVPN client service
After=network.target
 
[Service]
Type=simple
ExecStart=/usr/sbin/openvpn --cd /etc/openvpn/client --config /etc/openvpn/client/世纪互联.ovpn --askpass /etc/openvpn/client/password.txt --log-append /var/log/openvpn-client.log
Restart=always
 
[Install]
WantedBy=multi-user.target

# 启动
root@big-server:/etc/openvpn/client# systemctl enable openvpn-client.service --now
  1. 查看日志和网络信息

root@big-server:/etc/openvpn/client# tail -f /var/log/openvpn-client.log 
Thu Sep 25 23:26:15 2025 UDP link remote: [AF_INET]60.10.34.143:31194
Thu Sep 25 23:26:15 2025 [60.10.34.143] Peer Connection Initiated with [AF_INET]60.10.34.143:31194
Thu Sep 25 23:26:16 2025 TUN/TAP device tun0 opened
Thu Sep 25 23:26:16 2025 /sbin/ip link set dev tun0 up mtu 1500
Thu Sep 25 23:26:16 2025 /sbin/ip addr add dev tun0 local 10.100.255.10 peer 10.100.255.9
Error: Invalid prefix for given prefix length.
Thu Sep 25 23:26:18 2025 ERROR: Linux route add command failed: external program exited with error status: 2
RTNETLINK answers: File exists
Thu Sep 25 23:26:18 2025 ERROR: Linux route add command failed: external program exited with error status: 2
Thu Sep 25 23:26:18 2025 Initialization Sequence Completed
tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
        inet 10.100.255.10  netmask 255.255.255.255  destination 10.100.255.9
        inet6 fe80::39a6:fd51:504:c2d8  prefixlen 64  scopeid 0x20<link>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 100  (未指定)
        RX packets 12  bytes 2333 (2.3 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 19  bytes 2746 (2.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  1. 测试ping

root@big-server:/etc/openvpn/client# ping 10.100.255.1
PING 10.100.255.1 (10.100.255.1) 56(84) bytes of data.
64 字节,来自 10.100.255.1: icmp_seq=1 ttl=64 时间=32.2 毫秒
64 字节,来自 10.100.255.1: icmp_seq=2 ttl=64 时间=26.5 毫秒

VPN与网络技术
openvpn
License:  CC BY 4.0
Share

Further Reading

Sep 17, 2025

锐捷交换机基础配置

本文详细介绍了锐捷网络交换机(型号RG-S5300-48GT4XS-E和RG-S6510-48VS8CQ)的系统重置、登录配置、静态路由设置以及多台设备堆叠等操作步骤。首先,对于RG-S5300-48GT4XS-E,通过在开机时按特定组合键进入特权模式并删除现有配置文件来实现系统重置;而对于RG-S6510-48VS8CQ,则需要使用不同的按键组合及命令行指令完成类似操作。接着,文章展示了如何为设备配置SSH服务、Web管理界面以及DNS服务器地址,并设置了基本的网络安全措施如访问控制列表。此外,还提供了关于如何创建VLAN并将端口分配到指定VLAN内的指南,包括了端口聚合技术的应用实例。最后,文档说明了如何使两台或更多台交换机以堆叠形式工作,并确保它们之间能够高效地进行数据传输,同时也涵盖了直接连接两个交换机时所需配置Trunk模式的方法。

Jul 22, 2025

Docker 快速部署 OpenVPN

本文介绍了OpenVPN的部署和使用方法。OpenVPN是一种基于OpenSSL库的应用层虚拟专用网络(VPN)解决方案,适用于多种操作系统,提供安全的数据传输。文章详细描述了如何在Linux环境下通过Docker容器来部署OpenVPN服务端,包括拉取镜像、创建挂载目录、生成配置文件和密钥等步骤,并提供了客户端证书的生成与配置方法。此外,还介绍了如何通过脚本新增或删除用户以及进行网络环境配置,如静态路由设置和iptables规则配置,以确保数据包能够正确转发。最后,文章提到了如何在Windows、MacOS、iOS及Linux Ubuntu上安装并使用OpenVPN客户端连接到已部署的服务端。整个过程强调了安全性的重要性,同时也考虑到了易用性和灵活性。

OLDER

部署使用 JumpServer 堡垒机

NEWER

Kubernetes 安装部署 MySQL-Operater

Recently Updated

  • Kubernetes 安装部署 Alist 并配置 Onlyoffice
  • KubeSphere-04-Dev-ops 流水线插件的使用
  • KubeSphere-03-Logging 日志插件的使用
  • KubeSphere-02-Service Mesh 的使用
  • KubeSphere-01-介绍与基础使用

Trending Tags

KVM Service Mesh Docker shell 路由规则 Mysql Containerd GitOps 网络设备 Prometheus

Contents

©2025 甄天祥-Linux-个人小站. Some rights reserved.

Using the Halo theme Chirpy