Routing

Docker 對稱/基於策略的路由

  • July 3, 2019

背景

我有一個 debian 伺服器,它有 3 個網路介面,它們是:

  • 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 使用哪個路由?

快速解決方案:

  • 通過防火牆標記添加路由規則。帶有對應標記的數據包將通過單獨的路由表進行路由。
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

引用自:https://serverfault.com/questions/973578