ここでdockerについての解説をするのはおこがましいので、下記に覚え書きリンクを貼付ける
ローカルLAN環境でWEBサーバーを運用するだけなら良いが、当サイトにdockerを導入するに当たって一番の懸念事項がファイアウォールとの相性問題があった。
当サイトと同じUbuntu20.04LTSで下記のようなファイアウォール設定であったとして
xxxx@user$ sudo ufw status
Status: active
To Action From
-- ------ ----
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
80/tcp (v6) ALLOW Anywhere (v6)
443/tcp (v6) ALLOW Anywhere (v6)
dockerコンテナで8080ポートでwebサイトを立ち上げると、ファイアウォールをするりと抜けて外部に公開できてしまう(下記テキトウdocker-compose.yml)
web:
image: nginx
volumes:
- ./templates:/etc/nginx/templates
ports:
- "8080:80"
environment:
- NGINX_HOST=foobar.com
- NGINX_PORT=80
当サイトではufwファイアウォールの運用で特定IPアドレスからのアクセスを遮断している(メールサーバへの不正アクセスやwordpressへの不正ログイン試行を手動・自動で監視して遮断対象アドレスをリストアップ)。
ufwコマンドで設定するポートアクセス許可よりも前で設定するために、/etc/ufw/before.rules に記載のあるチェインufw-before-inputにログ出力と遮断コマンドを追加している。
# blacklist-ipset-S ここにブロック対象のアドレスを追加
-A ufw-before-input -s xxx.xxx.xxx.xxx -j auto-blocklist
# blacklist-ipset-S
# タグをつけたログを残してドロップさせるチェイン
# ファイルの先頭の*filter節に追加する必要あり?
-A auto-blocklist -j LOG --log-prefix "[BLOCKLIST]"
-A auto-blocklist -j DROP
こうすることでufw起動時にiptableの遮断リストが設定される。
しかし、上記のdockerコンテナはこの設定も効かず、遮断対象アドレスも通過してしまう。
そこで、まずdockerコンテナが何故ufwの設定を無視できるのかを調べてみる。(iptableコマンド)
user@server:~$ sudo iptables -nvL
Chain INPUT (policy DROP 2979 packets, 152K bytes)
pkts bytes target prot opt in out source destination
3687K 3750M ufw-before-logging-input all -- * * 0.0.0.0/0 0.0.0.0/0
3687K 3750M ufw-before-input all -- * * 0.0.0.0/0 0.0.0.0/0
83453 4149K ufw-after-input all -- * * 0.0.0.0/0 0.0.0.0/0
77337 3836K ufw-after-logging-input all -- * * 0.0.0.0/0 0.0.0.0/0
77337 3836K ufw-reject-input all -- * * 0.0.0.0/0 0.0.0.0/0
77337 3836K ufw-track-input all -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
4406K 5775M DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0
4406K 5775M DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0
3634K 4639M ACCEPT all -- * br-a52db8fb2804 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
80854 4814K DOCKER all -- * br-a52db8fb2804 0.0.0.0/0 0.0.0.0/0
675K 1102M ACCEPT all -- br-a52db8fb2804 !br-a52db8fb2804 0.0.0.0/0 0.0.0.0/0
70296 4218K ACCEPT all -- br-a52db8fb2804 br-a52db8fb2804 0.0.0.0/0 0.0.0.0/0
71973 318M ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
54110 3031K ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
0 0 ufw-before-logging-forward all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-before-forward all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-after-forward all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-after-logging-forward all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-reject-forward all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-track-forward all -- * * 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 4 packets, 196 bytes)
pkts bytes target prot opt in out source destination
3574K 2258M ufw-before-logging-output all -- * * 0.0.0.0/0 0.0.0.0/0
3574K 2258M ufw-before-output all -- * * 0.0.0.0/0 0.0.0.0/0
27893 2070K ufw-after-output all -- * * 0.0.0.0/0 0.0.0.0/0
27893 2070K ufw-after-logging-output all -- * * 0.0.0.0/0 0.0.0.0/0
27893 2070K ufw-reject-output all -- * * 0.0.0.0/0 0.0.0.0/0
27893 2070K ufw-track-output all -- * * 0.0.0.0/0 0.0.0.0/0
この辺りもこのために改めて勉強したため、理解不足かも知れないが、、、
普通にufwで設定するファイアウォールはINPUTチェインが主で、5行目のufw-before-inputのチェインから繋がっている(当サイトのアクセス遮断リストもここに繋げている)。それに対してdockerのファイアウォールはFORWARDチェインであり、16,20行目のDOCKERチェインでコンテナで設定したポートの待ち受けを実現している。それはufwのFORWARD設定よりも優先しているので、アクセス制限はそれよりも前で行う必要がある。ここでアレ?と思うのがDOCKERチェインよりも前にあるDOCKER-USERチェイン。このチェインの中身は空白で何もせずに帰ってくる仕組み。
ならば、ここに遮断リストを追加してやれば良いんじゃね?という感じで、
$ sudo iptables -A DOCKER-USER -s xxx.xxx.xxx.xxx -j DROP
としてみたら、無事遮断していることを確認(とりあえずスマホのIPアドレスを遮断してみて確認)。
実は英文の方のdocker docsにはそれっぽいことが書いてあった。。
次に問題はこのリストを起動時に読み込ませるにはだけど、これは何のことはなくufwの助けが借りられることが分かった。先の遮断リストをログ出力しつつDROPするチェインを追加した要領でDOCKER-USERチェインも/etc/ufw/before.rulesに記載してしまおうという思いつきで実験、、上手く動作したので下記に示します。
$ sudo cat /etc/ufw/before.rules
# Don't delete these required lines, otherwise there will be errors
# added for docker access limits. 21/08/17 by mamiyan
*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]
:DOCKER-USER - [0:0] # <---- これ追加
:auto-blocklist - [0:0] # <---- ログ出力&DROP処理用チェイン
:docker-blocklist - [0:0] # <---- 同上
# End required lines
・・・・・
# for blocklist custon chain 追加チェイン
-A auto-blocklist -j LOG --log-prefix "[DROPLIST]"
-A auto-blocklist -j DROP
# for docker custom chain 追加チェイン
-A docker-blocklist -j LOG --log-prefix "[DROPDOCKER]"
-A docker-blocklist -j DROP
# blacklist-ipset-S ここから
-A ufw-before-input -s xxx.xxx.xxx.xxx -j auto-blocklist
-A DOCKER-USER -s xxx.xxx.xxx.xxx -j docker-blocklist
# blacklist-ipset-E ここまでにリスト追記
24行25行のように二つのチェイン向けに遮断アドレスリストを追記して、$ sudo ufw reload
でdocker向けのファイアウォールが設定されます。その他のアクセス制限もこの/etc/ufw/before.rules の DOCKER-USERチェインに記載しておけば良いと思います。
この辺りは、ufwを少し触れる程度の知識からiptableの仕組みとチェインを触れる程度までじっくり勉強が出来て良かったと思います。またここでCentOSとかのRHL系でfirewalldとかになると変ってくるのだろうか。そもそもあちら側は相性問題は無いのかな。