零、背景
之前的几篇博文中提到过,相较于使用frp等进行端口转发或反向代理,我更倾向于通过VPN访问位于家中的设备和服务。理由主要有三:1)公网不暴露端口,安全性更佳;2)常用平台均可使用VPN客户端或L2TP接入,可以分帐号进行权限/访问控制,进一步提升安全性;3)可以通过VPN将位于不同地点的设备联系起来实现万物互联,便于进行异地备份和数据共享等操作。
经过试用选型后最终放弃了以ZeroTier、N2N等支持P2P的VPN(主要是考虑到客户端兼容有限以及P2P穿透效果不佳),选择SoftEther作为实际部署的VPN。虽然这意味着所有流量都需要经过服务器,但6Mbps的带宽对于简单服务而言还算够用,且SoftEther提供多种接入方式,可以兼容我使用的所有平台;在过去数年间它也重复展现出了其易用性。
但一直存在的问题是,我没有在Linux上成功接入过SoftEther VPN。这一问题在我购入群晖NAS之后变得更加急需解决:群晖DSM只能通过L2TP或OpenVPN方式接入SoftEther VPN,但两种方式均不能自定义虚拟网络适配器的IP地址。不能固定IP最终让我放弃了直接在群晖NAS上接入VPN的想法,转而考虑用Linux虚拟机接入VPN并做转发/反向代理的方式。于是乎经过一番研究和实操,成功将一台Ubuntu虚拟机接入SoftEther VPN并实现群晖服务转发,以下是实现过程和遇到的一些问题。
一、Ubuntu Sever接入SoftEther VPN
最前面提一句,如无必要,不建议Ubuntu安装使用Network Manager L2TP接入VPN,经我反复尝试均无法成功,且未找到在Ubuntu上使用的详尽文档(当然如果使用的Linux发行版使用了nm,那不妨一试)。
个人建议直接使用SoftEther VPN的Linux客户端,虽然不提供GUI,但提供了与其他平台一致的完整功能。客户端通过源码形式分发,需要自行下载编译。
1.1、编译安装VPN客户端
首先安装编译所需软件:
sudo apt install build-essential gnupg2 gcc
从SoftEther官网下载和解压相应平台SoftEther VPN Client源码后,进入源码目录,进行编译:
make
启动VPN客户端则非常简单:
sudo ./vpnclient start
1.2、配置客户端
由于不提供GUI,对VPN客户端的配置通过编译得到的vpncmd进行,但基本原理和Windows端的客户端配置相同。启动vpncmd:
./vpncmd
选择“2. Management of VPN Client”并回车。和Windows客户端一样,先创建虚拟适配器:
# 注意:为了直观展示vpncmd的交互,这里会一同给出提示文字,例如此处的“VPN Client>”
# 提示字符不是命令的一部分,不需要输入
# 通过NicCreate创建适配器
VPN Client>NicCreate 虚拟适配器名称
然后创建VPN账户并选择使用哪个虚拟适配器:
# 通过AccountCreate创建账户
# 此处的账户名仅作为客户端中的标识,不必与登入VPN的用户名一致
VPN Client>AccountCreate 账户名
# 根据提示输入VPN服务器地址和端口
Destination VPN Server Host Name and Port Number: 地址:端口
# 根据提示输入虚拟Hub名称
Destination Virtual Hub Name: 虚拟Hub名
# 此处需要输入登入VPN的用户名
Connecting User Name: 用户名
# 指定使用的虚拟适配器
Used Virtual Network Adapter Name: 虚拟适配器名称
接着为VPN账户配置密码:
# 为刚才创建的账户配置密码,注意这里是账户名而非登入用户名
VPN Client>AccountPassword 账户名
Password: ************
Confirm input: ************
Specify standard or radius: radius
随后可以启动VPN连接,或将VPN连接设置为随客户端启动:
# 连接该账户
VPN Client>AccountConnect 账户名
# 将该账户设为随客户端启动
VPN Client>AccountStartupSet 账户名
如果想对客户端进行更多配置或操作的话,可以在官网文档找到所有命令的使用说明。
1.3、固定虚拟适配器IP
熟悉SoftEther VPN和netplan的话,这一步就相对简单了。VPN客户端在创建虚拟适配器时,会在系统中添加一个名为“vpn_虚拟适配器名”的网络适配器。为这个适配器设定固定IP即可在连接VPN后使用固定IP,那么这里我就简单举个通过netplan实现的例子。
首先在/etc/netplan/
下创建配置文件,内容大概如下,注意修改适配器名称和IP地址:
network:
version: 2
ethernets:
适配器名称:
dhcp4: no
addresses:
- 地址/掩码位数
随后应用netplan修改,即完成固定IP设置:
sudo netplan apply
1.4、VPN客户端随系统启动
将VPN客户端作为服务启动的配置这里踩了一下坑:DigitalOcean和SoftEther官网提供的文档严重过时,如果参考了类似这两篇中提到的方法,而无法成功启动客户端的话,考虑换一个参考。
我参考的是AskUbuntu上的一篇回答,经过验证在Ubuntu 22.04上可用,注意修改一下路径:
# /etc/systemd/system/softether-vpnclient.service
[Unit]
Description=SoftEther VPN Client
After=network.target
[Service]
Type=forking
ExecStart=/path/to/client/dir/vpnclient start
ExecStop=/path/to/client/dir/vpnclient stop
[Install]
WantedBy=multi-user.target
然后就可以将VPN客户端通过systemctl控制启停和随系统启动了,方法不再赘述。
二、转发群晖NAS服务
最初考虑过用Nginx做反向代理,但反代只能代理HTTP/HTTPS服务,需要使用SMB等不基于HTTP的服务的话则需要另想办法。所以选用了socat
直接进行端口转发,管它是HTTP还是SMB,TCP还是UDP,老实转发就完事儿了:
sudo apt install socat
测试通过命令转发SMB的445端口:
sudo socat tcp-listen:445,reuseaddr,fork tcp:群晖NAS的IP:445
在转发用虚拟机和SMB客户机均连接到VPN时,可通过VPN访问虚拟机445端口连接至群晖SMB服务。
接下来参考这篇回答,将其写成服务并设置随系统启动,注意根据情况进行修改:
# /etc/systemd/system/smb-forward.service
[Unit]
Description=SMB port forward service
After=network.target
[Service]
Type=simple
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=smb-forward
ExecStart=/usr/bin/socat -d -d tcp-listen:445,reuseaddr,fork,bind=VPN适配器地址 tcp:NAS地址:445
Restart=on-failure
RestartSec=10s
[Install]
WantedBy=multi-user.target
注意:这里配置了bind=VPN适配器地址,socat将绑定并仅监听来自VPN的连接;但系统启动时若VPN未完成连接,绑定地址失败将导致服务启动失败;经测试可以给服务加入自动重启,启动失败后重新尝试,通常可以解决问题。
类似的,转发HTTP/HTTPS端口,即可通过VPN经转发用虚拟机访问群晖服务。如果有意为HTTPS配置证书,同样可以参考前述的回答,为其配置证书和密钥文件,这里便不再赘述,仅引用一下回答中的配置供快速查看。
https://unix.stackexchange.com/questions/658312/how-to-run-socat-as-a-systemd-service-to-bridge-two-remote-unix-sockets# /etc/systemd/system/dovecot-auth-bridge.service [Unit] Description=Dovecot auth bridge After=dovecot.service Requires=dovecot.service [Service] Type=simple StandardOutput=syslog StandardError=syslog SyslogIdentifier=dovecot-auth-bridge ExecStart=socat -d -d OPENSSL-LISTEN:9999,reuseaddr,fork,cert=/etc/ssl/certs/dovecotserver.pem,key=/etc/ssl/private/dovecotserver.key,bind=10.10.20.5,cafile=/etc/ssl/certs/eximserver.pem UNIX:/run/dovecot/auth-client Restart=always [Install] WantedBy=multi-user.target
三、小结
理清思路后整个配置并不算太麻烦,主要是找错参考资料的话会走不少弯路。文中记录并给出了每个步骤的参考以供未来参考。讲真HTTP和HTTPS以后可能还是会用Nginx反代并配置证书自动续签,免得忘记更新证书导致连不上;socat用于转发SMB确实简单实用,但直接这样转发HTTP服务多少有点简单粗暴了,还是希望通过Nginx提供更精细的配置和管理。