配置NAT网关实现内网机器访问公网
云服务的机器如果没有公网IP,默认无法访问公网,自己配置NAT网关可解

我这两天在折腾服务器的时候,发现有个无公网IP的机器不能访问公网。

我就纳闷了,我只是不在公网暴露了,怎么网都直接断了,不应该跟家里的网络一样,通过路由器这个出口访问公网。后来搜索之后,发现这个现象是by design,默认没有出口。内网机器想要访问公网,需要经过NAT网关的转发。你可以购买现成的NAT服务,我看到它是按照小时算钱的,感觉不会便宜。还有一种方式,就是将有公网IP的机器配置为NAT网关,内网机器访问公网的流量都走它。

准备好一台有公网IP的机器(下文称网关服务器),本文以Ubuntu 22.04为例,通过以下三步来实现目标。

开启IP转发

一台机器默认情况下,只接收目标IP地址为自己的报文,其他地址的报文会被丢弃。Linux内核有专门的IP转发功能来控制这一行为,默认情况下这个功能是关闭的,我们需要将它打开,以转发内网机器的报文,充当路由器。

我们可以先通过以下两个命令之一查看当前系统IP转发能力是否已经开启:

cat /proc/sys/net/ipv4/ip_forward
sysctl net.ipv4.ip_forward

输出1表示开启,输出0表示关闭。如果你系统没有专门配置过,基本都会输出0。可以选择任一方式来修改:

# 修改内核参数的映射文件
sudo echo 1 > /proc/sys/net/ipv4/ip_forward
sudo echo 0 > /proc/sys/net/ipv4/ip_forward

# 使用sysctl命令修改参数
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv4.ip_forward=0

不过上面的修改只是临时的,重启系统过之后,这个参数就会恢复默认值,只适合你临时测试下。

如果要持久化修改,需要在/etc/sysctl.conf中添加如下一行配置:

net.ipv4.ip_forward = 1

然后重新加载让它生效:

sudo sysctl -p # 此命令含义是从文件中读取配置,打印出来。别人都说这么做就会生效,那我姑且信了

更新路由表

记下来要告诉内网的机器,我们有了一台网关服务器,网络请求都可以交给它。

以阿里云为例,找到机器所在的VPC对应的路由表,添加一个路由条目。目标网段为0.0.0.0/0,表示所有目标都可以处理。下一跳的类型是ECS实例,并选择刚才配置好的网关服务器。

这样内网机器在访问网络的时候,路由器最终会将请求转发给网关服务器。

配置iptables

好了,网关服务器收到请求了,它也知道自己能转发报文了。下一步需要配置iptables。

我在网上搜索相关资料的时候,因为不熟悉iptables,导致运行的时候经常报错说是参数不对。作为一丝不苟的ISTJ,我实在是无法忍受复制粘贴带来的不确定性、不一致和不统一。但我还是克制住了自己,不要沉迷于研究iptables的机制,避免浪费过多的时间在上面,我这尿性,要是花时间把iptables研究透了,肯定就把初心仍在一边,又要专门再写一篇文章了😆。

直接贴出我尝试过之后可以生效的命令:

sudo iptables -t nat -A POSTROUTING -s [网段] -o eth0 -j MASQUERADE

命令含义大概是,在nat表的POSTROUTING链上,附加一条规则,所有从某个[网段]来的报文,都伪装(MASQUERADE)成从eth0网卡上发出的一样。也就是将报文的来源地址,改成网关服务器的eth0网卡的IP地址,而不是发出请求的子网机器的地址。这正反映了NAT的原本含义,也就是Network Address Translation,即网络地址转换。

其中的网段参数就是子网的网段,你可以查看VPC的网段,对于阿里云来说,大概都是以下其中一种:

  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16

配置完成之后,即刻就生效了,所有内网机器都可以访问公网,新购置的机器即使不做任何配置也能访问公网。不过目前还有最后一个问题:我们添加的规则,在重启之后,就失效了。得想办法让它持久化。

这个简单,安装个iptables插件,实现这个目的:

sudo apt install iptables-persistent

安装的过程就会提示你,是否将当前规则保存到/etc/iptables/rules.v4,你选择是就行,它的内容大概是:

# Generated by iptables-save v1.8.7 on Mon Jul  1 23:44:58 2024
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 172.16.0.0/12 -o eth0 -j MASQUERADE
COMMIT
# Completed on Mon Jul  1 23:44:58 2024

这其实正是iptables-save的输出。iptables-persistent的守护进程会在启动的时候读取规则并自动应用,其实它八成执行的就是iptables-restore命令来恢复规则。

为了做测试,我还真的重启了系统,最终在子网机器上尝试访问公网并成功了。

总结

感谢互联网,感谢在各种技术平台上总结自己经验的前辈们,让我完美地解决了此问题。同时我还得感谢阿里云的99计划,99元买到2核4G的机器,并且将宽带减配为0还能再退49,50元一年的机器简直划算极了。


最后修改于 2024-07-02