Docker 对称/基于策略的路由

Docker 对称/基于策略的路由

背景

我有一台具有 3 个网络接口的 Debian 服务器,它们是:

  • eno1(10.0.0.35/24)
  • eno1.10 (10.0.10.65/24)
  • eno1.40 (10.0.40.40/24)

这些接口之间有防火墙。服务器上的多个路由导致路由不对称,防火墙会将其作为无效流量阻止。

因此,我添加了一些基于策略的规则,以便目标/源 IP 地址保持不变。我通过/etc/network/interfaces如下编辑来实现此目的:

# The primary network interface
allow-hotplug eno1
iface eno1 inet dhcp
  post-up ip route add 10.0.0.0/24 dev eno1 table 1
  post-up ip route add default via 10.0.0.1 table 1
  post-up ip rule add from 10.0.0.35/32 table 1 priority 100
  post-up ip route flush cache
  pre-down ip rule del from 10.0.0.35/32 table 1 priority 100
  pre-down ip route flush table 1
  pre-down ip route flush cache

# VLANS
auto eno1.10
iface eno1.10 inet dhcp
  post-up ip route add 10.0.10.0/24 dev eno1.10 table 2
  post-up ip route add default via 10.0.10.1 table 2
  post-up ip rule add from 10.0.10.65/32 table 2 priority 110
  post-up ip route flush cache
  pre-down ip rule del from 10.0.10.65/32 table 2 priority 110
  pre-down ip route flush table 2
  pre-down ip route flush cache

auto eno1.40
iface eno1.40 inet dhcp
  post-up ip route add 10.0.40.0/24 dev eno1.40 table 3
  post-up ip route add default via 10.0.40.1 table 3
  post-up ip rule add from 10.0.40.40/32 table 3 priority 120
  post-up ip route flush cache
  pre-down ip rule del from 10.0.40.40/32 table 3 priority 120
  pre-down ip route flush table 3
  pre-down ip route flush cache

所有正在运行的服务服务器现在正常运行。

此外,我在服务器上运行一个 docker 主机,它托管一些绑定到服务器上不同接口的容器。

问题

现在的问题是,我创建的规则显然不适用于来自 docker 容器的流量,而且我无法访问它们,因为这些流量被阻止为无效。

我需要在这里做什么才能让 Docker 容器根据源 IP 知道使用哪条路由?

答案1

快速解决方案:

  • 通过防火墙标记添加路由规则,带有相应标记的数据包将通过单独的路由表进行路由。
ip rule add fwmark 0x1 lookup 1 pref 10001
ip rule add fwmark 0x2 lookup 2 pref 10002
ip rule add fwmark 0x3 lookup 3 pref 10003
  • 传入连接的标记取决于输入接口。connmark 目标将标记值保存在 conntrack 条目中。
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -i eno1 -j CONNMARK --set-mark 0x1
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -i eno1.10 -j CONNMARK --set-mark 0x2
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -i eno1.40 -j CONNMARK --set-mark 0x3
  • 将标记值从 conntrack 条目复制到防火墙标记。此后,回复的数据包将通过已添加的附加路由规则进行路由。使用附加-i匹配或按源地址匹配,否则您需要将直接连接的路由添加到附加表中。
iptables -t mangle -A PREROUTING -i docker0 -j CONNMARK --restore-mark
  • 您也可以使用源地址匹配来代替输入接口。
iptables -t mangle -A PREROUTING --src <container-subnet> -j --restore-mark
  • 该解决方案完美地适用于DNAT
  • 使用tcpdumpconntrack工具来解决问题。
  • 还要检查rp_filter。在某些情况下,它可能会丢弃数据包。最好将其设置为loose模式(sysctl -w net.ipv4.conf.all.rp_filter=2)。

更新

经过实验室的一些测试,我发现了一套完美的规则。它只需要一个标记值和每个上行链路一个额外的路由规则。当您在多个接口上使用公共地址时,它还可以处理复杂的情况。

  • 为每个上行链路创建一个额外的路由表并分配一个防火墙标记。
ip route add <uplink-subnet> dev <uplink-iface> table <uplink-table>
ip route add 0/0 via <uplink-gw> dev <uplink-iface> table <uplink-table>

ip rule add fwmark <uplink-mark> table <uplink-table>
  • 对于每个上行链路接口添加一条规则来标记传入连接:
iptables -t mangle -A PREROUTING -i <uplink-iface> -m conntrack --ctstate NEW --ctdir ORIGINAL -j CONNMARK --set-mark <uplink-mark>
...
  • 为所有上行链路添加两条规则来标记回复数据包:
iptables -t mangle -A PREROUTING -m conntrack ! --ctstate NEW --ctdir REPLY -m connmark ! --mark 0x0 -j CONNMARK --restore-mark

iptables -t mangle -A OUTPUT -m conntrack ! --ctstate NEW --ctdir REPLY -m connmark ! --mark 0x0 -j CONNMARK --restore-mark

相关内容