Docker コンテナーへのアクセスを fail2ban で制限する (ufw 環境)

  • watahani
  • 11 Minutes
  • February 18, 2024

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 で通信を制御できるようにした。正直こちらの設定も正直正確な理解が出来ていない。

/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 statusufw 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 で問題ないのか確認したいけど、まあ動いているんで今回はこれで。

関連記事