如何使用 Iptables 通过 Linux 网关转发端口
介绍
NAT,或网络地址转换,是一个通用术语,用于处理数据包以便将它们重定向到另一个地址。通常,这用于允许流量超越网络边界。实施 NAT 的主机通常可以访问两个或多个网络,并配置为在它们之间路由流量。
端口转发 是将对特定端口的请求转发到另一台主机、网络或端口的过程。由于此过程会修改传输中数据包的目的地,因此它被视为一种 NAT 操作。
在本教程中,我们将演示如何使用 iptables
通过 NAT 技术将端口转发到防火墙后面的主机。如果您配置了专用网络,但仍希望允许某些流量通过指定的网关机器进入,这将很有用。
先决条件
要遵循本指南,您需要:
- 两个 Ubuntu 20.04 服务器设置在同一个数据中心并启用了专用网络。在这些机器中的每一台上,您都需要设置一个具有
sudo
权限的非根用户帐户。您可以通过我们关于 Ubuntu 20.04 初始服务器设置指南的指南了解如何执行此操作。请确保跳过本指南的第 4 步,因为我们将在本教程中设置和配置防火墙。 - 在您的一台服务器上,使用
iptables
设置一个防火墙模板,这样它就可以作为您的防火墙服务器。您可以按照我们关于如何在 Ubuntu 20.04 上使用 Iptables 实施基本防火墙的指南来执行此操作。完成后,您的防火墙服务器应准备好使用以下内容:iptables-persistent
安装- 将默认规则集保存到
/etc/iptables/rules.v4
- 了解如何通过编辑规则文件或使用
iptables
命令来添加或调整规则
您在其中设置防火墙模板的服务器将用作您专用网络的防火墙和路由器。出于演示目的,第二个主机将配置一个 Web 服务器,该服务器只能使用其私有接口访问。您将配置防火墙机器以将在其公共接口上收到的请求转发到 Web 服务器,它将通过其私有接口到达该服务器。
主机详细信息
在开始之前,您需要知道您的两台服务器正在使用哪些接口和地址。
查找您的网络详细信息
要获得您自己系统的详细信息,首先要找到您的网络接口。您可以通过运行以下命令找到计算机上的接口以及与它们关联的地址:
- ip -4 addr show scope global
Sample Output2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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
Outputdefault via 111.111.111.111 dev eth0此输出中的接口信息(本例中为
eth0
)将是连接到您的默认网关的接口。这几乎可以肯定是您的公共界面。在您的每台机器上找到这些值,并使用它们来遵循本指南的其余部分。
本指南中使用的示例数据
为了使事情更清楚,我们将在整个教程中使用以下空地址和接口分配。请用您自己的值替换下面列出的值:
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
设置网络服务器
开始连接到您的 Web 服务器主机并使用您的
sudo
用户登录。安装 Nginx
第一步是在您的 Web 服务器主机上安装 Nginx 并将其锁定,以便它只监听其私有接口。这将确保您的 Web 服务器只有在您正确设置端口转发时才可用。
首先更新本地包缓存:
- sudo apt update
接下来,使用
apt
下载并安装软件:- sudo apt install nginx
限制 Nginx 到私有网络
Nginx安装好后,打开默认的server block配置文件,保证只监听私有接口。使用您喜欢的文本编辑器打开文件。这里我们将使用
nano
:- sudo nano /etc/nginx/sites-enabled/default
在里面,找到
listen
指令。它应该在配置顶部连续列出两次:server { listen 80 default_server; listen [::]:80 default_server ipv6only=on; . . . }
在第一个
listen
指令中,在80
之前添加您的 Web 服务器的私有 IP 地址和一个冒号,以告诉 Nginx 仅在私有接口上侦听。我们仅在本指南中演示 IPv4 转发,因此您可以删除为 IPv6 配置的第二个 listen 指令。接下来,修改
listen
指令,如下所示:server { listen 10.0.0.1:80 default_server; . . . }
完成后保存并关闭文件。如果您使用了
nano
,您可以按CTRL + X
,然后按Y
,然后按ENTER
来执行此操作。现在测试文件的语法错误:
- sudo nginx -t
Outputnginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful如果输出中没有错误,请重新启动 Nginx 以启用新配置:
- sudo systemctl restart nginx
验证网络限制
此时,验证您对 Web 服务器的访问级别很有用。
从您的防火墙服务器,尝试使用以下命令从专用接口访问您的 Web 服务器:
- curl --connect-timeout 5 10.0.0.1
如果成功,您的输出将产生以下消息:
Output<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> . . .如果您尝试使用公共接口,您将收到一条消息,指出它无法连接:
- curl --connect-timeout 5 203.0.113.1
Outputcurl: (7) Failed to connect to 203.0.113.1 port 80: Connection refused这些结果是预期的。
配置防火墙转发端口 80
现在您将在您的防火墙机器上实现端口转发。
在内核中启用转发
您需要做的第一件事是在内核级别启用流量转发。默认情况下,大多数系统都关闭转发。
要仅为此会话打开端口转发,请运行以下命令:
- echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
Output1要永久打开端口转发,您必须编辑
/etc/sysctl.conf
文件。您可以通过使用sudo
权限打开文件来执行此操作:- sudo nano /etc/sysctl.conf
在文件中,找到并取消注释如下所示的行:
net.ipv4.ip_forward=1
完成后保存并关闭文件。
然后应用此文件中的设置。首先运行以下命令:
- sudo sysctl -p
Outputnet.ipv4.ip_forward = 1然后运行相同的命令,但将
-p
标志替换为--system
:- sudo sysctl --system
Output. . . * 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为基础防火墙添加转发规则
接下来,您将配置您的防火墙,以便在端口
80
上流入您的公共接口 (eth0
) 的流量将被转发到您的私有接口 (eth1
).您在先决条件教程中配置的防火墙默认将
FORWARD
链设置为DROP
流量。您需要添加允许您将连接转发到 Web 服务器的规则。为了安全起见,您将把它锁定得相当严密,以便只允许您希望转发的连接。在
FORWARD
链中,您将接受发往端口80
的新连接,这些连接来自您的公共接口并前往您的私有接口。新连接由conntrack
扩展标识,并将具体由 TCP SYN 数据包表示,如下所示:- sudo iptables -A FORWARD -i eth0 -o eth1 -p tcp --syn --dport 80 -m conntrack --ctstate NEW -j ACCEPT
这将使第一个数据包(旨在建立连接)通过防火墙。您还需要允许该连接产生的任何后续双向流量。要允许公共和私有接口之间的
ESTABLISHED
和RELATED
流量,请运行以下命令。首先是您的公共界面:- sudo iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
然后对于您的私人界面:
- sudo iptables -A FORWARD -i eth1 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
仔细检查您在
FORWARD
链上的策略是否设置为DROP
:- sudo iptables -P FORWARD DROP
此时,您已允许公共接口和专用接口之间的某些流量通过防火墙。但是,您还没有配置实际告诉
iptables
如何转换和引导流量的规则。正确添加NAT规则定向报文
接下来,您将添加告诉
iptables
如何路由流量的规则。您需要执行两个单独的操作,以便iptables
正确更改数据包,以便客户端可以与 Web 服务器通信。第一个操作称为
DNAT
,将发生在nat
表的PREROUTING
链中。DNAT
是一种更改数据包目标地址的操作,以使其能够在网络之间传递时正确路由。公共网络上的客户端将连接到您的防火墙服务器,并且不知道您的专用网络拓扑。因此,您需要更改每个数据包的目标地址,以便当它在您的专用网络上发送时,它知道如何正确到达您的 Web 服务器。由于您只是配置端口转发,而不是对每个到达防火墙的数据包执行 NAT,因此您需要在规则中匹配端口
80
。您会将针对端口80
的数据包与您的 Web 服务器的私有 IP 地址(在以下示例中为10.0.0.1
)相匹配:- sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1
这个过程处理了一半的图片。数据包应该正确路由到您的网络服务器。但是,现在,数据包仍将以客户端的原始地址作为源地址。服务器将尝试将回复直接发送到该地址,这将导致无法建立合法的 TCP 连接。
在 DigitalOcean 上,离开具有不同源地址的 Droplet 的数据包实际上会被管理程序丢弃,因此您的数据包在这个阶段甚至永远不会到达 Web 服务器(这将通过暂时实施 SNAT 来修复)。这是一种反欺骗措施,旨在防止通过伪造请求中的源地址来请求将大量数据发送到受害者计算机的攻击。要了解更多信息,请阅读我们社区中的此回复。
要配置正确的路由,您还需要在数据包离开防火墙前往 Web 服务器的途中修改数据包的源地址。您需要将源地址修改为防火墙服务器的私有 IP 地址(以下示例中的
10.0.0.2
)。然后回复将被发送回防火墙,然后防火墙可以按预期将其转发回客户端。要启用此功能,请向
nat
表的POSTROUTING
链添加一条规则,该规则会在数据包发送到网络之前进行评估。您将通过 IP 地址和端口匹配发往您的 Web 服务器的数据包:- 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
一旦这条规则到位,你的网络服务器应该可以通过将你的网络浏览器指向你的防火墙机器的公共地址来访问:
- curl 203.0.113.2
Output<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> . . .您的端口转发设置现已完成。
调整永久规则集
现在您已经设置了端口转发,您可以将其保存到您的永久规则集中。
如果您不关心丢失当前规则集中的注释,请使用
netfilter-persistent
命令来使用iptables
服务并保存您的规则:- sudo service netfilter-persistent save
Output* 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 ]如果您想将评论保留在您的文件中,请打开它并手动编辑:
- sudo nano /etc/iptables/rules.v4
您需要为添加的
FORWARD
链规则调整filter
表中的配置。您还需要调整配置nat
表的部分,以便您可以添加PREROUTING
和POSTROUTING
规则。内容将类似于以下内容:*filter # Allow all outgoing, but drop incoming and forwarding packets by default :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] # Custom per-protocol chains :UDP - [0:0] :TCP - [0:0] :ICMP - [0:0] # Acceptable UDP traffic # Acceptable TCP traffic -A TCP -p tcp --dport 22 -j ACCEPT # Acceptable ICMP traffic # Boilerplate acceptance policy -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT -A INPUT -i lo -j ACCEPT # Drop invalid packets -A INPUT -m conntrack --ctstate INVALID -j DROP # Pass traffic to protocol-specific chains ## Only allow new connections (established and related should already be handled) ## For TCP, additionally only allow new SYN packets since that is the only valid ## method for establishing a new TCP connection -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 # Reject anything that's fallen through to this point ## Try to be protocol-specific w/ rejection message -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 # Rules to forward port 80 to our web server # Web server network details: # * Public IP Address: 203.0.113.1 # * Private IP Address: 10.0.0.1 # * Public Interface: eth0 # * Private Interface: eth1 # # Firewall network details: # # * Public IP Address: 203.0.113.2 # * Private IP Address: 10.0.0.2 # * Public Interface: eth0 # * Private Interface: 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 # End of Forward filtering rules # Commit the changes 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] # Rules to translate requests for port 80 of the public interface # so that we can forward correctly to the web server using the # private interface. # Web server network details: # * Public IP Address: 203.0.113.1 # * Private IP Address: 10.0.0.1 # * Public Interface: eth0 # * Private Interface: eth1 # # Firewall network details: # # * Public IP Address: 203.0.113.2 # * Private IP Address: 10.0.0.2 # * Public Interface: eth0 # * Private Interface: eth1 -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 # End of NAT translations for web server traffic COMMIT *security :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT
添加内容并调整值以反映您自己的网络环境后,保存并关闭文件。
接下来,测试规则文件的语法:
- sudo sh -c "iptables-restore -t < /etc/iptables/rules.v4"
如果未检测到错误,则加载规则集:
- sudo service netfilter-persistent reload
Output* Loading netfilter rules... 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 ]现在测试您的 Web 服务器是否仍然可以通过防火墙的公共 IP 地址访问:
- curl 203.0.113.2
这应该和以前一样工作。
结论
到目前为止,您应该熟悉使用
iptables
在 Linux 服务器上转发端口。该过程涉及在内核级别允许转发,设置访问权限以允许在防火墙系统的两个接口之间转发特定端口的流量,以及配置 NAT 规则以便正确路由数据包。这似乎是一个笨拙的过程,但它也展示了netfilter
数据包过滤框架和iptables
防火墙的灵活性。这可用于伪装您的专用网络拓扑,同时允许服务流量自由流过您的网关防火墙机器。