Docker と iptables

Linux 上において Docker はiptablesルールを利用して、ネットワークを分離します。 このことは実装に関わることであって、Docker がiptablesポリシーに挿入するルールを修正するべきではありません。 Docker が管理するポリシーとは別に、独自のポリシーを追加しようとする場合は、そこには重要な観点がいくつかあります。

Docker を起動するホストがインターネットに接続しているとします。 その場合は、ホスト上で稼動するコンテナーや他のサービスに対しての不正アクセスを防止するような iptables ポリシーを設定したいはずです。 ここではそれをどのように実現するか、そしてどこに気をつけるべきかを説明します。

Docker ルール前への iptables ポリシー追加

Docker はDOCKER-USERDOCKERという独自の 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.9910.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ルールを作らないようにすることは、完全にはできません。 生成されるルールは非常に複雑なものであり、ここでの説明の範囲を超えています。 iptablesfalseを設定すると、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ゾーンに挿入されます。

network, iptables