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ゾーンに挿入されます。