跳转至内容

简单的有状态防火墙

来自 ArchWiki

本文介绍如何使用 iptables 搭建状态防火墙。文章解释了这些规则的含义以及为何需要它们。为了简洁,本文分为两个主要部分:第一部分讨论单机防火墙,第二部分在第一部分防火墙的基础上搭建 NAT 网关。

警告 下方的规则按执行顺序给出,且仅应在本地登录时执行。如果您通过远程连接登录,在设置规则时可能会被踢出系统。为解决远程设置中的此问题,可以使用示例配置文件

前提条件

注意 您的内核需编译 iptables 支持。所有 Arch Linux 原生内核均支持 iptables。

首先,安装用户空间工具 iptables,或确认它们已安装。

本文假设当前没有任何 iptables 规则。若要检查当前规则集并确认没有规则,请运行以下命令:

# iptables-save
# Generated by iptables-save v1.4.19.1 on Thu Aug  1 19:28:53 2013
*filter
:INPUT ACCEPT [50:3763]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [30:3472]
COMMIT
# Completed on Thu Aug  1 19:28:53 2013

或者

# iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 156 packets, 12541 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 82 packets, 8672 bytes)
num   pkts bytes target     prot opt in     out     source               destination

如果存在规则,您可以尝试通过加载默认规则集来重置规则:

# iptables-restore < /etc/iptables/empty.rules

否则,请参阅 Iptables#重置规则

单机防火墙

注意 由于 iptables 在链内从上到下按线性顺序处理规则,建议将频繁命中的规则放在链的开头。当然,这取决于具体实现的逻辑,且规则执行有运行时代价,因此不应仅凭字节/数据包计数器的经验观察来随意重排规则。

创建必要的链

对于此基础设置,我们将创建两个用户自定义链,用于在防火墙中开启端口。

# iptables -N TCP
# iptables -N UDP

链名称可任意选择。我们选择这些名称只是为了对应后续规则中要处理的协议,后续规则始终会指定协议选项,例如 -p tcp

FORWARD 链

如果您想将机器设置为 NAT 网关,请参阅 #搭建 NAT 网关。对于单机而言,我们只需将 FORWARD 链的策略设置为 DROP 即可。

# iptables -P FORWARD DROP

OUTPUT 链

OUTPUT 链是过滤出站流量的强大工具,尤其是对于不需要运行浏览器或 P2P 工具(需要连接到互联网任意目的地)的服务器或其他设备。然而,正确设置 OUTPUT 链需要了解系统的预期用途。桌面系统、笔记本、云服务器和家用/企业服务器的安全规则设置会有很大差异。

在这个简单的示例中,我们将 OUTPUT 链的默认策略设置为 ACCEPT,即允许所有出站流量。这虽然安全性较低,但与许多系统的兼容性较好。

# iptables -P OUTPUT ACCEPT

INPUT 链

与之前的链类似,我们将 INPUT 链的默认策略设置为 DROP,以防有流量漏掉。丢弃所有流量并明确指定允许哪些流量,是构建安全防火墙的最佳方式。

警告 如果您通过 SSH 登录,执行以下命令将立即断开 SSH 会话。为避免此问题:
  1. 添加下方第一条 INPUT 链规则(这将保持会话开启),
  2. 添加一条常规规则以允许入站 SSH(以便在连接断开时能够重连),并且
  3. 设置策略。
# iptables -P INPUT DROP

任何网络接口接收到的数据包,若目的地是本机,首先都会通过 INPUT 链。在此链中,我们确保仅接受我们想要的数据包。有关数据包如何遍历内置链的简单 ASCII 示意图,请参阅 数据包如何遍历过滤器

添加到 INPUT 链的第一条规则将允许属于已建立连接的流量,或与这些连接相关的新有效流量(如 ICMP 错误或回显应答,即主机在被 ping 时返回的数据包)。ICMP 代表互联网控制报文协议。某些 ICMP 消息非常重要,有助于管理拥塞和 MTU,该规则会接受它们。

# iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

连接状态 ESTABLISHED 意味着之前的某条规则已允许初始连接尝试(--ctstate NEW),或者连接已处于活动状态(例如活动的远程 SSH 连接)。

第二条规则将接受来自“回环”(lo) 接口的所有流量,这对于许多应用程序和服务是必需的。

# iptables -A INPUT -i lo -j ACCEPT
注意 您可以在此处添加更多受信任的接口(例如 "eth1"),如果您不希望/不需要防火墙过滤这些流量。但请注意,如果您有 NAT 设置将任何流量从网络中其他任何地方(比如路由器)重定向到该接口,它将不受任何其他设置的影响直接通过。

