web-dev-qa-db-ja.com

Dockerがlibvirtブリッジネットワークを破壊する

この問題は私を夢中にさせています。 Ubuntu 18.04のフレッシュインストールを実行します。

  • ファイアウォールを管理するufw
  • br0ブリッジ
  • lxdおよびlibvirt(KVM)

私は標準のdocker.ioパッケージを試してみましたが、パッケージはdocker自身のdebリポジトリから作成されました。

ポートをバインドするIP(例:-p 10.58.26.6:98800:98800)を選択し、UFWでポートを開くDockerコンテナーを展開できるようにしたい。

しかし、Dockerはbr0ブリッジを混乱させるiptablesルールを作成するようです(たとえば、ホストはlibvirtゲストにpingできない)。

私は周りを見回しましたが、セキュリティに配慮した優れたソリューションを見つけることができません。

iptables -I FORWARD -i br0 -o br0 -j ACCEPTseemsを手動で実行すると、すべてが機能するようになります。

また、Dockerデーモンに"iptables": falseを設定すると、ブリッジは正常に動作しますが、Dockerのコンテナーの下りネットワークが壊れます。

単一のUFWのファイルを編集することで、このソリューションは単純に見えました https://stackoverflow.com/a/51741599/1091772 が、まったく機能しません。

これを永続的に解決し、再起動まで生き残るためのベストプラクティスと安全な方法は何ですか?

EDIT:COMMITの前の-A ufw-before-forward -i br0 -o br0 -j ACCEPTの最後に/etc/ufw/before.rulesを追加してしまいました。これを修正と見なすことはできますか、それともいくつかの問題を引き起こしませんか?

7
Laurent

問題、実際には機能: br_netfilter

説明から、論理的な説明は bridge netfilter code が有効になっていることだけだと思います:ステートフルブリッジファイアウォールやiptablesの活用など、さまざまな用途に使用できます'すべてをebtablesに複製する必要がない(または複製できない)ブリッジパスの一致およびターゲット。ネットワークレイヤー2のイーサネットブリッジコードであるネットワークレイヤーをまったく無視すると、IPレベル、つまりネットワークレイヤー3で動作するiptablesがupcallされるようになります。ホストとすべてのコンテナ、またはなし。何が起こっているかを理解し、何を探すべきかを知ったら、適応した選択を行うことができます。

Netfilterプロジェクトは、br_netfilterが有効な場合のさまざまな ebtables/iptables相互作用 について説明します。特に興味深いのは セクション7 のように、ブリッジパスからの意図しない影響を回避するために、明らかな影響のないいくつかのルールが必要になる場合がある理由を説明します。

iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -d 172.16.1.0/24 -j ACCEPT
iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -j MASQUERADE

同じLAN上の2つのシステムがブリッジでNAT処理されるのを回避するため(ブリッジの例を参照)。

問題を回避するための選択肢はいくつかありますが、詳細をすべて知りたくない場合や、一部のiptablesルール(他の名前空間に隠されている場合もある)が中断されるかどうかを確認したくない場合は、おそらく最善の選択です。

  • br_netfilterモジュールがロードされるのを永久に防ぎます。通常、blacklistでは不十分です。installを使用する必要があります。これは、br_netfilterに依存するアプリケーションで問題が発生しやすい選択肢です。明らかにDocker、Kubernetes、...

    echo install br_netfilter /bin/true > /etc/modprobe.d/disable-br-netfilter.conf
    
  • モジュールをロードしますが、その効果を無効にします。 iptables 'の効果の場合:

    sysctl -w net.bridge.bridge-nf-call-iptables=0
    

    起動時にこれを配置する場合、モジュールを最初にロードする必要があります。そうしないと、このトグルはまだ存在しません。

