Debian
ufw
docker

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


いきなり対処法

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).