第三条规则将丢弃所有状态为 "INVALID" 的流量。流量可分为四种“状态”类别:NEW、ESTABLISHED、RELATED 或 INVALID,这正是使其成为“状态”防火墙而非安全性较低的“无状态”防火墙的原因。状态通过 "nf_conntrack_*" 内核模块跟踪,该模块会在您添加规则时由内核自动加载。

  • 此规则将丢弃所有包头或校验和无效、TCP 标志无效、ICMP 消息无效(如我们在未向主机发送任何信息时收到的端口不可达消息)以及顺序错误的数据包。顺序错误可能是由序列预测或其他类似攻击引起的。"DROP" 目标会直接丢弃数据包而不进行任何响应,这与会礼貌拒绝的 REJECT 不同。我们使用 DROP,因为对于 INVALID 数据包没有合适的 "REJECT" 响应,且我们不想让对方知道我们接收到了这些数据包。
  • ICMPv6 邻居发现数据包不会被跟踪,且总是被归类为 "INVALID",尽管它们并未损坏。请记住这一点,并在该规则之前接受它们!请以 root 身份运行 iptables -A INPUT -p 41 -j ACCEPT
# iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

下一条规则将接受所有新的入站 ICMP 回显请求(即 ping)。只有第一个数据包会被视为 NEW,其余的将由 RELATED, ESTABLISHED 规则处理。由于该计算机不是路由器,因此无需允许其他状态为 NEW 的 ICMP 流量。

# iptables -A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT

现在,我们将 TCP 和 UDP 链附加到 INPUT 链,以处理所有新的入站连接。一旦某个连接被 TCP 或 UDP 链接受,它就会被 RELATED/ESTABLISHED 流量规则接管。TCP 和 UDP 链要么接受新的入站连接,要么礼貌地拒绝它们。新的 TCP 连接必须以 SYN 数据包启动。

# iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP
# iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
注意 NEW 并不一定意味着 --syn。但是,匹配 "NEW 但不是 --syn" 的数据包很少是恶意的,不应该被直接丢弃。相反,它们应由下一条规则直接以 TCP RESET 拒绝。此外,--syn 并不等同于 --tcp-flags SYN SYN。详情请参阅 iptables-extensions(8)

如果端口未开放,我们使用 TCP RESET 数据包拒绝 TCP 连接,并使用 ICMP 端口不可达消息拒绝 UDP 流。这模拟了默认的 Linux 行为(符合 RFC),并允许发送方快速关闭连接并进行清理。

# iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
# iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset

对于其他协议,我们在 INPUT 链末尾添加最后一条规则,以 icmp 协议不可达消息拒绝所有剩余的入站流量。这也模拟了 Linux 的默认行为。

# iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable

生成的 iptables.rules 文件

运行上述所有命令后 iptables.rules 文件的示例

