Docker Network - Container communication
Default bridge network における、 Container communication についての説明。
コンテナと外部の通信
コンテナが外部と疎通可能か、については 2 つの要素により制御される。
1 つ目はホストマシンが IP パケットをフォワードするか。 2 つ目はホストの iptables で通信が許可されているか。
IP パケットのフォワーディングは ip_forward という kernel パラメータで決定される。パラメータが 1 に設定されているときのみ、コンテナに IP パケットをフォワードできる。
Docker 側で特に何も指定せず起動した場合はデフォルトで --ip-forward=true が有効化され、 ip_forward が 1 に設定される。 --ip-forward=false を指定したとしても、カーネル側で有効化しているのであれば問題はない。
以下の手順にて、 kernel 設定をマニュアルで変更可能。
$ sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 0
$ sysctl net.ipv4.conf.all.forwarding=1
$ sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 1
2.5. TURNING ON PACKET FORWARDING
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/load_balancer_administration/s1-lvs-forwarding-vsa
また、この設定は Network Driver が host の場合は特に影響を受けない。
コンテナから外部のネットワークへの疎通性を確保するために、基本的には Docker 側ではこれを有効にする。また、複数ブリッジがある場合などコンテナ間の通信でも必要となる場合がある。
もし --iptables=false と設定をした場合は、 Docker はデーモン開始時に iptables を書き換えない。代わりに、 DOCKER filter chain にフォワーディングルールを追記する。
もし事前に DOCKER や DOCKER-ISOLATION の filter rule が存在していた場合、 Docker はこれらを消去する。そのため、なにか制限を行うようなルールを書き込む場合は Docker 起動後に実施する必要がある。
Docker のフォワードルールではすべての external source IP がデフォルトで許可される。もし個別に拒否したい場合は DOCKER filter の先頭に negated rule を挿入する必要がある。
例えば、 source IP 8.8.8.8 からのみのアクセスを許可したい場合は、以下のようなフィルタを追加する。
$ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP
コンテナ間での通信
コンテナ間の通信が可能か、については OS レベルでは 2 つの要素により制御される。
ネットワークトポロジ的に接続性があるか
デフォルトでは、すべてのコンテナは docker0 にアタッチされ、疎通性が提供される。後ほど、その他の考えられるトポロジについて説明する。
iptables にて正しく許可しているか
Docker は --iptables=false が設定されている場合、デーモン開始時にシステムの iptables を書き換えることはない。
デフォルトの --icc=true を維持している場合、 Docker は FORWARD chain に blanket ACCEPT を追加する。 --icc=false の場合は DROP をセットする。
※ icc = inter-container communication
--icc=true を有効のまま使うか否かは、 --icc=false を利用すれば iptables にて他のコンテナを守ることができるという点があり、悩ましい問題である。
もし最もセキュアな手段である --icc=false を有効化した場合、どうすれば特定のコンテナに通信が可能になるのか。答えは --link=CONTAINER_NAME_or_ID:ALIAS を使うことである。
もし Docker デーモンが --icc=false と --iptables=true の状態で稼働している場合、 --link= で Docker run を実行すると新しいコンテナが別のコンテナの公開されてるポート (Dockerfile にて EXPOSE が書かれているもの) に接続するために iptables の ACCEPT ルールを挿入する。
※ --link= オプションにおける CONTAINER_NAME は、 Docker により自動生成される名前か、 --name= にて指定されるものとなる。
FORWARDchain にて ACCEPT や DROP が入っていないか、 iptables コマンドを実行して確認できる。
# When --icc=false, you should see a DROP rule:
$ sudo iptables -L -n
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
...
# When a --link= has been created under --icc=false,
# you should see port-specific ACCEPT rules overriding
# the subsequent DROP policy for all other packets:
$ sudo iptables -L -n
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80
ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80
コンテナとホストの通信
セキュリティ的に、 Docker は iptables にてコンテナがホストマシン外部からアクセスされることを防いでいる。デフォルトポリシーとして FORWARD chain に DROP を設定している。
以下のコマンドで、手動で override できる。
$ sudo iptables -P FORWARD ACCEPT
これはホストマシンの再起動時にリセットされる。
Ref docs
Understand container communication
https://docs.docker.com/v17.09/engine/userguide/networking/default_network/container-communication
Can I disable inter-container communication (icc) in Docker EE?
https://success.docker.com/article/can-i-disable-inter-container-communication-icc-in-docker-ee
