LoginSignup
9

More than 3 years have passed since last update.

Debian 10(buster) + ufw + Docker: Dockerコンテナが外部と通信できない問題の対処

Last updated at Posted at 2019-02-18

いきなり対処法

update-alternatives --config iptablesで,iptables-legacyを使うよう変更する.

$ sudo update-alternatives --config iptables
alternative iptables (/usr/sbin/iptables を提供) には 2 個の選択肢があります。

  選択肢    パス                     優先度  状態
------------------------------------------------------------
* 0            /usr/sbin/iptables-nft      20        自動モード
  1            /usr/sbin/iptables-legacy   10        手動モード
  2            /usr/sbin/iptables-nft      20        手動モード

現在の選択 [*] を保持するには <Enter>、さもなければ選択肢の番号のキーを押してください: 1
update-alternatives: /usr/sbin/iptables (iptables) を提供するためにマニュアルモードで /usr/sbin/iptables-legacy を使います

$ sudo reboot

いきなり結論

  • Debian 10はiptables v1.8系を採用.nftables APIを使うiptables-nftと旧来のiptables-legacyが用意されている.
  • デフォルトはiptables-nftだが,Dockerはiptables-legacyを使う.
  • この状態で,ufwなどを使ってiptables-nftで定義を設定すると,iptables-legacyの定義は無視される(ようだ).その結果,外部との通信ができない.
  • そこで,iptables-legacyをデフォルトに変更し,定義をlegacy側に寄せて対処する.

詳細

ufwとiptables

ufw - Uncomplicated Firewall は, ufw allow 22/tcp などと書くだけでポート開放ができる,お手軽なファイアウォール管理ツールです.

内部的には iptables(Netfilter) のラッパーのような位置づけで, iptables の忘れやすいコマンド体系を忘れたままにしておけます.もともとUbuntu向けのツールだったように思いますが,かなり前からDebianでも利用が可能です.

iptables v1.8 - nftables APIサポート

現時点で testing である Debian GNU/Linux 10 (buster) では,iptablesが1.6から1.8にバージョンアップされています.

iptables 1.8では,新たに nftables Kernel API1を用いたiptables-nftツールが提供され,旧来のツールはiptables-legacyで使用することが出来るようになっています.

iptablesはというと,Debian 10はデフォルトでiptables-nftへのシンボリックリンクが張られています.ufw等はこのデフォルトを使用します.

$ ls -l /usr/sbin/iptables
lrwxrwxrwx 1 root root 26  2月 17 11:44 /usr/sbin/iptables -> /etc/alternatives/iptables
$ ls -l /etc/alternatives/iptables
lrwxrwxrwx 1 root root 22  2月 18 22:53 /etc/alternatives/iptables -> /usr/sbin/iptables-nft

Dockerは iptables-legacy を使う

一方,Dockerはiptables-nftがうまく扱えず,デフォルトがどうあれ必ずiptables-legacyを使うように修正が加えられています2 3

ufw + Docker = iptables-nft + iptables-legacy

ここで,デフォルトのままufwとDockerを使うと,iptables-nftiptables-legacyの両方の定義が存在することになります.

$ sudo iptables-nft -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
(省略)

$ sudo iptables-legacy -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

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
(省略)

ここから先は推測が入りますが,この状態になると iptables-nft の定義のみが有効となるようです.そのため,Dockerがせっかく書き換えてくれた定義が効かず,コンテナが外部と通信できなくなってしまいます.

pingでIPアドレスを指定すると通るが,ホスト名を指定するとDNSが引けない,という状況です.

$ sudo docker run -it --rm busybox
/ # ping github.com
ping: bad address 'github.com'
/ # ping 192.30.255.112
PING 192.30.255.112 (192.30.255.112): 56 data bytes
64 bytes from 192.30.255.112: seq=0 ttl=49 time=133.961 ms
64 bytes from 192.30.255.112: seq=1 ttl=49 time=134.677 ms

対処: iptables-legacyをデフォルトにする

さて,対処法ですが,ufwも含めてみんなiptables-legacyを使うようにします.

Docker doesn't work with iptables v1.8.1 · Issue #38099 · moby/mobyのIssue commentにもあるように,デフォルトのiptablesは選べるようになっています.そこで,iptables-legacyをデフォルトに変更します.

$ sudo update-alternatives --config iptables
alternative iptables (/usr/sbin/iptables を提供) には 2 個の選択肢があります。

  選択肢    パス                     優先度  状態
------------------------------------------------------------
* 0            /usr/sbin/iptables-nft      20        自動モード
  1            /usr/sbin/iptables-legacy   10        手動モード
  2            /usr/sbin/iptables-nft      20        手動モード

現在の選択 [*] を保持するには <Enter>、さもなければ選択肢の番号のキーを押してください: 1
update-alternatives: /usr/sbin/iptables (iptables) を提供するためにマニュアルモードで /usr/sbin/iptables-legacy を使います

あとは再起動すれば完了です.

iptables-nftでの定義が残っていても,iptables-legacyをデフォルトとしていると,legacyが優先されるようです.

参考

  1. API自体は2014年1月にリリースされたLinux Kernel 3.13から提供されている

  2. 本来iptables-nftでも同じコマンドで通るはずなのですが,謎.Pull requestには『トランスレータのバグかもね? 追っかけきれてないけど😅』みたいなコメントが見られる.

  3. nftablesサポートについてはかなり前からIssueが上がっている様子([feature request] nftables support · Issue #26824 · moby/moby).

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
9