OpenVPN笔记-01

Overview

1. 前言

VPN(Virtual Private Network),顾名思义,即虚拟专用网络,日常的主要应用都是在公网上创建专用网络,打通内网设备,笔者平时在家办公第一件事情就是连接公司VPN,然后ssh进入公司内网。

大多数情况下,许多人都把VPN和代理混淆了,例如使用VPN作为代理访问国外网站,结果被干扰得一塌糊涂。作为一款已经20岁的应用,OpenVPN的支持几乎遍布所有操作系统,连搬瓦工的控制台页面都有一键部署OpenVPN服务端的选项,但它给笔者的印象仍旧是:一种容易被探测和干扰的过时代理手段,因此笔者此前几乎没有主动了解过它。

笔者所在的公司目前提供了三种VPN接入方式:Cisco IPSec(macOS原生支持)、FortiClient(支持SSL VPN和IPSec)、OpenVPN,目前FortiClient在macOS 12上一运行就白屏,只能选择Cisco IPSec或者OpenVPN。Cisco IPSec配置简单,但有时连接不稳定,笔者就曾因为这个问题段时间内重复登录,导致账号被公司临时封禁,惊出一身冷汗,恰好最近也有使用OpenVPN的场景,就研究了下OpenVPN的运行原理和部署方案,这里稍做记录,当作一个备用方案。

2. 安装与配置OpenVPN服务端

服务端为Debian 10,其他发行版如Ubuntu 20.04和CentOS 7,也都一键就可以安装OpenVPN,版本都为2.4.xx。这里使用tap模式,用户名密码认证,且使用临时文件验证

1apt install openvpn -y

安装后创建配置文件 /etc/openvpn/server/tap0.conf,内容如下:

 1# 监听443端口
 2port 443
 3# 使用TCP作为传输方式,客户端会在一个TCP连接上承载所有流量
 4proto tcp
 5# 使用TAP模式
 6dev tap
 7# 设置MTU为1500
 8tun-mtu 1500
 9# 设置拓扑结构为子网
10topology subnet
11# 设置VPN网段,转换为CIDR格式对应10.100.0.0/24
12server 10.100.0.0 255.255.255.0
13# 客户端配置文件,若需要为客户端分配固定IP可以在此设置,按用户名(或CN)存放文件
14# client-config-dir /etc/openvpn/ccd
15# 自签名的根证书
16ca /etc/openvpn/server/ca.crt
17# 自签名的服务端证书
18cert /etc/openvpn/server/server.crt
19# 自签名的服务端密钥
20key /etc/openvpn/server/server.key
21# dh交换参数
22dh /etc/openvpn/server/dh2048.pem
23tls-server
24tls-version-min 1.2
25# 认证方式设置
26verify-client-cert none
27# 使用用户名作为CN
28username-as-common-name
29# 设置脚本安全等级,等级2允许通过文件传递认证信息
30script-security 2
31# 使用临时文件验证,用户名和密码保存在临时文件中,作为参数传给可执行文件
32auth-user-pass-verify /etc/openvpn/server/verify via-file
33# others
34keepalive 10 120
35cipher AES-256-CBC
36persist-key
37persist-tun
38# 状态文件,在此文件中可查看已连接的客户端信息
39status /var/log/openvpn/openvpn-status.log
40# 日志文件
41log-append  /var/log/openvpn/openvpn.log
42verb 3

认证插件如下:github.com/mikumaycry/example/2021/verify/main.go

简单说逻辑就是:取用户名的MD5来获取对应目录下的文件,读取文件后对比password,当然可以自定义一些更复杂的认证措施。

使用openssl生成随机的账号密码和配置文件

 1username=$(openssl rand -hex 16)
 2password=$(openssl rand -hex 16)
 3filename=$(echo -n $uaername|md5sum|awk '{print $1}')
 4mkdir -p /etc/openvpn/server/verify-ccd
 5cat << EOF > /etc/openvpn/server/verify-ccd/$filename
 6$password
 7EOF
 8echo "username $username"
 9echo "password $password"
10echo "file $filename"

启动服务

