iptablesを使っている環境でdockerコンテナを立ち上げたら、アクセス許可をするルールを追加してないのに外部からアクセスできてしまうという出来事があった。調べてみたらこのドキュメントを見つけた。
Docker and iptables | Docker Documentation
By default, all external source IPs are allowed to connect to the Docker host. To allow only a specific IP or network to access the containers, insert a negated rule at the top of the DOCKER-USER filter chain.
dockerによって外部IPからのアクセスを全て許可するようにルールが自動で追加されるということだ。
例えばiptablesを使っている環境でnginxのコンテナを立ち上げると、dockerによって外部IPからアクセスできるようにDOCKER
というchainにルールが自動追加される。
もしアクセス制限をかけたい場合はDOCKER-USER
というchainにルールを追加する必要がある。
実際に試してみる
VagrantでCentOS7な環境を用意してそこでnginxコンテナを起動してみる。
nginxコンテナを起動すると自動でルールが追加されている
コンテナを起動していない状態ではDOCKER
chainには何も追加されていない。
[vagrant@centos7 ~]$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ sudo iptables -L DOCKER Chain DOCKER (1 references) target prot opt source destination [vagrant@centos7 ~]$
nginxのコンテナを起動してみる。
[vagrant@centos7 ~]$ sudo docker run -d --rm --name nginx -p 80:80 nginx:latest ed01522e7ee85681bf2a6271576613200947df778f4af4ba8df3180b4966423c [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ed01522e7ee8 nginx:latest "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp nginx [vagrant@centos7 ~]$
するとDOCKER
chainに172.17.0.2:80宛のアクセスを許可するルールが追加されている。
ちなみに172.17.0.2はコンテナに割り当てられているIPアドレス。
natテーブルをみると、PREROUTING
chainから転送される形でDOCKER
chainにホスト(Vagrant環境)の80ポート宛のアクセスを172.17.0.2:80宛にnatするためのルールが追加されていることがわかる。
[vagrant@centos7 ~]$ sudo iptables -L DOCKER Chain DOCKER (1 references) target prot opt source destination ACCEPT tcp -- anywhere 172.17.0.2 tcp dpt:http [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ sudo iptables -L -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination PREROUTING_direct all -- anywhere anywhere PREROUTING_ZONES_SOURCE all -- anywhere anywhere PREROUTING_ZONES all -- anywhere anywhere DOCKER all -- anywhere anywhere ADDRTYPE match dst-type LOCAL ... Chain DOCKER (2 references) target prot opt source destination RETURN all -- anywhere anywhere DNAT tcp -- anywhere anywhere tcp dpt:http to:172.17.0.2:80 ... [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ sudo docker network inspect bridge [ { ... "Containers": { "ed01522e7ee85681bf2a6271576613200947df778f4af4ba8df3180b4966423c": { "Name": "nginx", "EndpointID": "c5e4775c43de4e80355a24a4f1be4b7fc91969236fcf53dbb0bdb47054f764d6", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" } ... } ] [vagrant@centos7 ~]$
この状態でホスト(Vagrant環境)の外からcurlを実行すると200が返ってくる。
$ curl --head localhost HTTP/1.1 200 OK Server: nginx/1.21.5 Date: Fri, 31 Dec 2021 06:21:54 GMT Content-Type: text/html Content-Length: 615 Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT Connection: keep-alive ETag: "61cb2d26-267" Accept-Ranges: bytes
INPUT
chainにルールを入れても制限はできない
PREROUTING
にnatするためのルールが追加されているので、INPUT
chainにDROPするルールを追加しても効果はなく外からアクセスできてしまう。
[vagrant@centos7 ~]$ sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j DROP [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ sudo iptables -L INPUT Chain INPUT (policy ACCEPT) target prot opt source destination DROP tcp -- anywhere anywhere tcp dpt:http ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT all -- anywhere anywhere ...
$ curl localhost HTTP/1.1 200 OK Server: nginx/1.21.5 Date: Fri, 31 Dec 2021 06:35:46 GMT Content-Type: text/html Content-Length: 615 Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT Connection: keep-alive ETag: "61cb2d26-267" Accept-Ranges: bytes
DOCKER-USER
というchainにルールを挿入すれば制限をかけられる
冒頭に書いたとおり、アクセス制限をかけたいときはDOCKER-USER
というchainにルールを挿入すればOK。
何もルールを追加していない状態だとこうなっている。
FORWARD
chainの先頭にDOCKER-USER
chainが挿入されている。
[vagrant@centos7 ~]$ sudo iptables -L DOCKER-USER Chain DOCKER-USER (1 references) target prot opt source destination RETURN all -- anywhere anywhere [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ sudo iptables -L FORWARD Chain FORWARD (policy DROP) target prot opt source destination DOCKER-USER all -- anywhere anywhere DOCKER-ISOLATION-STAGE-1 all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED DOCKER all -- anywhere anywhere ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT all -- anywhere anywhere FORWARD_direct all -- anywhere anywhere FORWARD_IN_ZONES_SOURCE all -- anywhere anywhere FORWARD_IN_ZONES all -- anywhere anywhere FORWARD_OUT_ZONES_SOURCE all -- anywhere anywhere FORWARD_OUT_ZONES all -- anywhere anywhere DROP all -- anywhere anywhere ctstate INVALID REJECT all -- anywhere anywhere reject-with icmp-host-prohibited [vagrant@centos7 ~]$
DOCKER-USER
に80ポートへのアクセスをDROPするルールを追加する。
[vagrant@centos7 ~]$ sudo iptables -I DOCKER-USER -p tcp -m tcp --dport 80 -j DROP [vagrant@centos7 ~]$ [vagrant@centos7 ~]$ sudo iptables -L DOCKER-USER Chain DOCKER-USER (1 references) target prot opt source destination DROP tcp -- anywhere anywhere tcp dpt:http RETURN all -- anywhere anywhere [vagrant@centos7 ~]$
アクセス制限をかけられていることを確認できた。
$ curl --head localhost curl: (56) Recv failure: Connection reset by peer
ちなみにDOCKER-USERS
chainではなくFORWARD
chainにルールを追加しても同じことができる。
でもFORWARD
chainに直接書くと傍から見たらdocker用のアクセス制限であることがわかりにくいので、ドキュメントどおりにDOCKER-USER
chainにルールを足したほうが良いだろう。
というわけで、iptablesを使っている環境でコンテナを立ち上げる場合は、DOCKER-USER
chainにアクセス制限をかけるためのルールを追加することをお忘れなく。