これら2つの以前の選択は、確実にiptables一致を混乱させます -m physdevxt_physdevモジュールそれ自体がロードされると、br_netfilterモジュールが自動ロードされます(これは、コンテナーから追加されたルールがロードをトリガーした場合でも発生します)。これでbr_netfilterは読み込まれなくなり、-m physdevはおそらく一致しません。

  • OPのように、必要に応じてbr_netfilterの影響を回避します。 セクション7 で説明されているように、さまざまなチェーン(PREROUTING、FORWARD、POSTROUTING)にこれらの明らかなノーオペレーションルールを追加します。例えば:

    iptables -t nat -A POSTROUTING -s 172.18.0.0/16 -d 172.18.0.0/16 -j ACCEPT
    
    iptables -A FORWARD -i br0 -o br0 -j ACCEPT
    

    いくつかのまれなDNAT設定を除いて、同じIP LAN内のトラフィックはルーティングされないため、これらのルールが一致することはありません。ただし、br_netfilterのおかげで一致します。これは、switchedフレーム(IPパケットに「アップグレード」)を最初に呼び出すためです。 bridge。次に、それらはroutedパケットに対して再度呼び出され、routerを無関係のインターフェースに転送します(ただし、一致しません)。

  • ブリッジにIPを置かないでください。そのIPをvethインターフェイスの一方の端に、ブリッジのもう一方の端に配置します。これにより、ブリッジがルーティングと相互作用しないことが保証されますが、そうではありません。ほとんどのコンテナー/ VM共通製品を実行しています。

  • 独自の分離されたネットワーク名前空間でブリッジを非表示にすることもできます(今回は、他のebtablesルールから分離したい場合にのみ役立ちます)。

  • すべてを nftables に切り替えます。これにより、指定された目標の中でこれらの ブリッジの相互作用の問題 が回避されます。現時点では、ブリッジファイアウォールは利用可能なステートフルサポートを備えていません。それは [〜#〜] wip [〜#〜] ですが、「アップコール」がないため、利用可能な場合はよりクリーンであることが約束されています。 。

br_netfilter(例:-m physdev)のロードをトリガーするものを検索し、それを回避できるかどうかを確認して、続行方法を選択する必要があります。


ネットワーク名前空間の例

ネットワーク名前空間を使用していくつかの効果を再現しましょう。 ebtablesルールはどこでも使用されないことに注意してください。また、この例は、Debianバスターでデフォルトで有効になっている nftables over iptables ではなく、通常のレガシー iptables に依存していることにも注意してください。

多くのコンテナー使用法と同様の単純なケースを再現してみましょう:ルーター192.168.0.1/192.0.2.100がNAT背後に2つのホスト:192.168.0.101と192.168.0.102を持ち、ブリッジ上のブリッジにリンクされています) 2つのホストは、ブリッジを介して、同じLANで直接通信できます。

#!/bin/sh

for ns in Host1 Host2 router; do
    ip netns del $ns 2>/dev/null || :
    ip netns add $ns
    ip -n $ns link set lo up
done

ip netns exec router sysctl -q -w net.ipv4.conf.default.forwarding=1

ip -n router link add bridge0 type bridge
ip -n router link set bridge0 up
ip -n router address add 192.168.0.1/24 dev bridge0

for i in 1 2; do
    ip -n Host$i link add eth0 type veth peer netns router port$i
    ip -n Host$i link set eth0 up
    ip -n Host$i address add 192.168.0.10$i/24 dev eth0
    ip -n Host$i route add default via 192.168.0.1
    ip -n router link set port$i up master bridge0
done

#to mimic a standard NAT router, iptables rule voluntarily made as it is to show the last "effect"
ip -n router link add name eth0 type dummy
ip -n router link set eth0 up
ip -n router address add 192.0.2.100/24 dev eth0
ip -n router route add default via 192.0.2.1
ip netns exec router iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j MASQUERADE

カーネルモジュールbr_netfilterをロードして(後でないことを確認)、(not-per-namespace)トグルを使用してその効果を無効にしますbridge-nf-call-iptables、初期ネームスペースでのみ使用可能:

modprobe br_netfilter
sysctl -w net.bridge.bridge-nf-call-iptables=0

警告:繰り返しますが、これは -m physdev のようなiptablesルールをホスト上の任意の場所または依存するコンテナbr_netfilterがロードされ、有効になっています。

Icmp pingトラフィックカウンターをいくつか追加してみましょう。

ip netns exec router iptables -A FORWARD -p icmp --icmp-type echo-request
ip netns exec router iptables -A FORWARD -p icmp --icmp-type echo-reply

Pingしましょう:

# ip netns exec Host1 ping -n -c2 192.168.0.102
PING 192.168.0.102 (192.168.0.102) 56(84) bytes of data.
64 bytes from 192.168.0.102: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 192.168.0.102: icmp_seq=2 ttl=64 time=0.058 ms

--- 192.168.0.102 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1017ms
rtt min/avg/max/mdev = 0.047/0.052/0.058/0.009 ms

カウンターは一致しません:

# ip netns exec router iptables -v -S FORWARD
-P FORWARD ACCEPT -c 0 0
-A FORWARD -p icmp -m icmp --icmp-type 8 -c 0 0
-A FORWARD -p icmp -m icmp --icmp-type 0 -c 0 0

bridge-nf-call-iptablesを有効にして、もう一度pingします。

# sysctl -w net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-iptables = 1
# ip netns exec Host1 ping -n -c2 192.168.0.102
PING 192.168.0.102 (192.168.0.102) 56(84) bytes of data.
64 bytes from 192.168.0.102: icmp_seq=1 ttl=64 time=0.094 ms
64 bytes from 192.168.0.102: icmp_seq=2 ttl=64 time=0.163 ms

--- 192.168.0.102 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1006ms
rtt min/avg/max/mdev = 0.094/0.128/0.163/0.036 ms

今回切り替えられたパケットは、iptablesのfilter/FORWARDチェーンで一致しました。

