NAT,即网络地址转换,是一个通用术语,用于篡改数据包以将其重定向到另一个地址。通常,这用于允许流量跨越网络边界。实现 NAT 的主机通常可以访问两个或两个以上的网络,并配置为在它们之间路由流量。
端口转发是将特定端口的请求转发到另一个主机、网络或端口的过程。由于此过程修改了飞行中的数据包的目的地,因此被视为一种 NAT 操作。
在本教程中,我们将演示如何使用 iptables
通过 NAT 技术将端口转发到防火墙后面的主机。如果您已经配置了一个私有网络,但仍希望通过指定的网关机器允许某些流量进入,则这将非常有用。
要按照本指南进行操作,您需要:
sudo
权限的非root用户帐户。您可以参考我们的 Ubuntu 20.04 初始服务器设置指南来了解如何操作。请确保跳过该指南的第 4 步,因为我们将在本教程中设置和配置防火墙。iptables
设置防火墙模板,以便它可以作为您的防火墙服务器。您可以按照我们的指南《如何在 Ubuntu 20.04 上使用 iptables 实现基本防火墙》来完成此操作。完成后,您的防火墙服务器应具备以下功能:
iptables-persistent
/etc/iptables/rules.v4
iptables
命令添加或调整规则您设置防火墙模板的服务器将作为私有网络的防火墙和路由器。为了演示目的,第二台主机将配置为仅可使用其私有接口访问的 Web 服务器。您将配置防火墙机器以将其公共接口收到的请求转发到 Web 服务器,后者将在其私有接口上接收到这些请求。
在开始之前,您需要知道您的两台服务器正在使用的接口和地址。
要获取自己系统的详细信息,请首先查找您的网络接口。您可以通过运行以下命令找到您的机器上的接口和与之关联的地址:
ip -4 addr show scope global
[secondary_label 示例输出]
2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 203.0.113.1/18 brd 45.55.191.255 scope global eth0
valid_lft forever preferred_lft forever
3: eth1: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 10.0.0.1/16 brd 10.132.255.255 scope global eth1
valid_lft forever preferred_lft forever
突出显示的输出显示了两个接口(eth0
和 eth1
)以及分配给每个接口的地址(分别为 203.0.113.1
和 10.0.0.1
)。要找出这些接口中哪一个是您的公共接口,请运行以下命令:
ip route show | grep default
default via 111.111.111.111 dev eth0
此输出中的接口信息(在此示例中为 eth0
)将是连接到默认网关的接口。这几乎肯定是您的公共接口。
在每台机器上找到这些值,并使用它们来按照本指南的其余部分进行操作。
为了更清晰地说明问题,我们将在整个教程中使用以下空地址和接口分配。请用您自己的值替换以下列出的值:
Web 服务器网络详细信息:
203.0.113.1
10.0.0.1
eth0
eth1
防火墙网络详细信息:
203.0.113.2
10.0.0.2
eth0
eth1
开始连接到您的 Web 服务器主机,并使用您的 sudo
用户登录。
第一步是在您的 Web 服务器主机上安装 Nginx,并将其锁定,以便它只监听其私有接口。这将确保只有在正确设置端口转发时,您的 Web 服务器才可用。
首先更新本地软件包缓存:
[environment second]
sudo apt update
接下来,使用 apt
下载并安装软件:
[environment second]
sudo apt install nginx
安装 Nginx 后,打开默认的服务器块配置文件,确保它只监听私有接口。使用您喜欢的文本编辑器打开文件。这里我们将使用 nano
:
[environment second]
sudo nano /etc/nginx/sites-enabled/default
在文件中找到 listen
指令。它应该在配置文件的顶部连续列出两次:
[environment second]
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
. . .
}
在第一个 listen
指令处,添加您的 Web 服务器私有 IP 地址和冒号,然后是 80
,告诉 Nginx 只在私有接口上监听。在本指南中,我们仅演示 IPv4 转发,因此可以删除为 IPv6 配置的第二个 listen
指令。
接下来,修改 listen
指令如下:
[environment second]
server {
listen 10.0.0.1:80 default_server;
. . .
}
完成后保存并关闭文件。如果您使用的是 nano
,可以按 CTRL + X
,然后输入 Y
,最后按 ENTER
来完成。
现在测试文件是否存在语法错误:
[environment second]
sudo nginx -t
[environment second]
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
如果输出中没有错误,重新启动 Nginx 以启用新配置:
[environment second]
sudo systemctl restart nginx
此时,验证您对 Web 服务器的访问权限非常有用。
从防火墙服务器上,尝试使用以下命令从私有接口访问您的 Web 服务器:
[environment third]
curl --connect-timeout 5 10.0.0.1
如果成功,您将得到以下消息:
[environment third]
Welcome to nginx!
Welcome to nginx!
. . .
如果尝试使用公共接口,则会收到连接失败的消息:
[environment third]
curl --connect-timeout 5 203.0.113.1
[environment third]
curl: (7) Failed to connect to 203.0.113.1 port 80: Connection refused
这些结果是预期的。
现在,您将开始在防火墙机器上实现端口转发。
首先,您需要在内核级别启用流量转发。默认情况下,大多数系统都关闭了转发。
要仅在本次会话中打开端口转发,请运行以下命令:
[environment third]
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
[environment third]
1
要永久打开端口转发,您需要编辑 /etc/sysctl.conf
文件。您可以使用 sudo
权限打开文件:
[environment third]
sudo nano /etc/sysctl.conf
在文件中找到并取消注释以下行:
[environment third]
net.ipv4.ip_forward=1
完成后保存并关闭文件。
然后应用此文件中的设置。首先运行以下命令:
[environment third]
sudo sysctl -p
[environment third]
net.ipv4.ip_forward = 1
然后运行相同的命令,但将 -p
标志替换为 --system
:
[environment third]
sudo sysctl --system
[environment third]
. . .
* Applying /usr/lib/sysctl.d/50-pid-max.conf ...
kernel.pid_max = 4194304
* Applying /etc/sysctl.d/99-cloudimg-ipv6.conf ...
net.ipv6.conf.all.use_tempaddr = 0
net.ipv6.conf.default.use_tempaddr = 0
* Applying /etc/sysctl.d/99-sysctl.conf ...
net.ipv4.ip_forward = 1
* Applying /usr/lib/sysctl.d/protect-links.conf ...
fs.protected_fifos = 1
fs.protected_hardlinks = 1
fs.protected_regular = 2
fs.protected_symlinks = 1
* Applying /etc/sysctl.conf ...
net.ipv4.ip_forward = 1
接下来,您将配置防火墙,以便流向公共接口(eth0
)上端口80
的流量将被转发到您的私有接口(eth1
)。
在先决条件教程中配置的防火墙默认将FORWARD
链设置为DROP
流量。您需要添加规则,以允许您转发到 Web 服务器的连接。出于安全考虑,您将严格限制这一点,以便只允许您希望转发的连接。
在FORWARD
链中,您将接受来自公共接口并前往私有接口的目标端口80
的新连接。新连接由conntrack
扩展标识,并将具体由 TCP SYN 数据包表示,如下所示:
[environment third]
sudo iptables -A FORWARD -i eth0 -o eth1 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
这将允许通过防火墙的用于建立连接的第一个数据包。您还需要允许由该连接产生的双向任何后续流量。为了允许公共和私有接口之间的ESTABLISHED
和RELATED
流量,请运行以下命令。首先是您的公共接口:
[environment third]
sudo iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
然后是您的私有接口:
[environment third]
sudo iptables -A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
双重检查FORWARD
链上的策略是否设置为DROP
:
[environment third]
sudo iptables -P FORWARD DROP
到目前为止,您已允许公共和私有接口之间的某些流量通过防火墙。但是,您尚未配置规则,告诉iptables
如何转换和定向流量。
接下来,您将添加规则,告诉iptables
如何路由您的流量。为了使iptables
能够正确地修改数据包,您需要执行两个单独的操作。
第一个操作称为DNAT
,将在nat
表的PREROUTING
链中进行。DNAT
是一种操作,它修改数据包的目标地址,以便在网络之间传递时能够正确路由。公共网络上的客户端将连接到您的防火墙服务器,并不了解您的私有网络拓扑。因此,您需要修改每个数据包的目标地址,以便在发送到您的私有网络时,它知道如何正确到达您的 Web 服务器。
由于您只配置端口转发而不对击中防火墙的每个数据包执行 NAT,您将希望在规则中匹配端口80
。您将匹配针对端口80
的数据包到您的 Web 服务器的私有 IP 地址(在以下示例中为10.0.0.1
):
[environment third]
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1
这个过程完成了一半的工作。数据包应该能够正确路由到您的 Web 服务器。但是,目前,数据包仍将具有客户端的原始地址作为源地址。服务器将尝试直接将回复发送到该地址,这将使建立合法的 TCP 连接变得不可能。
为了配置正确的路由,您还需要在数据包离开防火墙前往 Web 服务器时修改数据包的源地址。您需要将源地址修改为您的防火墙服务器的私有 IP 地址(在以下示例中为10.0.0.2
)。然后,回复将被发送回防火墙,防火墙可以将其按预期转发回客户端。
为了启用此功能,向nat
表的POSTROUTING
链添加规则,该链在数据包发送到网络之前进行评估。您将匹配目标为您的 Web 服务器的 IP 地址和端口的数据包:
[environment third]
sudo iptables -t nat -A POSTROUTING -o eth1 -p tcp --dport 80 -d 10.0.0.1 -j SNAT --to-source 10.0.0.2
一旦这个规则放置好,您的 Web 服务器应该可以通过将您的 Web 浏览器指向防火墙机器的公共地址来访问:
[environment second]
curl 203.0.113.2
[environment second]
Welcome to nginx!
Welcome to nginx!
. . .
您的端口转发设置现在已经完成。
现在您已经设置了端口转发,可以将其保存到您的永久规则集中。
如果您不在意丢失当前规则集中的注释,可以使用 netfilter-persistent
命令来使用 iptables
服务并保存您的规则:
[environment third]
sudo service netfilter-persistent save
[environment third]
* Saving netfilter rules... run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables save
[ OK ]
如果您想保留文件中的注释,可以打开文件并手动编辑:
[environment third]
sudo nano /etc/iptables/rules.v4
您需要调整 filter
表中添加的 FORWARD
链规则的配置。您还需要调整配置 nat
表的部分,以便添加您的 PREROUTING
和 POSTROUTING
规则。内容将类似于以下内容:
[environment third]
*filter
# 允许所有出站流量,但默认情况下拒绝入站和转发数据包
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# 自定义协议链
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]
# 可接受的 UDP 流量
# 可接受的 TCP 流量
-A TCP -p tcp --dport 22 -j ACCEPT
# 可接受的 ICMP 流量
# 基本接受策略
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT
# 拒绝无效数据包
-A INPUT -m conntrack --ctstate INVALID -j DROP
# 将流量传递到特定协议链
## 仅允许新连接(已建立和相关的连接应已处理)
## 对于 TCP,另外只允许新的 SYN 数据包,因为这是建立新 TCP 连接的唯一有效方法
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP
# 拒绝通过到达此点的任何内容
## 尝试使用特定于协议的拒绝消息
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
# 将端口 80 的流量转发到我们的 Web 服务器
# Web 服务器网络详细信息:
# * 公共 IP 地址:203.0.113.1
# * 私有 IP 地址:10.0.0.1
# * 公共接口:eth0
# * 私有接口:eth1
#
# 防火墙网络详细信息:
#
# * 公共 IP 地址:203.0.113.2
# * 私有 IP 地址:10.0.0.2
# * 公共接口:eth0
# * 私有接口:eth1
-A FORWARD -i eth0 -o eth1 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
-A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 转发过滤规则结束
# 提交更改
COMMIT
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# 规则以正确转发公共接口的端口 80 请求
# 以便我们可以使用私有接口正确转发到 Web 服务器。
# Web 服务器网络详细信息:
# * 公共 IP 地址:203.0.113.1
# * 私有 IP 地址:10.0.0.1
# * 公共接口:eth0
# * 秉承接口:eth1
#
# 防火墙网络详细信息:
#
# * 公共 IP 地址:203.0.113.2
# * 私有 IP 地址:10.0.0.2
# * 公共接口:eth0
-A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1
-A POSTROUTING -d 10.0.0.1 -o eth1 -p tcp --dport 80 -j SNAT --to-source 10.0.0.2
COMMIT
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
添加内容并调整数值以反映您自己的网络环境后,保存并关闭文件。
接下来,测试您的规则文件的语法:
```custom_prefix(firewall\s$)
[environment third]
sudo sh -c "iptables-restore -t < /etc/iptables/rules.v4"
如果没有检测到错误,请加载规则集:
[environment third]
sudo service netfilter-persistent reload
[environment third]
* 加载 netfilter 规则... run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables start
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables start
[ OK ]
现在测试您的网络服务器是否仍然可以通过防火墙的公共 IP 地址访问:
[environment second]
curl 203.0.113.2
这应该与之前的操作一样正常。
到目前为止,您应该已经熟悉了在 Linux 服务器上使用 iptables
转发端口的操作。该过程涉及在内核级别允许转发,设置访问权限以允许在防火墙系统的两个接口之间转发特定端口的流量,并配置 NAT 规则以便正确路由数据包。这可能看起来是一个繁琐的过程,但它也展示了 netfilter
数据包过滤框架和 iptables
防火墙的灵活性。这可以用来掩盖您的私有网络拓扑,同时允许服务流量自由地通过您的网关防火墙机器。