前言
VPN全称为Virtual private network,虚拟专用网络,工作原理是在客户端和服务端之间建立一条安全隧道,从而实现安全的访问内部网络。
请注意搭建国内VPN服务是合法的,非法的是利用VPN非法连接海外服务器,本文不会涉及违规内容!
常用的协议有L2TP、PPTP、IPSec、IKEV2、Wireguard、OpenVPN等,排列靠前的均为一些老经典的协议,相信CS专业的同学可能在课上都学会并且极为了解了,但这些协议由于相对较老,会出现安全系数低、容易被防火墙拦截、消耗性能高、上手难等问题,目前我个人推荐使用的是OpenVPN和Wireguard。Wireguard较新且开源,性能和易用性高,但目前支持度不是很高,感兴趣的同学可以自行研究,这里主要研究OpenVPN。
OpenVPN 是一种开源、跨平台、也是目前最常用的VPN 加密协议,使用 OpenSSL 加密库的 SSL/TLS 进行密钥交换,以便严密保护点对点或站点到站点连接,并且通过将数据分成小包来传输数据,协议开源且跨平台。
需求分析
使用OpenVPN的需求无非大致可以分为两种,第一种是需要访问内部网络,但又不想通过端口映射/反向代理将服务暴露在公网,所以搭建一个OpenVPN服务从而实现安全的访问内部网络;其次就是想实现两个主机或网络安全互通,甚至是两个内网,可以通过在一台有公网IP的VPS上搭建OpenVPN来实现两个内网的主机或网络互通。
(两种常见VPN使用场景示例图,非专业网工出身,拓扑图若有错误请您不吝赐教)
服务端
搭建OpenVPN的服务器系统没有要求,我这里使用的是Ubuntu Server 22.04系统
首先我们需要生成用于通信加密的证书,这里使用easy-rsa来生成
# 使用包管理器安装easy-rsa(centos系使用yum,debian系使用apt)
# cent系可能需要首先安装epel源 sudo yum install epel-release
sudo apt install easy-rsa
# 切换到easy-rsa安装目录
cd /usr/share/easy-rsa
# 在当前目录创建PKI目录
./easyrsa init-pki
# 创建根证书,会要求设置密码并设置名称
./easyrsa build-ca
# 创建server端证书和私钥文件,nopass表示不加密私钥文件,会要求设置名称
./easyrsa gen-req server nopass
# 给server端证书签名,会要求确认并输入之前设置的密码
./easyrsa sign server server
# 创建密钥交换使用的DH密钥
./easyrsa gen-dh
# 创建client端的证书和私钥文件,nopass表示不加密私钥文件,会要求设置名称
./easyrsa gen-req client nopass
# 给client端证书签名,会要求确认并输入之前设置的密码
./easyrsa sign client client
创建完成后可以使用tree命令查看创建情况,可以看到有ca.crt、client.crt、server.crt等证书文件
随后便可安装OpenVPN
# 使用apt安装openvpn
sudo apt install openvpn
# 编辑配置文件
/etc/openvpn/server.conf
配置文件以及参数如下,可以根据需求修改,我这里是配置的密码登录
port 11194 #端口
proto udp #协议
dev tun #采用路由隧道模式
ca /usr/share/easy-rsa/pki/ca.crt #ca证书的位置
cert /usr/share/easy-rsa/pki/issued/server.crt #服务端公钥的位置
key /usr/share/easy-rsa/pki/private/server.key #服务端私钥的位置
dh /usr/share/easy-rsa/pki/dh.pem #证书校验算法
server 192.168.13.0 255.255.255.0 #给客户端分配的地址池
push "route 172.19.0.0 255.255.255.0" #允许客户端访问的内网网段
push "dhcp-option DNS 114.114.114.114" # 下发给客户端的DNS
ifconfig-pool-persist ipp.txt #地址池记录文件位置,未来让openvpn客户端固定ip地址使用的
keepalive 10 120 #存活时间,10秒ping一次,120秒如果未收到响应则视为断线
max-clients 10 #最多允许10个客户端连接
status openvpn-status.log #日志位置,记录openvpn状态
log /var/log/openvpn.log #openvpn日志记录位置
verb 3 #openvpn版本
client-to-client #允许客户端与客户端之间通信
topology subnet
persist-key #通过keepalive检测超时后,重新启动VPN,不重新读取
persist-tun #检测超时后,重新启动VPN,一直保持tun是linkup的,否则网络会先linkdown然后再linkup
duplicate-cn #客户端密钥(证书和私钥)是否可以重复
compress lz4-v2 # 使用lz4-v2压缩
push "compress lz4-v2" # 推送给客户端压缩方式
script-security 3 #允许使用自定义脚本
cipher AES-256-CBC # 指定使用AES-256-CBC加密
push "cipher AES-256-CBC" # 下发给客户端加密算法
auth SHA256 # 指定验证使用SHA256算法
auth-user-pass-verify /etc/openvpn/server/check.sh via-env #指定认证脚本
username-as-common-name #用户密码登陆方式验证
verify-client-cert none #允许仅使用用户密码登录
进行用户名密码验证的check.sh
#!/bin/bash
PASSFILE="/etc/openvpn/users" #密码文件 用户名 密码明文
LOG_FILE="/var/log/openvpn-password.log" #用户登录情况的日志
TIME_STAMP=`date "+%Y-%m-%d %T"`
if [ ! -r "${PASSFILE}" ]; then
echo "${TIME_STAMP}: Could not open password file \"${PASSFILE}\" for reading." >> ${LOG_FILE}
exit 1
fi
CORRECT_PASSWORD=`awk '!/^;/&&!/^#/&&$1=="'${username}'"{print $2;exit}' ${PASSFILE}`
if [ "${CORRECT_PASSWORD}" = "" ]; then
echo "${TIME_STAMP}: User does not exist: username=\"${username}\",password=\"${password}\"." >> ${LOG_FILE}
exit 1
fi
if [ "${password}" = "${CORRECT_PASSWORD}" ]; then
echo "${TIME_STAMP}: Successful authentication: username=\"${username}\"." >> ${LOG_FILE}
exit 0
fi
echo "${TIME_STAMP}: Incorrect password: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE}
exit 1
随后还需要创建一个users文件,用于存储用户名和密码,每一行格式为 用户名 密码,完成后即可启动服务端
# 启动OpenVPN服务端
systemctl start openvpn@server
# 设置开机自启动
systemctl enable openvpn@server
# 开启内核转发,用户连接其他内网服务器
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
# 可选,添加路由
route add -net 192.168.13.0 netmask 255.255.255.0 gw 172.19.0.1
# 可选,开启NAT,从而可以访问内网其他服务器 (推荐)
iptables -t nat -A POSTROUTING -s 192.168.13.0/24 -o ens160 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 192.168.13.0/24 -o ens160 -j SNAT --to-source 172.19.0.1
# 验证OpenVPN服务是否成功启动
systemctl status openvpn@server
若需要指定客户端IP地址,需要修改配置文件以及添加ccd
# 配置文件添加读取特殊客户端配置文件的目录为ccd
client-config-dir ccd
# 编辑ccd/user01 (commonname)
ifconfig-push 192.168.13.10 255.255.255.0
客户端
客户端支持Windows、Linux、Macos,这里有OpenVPN-GUI和OpenVPN Connect,区别是OpenVPN-GUI是社区的开源应用,占用也较小,若是服务器可以选这个,而Connect的UI会友好些,同时占用也会更大,适合客户端使用。
安装完成后需要导入配置文件,配置文件示例如下,名称格式为xxxx.ovpn
remote后面的是服务端地址和端口,route-nopull和route是让客户端默认不走vpn,防止出现流量过大等情况,仅请求需要走VPN的地址才把流量发送到vpn网关。<ca></ca>中的内容是ca.crt的证书内容,嵌入配置文件便于使用,由于前面配置了用户名密码登录,所以这里只需要ca证书即可
client
dev tun
proto udp
remote xxx.xxx.xxx.xxx 11194
resolv-retry infinite
nobind
verb 3
auth SHA256
allow-compression yes
persist-key
auth-user-pass
auth-nocache
<ca>
xxx
</ca>
添加完配置文件后即可启动VPN