1systemctl enable --now [email protected]

检查网卡与日志

 1➜  ip addr show tap0
 264: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UNKNOWN group default qlen 100
 3    link/ether 5e:17:fa:5a:82:3a brd ff:ff:ff:ff:ff:ff
 4    inet 10.100.0.1/24 brd 10.100.0.255 scope global tap0
 5       valid_lft forever preferred_lft forever
 6    inet6 fe80::5c17:faff:fe5a:823a/64 scope link
 7       valid_lft forever preferred_lft forever
 8➜  ~ tail /var/log/openvpn/openvpn.log
 9/sbin/ip addr add dev tap0 10.100.0.1/24 broadcast 10.100.0.255
10Could not determine IPv4/IPv6 protocol. Using AF_INET
11Socket Buffers: R=[87380->87380] S=[65536->65536]
12Listening for incoming TCP connection on [AF_INET][undef]:443
13TCPv4_SERVER link local (bound): [AF_INET][undef]:443
14TCPv4_SERVER link remote: [AF_UNSPEC]
15MULTI: multi_init called, r=256 v=256
16IFCONFIG POOL: base=10.100.0.2 size=253, ipv6=0
17MULTI: TCP INIT maxclients=1024 maxevents=1028
18Initialization Sequence Completed

3. 安装与配置OpenVPN客户端

tap模式仅支持Linux和安装了tap插件的Windows系统,这里使用Linux演示,客户端也使用Debian 10,安装好openvpn后,创建配置文件 /etc/openvpn/client/tap0.conf,内容如下:

 1client
 2dev tap
 3nobind
 4proto tcp
 5tun-mtu 1500
 6remote 服务器IP 服务器端口
 7cipher AES-256-CBC
 8# 认证文件,第一行为用户名,第二行为密码
 9auth-user-pass /etc/openvpn/client/auth.txt
10ca /etc/openvpn/client/ca.crt
11persist-key
12persist-tun
13status /var/log/openvpn/openvpn-status.log
14log-append  /var/log/openvpn/openvpn.log

启动服务

1systemctl enable --now [email protected]

检查网卡与日志

 1➜  ~ ip addr show tap0
 266: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UNKNOWN group default qlen 100
 3    link/ether 8a:2b:21:61:ab:db brd ff:ff:ff:ff:ff:ff
 4    inet 10.100.0.2/24 brd 10.100.0.255 scope global tap0
 5       valid_lft forever preferred_lft forever
 6    inet6 fe80::882b:21ff:fe61:abdb/64 scope link
 7       valid_lft forever preferred_lft forever
 8➜  ~ tail /var/log/openvpn/openvpn.log
 9Attempting to establish TCP connection with [AF_INET]xxx.xxx.xxx.xxx:xxx [nonblock]
10TCP connection established with [AF_INET]xxx.xxx.xxx.xxx:xxx
11TCP_CLIENT link local: (not bound)
12TCP_CLIENT link remote: [AF_INET]xxx.xxx.xxx.xxx:xxx
13WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
14[Test-Server] Peer Connection Initiated with [AF_INET]xxx.xxx.xxx.xxx:xxx
15TUN/TAP device tap0 opened
16/sbin/ip link set dev tap0 up mtu 1500
17/sbin/ip addr add dev tap0 10.100.0.2/24 broadcast 10.100.0.255
18Initialization Sequence Completed

可以看到分配了10.100.0.2的内网IP,测试一下联通性

1➜  ~ ping 10.100.0.1
2PING 10.100.0.1 (10.100.0.1) 56(84) bytes of data.
364 bytes from 10.100.0.1: icmp_seq=1 ttl=64 time=391 ms
464 bytes from 10.100.0.1: icmp_seq=2 ttl=64 time=180 ms
564 bytes from 10.100.0.1: icmp_seq=3 ttl=64 time=183 ms
664 bytes from 10.100.0.1: icmp_seq=4 ttl=64 time=181 ms

10.100.0.0/24段的流量默认路由到了tap0,如果需要全局代理,设置默认路由即可

4. 公网IP绑定与内网服务暴露

