ubuntu ファイアーウォール :Docker が UFW 管理外のポートを開けてしまう問題

UFW とは ファイアーウォールを設定・管理するツールで、Ubuntuでは標準的に利用します。このツールを使用すれば、iptables を使うよりはるかに簡単にファイアーウォールを構築できます。
UFW - Community Help Wiki

Docker はご存知ですね。
Docker - Build, Ship, and Run Any App, Anywhere

遭遇した問題

今回 UFW と docker の両方を使用するサーバで、UFW で許可していないポート番号がいつのまにか空いてしまった問題に遭遇しました。 次のような感じです。

  • ubuntu xenial サーバに対し、UFW で 22番と443番だけアクセス可能、その他は遮断というファイアーウォールをセットアップ
  • 同サーバに Docker 入れて、nginx コンテナ起動、この時 “-p 80:80” オプションを付与
  • 外部からこのサーバにアクセスしたら、UFW で開けていないはずの 80番ポートのページが見れてしまった
  • UFW の設定を再確認してみたが、やっぱし 80番ポートは Allow してない

なんだかやばい感じです。
ちな、UFW の設定内容は以下の通りです。

$ sudo ufw status verbose

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To         Action      From
--         ------      ----
443        ALLOW IN    Anywhere
22         LIMIT IN    Anywhere
443 (v6)   ALLOW IN    Anywhere (v6)
22 (v6)    LIMIT IN    Anywhere (v6)

問題の原因

Docker の問題

調査したところ、ひとつの原因は … Docker でした。docker run の -p や -P オプションでコンテナを起動すると、Docker は、その指定されたポート番号について、ファイアーウォールに穴を開ける処理をします。

UFWの問題

では、どうして UFW で確認できなかったのでしょうか? それは … 直接 iptables のルール一覧を確認すれば分かるかもしれません。

$ sudo iptables -L

Chain INPUT (policy DROP)
target     prot opt source               destination
ufw-before-logging-input  all  --  anywhere             anywhere
ufw-before-input  all  --  anywhere             anywhere
ufw-after-input  all  --  anywhere             anywhere
ufw-after-logging-input  all  --  anywhere             anywhere
ufw-reject-input  all  --  anywhere             anywhere
ufw-track-input  all  --  anywhere             anywhere

Chain FORWARD (policy DROP)
target     prot opt source               destination
DOCKER-USER  all  --  anywhere             anywhere
DOCKER-ISOLATION  all  --  anywhere             anywhere

... 省略 ...

Chain DOCKER (2 references)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             172.18.0.4           tcp dpt:https
ACCEPT     tcp  --  anywhere             172.18.0.4           tcp dpt:http

Chain DOCKER-ISOLATION (1 references)
target     prot opt source               destination
DROP       all  --  anywhere             anywhere
DROP       all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere

Chain DOCKER-USER (1 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

Chain ufw-after-forward (1 references)
target     prot opt source               destination

... 省略 ...

Chain ufw-user-input (1 references)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
ACCEPT     udp  --  anywhere             anywhere             udp dpt:https

... 省略 ...

UFW が設定したルールには、"ufw-[hogehoge]“ という名前が付いています。一方、"DOCKER” で始まるチェーンも見ることができますが、こちらは明らかに Docker によって追加されたルールですね。

どうやら UFW は 自分が設定したルール、すなわち “ufw-[hogehoge]” についてのみ管理対象としているようです。従って、Docker など他のプログラムや、手作業で追加したルールについては関与していません。これこそがもう一つの原因であり、運用する上で大変注意が必要である事項であります。

ちなみに、Docker が勝手にポートを開けるのを抑える方法

ubuntu の場合だと、”/etc/default/docker” の ”DOCKER_OPTS" オプションに、"–iptables=false" を追加すればいいようです。

DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4 --iptables=false" 

なお、オプションを有効にするため Docker の再起動を忘れないようにしてください。

まとめ

-p や -P オプション(also EXPOSE)をつけて “docker run” すると Docker が 該当するポートを許可する設定を iptables に追加する。

UFW は自分で設定したルール以外は関与しない。このため、UFW で確認できる設定内容がファイアーウォールの全てではないことに注意すること。

ファイアーウォールについて、最終的には iptables コマンドでも確認した方が良いと思われます。

参考