所以我有一个快速而粗糙的防火墙,我计划使用 nftables 在我的 vps 上使用它。这是初始规则
table inet filter {
chain input {
type filter hook input priority 0;
# Allow connections from localhost
iif "lo" accept
# Allow connections on port 5432 (PostgreSQL) from localhost
tcp dport 5432 iif "lo" accept
# Allow ping
icmp type { echo-request, echo-reply } limit rate 4/second accept
icmpv6 type { echo-request, echo-reply } limit rate 4/second accept
# Allow specific ports from remote hosts via TCP
tcp dport { 53, 80, 22,443, 1194, 8080 } accept
# Allow specific ports from remote hosts via UDP
udp dport { 53, 1194 } accept
# Drop all other incoming traffic
drop
}
}
但是如果我应用这个,我无法从我的 vps 实例 ping 。ping 命令(例如 ping yahoo.com)就挂在那里。但是如果我删除drop
,我可以 ping 到公共,但这会破坏我的防火墙的目的,不是吗?但请 cmiiw,因为我是这方面的新手。有人可以帮忙吗?谢谢。
答案1
不起作用的原因
原因 1:IPv6 NDP 被丢弃
当前规则集阻止 IPv6 在以太网上正常工作,因为它会丢弃与以下相关的 ICMPv6 流量:新民主党这相当于 IPv6 的 ARP。因此,它会在接下来的几秒或几分钟内终止大多数设置的 IPv6 连接(可以看到ip -6 monitor neigh
路由器条目变为 REACHABLE->STALE->PROBE->FAILED,而不是 REACHABLE->STALE->PROBE->REACHABLE),就像当 ARP 被防火墙保护时,以太网上的 IPv4 会停止工作一样。
虽然这不会对 IPv4 产生太大影响,但它会造成以下情况:PMTUD 黑洞可能由于相同的原因而出现:不允许接受相关的 ICMP 错误。
尝试时ping yahoo.com
,如果通过 IPv6 进行尝试,则会失败。
现在让我们假设该系统上没有使用 IPv6(但以下段落仍然适用于 IPv6)。它不起作用还有一个更重要的原因。
原因 2:回程流量丢失
同样,当尝试时ping yahoo.com
,会向一个或多个其他 DNS 服务器发出 DNS 查询以解析地址。回复从远程端口 53 发送到本地随机端口。没有规则允许此类回复流量:它会被丢弃,因此 DNS 会失败。当系统是使用 UDP 或 TCP 的客户端,而不是回复远程客户端对 VPS 上明确允许的端口的请求的服务器时,大多数尝试都会发生同样的情况。
ping
到 IPv4 地址的A仍然应该会成功,因为规则集明确接受此类 ping 回复。
修复它
回复流量需要状态防火墙
这个问题无法通过无状态防火墙解决,否则防火墙本应提供的安全性将失效。不能只添加:udp sport 53 accept
+tcp sport { 53, 443 } accept
来允许 DNS回复和 HTTPS回复正常工作,因为任何远程攻击都可以使用这样的源端口访问任何端口,从而访问 VPS 系统上的任何服务。
只需使用 Netfilter 的状态功能连接跟踪自动允许返回流量并且仅允许返回流量以及相关流量,默认情况下这些流量将是可能收到的 ICMP 错误(从而恢复对 PMTUD 的正确处理并防止远程拒绝的 UDP 服务超时,当然还有任何瞬态 Internet 错误)。首先添加此规则:
ct state established,related accept
那么就不再需要允许传入的 ICMP 回显答复:它已在上面处理。
IPv6 依赖于 NDP,因此也依赖于 ICMPv6 才能正常工作
在弄清楚应该允许哪些 ICMPv6 类型来保持 NDP 文档中的正确连接之前 (RFC 4861:IP 版本 6 (IPv6) 的邻居发现),可以通过以下方式允许所有这些:
meta l4proto ipv6-icmp accept
icmpv6
这是当不使用匹配时使用的语法。
如果你坚持要过滤它们,这些应该足以让终端系统获得连接。其中一些 ICMPv6 数据包可能由连接跟踪在上面的短路状态防火墙规则中,但大多数不会,因为它们不是回复,或者因为涉及多播(并且无法正确跟踪):
icmpv6 type { nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
如果使用或提供多播服务,则需要更多,可能是其中一些或全部(它们不是 NDP 的一部分而是 MLD,相当于 IPv4 的 IGMP):
# nft describe icmpv6 type | grep mld
mld-listener-query 130
mld-listener-report 131
mld-listener-done 132
mld-listener-reduction 132
mld2-listener-report 143
如果 LAN 中有多个路由器(nd-redirect
),如果服务器也是路由器(nd-router-solicit
),包括通过 IPv6 托管容器或 VM 等,则情况会更多。
建议的规则集
在通用状态规则之前处理 ping:否则一旦 ping 流开始,它将被状态规则短路,并且没有机会进行速率限制。这种情况可能需要进一步重新排序、过滤或重构。
table inet filter # for idempotence
delete table inet filter # for idempotence
table inet filter {
chain input {
type filter hook input priority 0; policy accept;
# Allow incoming ping
# but still honor limit and drop the excess
# It could have been rewritten using anonymous chains
# but I kept it simple.
# It has to be done as an exception to the stateful rule
# so before it.
icmp type echo-request limit rate 4/second accept
icmp type echo-request drop
icmpv6 type echo-request limit rate 4/second accept
icmpv6 type echo-request drop
# firewall becomes stateful from here:
# bulk of the traffic will be handled by this single rule
ct state established,related accept
# Allow connections from localhost
iif "lo" accept
# Allow connections on port 5432 (PostgreSQL) from localhost
# tcp dport 5432 iif "lo" accept # already allowed by rule above
# Allow specific ports from remote hosts via TCP
tcp dport { 53, 80, 22,443, 1194, 8080 } accept
# Allow specific ports from remote hosts via UDP
udp dport { 53, 1194 } accept
# minimal ICMPv6 support for an end-node system
# non-LAN ICMPv6 (as well as IPv4 ICMP) packets used to report
# errors are handled by the generic stateful rule.
icmpv6 type { nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
# Drop all other incoming traffic
drop
}
}
答案2
尝试一下这个输入过滤器。我认为最后的 drop 会丢弃所有流量,包括 icmp。
您需要将默认策略设置为丢弃,而不是最后丢弃。
type filter hook input priority 0; policy drop;
table inet filter {
chain input {
type filter hook input priority
0; policy drop;
# Allow connections from localhost
iif "lo" accept
# Allow connections on port 5432 (PostgreSQL) from localhost
tcp dport 5432 iif "lo" accept
# Allow ping
icmp type { echo-request, echo-reply } limit rate 4/second accept
icmpv6 type { echo-request, echo-reply } limit rate 4/second accept
# Allow specific ports from remote hosts via TCP
tcp dport { 53, 80, 22,443, 1194, 8080 } accept
# Allow specific ports from remote hosts via UDP
udp dport { 53, 1194 } accept
}
}