Ubuntu 22.04 を使っているサーバーでは、基本的に Firewall として ufw を使っていると思う。しかしながら、Docker コンテナーへのアクセスは Docker が自動的に iptables のチェーンを作成し、ufw を迂回するため、ufw での制限が効かない。知らずにめちゃくちゃハマった。
GitHub - linuxserver/docker-fail2ban を利用することで Docker が自動で作成する DOKCER-USER チェーンを考慮した iptables の構成を追加してくれるよう (iptables 直接編集だと FOWARD もまとめてブロックしてくれるのか?理解できていない。) だが、設定ファイル のどの辺が効いているのか解読するのがキツかった。
最終的な構成
fail2ban は元々ホストマシンで動かすつもりだったので、以下のサイトを参考に /etc/ufw/after.rules
を追加して ufw route で通信を制御できるようにした。正直こちらの設定も正直正確な理解が出来ていない。
- 参考: Dockerとufwでポートのアクセス制御をする方法 - アプリ開発備忘録
- 参考: chaifeng/ufw-docker: To fix the Docker and UFW security flaw without disabling iptables
/etc/ufw/after.rules
に追記
chaifeng/ufw-docker: To fix the Docker and UFW security flaw without disabling iptables そのままコピペで追加
# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
-A DOCKER-USER -j RETURN
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER
ufw の動作検証
以下コマンドで特定 IP から Docker コンテナーへのアクセスが制限できることを確認
ufw route insert 1 deny from <ip> to any
ufw reload
ufw insert
ではなく、ufw route insert
とすること
以下コマンドでルールを削除し、コンテナーへのアクセスが可能なことを確認
ufw route delete deny from <ip> to any
ufw reload
Docker ログの構成
コンテナーのログは Azure Monitor に送信したかったけど、ざっと調べた限りでは syslog 以外を転送するのはめんどそうなので、Docker のログも syslog に全部送ることに。
services:
nginx-proxy:
image: nginxproxy/nginx-proxy
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs:rw
- ./vhost.d:/etc/nginx/vhost.d
- ./html:/usr/share/nginx/html
- ./custom-nginx.conf:/etc/nginx/conf.d/custom-nginx.conf:ro
networks:
proxy-network:
ipv4_address: 172.18.0.2
labels:
- com.github.nginx-proxy.nginx-proxy.keepalive=auto
logging:
driver: syslog
options:
tag: "nginx-proxy"
ログがでることを確認
sudo tail -f /var/log/syslog | grep nginx-proxy
fail2ban の設定
/etc/fail2ban/jail.d/nginx-proxy.local
に以下の設定を追加
[nginx-proxy]
enabled = true
port = http,https
filter = nginx-proxy
logpath = /var/log/syslog
maxretry = 5
bantime = 600
findtime = 600
フィルターは /etc/fail2ban/filter.d/nginx-proxy.conf
に以下の設定を追加して 404 エラーを検知するようにした
[Definition]
failregex = ^.*nginx-proxy.*\.example\.com\s\s-\s-\s.*HTTP/.*" 404
フィルターがうまく動いているかは fail2ban-regex で確認する
sudo fail2ban-regex /var/log/syslog /etc/fail2ban/filter.d/nginx-proxy.conf
設定ファイルをコピーして .local ファイルを編集
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
-#ignoreip = 127.0.0.1/8 ::1
+ ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16 172.0.0.0/12
- banaction = iptables-multiport
+ banaction = ufw
enable = true
(もともとの iptables-multiport だったら初期設定で動いてた説はある)
ufw で ufw route もブロックするよう、/etc/fail2ban/action.d/ufw.conf
を修正
actionban = [ -n "<application>" ] && app="app <application>"
ufw insert <insertpos> <blocktype> from <ip> to <destination> $app &&
+ ufw route insert <insertpos> <blocktype> from <ip> to <destination> $app
actionunban = [ -n "<application>" ] && app="app <application>"
ufw delete <blocktype> from <ip> to <destination> $app&&
+ ufw route delete <blocktype> from <ip> to <destination> $app
設定を変更したら fail2ban を再起動
sudo systemctl restart fail2ban
動作確認
実際に 404 を発生させるアクセスを試みてブロックされるか検証。ブロックされたら fail2ban-client status
と ufw status
で確認
> sudo fail2ban-client status nginx-proxy
Status for the jail: nginx-proxy
|- Filter
| |- Currently failed: 1
| |- Total failed: 20
| `- File list: /var/log/syslog
`- Actions
|- Currently banned: 1
|- Total banned: 4
`- Banned IP list: 20.247.234.38
> sudo ufw status
Status: active
To Action From
-- ------ ----
Anywhere DENY 20.247.234.38
...
Anywhere DENY FWD 20.247.234.38
ちゃんとブロックされた。
iptables -L で Docker 起動時に作られるルールを理解して、fail2ban の既定の iptables-multicast で問題ないのか確認したいけど、まあ動いているんで今回はこれで。