/etc/iptables/iptables.rules
# Generated by iptables-save v1.4.18 on Sun Mar 17 14:21:12 2013
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:TCP - [0:0]
:UDP - [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
-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
COMMIT
# Completed on Sun Mar 17 14:21:12 2013

该文件可以通过以下方式生成并保存:

# iptables-save -f /etc/iptables/iptables.rules

并可用于继续后续章节。如果您正在通过 SSH 远程设置防火墙,请在继续之前附加以下规则以允许新的 SSH 连接(请根据需要调整端口):

# iptables -A TCP -p tcp --dport 22 -j ACCEPT

TCP 和 UDP 链

TCP 和 UDP 链包含用于接受特定端口的新的入站 TCP 连接和 UDP 流的规则。

注意 您需要在此处添加规则以接受入站连接,例如 SSH、HTTP 或其他您希望远程访问的服务。

开放端口以允许传入连接

若要接受 Web 服务器端口 80 的入站 TCP 连接:

# iptables -A TCP -p tcp --dport 80 -j ACCEPT

若要接受 Web 服务器 (HTTPS) 端口 443 的入站 TCP 连接:

# iptables -A TCP -p tcp --dport 443 -j ACCEPT

若要允许远程 SSH 连接(端口 22):

# iptables -A TCP -p tcp --dport 22 -j ACCEPT

若要接受 DNS 服务器(端口 53)的入站 TCP/UDP 请求:

# iptables -A TCP -p tcp --dport 53 -j ACCEPT
# iptables -A UDP -p udp --dport 53 -j ACCEPT

请参阅 iptables(8) 以获取更高级的规则,例如匹配多个端口。

端口敲击

端口敲击 (Port knocking) 是一种从外部打开防火墙默认关闭端口的方法。其工作原理是要求连接尝试一系列预定义的关闭端口。当接收到正确的端口“敲击”(连接尝试)序列时,防火墙会打开特定端口以允许连接。有关详细信息,请参阅 Port knocking

防范欺骗攻击

注意 rp_filter 目前在 /usr/lib/sysctl.d/50-default.conf 中默认设置为 2,因此无需执行以下步骤。

阻止来自互联网或本地网络的保留本地地址通常通过在 sysctl 中将 rp_filter (反向路径过滤) 设置为 1 来完成。为此,请在您的 /etc/sysctl.d/90-firewall.conf 文件中添加以下行(详情参见 sysctl),以启用 Linux 内核内置的源地址验证。相比为每种情况编写单独的 iptables 规则,内核级别的验证处理欺骗效果更好。

net.ipv4.conf.all.rp_filter=1

如果需要统计(及更好的日志记录),也可以使用 netfilter 实现:

# iptables -t mangle -I PREROUTING -m rpfilter --invert -j DROP
注意 没有必要同时在这两个地方启用。Netfilter 方法是现代的选择,也适用于 IPv6。

对于使用非对称路由的小众设置,需要使用 rp_filter=2 sysctl 选项。将 --loose 开关传递给 rpfilter 模块,在 netfilter 中也能达到同样的效果。

“隐藏”您的计算机

如果您运行的是桌面机,阻止某些入站请求可能是一个好主意。

阻止 ping 请求

“Ping”请求是发送到目标地址的 ICMP 数据包,用于确保设备间的连通性。如果网络工作正常,您可以安全地阻止所有 ping 请求。重要的是要注意,这并不能真正隐藏您的计算机——发送给您的任何数据包都会被拒绝,因此您仍然会显示在 IP 范围的简单 nmap “ping 扫描”结果中。

这只是一种初级的“保护”,且会给未来的问题调试带来麻烦。这仅应出于教学目的进行。

若要阻止回显请求,请在您的 /etc/sysctl.d/90-firewall.conf 文件中添加以下行(详情参见 sysctl):

net.ipv4.icmp_echo_ignore_all = 1

更多信息请查阅 iptables(8),或阅读网页 http://www.snowman.net/projects/ipt_recent/ 上的文档和示例。

欺骗端口扫描器

  • 这会使您面临 DoS (拒绝服务) 攻击的风险。攻击者可以发送带有伪造 IP 的数据包,从而导致它们被禁止连接到您的服务。
  • 如果来自某个地址到目标端口的某些数据包被 conntrack 模块视为 INVALID,此技巧可能会屏蔽合法的 IP 地址。为避免被列入黑名单,一种解决方法是允许发送到该特定目标端口的所有数据包。

攻击者使用端口扫描来识别您计算机上的开放端口。这使他们能够识别并对您运行的服务进行指纹识别,并可能针对它们发动攻击。

INVALID 状态规则将处理除 UDP、ACK 和 SYN 扫描(在 nmap 中分别为 -sU-sA-sS)之外的每种端口扫描。

ACK 扫描 不用于识别开放端口,而是用于识别被防火墙过滤的端口。由于针对所有状态为 NEW 的 TCP 连接进行了 SYN 检查,ACK 扫描发送的每一个数据包都将被 TCP RESET 数据包正确拒绝。有些防火墙会丢弃这些数据包,这反而允许攻击者映射出防火墙规则。

recent 模块可用于欺骗其余两种类型的端口扫描。recent 模块用于将主机添加到“recent”列表中,该列表可用于指纹识别并停止某些类型的攻击。当前的 recent 列表可以在 /proc/net/xt_recent/ 中查看。

SYN 扫描

在 SYN 扫描中,端口扫描器向每个端口发送一个 SYN(同步)数据包以发起 TCP 连接。关闭的端口返回 TCP RESET 数据包,或者被严格的防火墙丢弃,而开放的端口则返回 SYN ACK 数据包。

recent 模块可用于跟踪有拒绝连接尝试的主机,并对它们发送到开放端口的任何 SYN 数据包返回一个 TCP RESET,就好像端口已关闭一样。如果开放端口是第一个被扫描的端口,则仍会返回 SYN ACK,因此,为了使此方法持续有效,建议将 ssh 等运行中的应用程序部署在非标准端口上。

首先,在 TCP 链的顶部插入一条规则。此规则会对在过去 60 秒内进入 TCP-PORTSCAN 列表的任何主机返回 TCP RESET。--update 开关会导致 recent 列表更新,这意味着 60 秒的计数器会被重置。

# iptables -I TCP -p tcp -m recent --update --rsource --seconds 60 --name TCP-PORTSCAN -j REJECT --reject-with tcp-reset

接下来,需要修改拒绝 TCP 数据包的规则,将带有拒绝数据包的主机添加到 TCP-PORTSCAN 列表。

# iptables -D INPUT -p tcp -j REJECT --reject-with tcp-reset
# iptables -A INPUT -p tcp -m recent --set --rsource --name TCP-PORTSCAN -j REJECT --reject-with tcp-reset
UDP 扫描

UDP 端口扫描类似于 TCP SYN 扫描,不同之处在于 UDP 是一种“无连接”协议。没有握手或确认。扫描器向每个 UDP 端口发送 UDP 数据包。关闭的端口应该返回 ICMP 端口不可达消息,开放的端口则不返回响应。由于 UDP 不是一种“可靠”协议,扫描器无法知道数据包是否丢失,因此必须对每个没有返回响应的端口进行多次检查。

Linux 内核发送 ICMP 端口不可达消息的速度非常慢,因此对 Linux 机器进行完整的 UDP 扫描需要超过 10 小时。不过,常见的端口仍可能被识别,因此对 UDP 扫描采取与 SYN 扫描相同的对策是一个好主意。

首先,在 UDP 链的顶部添加一条规则,以拒绝来自 UDP-PORTSCAN 列表主机的包。

# iptables -I UDP -p udp -m recent --update --rsource --seconds 60 --name UDP-PORTSCAN -j REJECT --reject-with icmp-port-unreachable

接下来,修改 UDP 的拒绝数据包规则。

# iptables -D INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
# iptables -A INPUT -p udp -m recent --set --rsource --name UDP-PORTSCAN -j REJECT --reject-with icmp-port-unreachable
恢复最终规则

如果使用了上述一种或两种端口扫描欺骗技巧,最终的默认规则将不再是 INPUT 链中的最后一条规则。它必须是最后一条,否则它会拦截您刚刚添加的欺骗端口扫描器规则,使其失效。只需删除(-D)该规则,然后使用追加(-A)再次添加,这会将其放置在链的末尾。

# iptables -D INPUT -j REJECT --reject-with icmp-proto-unreachable
# iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable

防范其他攻击

请参阅 sysctl#TCP/IP 堆栈强化 以获取相关的内核参数。

暴力破解攻击

不幸的是,针对通过外部 IP 地址访问的服务的暴力破解攻击很常见。原因之一是这类攻击很容易利用现有的许多工具执行。幸运的是,有多种方法可以保护服务免受此类攻击。一种是使用适当的 iptables 规则,在一定数量的数据包尝试发起连接后,激活并封禁该 IP。另一种是使用专用守护进程,监控日志文件中的失败尝试并相应地封禁 IP。

警告 使用 IP 黑名单可以阻止简单的攻击,但它依赖于额外的守护进程和成功的日志记录(包含 /var 的分区可能会满,尤其是如果攻击者正在猛烈冲击服务器)。此外,一旦攻击者知道了您的 IP 地址,他们可以发送带有伪造源报头的包,导致您自己被锁定在服务器之外。SSH 密钥提供了一种优雅的暴力破解解决方案,且没有这些问题。

有两个在多次密码错误后封禁 IP 的软件包:Fail2ban,或者特别是针对 sshdSshguard。这两个应用程序会更新 iptables 规则,临时或永久地拒绝来自攻击者的未来连接。

以下规则给出了使用 iptables 减轻 SSH 暴力破解攻击的示例配置。

# iptables -N IN_SSH
# iptables -N LOG_AND_DROP
# iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j IN_SSH
# iptables -A IN_SSH -m recent --name sshbf --rttl --rcheck --hitcount 3 --seconds 10 -j LOG_AND_DROP
# iptables -A IN_SSH -m recent --name sshbf --rttl --rcheck --hitcount 4 --seconds 1800 -j LOG_AND_DROP 
# iptables -A IN_SSH -m recent --name sshbf --set -j ACCEPT
# iptables -A LOG_AND_DROP -j LOG --log-prefix "iptables deny: " --log-level 7
# iptables -A LOG_AND_DROP -j DROP

大多数规则应该是不言自明的:第一条规则允许在十秒内最多三个连接数据包,并丢弃来自该 IP 的进一步尝试。下一条规则添加了一个小窍门,允许在 30 分钟内最多四次命中。这是因为有些暴力破解攻击实际上进行得很慢,而不是突发尝试。这些规则使用了许多额外的选项。要了解更多信息,请查看此示例的原始参考资料:compilefailure.blogspot.com。LOG_AND_DROP 链用于记录被丢弃的连接。

上述规则可用于保护任何服务,尽管 SSH 守护进程可能是最常需要保护的。

关于顺序,必须确保 -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j IN_SSH 在 iptables 序列中处于正确位置:它应该在 TCP 链被附加到 INPUT 之前,以便首先捕获新的 SSH 连接。如果已完成本维基的所有先前步骤,则以下定位是有效的:

...
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j IN_SSH
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
...
提示 为了在设置后自行测试规则,实际的封禁可能会减慢测试速度,导致难以微调参数。您可以查看 cat /proc/net/xt_recent/sshbf 来监视传入的尝试。若要在测试期间解除对自己 IP 的封禁,需要 root 权限:echo / > /proc/net/xt_recent/sshbf

IPv6

如果您不使用 IPv6,可以考虑禁用它,否则请按照以下步骤启用 IPv6 防火墙规则。

复制本示例中使用的 IPv4 规则作为基础,并将任何 IP 从 IPv4 格式更改为 IPv6 格式。

# cp /etc/iptables/iptables.rules /etc/iptables/ip6tables.rules

此示例中的几条规则必须针对 IPv6 进行调整。ICMP 协议在 IPv6 中已更新,取代了 IPv4 使用的 ICMP 协议。因此,拒绝错误返回代码 --reject-with icmp-port-unreachable--reject-with icmp-proto-unreachable 必须转换为 ICMPv6 代码。

可用的 ICMPv6 错误代码列在 RFC 4443 中,其中规定防火墙规则拦截的连接尝试应使用 --reject-with icmp6-adm-prohibited。这样做基本上会通知远程系统连接被防火墙拒绝,而不是被监听的服务拒绝。

如果更倾向于不明确告知存在防火墙过滤,也可以在拒绝数据包时不发送该消息:

 -A INPUT -j REJECT

上述操作将使用默认错误返回代码 --reject-with icmp6-port-unreachable 进行拒绝。但请注意,识别防火墙是端口扫描应用程序的基本功能,大多数应用程序无论如何都能识别出来。

本文章或章节需要扩充。

原因: 应添加哪些 ICMPv6 特性来使规则与本文使用的 IPv4 规则相当?(在 Talk:Simple_stateful_firewall#ICMP blocking 中讨论)

在下一步中,确保针对所有新的入站 ICMP 回显请求(ping)的规则中,协议和扩展名已更改为适用于 IPv6。

# ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -m conntrack --ctstate NEW -j ACCEPT

Netfilter conntrack 似乎不跟踪 ICMPv6 邻居发现协议(IPv6 等效于 ARP),因此我们需要为所有直接连接的子网允许 ICMPv6 流量,而不考虑状态。以下内容应在丢弃 --ctstate INVALID 之后、但在任何其他 DROP 或 REJECT 目标之前插入,并为每个直接连接的子网添加相应的一行:

# ip6tables -A INPUT -s fe80::/10 -p ipv6-icmp -j ACCEPT

如果您想启用 DHCPv6,则需要接受 UDP 端口 546 上的入站连接。

# ip6tables -A INPUT -p udp --sport 547 --dport 546 -j ACCEPT

由于 IPv6 没有内核反向路径过滤,您可能需要通过以下命令在 ip6tables 中启用它:

# ip6tables -t mangle -A PREROUTING -m rpfilter -j ACCEPT
# ip6tables -t mangle -A PREROUTING -j DROP

保存规则

规则集现已完成,应保存到文件中,以便在每次启动时加载。

使用以下命令保存 IPv4 和 IPv6 规则:

# iptables-save -f /etc/iptables/iptables.rules
# ip6tables-save -f /etc/iptables/ip6tables.rules

生成的 ip6tables.rules 文件

运行上述所有命令后的 ip6tables.rules 文件示例:

/etc/iptables/ip6tables.rules
# Generated by ip6tables-save v1.8.2 on Sat Apr 20 10:53:41 2019
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:TCP - [0:0]
:UDP - [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -s fe80::/10 -p ipv6-icmp -j ACCEPT
-A INPUT -p udp --sport 547 --dport 546 -j ACCEPT
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
-A INPUT -p udp -j REJECT --reject-with icmp6-adm-prohibited
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
COMMIT
# Completed on Sat Apr 20 10:53:41 2019

然后启用启动 iptables.serviceip6tables.service。检查服务状态以确保规则正确加载。

搭建 NAT 网关

本指南的这一部分涉及 NAT 网关。假设您已经阅读了本指南的第一部分,并按照上述说明设置了 INPUTOUTPUTTCPUDP 链。到目前为止的所有规则都是在 filter 表中创建的。在本节中,我们还需要使用 nat 表。关于这种情况的 ASCII 示意图请见 控制 NAT 对象

设置 filter 表

创建必要的链

在我们的设置中,我们将使用以下命令在 filter 表中创建两个新链:fw-interfacesfw-open

# iptables -N fw-interfaces
# iptables -N fw-open

设置 FORWARD 链

设置 FORWARD 链与第一部分中的 INPUT 链类似。

现在我们设置一条带有 conntrack 匹配的规则,与 INPUT 链中的规则相同:

# iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

下一步是为受信任的接口启用转发,并使所有数据包通过 fw-open 链。

# iptables -A FORWARD -j fw-interfaces 
# iptables -A FORWARD -j fw-open 

其余数据包会被 ICMP 消息拒绝:

# iptables -A FORWARD -j REJECT --reject-with icmp-host-unreachable
# iptables -P FORWARD DROP

设置 fw-interfaces 和 fw-open 链

fw-interfacesfw-open 链的含义将在我们分别处理 nat 表中的 POSTROUTINGPREROUTING 链时解释。

设置 nat 表

在本节中,我们假设出站接口(具有公网 IP 的接口)是 ppp0。请记住,如果您的出站接口名称不同,则必须在以下所有规则中更改名称。

设置 POSTROUTING 链

现在,我们必须定义谁被允许连接到互联网。假设我们在 eth0 上有子网 192.168.0.0/24(意味着所有 192.168.0.* 形式的地址)。我们首先需要在 FORWARD 表中接受此接口上的机器,这就是为什么我们在上面创建了 fw-interfaces 链:

# iptables -A fw-interfaces -i eth0 -j ACCEPT

现在,我们必须修改所有出站数据包,使其源地址为我们的公网 IP 地址,而不是本地 LAN 地址。为此,我们使用 MASQUERADE 目标:

# iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o ppp0 -j MASQUERADE

不要忘记上面的 -o ppp0 参数。如果您省略它,您的网络将会乱套。

假设我们在接口 eth1 上有另一个子网 10.3.0.0/16(意味着所有 10.3.*.* 地址)。我们再次添加与上面相同的规则:

# iptables -A fw-interfaces -i eth1 -j ACCEPT
# iptables -t nat -A POSTROUTING -s 10.3.0.0/16 -o ppp0 -j MASQUERADE

最后一步是启用数据包转发(如果尚未启用)。

来自这些子网的机器现在可以使用您的新 NAT 机器作为它们的网关。请注意,您可能需要设置 DNS 和 DHCP 服务器以简化客户端机器上的网络设置。这不在本指南的讨论范围内。

设置 PREROUTING 链

有时,我们想将传入数据包的地址从网关更改为 LAN 机器。为此,我们使用上面定义的 fw-open 链,以及 nat 表中的 PREROUTING 链,如以下两个简单示例所示。

首先,我们要将所有入站 SSH 数据包(端口 22)转发到机器 192.168.0.5 的 ssh 服务器:

# iptables -t nat -A PREROUTING -i ppp0 -p tcp --dport 22 -j DNAT --to 192.168.0.5
# iptables -A fw-open -d 192.168.0.5 -p tcp --dport 22 -j ACCEPT

第二个示例将向您展示如何将数据包转发到与入站端口不同的端口。我们想将端口 8000 上的任何入站连接转发到我们 192.168.0.6 上端口 80 的 Web 服务器:

# iptables -t nat -A PREROUTING -i ppp0 -p tcp --dport 8000 -j DNAT --to 192.168.0.6:80
# iptables -A fw-open -d 192.168.0.6 -p tcp --dport 80 -j ACCEPT

同样的设置也适用于 udp 数据包。

保存规则

保存规则

# iptables-save -f /etc/iptables/iptables.rules

这假设您已经按照上述步骤启用了 iptables systemd 服务。

参见

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.