【新年新活】大内网2.0!抽象的异地组网+固定出口公网IP方案
大家好,欢迎来看我的新年新活[1]。本期作为之前的大内网战略[2]的延续,又带来的新的方案。
之前的方案有一些弊端,WireGuard原生使用UDP协议,在部分网络环境下会遭遇严重的丢包或拦截。虽然当时我们可以借助Phantun进行缓解,利用其将UDP数据流转换为伪装TCP数据流的特性,强行穿透三层和四层(NAPT)防火墙或NAT设备,但这种外挂工具链并不能在所有操作系统上完美兼容。因此,为了彻底解决协议层面的阻碍,本期我们将换用更成熟稳定的OpenVPN作为本项目的基础环境。
而且,我们使用的服务器是具有公网IP的。我们可以通过NAT端口转发,将我们大内网中的某个端口映射到公网上。比如说你的大内网中有一台服务器,这个时候你可以将服务器的业务端口映射到公网,然后将管理端口保留在大内网中进行异地管理。
在开始实操之前,依旧是我们传统的:
免责声明 (叠甲环节)
我需要使用专门的段落强调一下,此处所提到的 VPN 是一种技术,正确使用是合法的,目前国内有许多企业或者高校仍在使用 VPN 技术进行远程办公,所以不要谈 VPN 色变。
在开始之前,我们需要明确的三点是:
- 如果你在学校或者单位使用,请清楚由于此行为导致的危害,包括但不限于由于你的操作而导致的有意或者无意中的泄密或者危害到单位或者学校的其他设备的安全以及之后的经济或者人身安全。如果你符合条件,并执意要继续操作,则造成的所有责任均为你个人承担。
- 如果你正准备跟随本文的描述进行实践操作,请通篇阅读,并再三思考此文是否适合你。本文仅描述其中的一个个例(即我本人使用的方案),并不适合每个人,请在合适的时机做出自己的思考,切勿照搬照抄(每个人的网络环境都不一样)。
- 在操作之前,你需要具备基础的视力、计算机操作能力、Linux 基础知识、网络的基础知识、信息的检索能力。
抽象的异地组网
本期我们准备的是一个如图所示的拓扑环境。

目前的网络环境是:
openvpn-server和client-test可以互访。server-test可以访问到openvpn-server.client-test无法访问到server-test.
我们要达到的效果是:
- 在OpenVPN环境下,
client-test和server-test可以互访。 - 在公网环境下,
client-test只能访问到NAS网页,不允许连接SSH.
安装 OpenVPN 服务端
本期内容中,我使用的依旧是Ubuntu 24.04 LTS.
在这个项目中,我们使用angristan/openvpn-install项目在服务端安装和部署OpenVPN.
angristan/openvpn-install
Set up your own OpenVPN server on Debian, Ubuntu, Fedora, CentOS, Arch Linux and more
按照文档[3]要求,我们复制指令并进行下载:
curl -O https://raw.githubusercontent.com/angristan/openvpn-install/master/openvpn-install.sh
chmod +x openvpn-install.sh
由于swupdate.openvpn.net被屏蔽,你无法使用OpenVPN的官方仓库安装,需要修改脚本使得绕过官方仓库进行安装。(如果你目前不处于受到限制的网络可以忽略这一步)
Ubuntu的官方仓库中包含OpenVPN,如果没有配置自定义仓库,apt会从官方仓库中安装。如果你使用其他发行版,请另辟蹊径。
思路:如果你使用其他官方仓库中不包含OpenVPN的发行版,可以尝试提前安装OpenVPN,注释或者让脚本的步骤始终为
true.
使用这个命令在脚本中搜索这一步:
cat -n openvpn-install.sh | grep swupdate.openvpn.net

然后使用vim打开这个文件:
vim openvpn-install.sh
跳转到指定行
:1843
将curl -fsSL https://swupdate.openvpn.net/repos/repo-public.gpg -o /etc/apt/keyrings/openvpn-repo-public.asc替换,使其返回值始终为true.


