前言

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

届ける言葉を今は育ててる
最后更新于 2024-08-17