上述的配置中,笔者只将VPN作为一个打通内网的工具来使用,如果需要为内网设备绑定外网IP或暴露内网服务到公网时,需要使用iptables设置转发规则。

回到server,查看所有网卡,如下:

 1➜  ~ ip addr
 21: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
 3    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
 4    inet 127.0.0.1/8 scope host lo
 5       valid_lft forever preferred_lft forever
 6    inet6 ::1/128 scope host
 7       valid_lft forever preferred_lft forever
 82: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
 9    link/ether 02:00:13:53:00:01 brd ff:ff:ff:ff:ff:ff
10    inet 200.59.100.2/24 brd 156.59.100.255 scope global eth0
11       valid_lft forever preferred_lft forever
12    inet 200.59.100.3/24 brd 156.59.100.255 scope global secondary eth0
13       valid_lft forever preferred_lft forever
14    inet 200.59.100.4/24 brd 156.59.100.255 scope global secondary eth0
15       valid_lft forever preferred_lft forever
16    inet 200.59.100.5/24 brd 156.59.100.255 scope global secondary eth0
17       valid_lft forever preferred_lft forever
18    inet 200.59.100.6/24 brd 156.59.100.255 scope global secondary eth0
19       valid_lft forever preferred_lft forever
2064: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UNKNOWN group default qlen 100
21    link/ether 5e:17:fa:5a:82:3a brd ff:ff:ff:ff:ff:ff
22    inet 10.100.0.1/24 brd 10.100.0.255 scope global tap0
23       valid_lft forever preferred_lft forever
24    inet6 fe80::5c17:faff:fe5a:823a/64 scope link
25       valid_lft forever preferred_lft forever

假设我们在eth0上绑定了200.59.100.2~200.59.100.6的IP,现在需要将这些IP一对一绑定到10.100.0.2~10.100.0.6的内网设置,且每个设备需要暴露80端口,则执行以下iptables命令

 1# 添加1条POSTROUTING上的SNAT规则,让10.100.0.0/24段流量走tap0设备
 2iptables -t nat -A POSTROUTING -d 10.100.0.0/24 -o tap0 -j SNAT --to-source 10.100.0.1
 3# 添加5条POSTROUTING上的SNAT规则,一对一绑定出站流量
 4iptables -t nat -A POSTROUTING -s 10.100.0.2/32 ! -d 10.100.0.0/24 -j SNAT --to-source 200.59.100.2
 5iptables -t nat -A POSTROUTING -s 10.100.0.3/32 ! -d 10.100.0.0/24 -j SNAT --to-source 200.59.100.3
 6iptables -t nat -A POSTROUTING -s 10.100.0.4/32 ! -d 10.100.0.0/24 -j SNAT --to-source 200.59.100.4
 7iptables -t nat -A POSTROUTING -s 10.100.0.5/32 ! -d 10.100.0.0/24 -j SNAT --to-source 200.59.100.5
 8iptables -t nat -A POSTROUTING -s 10.100.0.6/32 ! -d 10.100.0.0/24 -j SNAT --to-source 200.59.100.6
 9# 添加5条PREROUTING上的DNAT规则,允许80端口的进站流量
10iptables -t nat -A PREROUTING -d 200.59.100.2/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.100.0.2:80
11iptables -t nat -A PREROUTING -d 200.59.100.3/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.100.0.3:80
12iptables -t nat -A PREROUTING -d 200.59.100.4/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.100.0.4:80
13iptables -t nat -A PREROUTING -d 200.59.100.5/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.100.0.5:80
14iptables -t nat -A PREROUTING -d 200.59.100.6/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.100.0.6:80

在配置完成后,以 10.100.0.2 设备为例:

  • 访问内网IP:10.100.0.2 -> 10.100.0.1 -> 10.00.0.3
  • 访问外网IP:10.100.0.2 -> 10.100.0.1 -> 200.59.100.2 -> 8.8.8.8
  • 外网访问80端口:8.8.8.8 -> 200.59.100.2:80 -> 10.100.0.1 -> 10.100.0.2:80
comments powered by Disqus