如何使用Iptables通过Linux网关转发端口

来自菜鸟教程
跳转至:导航、​搜索

介绍

NAT,或网络地址转换,是一个通用术语,用于修改数据包以便将它们重定向到另一个地址。 通常,这用于允许流量超越网络边界。 实现 NAT 的主机通常可以访问两个或多个网络,并配置为在它们之间路由流量。

端口转发是将特定端口的请求转发到另一个主机、网络或端口的过程。 由于此过程会修改传输中数据包的目的地,因此它被视为一种 NAT 操作。

在本教程中,我们将演示如何使用 iptables 通过使用 NAT 技术将端口转发到防火墙后面的主机。 如果您已配置专用网络,但仍希望允许某些流量在内部通过指定的网关机器,这将非常有用。

先决条件

要遵循本指南,您将需要:

  • 两台 Ubuntu 20.04 服务器设置在同一个数据中心并启用了专用网络。 在每台机器上,您都需要设置一个具有 sudo 权限的非 root 用户帐户。 您可以通过我们在 Ubuntu 20.04 初始服务器设置指南 上的指南了解如何执行此操作。 确保跳过本指南的 步骤 4,因为我们将在本教程中设置和配置防火墙。
  • 在您的 一个服务器 上,使用 iptables 设置防火墙模板,以便它可以用作您的防火墙服务器。 您可以按照我们关于 如何在 Ubuntu 20.04 上使用 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

突出显示的输出显示两个接口(eth0eth1)以及分配给每个接口的地址(分别为 203.0.113.110.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 服务器

开始连接到您的 Web 服务器主机并使用您的 sudo 用户登录。

安装 Nginx

第一步是在您的 Web 服务器主机上安装 Nginx 并将其锁定,以便它只侦听其私有接口。 这将确保您的 Web 服务器只有在您正确设置端口转发时才可用。

首先更新本地包缓存:

sudo apt update

接下来,使用apt下载安装软件:

sudo apt install nginx

将 Nginx 限制为专用网络

安装好 Nginx 后,打开默认的服务器块配置文件,确保它只监听私有接口。 使用您喜欢的文本编辑器打开文件。 这里我们将使用 nano

sudo nano /etc/nginx/sites-enabled/default

在里面,找到 listen 指令。 它应该在配置顶部连续列出两次:

/etc/nginx/sites-enabled/default

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    . . .
}

在第一个 listen 指令中,在 80 之前添加您的 Web 服务器的私有 IP 地址和一个冒号,以告诉 Nginx 仅侦听私有接口。 我们仅在本指南中演示 IPv4 转发,因此您可以删除为 IPv6 配置的第二个监听指令。

接下来,修改 listen 指令如下:

/etc/nginx/sites-enabled/default

server {
    listen 10.0.0.1:80 default_server;

    . . .
}

完成后保存并关闭文件。 如果您使用 nano,您可以按 CTRL + X,然后按 YENTER 来执行此操作。

现在测试文件的语法错误:

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

在文件中,找到并取消注释如下所示的行:

/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

这将使第一个用于建立连接的数据包通过防火墙。 您还需要允许由该连接产生的两个方向的任何后续流量。 要允许公共和私有接口之间的 ESTABLISHEDRELATED 流量,请运行以下命令。 首先是您的公共界面:

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

这个过程处理了一半的图片。 数据包应正确路由到您的 Web 服务器。 但是,现在,数据包仍将客户端的原始地址作为源地址。 服务器将尝试将回复直接发送到该地址,这将无法建立合法的 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

一旦此规则到位,您的 Web 服务器应该可以通过将您的 Web 浏览器指向您的防火墙计算机的公共地址来访问:

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

您需要调整 filter 表中添加的 FORWARD 链式规则的配置。 您还需要调整配置 nat 表的部分,以便添加 PREROUTINGPOSTROUTING 规则。 内容将类似于以下内容:

/etc/iptables/rules.v4

*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 防火墙的灵活性。 这可以用来伪装您的专用网络的拓扑结构,同时允许服务流量自由地通过您的网关防火墙机器。