这个问题快把我逼疯了。我运行全新安装的 Ubuntu 18.04,其中:
- ufw 管理防火墙
- br0 桥
- lxd 和 libvirt (KVM)
我尝试了股票 docker.io 包和包形成 docker 自己的 deb 存储库。
我希望能够部署 docker 容器,选择 ip 来绑定其端口(例如 -p 10.58.26.6:98800:98800),然后使用 UFW 打开端口。
但 docker 似乎创建了扰乱 br0 桥的 iptables 规则(例如,主机无法 ping libvirt 来宾)
我环顾四周,找不到好的安全意识解决方案。
手动iptables -I FORWARD -i br0 -o br0 -j ACCEPT
操作似乎使一切正常。
docker daemon 的设置也"iptables": false
允许网桥正常运行,但会破坏 docker 的容器出口网络。
通过编辑单个 UFW 的文件https://stackoverflow.com/a/51741599/1091772 ,我发现这个解决方案看起来很简单,但它根本不起作用。
永久解决这个问题的最佳实践和安全方法是什么,在重新启动后幸存下来?
编辑:
我最终-A ufw-before-forward -i br0 -o br0 -j ACCEPT
在/etc/ufw/before.rules
提交之前添加。我可以将其视为解决方案还是不会引发一些问题?
问题,其实是一个特性:br_netfilter
从描述中,我相信唯一合乎逻辑的解释是启用了网桥 netfilter 代码:用于有状态网桥防火墙或利用iptables的匹配和来自网桥路径的目标,而不必(或能够)复制它们在ebtables中。完全不考虑网络分层,网络第 2 层的以太网桥代码现在调用在 IP 级别(即网络第 3 层)工作的iptables。它只能在全局范围内启用:对于主机和每个容器,或者没有。一旦了解了正在发生的事情并知道要寻找什么,就可以做出适当的选择。
netfilter 项目描述了启用br_netfilter时的各种
ebtables
/iptables
交互。尤其令人感兴趣的是第 7 节解释了为什么有时需要一些没有明显效果的规则来避免桥接路径的意外影响,例如使用:避免同一 LAN 上的两个系统被网桥 NAT(参见下面的示例)。
你有几个选择来避免你的问题,但是如果你不想知道所有的细节,也不想验证某些 iptables 规则(有时隐藏在其他命名空间中)是否会被破坏,那么你所做的选择可能是最好的:
永久阻止加载br_netfilter模块。通常
blacklist
是不够的,install
必须使用。对于依赖br_netfilter的应用程序来说,这是一个容易出现问题的选择:显然是 Docker、Kubernetes、...加载模块,但禁用其效果。对于iptables的效果是:
如果将其放在启动时,则应首先加载模块,否则此切换将不存在。
这两个先前的选择肯定会破坏iptables匹配
-m physdev
:xt_physdev模块在自身加载时会自动加载br_netfilter模块(即使从容器添加的规则触发了加载也会发生这种情况)。现在br_netfilter不会被加载,-m physdev
可能永远不会匹配。在需要时解决 br_netfilter 的效果,例如 OP:如第 7 节所述,在各种链(PREROUTING、FORWARD、POSTROUTING)中添加那些明显的无操作规则。例如:
这些规则永远不应该匹配,因为同一 IP LAN 中的流量不会被路由,除了一些罕见的 DNAT 设置。但是多亏了br_netfilter,它们确实匹配,因为它们首先被调用用于穿越桥的交换帧(“升级”为 IP 数据包) 。然后再次调用它们以获取通过路由器到达不相关接口的路由数据包(但不会匹配)。
不要将 IP 放在网桥上:将该 IP 放在
veth
接口的一端,另一端放在网桥上:这应该确保网桥不会与路由交互,但这不是大多数容器/VM 所做的常见的产品。您甚至可以将网桥隐藏在其自己的隔离网络名称空间中(这只有在这次想要与其他ebtables规则隔离时才有用)。
将所有内容都切换到nftables,其中规定的目标将避免这些桥梁交互问题。目前,桥接防火墙没有可用的状态支持,它仍然是WIP,但承诺在可用时会更干净,因为不会有任何“upcall”。
您应该搜索触发br_netfilter加载的原因(例如:)
-m physdev
,看看您是否可以避免它,以选择如何继续。网络命名空间示例
让我们使用网络命名空间重现一些效果。请注意,任何地方都不会使用任何ebtables规则。另请注意,此示例依赖于通常的 legacy
iptables
,而不是Debian buster 默认启用的iptables over nftables 。让我们重现一个与许多容器使用类似的简单案例:路由器 192.168.0.1/192.0.2.100 执行 NAT,后面有两个主机:192.168.0.101 和 192.168.0.102,与路由器上的网桥相连。两台主机可以通过网桥在同一个 LAN 上直接通信。
让我们加载内核模块br_netfilter(以确保它不会稍后)并使用(非每个命名空间)切换bridge-nf-call-iptables禁用它的效果,仅在初始命名空间中可用:
警告:同样,这可能会破坏iptables规则,例如
-m physdev
主机上的任何位置或依赖于加载和启用br_netfilter的容器中的任何位置。让我们添加一些 icmp ping 流量计数器。
让我们ping:
计数器不匹配:
让我们启用bridge-nf-call-iptables并再次 ping:
这次交换的数据包在 iptables 的过滤器/转发链中得到了匹配:
让我们设置一个 DROP 策略(将默认计数器归零)并再试一次:
桥接代码通过 iptables 过滤交换的帧/数据包。让我们像在 OP 中一样添加绕过规则(这将使默认计数器再次归零),然后再试一次:
让我们看看在来自 host1 的 ping 期间在 host2 上实际收到的内容:
...而不是源 192.168.0.101。MASQUERADE 规则也从桥接路径中调用。为避免这种情况,请在之前添加(如第 7 节的示例中所述)异常规则,或声明一个非网桥传出接口,如果可能的话(现在它可用,
-m physdev
如果它必须是网桥,您甚至可以使用它.. .)。随机相关:
LKML/netfilter-dev: br_netfilter: 在非初始 netns中启用:这将有助于在每个命名空间而不是全局启用此功能,从而限制主机和容器之间的交互。
netfilter-dev: netfilter: physdev: 放松 br_netfilter 依赖:仅仅试图删除一个不存在的physdev规则可能会产生问题。
netfilter-dev:对网桥的连接跟踪支持:WIP 网桥 netfilter 代码使用 nftables 准备有状态的网桥防火墙,这一次更优雅。我认为摆脱 iptables (的内核端 API)的最后步骤之一。
如果上述威胁无法解决您的问题,以下是我在 Debian Stretch 上解决问题的方法。
1、保存你当前的 iptables
2、删除所有Docker 创建的规则
3、添加itpables规则以接受任何到INPUT、FORWARD和OUTPUT的流量
4、重启你的Docker
完成第 3 步后,您可以从另一台 PC ping 被阻止的 libvert KVM 主机,您将看到 ICMP 响应。
重新启动 Docker 还将其所需的 iptables 规则添加回您的计算机,但它不会再阻止您的桥接 KVM 主机。
如果上述解决方案不适合您,您可以使用以下命令恢复 iptables:
恢复 iptables