请勿在不恰当的地方使用相关技术,否则后果自负
需求背景
众所周知,将SSH端口直接暴露在公网上是有一定风险的。尽管基于证书的验证是相当安全的,但是只要端口暴露了就总会有人试图往里面泼水。在阿里云上也是一样。很显然,只是需要SSH连接的话可以用网页端的免密登录来进行操作。然而,有的时候需要的并不仅仅是一个SSH端口。例如,YDJSIR在做数据集成的作业时就发现Hadoop,Spark等软件一旦启动会暴露非常多的端口,且默认状态下都没有鉴权,这显然是很危险的。特别是Spark,总要绑定一个网卡实际有的IP才行,但那是阿里云内网IP,没法直接访问(其实有一个workaround,先给当前ECS实例加一个弹性网卡,然后开一个弹性公网IP,以EIP网卡可见模式
绑定即可)。然而,即使可以访问,这也是非常不安全的。为了解决上述问题,最直接的思路显然是想办法让运维/开发人员的设备与云服务器置身同一内网中。不过,阿里云提供的相关服务门槛相当高,不适合小微开发者。既然如此,那么就自己动手,丰衣足食吧。YDJSIR选择了开源的OpenVPN (UDP+TUN方案)来解决这样的需求。为什么选择它?他和传统L2TP,PPTP相比要现代、安全和高效,和Wireguard相比有更好的跨平台支持和文档(也许?)。那为什么YDJSIR不用第二代光线呢?YDJSIR知道非国际线路大概没事但是不想去触发alert。OpenVPN阿里云是有在国内站给官方教程的,那就是它了吧(参考资料附)毕竟YDJSIR从初中开始就在用OpenVPN了。
实现效果
开发者可以以ECS0为跳板,连接其提供的OpenVPN后直接用内网IP访问VPC内多个虚拟交换机下的资源。
ECS0配置选择1C512M,x86架构即可,毕竟按量对外一条宽带最大也就200M。
参考资料
序号 | 标题 | 链接 | 备注 |
---|---|---|---|
1 | How To Set Up and Configure an OpenVPN Server on CentOS 7 | https://www.digitalocean.com/community/tutorials/how-to-set-up-and-configure-an-openvpn-server-on-centos-7 | 本文主要参考的方案 |
2 | 【干货】ECS服务器OPENVPN搭建,方便管理所有内网服务器 | https://chowdera.com/2021/05/20210528104005167Q.html | 本文配置的重要参考 |
3 | 在CentOS系统的ECS实例中如何配置OpenVPN | https://help.aliyun.com/document_detail/42521.html | 阿里云关于OpenVPN的帮助文档 |
4 | 解决通过openvpn能ping通服务器,tcp连接不通的问题 | https://www.icode9.com/content-4-148712.html | 解决能ping不能TCP访问的问题 |
5 | Linux实例常用内核网络参数介绍与常见问题处理 | https://help.aliyun.com/document_detail/41334.html | 阿里云官方关于参考4的解释 |
配置过程
下面命令均在阿里云网页版终端中使用一个非root但有管理员权限的用户ecs-assist-user
完成。该云服务器的操作系统是CentOS 7.9,安全组已对外放开1194端口的UDP访问。
软件安装
1 | sudo yum update -y |
按照参考1的说法,easy-rsa
2 的文档更为齐全,因此暂定使用easy-rsa2。请注意以下这里OpenVPN的版本,并在后面涉及的路径处自行对应修改。
1 | wget -O /tmp/easyrsa https://github.com/OpenVPN/easy-rsa-old/archive/2.3.3.tar.gz |
生成证书
首先,新建一个用来放密钥的文件目录。
1 | sudo mkdir /etc/openvpn/easy-rsa/keys |
而后,修改生成密钥所需要的变量。当然,具体生成证书的时候,下面的值只是默认值,可以修改,但是显然这里写好了下面省事。
1 | sudo vim /etc/openvpn/easy-rsa/vars |
1 | # /etc/openvpn/easy-rsa/vars 在对应的位置修改 |
而后,让这些环境变量在当前终端中生效。
1 | source /etc/openvpn/easy-rsa/vars |
接下来,让我们生成所需要的证书。
1 | cd /etc/openvpn/easy-rsa |
服务端配置
配置文件
下面的配置文件为/etc/openvpn/server.conf
1 | local $"要绑定的网卡的==内网==IP" |
配置详解
1、==OpenVPN监听的地址==。这里必须写系统内ifconfig
显示的实际IP,不能写它对应的公网IP。OpenVPN通过这个绑定到具体网卡。当然,如果你启用了EIP可见模式,那么这里显然就是公网IP了。
2、监听的端口号
3、选择TCP/UDP模式。
4、选择TAP/TUN模式。注意TCP要搭配TAP食用。TAP工作在第二层而TUN工作在第三层,后者效率可能更高。具体区别和优缺点请自行检索。
5、CA证书路径
6、服务端证书路径
7、密钥分发文件路径、
8、服务端证书密钥路径
9、TLS加密文件,详见https://openvpn.net/vpn-server-resources/tls-control-channel-security-in-openvpn-access-server/
10、OpenVPN的内网网段和子网掩码
11、存储已分配的IP地址的文件位置
12、==允许为同一个客户端证书对应的多个连接分配多个IP地址==。如果希望复用一份配置文件的话,这是必须的,不然会出现IP地址冲突。
13、保活时间
14、传输时的压缩算法
15&16、使OpenVPN以低权限等级运行,以此增强安全性。
17&18、
19&20、状态与日志文件的路径
21、日志文件级别
23&24、==配置你需要让客户端能访问到的阿里云VPC网段==。这些网段首先肯定是你的服务器本身就能访问到的网段。这一部分请根据业务实际需要具体配置。
配置路由
配置转发规则
我们利用firewalld
及其背后的iptables
实现转发。在之前,我们只是完成了OpenVPN链路上本身的连接,要让数据在OpenVPN网卡和目标网卡中自由流动,我们需要配置路由与防火墙规则。第5行,将OpenVPN子网下的地址用静态NAT的方式映射到目标网卡上。这样一来,我们的OpenVPN就能联通其他内网了。注意这里只转发了IPv4,不过问题不大。
之所以使用firewalld
而不是直接使用iptables
,是因为前者可以持久化保存已配置
1 | sudo firewall-cmd --permanent --add-masquerade # 开启IP masquerade |
修改系统内核参数
1 | sudo vim /etc/sysctl.conf |
修改完成后的效果如下面所示。
1 | vm.swappiness = 0 |
最后两行,具体情况请参考5的问题5。这是通过搜索引擎得出参考4后额外找的资料。
不配置这个会导致能ping通阿里云内网其他设备,但是无法与他们建立任何TCP连接的情况。
输入以下命令,使得以上的参数生效。
1 | sudo sysctl -p |
启动服务端
1 | systemctl enable openvpn@server |
重启服务器,接下来,OpenVPN将伴随系统一起启动。
客户端配置
配置文件与密钥
下方的文件应该是跨平台的。客户端需要以下内容:
client.opvn
1 | client |
ca.crt
服务端前文已生成client.crt
服务端前文已生成client.key
服务端前文已生成myvpn.tlsauth
服务端前文已生成
Windows平台配置
1、安装OpenVPN提供的安装包。实测2.5.4可用。
2、把配置文件复制进用户目录下的OpenVPN
子目录下。效果如下图所示。
3、启动OpenVPN并连接。

Linux平台配置
1、安装Openvpn。这个直接从软件源装即可,Ubuntu用apt,CentOS用yum……在此不再赘述。版本尽量在2.4-2.5之间。
2、将配置文件及其依赖的文件复制到/etc/openvpn/client/config_v2
3、运行如下命令启动OpenVPN并连接。
1 | sudo openvpn --daemon --cd /etc/openvpn/client/config_v2 --config client.ovpn --log-append /var/log/openvpn.log |
总结回顾
这本来可能是一次很常规的部署活动,却意外地引入了内核参数调整禁用TCP时间戳这一步。为了解决这个问题,YDJSIR研究了很久,一直以为是自己配置的问题。直到忍不住用搜索引擎去搜,才发现是OpenVPN之外的原因。计网是复杂的,对于TCP/UDP连接模式的拷问,是必要的。YDJSIR在计算机网络方面要学的还有很多。