Docker と iptables
Linux 上において Docker はiptables
ルールを利用して、ネットワークを分離します。
このことは実装に関わることであって、Docker がiptables
ポリシーに挿入するルールを修正するべきではありません。
Docker が管理するポリシーとは別に、独自のポリシーを追加しようとする場合は、そこには重要な観点がいくつかあります。
Docker を起動するホストがインターネットに接続しているとします。 その場合は、ホスト上で稼動するコンテナーや他のサービスに対しての不正アクセスを防止するような iptables ポリシーを設定したいはずです。 ここではそれをどのように実現するか、そしてどこに気をつけるべきかを説明します。
Docker ルール前への iptables ポリシー追加
Docker はDOCKER-USER
、DOCKER
という独自の iptables チェーンをインストールします。
これによって受信パケットは、常にこの 2 つのチェーンによって最初にチェックが行われます。
Docker のiptables
ルールはすべて、このDOCKER
チェーンに追加されます。
このチェーンを手動で操作してはなりません。
Docker のルールよりも前にロードしたいルールがある場合は、DOCKER-USER
チェーンにそのルールを追加します。
こういったルールは、Docker が自動生成するどのルールよりも先に適用されます。
FORWARD
チェーンに追加されたルールは、手動によるもの、あるいは iptables ベースのファイアウォールによるものであっても、Docker のチェーンの後で評価されます。
これが何を意味するかといえば、Docker によってポートを公開したら、ファイアウォールでどのような設定を行っていても、そのポートは必ず公開されるということです。
Docker によってポートを公開している状態で、ファイアウォールによるルールを適用したい場合は、そのルールをDOCKER-USER
チェーンに追加しなければ なりません。
Docker ホストへの接続制限
外部のソース IP は、Docker ホストに対する接続がデフォルトですべて許可されます。
コンテナーへの接続を特定の IP やネットワークのみとする場合は、DOCKER-USER
フィルターチェーンの上位に、除外ルールを設定します。
たとえば以下のルールは、192.168.1.1
は除く残りの IP アドレスからの外部アクセスを制限します。
$ iptables -I DOCKER-USER -i ext_if ! -s 192.168.1.1 -j DROP
ext_if
の部分は、利用するホストの実際の外部インターフェースに合わせて変更することを忘れないでください。
このかわりに、ソースサブネットからの接続を許可する方法もあります。
以下のルールは、サブネット192.168.1.0/24
からのアクセスのみ許可します。
$ iptables -I DOCKER-USER -i ext_if ! -s 192.168.1.0/24 -j DROP
また--src-range
を使えば、アクセスを許可する IP アドレスを範囲指定することができます。
(--src-range
や--dst-range
を用いる際には、-m iprange
を合わせて指定する必要があります。)
$ iptables -I DOCKER-USER -m iprange -i ext_if ! --src-range 192.168.1.1-192.168.1.3 -j DROP
-s
あるいは--src-range
と-d
あるいは--dst-range
を組み合わせて、ソース、デスティネーションの双方を制御することができます。
たとえば Docker デーモンが192.168.1.99
と10.1.2.3
を待ち受けている場合、10.1.2.3
を指定するルールを生成し、192.168.1.99
をそのまま残すようにします。
iptables
は複雑なものであり、iptables
ルールはさらに複雑であることから、ここでの説明範囲を超えます。
より詳しくは Netfilter.org HOWTO を参照してください。
ルーター上の Docker
Docker ではFORWARD
チェーンに対してDROP
も設定します。
Docker ホストがルーターとしても動作している場合、ルーターはもはや、どのトラフィックもフォワードしないということになります。
システム内においてルーターとしての機能を維持したい場合は、DOCKER-USER
チェーンに明示的にACCEPT
ルールを追加して、これを許可するようにします。
$ iptables -I DOCKER-USER -i src_if -o dst_if -j ACCEPT
Docker による iptables 操作の防止
Docker Engine の設定ファイル/etc/docker/daemon.json
において、iptables
キーをfalse
に設定することができますが、たいていのユーザーにとって、これを行うのは適切ではありません。
Docker がiptables
ルールを作らないようにすることは、完全にはできません。
生成されるルールは非常に複雑なものであり、ここでの説明の範囲を超えています。
iptables
にfalse
を設定すると、Docker Engine におけるコンテナーネットワーク機能が壊れる可能性が高くなります。
システムインテグレーターの方が、別のアプリケーション内に Docker ランタイムを含めてビルドしたい場合は、moby
プロジェクト を調べてみてください。
コンテナーへのデフォルトバインドアドレス設定
デフォルトで Docker デーモンは、ホスト上でのあらゆるアドレスを意味する0.0.0.0
において、ポートを公開します。
この状態を変更して、公開するポートは内部 IP アドレスに対してのみとする場合は、--ip
オプションを使って、別の IP アドレスを指定します。
ただし--ip
による設定は デフォルトを 変更するだけであり、その IP に対してサービスを 限定 するものではありません。
Firewalld との統合
Docker version 20.10.0 またはそれ以上の利用にあたって、--iptables
を有効にして firewalld を利用する場合、Docker は自動的にfirewalld
のゾーンとしてdocker
を生成し、生成するネットワークインターフェース(たとえばdocker0
など)すべてをdocker
ゾーンに加えることでシームレスなネットワークを実現します。
Docker のネットワークインターフェースをゾーンから除くには、以下のfirewalld
コマンドを実行することを覚えておいてください。
# ゾーンと Docker インターフェースは適切なものに書き換えてください。
$ firewall-cmd --zone=trusted --remove-interface=docker0 --permanent
$ firewall-cmd --reload
dockerd
デーモンを再起動すれば、インターフェースがdocker
ゾーンに挿入されます。