然后保存并退出。
使用这个命令开始交互式的安装流程:
sudo ./openvpn-install.sh interactive
=== OpenVPN Installer ===
The git repository is available at: https://github.com/angristan/openvpn-install
I need to ask you a few questions before starting the setup.
You can leave the default options and just press enter if you are okay with them.
Detecting server IP addresses...
IPv4 address detected: 192.168.242.128
No IPv6 address detected
What IP version should clients use to connect to this server?
1) IPv4
2) IPv6
Endpoint type [1-2]: 1
这里选择用户通过IPv4地址或者是IPv6地址连接这台服务器,这里通常选择IPv4.
Server listening IPv4 address:
IPv4 address: 192.168.242.128
这里通常默认就好。如果你的服务器有多块网卡,可以使用Ctrl+C退出,并使用ip address(简写ip a)查看服务器的网卡,并在这个位置填写对应的IP地址。在实际应用场景中,服务器应避免使用DHCP,以防止IP地址变化导致前功尽弃。
undefined@hestudio-testserver:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:df:e8:91 brd ff:ff:ff:ff:ff:ff
altname enp2s1
inet 192.168.242.128/24 metric 100 brd 192.168.242.255 scope global dynamic ens33
valid_lft 1557sec preferred_lft 1557sec
inet6 fe80::20c:29ff:fedf:e891/64 scope link
valid_lft forever preferred_lft forever
例如,我的ip address输出结果如上。如果使用ens33网卡(我的机器只有一块网卡),那IPv4 address应该填写192.168.242.128. 如果你上一步选择了IPv6,这个位置应该填写对应的IP地址fe80::20c:29ff:fedf:e891.
It seems this server is behind NAT. What is its public IPv4 address or hostname?
We need it for the clients to connect to the server.
Public IPv4 address or hostname: xxx.xxx.xxx.xxx
这一步应该会自动填充你的服务器的公网IP。请确保此处显示的IP为你的服务器的出口公网IP,这个IP地址(或者域名)会被作为自动生成的ovpn文件中的服务器地址,请改为最终要使用的IP或者域名地址(不含端口号)。
What IP versions should VPN clients use?
This determines both their VPN addresses and internet access through the tunnel.
1) IPv4 only
2) IPv6 only
3) Dual-stack (IPv4 + IPv6)
Client IP versions [1-3]: 1
这一步我们要选择向VPN客户端分配的IP地址类型。本期我们的目的是用于异地组网,不需要使用服务器作为公网出口,只使用IPv4管理更方便。
IPv4 VPN subnet:
1) Default: 10.8.0.0/24
2) Custom
IPv4 subnet choice [1-2]: 1
这里会要求配置内网网段范围,可以根据个人喜好或者网络环境要求配置,我在这里选择默认。
What port do you want OpenVPN to listen to?
1) Default: 1194
2) Custom
3) Random [49152-65535]
Port choice [1-3]: 1
这里配置OpenVPN监听的端口号,同样可以根据个人喜好或者网络环境要求配置,我在这里选择默认。
What protocol do you want OpenVPN to use?
UDP is faster. Unless it is not available, you shouldn't use TCP.
1) UDP
2) TCP
Protocol [1-2]: 1
这里选择数据传输协议。一般情况下,使用UDP是最好的,但是如果你的网络有严重的UDP丢包问题[4],或者运营商对UDP进行QoS限制,则选择TCP是更好的选择。
What DNS resolvers do you want to use with the VPN?
1) Current system resolvers (from /etc/resolv.conf)
2) Self-hosted DNS Resolver (Unbound)
3) Cloudflare (Anycast: worldwide)
4) Quad9 (Anycast: worldwide)
5) Quad9 uncensored (Anycast: worldwide)
6) FDN (France)
7) DNS.WATCH (Germany)
8) OpenDNS (Anycast: worldwide)
9) Google (Anycast: worldwide)
10) Yandex Basic (Russia)
11) AdGuard DNS (Anycast: worldwide)
12) NextDNS (Anycast: worldwide)
13) Custom
DNS [1-13]: 3
这里会要求选择DNS地址,我们只作为异地组网使用,不需要DNS. 可以选择1,然后后续删掉对应配置。
Do you want to allow a single .ovpn profile to be used on multiple devices simultaneously?
Note: Enabling this disables persistent IP addresses for clients.
Allow multiple devices per client? [y/n]: n
这里选择是否允许单个ovpn文件可以被多台设备同时使用。在一般情况下,单个ovpn文件只能同时被一台设备连接,如果连接第二台设备将和第一台设备断开连接。这个管理方案非常适合企业运维部门统一管理设备。但是如果是个人家用的话,这样配置反而是一个负担,你需要为你的每一台设备都生成一个配置文件,增加了不必要的麻烦和管理成本。
我的建议是,如果是个人使用,则设置为y,这样将会自动为每一台设备分配IP地址。而网段内的服务器设备(例如NAS)则可以单独配置文件并指定IP.
- 为NAS等服务器设备每台单独生成配置文件,并固定IP. 每台设备一个配置文件。
- 生成一个配置文件,不指定IP地址,这个配置文件可以同时在电脑和手机上使用。
Do you want to customize the tunnel MTU?
MTU controls the maximum packet size. Lower values can help
with connectivity issues on some networks (e.g., PPPoE, mobile).
1) Default (1500) - works for most networks
2) Custom
MTU choice [1-2]: 1
这一步用于配置MTU值[5],如果你的个人设备是Windows系统,可以使用此方式测试允许的最大的MTU值。
使用以下命令测试当前网络允许的最大MTU:
Windows:
ping -n 4 -f <IP地址> -l <MTU>
Linux:
ping -c 4 -M do -s <MTU> <IP地址>
设置的MTU要求比测出的结果低,一般设置为1450或者1400即可。如果在日常使用中出现问题,可以尝试在配置文件中调整MTU值。
Choose the authentication mode:
1) PKI (Certificate Authority) - Traditional CA-based authentication (recommended for larger setups)
2) Peer Fingerprint - Simplified WireGuard-like authentication using certificate fingerprints
Note: Fingerprint mode requires OpenVPN 2.6+ and is ideal for small/home setups
Authentication mode [1-2]: 1
这一步选择鉴权方案。方案一是传统的鉴权方案,方案二是类似于WireGuard的鉴权方案。通常情况下选择1.
Do you want to customize encryption settings?
Unless you know what you're doing, you should stick with the default parameters provided by the script.
Note that whatever you choose, all the choices presented in the script are safe (unlike OpenVPN's defaults).
See https://github.com/angristan/openvpn-install#security-and-encryption to learn more.
Customize encryption settings? [y/n]: n
这里问是否自定义加密方案,通常情况下不需要,默认即可。如果有需求的话可以尝试阅读文档[6]自行调整。
Okay, that was all I needed. We are ready to setup your OpenVPN server now.
You will be able to generate a client at the end of the installation.
Press any key to continue...
在这一步按任意键进行安装。
=== New Client Setup ===
Tell me a name for the client.
The name must consist of alphanumeric characters, underscores, or dashes (max 64 characters).
Client name:
在安装后,可以创建客户端配置文件。我们需要去做一些修改,跳过此步骤,按Ctrl+C强制停止。
安装完成后,我们需要删除脚本自动在/etc/apt/sources.list.d添加的OpenVPN官方仓库,防止在后续使用中出现拉取仓库导致的超时问题。
sudo rm /etc/apt/sources.list.d/openvpn-aptrepo.list
部署 OpenVPN 服务端
我们现在需要修改配置文件,让OpenVPN的环境符合我们的要求。使用此脚本安装后,配置文件会在/etc/openvpn/server目录。

首先,修改server.conf文件。找到dhcp-option DNS相关字样,将包含此内容的全部删除。在这里,我们只使用OpenVPN用于内网连接,配置了DNS也是没有用的。
然后,找到redirect-gateway,然后将其删除。我们不需要使用这个服务器的网关上网。
最后,我们需要向客户端推送路由,让这个网段的地址通过OpenVPN的虚拟网卡解析。找到server,复制后面的网段信息。并按照以下格式填写。
如你的server是这样配置的:
server 10.8.0.0 255.255.255.0
就在下面新增一行,然后这样写:
push "route 10.8.0.0 255.255.255.0"
然后保存这个文件。
下一步,我们需要修改client-template.txt文件。这个文件是客户端配置文件的模板文件。编辑这个文件,找到setenv opt block-outside-dns # Prevent Windows 10 DNS leak,然后将这一行删除。这个配置的本意是防止Windows使用外部的DNS出现DNS泄露的问题。我们都没有配置DNS,保留这一行会导致Windows设备无法正常上网。
(可选但推荐)你可以将client-template.txt文件的tun-mtu一行删掉,这样在后续服务端修改MTU时无需额外修改客户端配置文件,系统会自动下发相关配置。
在完成修改后,使用这个命令重启OpenVPN Server:
sudo systemctl restart openvpn-server@server.service
然后前往你下载openvpn-install.sh脚本的位置,并运行此脚本进行客户端配置文件创建。
sudo ./openvpn-install.sh interactive
=== OpenVPN Management ===
The git repository is available at: https://github.com/angristan/openvpn-install
[OK] OpenVPN is already installed.
What do you want to do?
1) Add a new user
2) List client certificates
3) Revoke existing user
4) Renew certificate
5) Remove OpenVPN
6) List connected clients
7) Exit
Select an option [1-7]:
这里我们选择1进行新建用户。
=== New Client Setup ===
Tell me a name for the client.
The name must consist of alphanumeric characters, underscores, or dashes (max 64 characters).
Client name:
这里需要给客户端命名,这个命名将会在后面自定义客户端配置时用到。名称必须由字母数字字符、下划线或破折号组成(最多 64 个字符)。
How many days should the client certificate be valid for?
Certificate validity (days): 3650
这个位置设置证书的过期时间,一般保持默认即可。
Do you want to protect the configuration file with a password?
(e.g. encrypt the private key with a password)
1) Add a passwordless client
2) Use a password for the client
Select an option [1-2]: 1
这里可以选择是否给配置文件添加密码。一般来说,不建议给服务器配置文件添加密码,方便无人值守的自动启动。
在完成后,你可以将配置文件拷贝到客户端机器并导入。如果你使用Ubuntu作为客户端机器,可以参考这个导入方案。
将配置文件放到/etc/openvpn/client并使用xyz.conf的方式命名,然后运行以下命令:
sudo systemctl enable openvpn-client@xyz.service
sudo systemctl start openvpn-client@xyz.service
其中,xyz应该修改为你的配置文件名称。
在这个实验中,我们创建了server-test和client-test两个配置文件。现在,我们需要给server-test进行IP固定。
前往/etc/openvpn/server/ccd/目录,并在这个目录新建一个和你的客户端一致的名称。比如我的客户端名称是server-test就新建这个文件。
cd /etc/openvpn/server/ccd/
sudo touch server-test
然后设置一个在你的服务器网段内的IP地址,比如你要设置的IP地址是10.8.0.100,则可以这样设置。
ifconfig-push 10.8.0.100 255.255.255.0
现在已经实现client-test和server-test互访。