# ip netns exec router iptables -v -S FORWARD
-P FORWARD ACCEPT -c 4 336
-A FORWARD -p icmp -m icmp --icmp-type 8 -c 2 168
-A FORWARD -p icmp -m icmp --icmp-type 0 -c 2 168

DROPポリシー(デフォルトのカウンターをゼロにする)を入力して、もう一度試してみましょう。

# ip netns exec Host1 ping -n -c2 192.168.0.102
PING 192.168.0.102 (192.168.0.102) 56(84) bytes of data.

--- 192.168.0.102 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1008ms

# ip netns exec router iptables -v -S FORWARD
-P FORWARD DROP -c 2 168
-A FORWARD -p icmp -m icmp --icmp-type 8 -c 4 336
-A FORWARD -p icmp -m icmp --icmp-type 0 -c 2 168

ブリッジコードは、iptablesを介してスイッチフレーム/パケットをフィルタリングしました。 OPのようにバイパスルール(デフォルトのカウンターを再びゼロにする)を追加して、もう一度試してみましょう。

# ip netns exec router iptables -A FORWARD -i bridge0 -o bridge0 -j ACCEPT
# ip netns exec Host1 ping -n -c2 192.168.0.102
PING 192.168.0.102 (192.168.0.102) 56(84) bytes of data.
64 bytes from 192.168.0.102: icmp_seq=1 ttl=64 time=0.132 ms
64 bytes from 192.168.0.102: icmp_seq=2 ttl=64 time=0.123 ms

--- 192.168.0.102 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1024ms
rtt min/avg/max/mdev = 0.123/0.127/0.132/0.012 ms

# ip netns exec router iptables -v -S FORWARD
-P FORWARD DROP -c 0 0
-A FORWARD -p icmp -m icmp --icmp-type 8 -c 6 504
-A FORWARD -p icmp -m icmp --icmp-type 0 -c 4 336
-A FORWARD -i bridge0 -o bridge0 -c 4 336 -j ACCEPT

Host1からのping中にHost2で実際に受信されたものを見てみましょう。

# ip netns exec Host2 tcpdump -l -n -s0 -i eth0 -p icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
02:16:11.068795 IP 192.168.0.1 > 192.168.0.102: ICMP echo request, id 9496, seq 1, length 64
02:16:11.068817 IP 192.168.0.102 > 192.168.0.1: ICMP echo reply, id 9496, seq 1, length 64
02:16:12.088002 IP 192.168.0.1 > 192.168.0.102: ICMP echo request, id 9496, seq 2, length 64
02:16:12.088063 IP 192.168.0.102 > 192.168.0.1: ICMP echo reply, id 9496, seq 2, length 64

...ソース192.168.0.101の代わりに。 MASQUERADEルールもブリッジパスから呼び出されました。これを回避するには、前に例外ルールを追加するか( (セクション7 の例で説明))、または可能な場合はブリッジ以外の発信インターフェースを記述します(これが利用可能になった場合、-m physdevブリッジにする必要がある場合...).


ランダムに関連:

LKML/netfilter-dev: br_netfilter:非初期netnsで有効にする :グローバルではなくネームスペースごとにこの機能を有効にすると、ホストとコンテナー間の相互作用が制限されます。

netfilter-dev: netfilter:physdev:br_netfilter依存関係を緩和 :存在しないphysdevルールを削除しようとすると、問題が発生する可能性があります。

netfilter-dev: ブリッジの接続追跡サポート :nftablesを使用してステートフルブリッジファイアウォールを準備するWIPブリッジネットフィルターコード。今回はよりエレガントに。 iptables(カーネルサイドAPI)を取り除くための最後のステップの1つだと思います。

7
A.B

上記の脅威で問題が解決しない場合は、Debian Stretchで問題を解決した方法を次に示します。

  • まず、現在のiptablesを保存します

    iptables-save > your-current-iptables.rules
    
  • 2番目、削除[〜#〜] all [〜#〜] Dockerが作成したルール

    iptables -D <DOCKER-CHAIN-RULES> <target-line-number>
    
  • 3番目に、入力、転送、出力へのトラフィックを受け入れるitpablesルールを追加します

    iptables -I INPUT -j ACCEPT
    iptables -I FORWARD -j ACCEPT
    iptables -I OUTPUT -j ACCEPT
    
  • 4番目に、Dockerを再起動します

    service docker restart
    

手順3が完了すると、ブロックされたlibvert KVMホストに別のPCからpingを実行できます。ICMP応答が表示されます。

Dockerを再起動すると、必要なiptablesルールもマシンに追加されますが、ブリッジされたKVMホストをブロックしなくなります。

上記の解決策が機能しない場合は、次のコマンドを使用してiptablesを復元できます。

  • Iptablesを復元する

    iptables-restore < your-current-iptables.rules
    
0
Vincent P