client-test连接server-test截图固定出口公网IP方案
我们已经实现了利用OpenVPN打通了本来相互隔离的设备。接下来,我们将开启防火墙,并将内网的指定端口转发到公网。
在接下来的操作中,我将切换到root权限进行。
sudo su -
安装firewalld
我们将使用firewalld实现防火墙与端口转发。在Ubuntu系统中,通常默认使用ufw.我们首先需要将ufw关闭。
ufw disable
systemctl stop ufw
然后安装firewalld
apt update
apt install firewalld
部署firewalld
使用这个命令获取可用的zone[7](区域)
root@hestudio-testserver:~# firewall-cmd --get-zones
block dmz drop external home internal public trusted work
我们可以看到输出如上。在一般情况下,服务器对外工作的网卡应该在public区域,服务器在内网工作的网卡应该工作在internal区域。
root@hestudio-testserver:~# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:df:e8:91 brd ff:ff:ff:ff:ff:ff
altname enp2s1
3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1450 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 500
link/none
我们可以看到当前设备的网卡信息,ens33是连接公网的网卡,而tun0是内网网卡。我们需要将ens33放到public区域,将tun0放在internal区域。
firewall-cmd --permanent --zone=public --add-interface=ens33
firewall-cmd --permanent --zone=internal --add-interface=tun0
firewall-cmd --reload
请在执行命令时务必加入
--permanent参数[8],否则将为临时配置,重启后会消失。
然后使用如下命令查看活动的区域:
root@hestudio-testserver:~# firewall-cmd --get-active-zones
internal
interfaces: tun0
public (default)
interfaces: ens33
出现类似的界面则代表配置成功。
接下来将为internal防火墙区域打开masquerade[9].
firewall-cmd --permanent --zone=internal --add-masquerade
firewall-cmd --reload
我们需要放行OpenVPN端口、SSH端口、以及其他需要的端口。
firewall-cmd --permanent --zone=public --add-port=22/tcp
firewall-cmd --permanent --zone=public --add-port=1194/tcp
firewall-cmd --permanent --zone=internal --add-port=22/tcp
firewall-cmd --reload
请根据你实际的情况编写命令。
在完成后,重新连接OpenVPN,确保客户端可以正常建立连接。
端口映射
现在,我们将server-test的5666端口映射到公网的8080端口,并在client-test断开OpenVPN的状态下使用openvpn-server的IP地址访问NAS.
firewall-cmd --permanent --zone=public --add-forward-port=port=8080:proto=tcp:toport=5666:toaddr=10.8.0.100
firewall-cmd --reload
配置后,即可在公网直接访问到内网中的NAS。

之前的大内网战略: 使用WireGuard组建大内网环境 ↩︎
文档: https://github.com/angristan/openvpn-install?tab=readme-ov-file#usage ↩︎
之前在大内网1.0的时候提到过: https://www.hestudio.net/posts/wireguard-tutorial.html#udp-流量伪装-phantun ↩︎
Security and Encryption: https://github.com/angristan/openvpn-install#security-and-encryption ↩︎
firewalld zone: https://firewalld.org/documentation/zone/ ↩︎
firewall-cmd man-page: https://firewalld.org/documentation/man-pages/firewall-cmd ↩︎
Network address translation (NAT) Wikipedia: https://en.wikipedia.org/wiki/Network_address_translation ↩︎