ラベル iptables の投稿を表示しています。 すべての投稿を表示
ラベル iptables の投稿を表示しています。 すべての投稿を表示

2025年12月17日水曜日

fail2ban で ban 情報を永続化する方法

fail2ban で ban 情報を永続化する方法

概要

これまでの方法だと docker compose down -> up -d すると ban 情報もクリアされてしまうので永続化する方法を紹介します

環境

  • Ubuntu 24.04
  • docker
  • linuxserver/fail2ban 1.1.0-r2-ls26
  • fail2ban 1.1.0

修正箇所

  • /config/fail2ban/fail2ban.sqlite3 を永続化する
  • fail2ban.sqlite3 をホスト側に先に作成する (ファイルがないとディレクトリマウントになり fail2ban の起動に失敗するため)

前回からの差分

先に

  • mkdir data
  • touch data/fail2ban.sqlite3

をしておきます
そして以下を書き換えて down -> up -d すれば OK です

diff --git a/compose.yaml b/compose.yaml
index 7a8a33f..5381790 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -20,6 +20,7 @@ services:
       - ./config/jail.local:/config/fail2ban/jail.d/jail.local
       - ./config/filter.d/nginx-404.conf:/config/fail2ban/filter.d/nginx-404.conf
       - ./config/action.d/slack.conf:/config/fail2ban/action.d/slack.conf
+      - ./data/fail2ban.sqlite3:/config/fail2ban/fail2ban.sqlite3
       - ./nginx/log:/remotelogs/nginx:ro  # nginxログを読み取り専用で監視
     environment:
       - PUID=1000  # 自身のユーザIDとグループIDに合わせて変更 (config/ 配下の権限がコンテナ側のユーザIDで変更されてしまうため編集ができなくなる)

最後に

sqlite3 のファイルを保持しましょう
down 中は解放状態になるので注意しましょう

2025年12月16日火曜日

fail2ban だけ network_mode: host で動作させる方法

fail2ban だけ network_mode: host で動作させる方法

概要

前回の続きです
なぜか nginx も network_mode: host にしていたので nginx は docker ネットワーク上で動作させます

環境

  • Ubuntu 24.04
  • docker
  • linuxserver/fail2ban 1.1.0-r2-ls26
  • fail2ban 1.1.0

修正箇所

  • jail.local の chain を DOCKER-USER に変更
  • nginx コンテナの network_mode を削除し ports でバインドするように変更

前回からの差分

diff --git a/compose.yaml b/compose.yaml
index 1f4124c..7a8a33f 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -2,7 +2,9 @@ services:
   nginx:
     image: nginx:latest
     container_name: nginx
-    network_mode: host
+    ports:
+      - "80:80"
+      - "443:443"
     volumes:
       - ./nginx/log:/var/log/nginx  # このログをfail2banで監視
     restart: unless-stopped
diff --git a/config/jail.local b/config/jail.local
index 82c38b7..3e40cb0 100644
--- a/config/jail.local
+++ b/config/jail.local
@@ -78,7 +78,7 @@ action  = %(action_)s
 [nginx-404]
 # Custom filter to detect excessive 404 errors
 enabled  = true
-chain    = INPUT
+chain    = DOCKER-USER
 port     = http,https
 filter   = nginx-404
 logpath  = /remotelogs/nginx/access.log

最後に

chain は iptables の chain に紐づいています
なぜ最初からこうしなかったのだろうか

2025年12月15日月曜日

fail2ban で ban の際に slack に通知する

fail2ban で ban の際に slack に通知する

概要

前回続きです
ban になったら slack に通知するようにしてみました

環境

  • Ubuntu 24.04
  • docker
  • linuxserver/fail2ban 1.1.0-r2-ls26
  • fail2ban 1.1.0

compose.yaml

ユーザの権限部分など少し変更しています
作成したアクションの設定ファイルもマウントするようにしています

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    network_mode: host
    volumes:
      - ./nginx/log:/var/log/nginx  # このログをfail2banで監視
    restart: unless-stopped

  fail2ban:
    image: linuxserver/fail2ban:latest
    container_name: fail2ban
    network_mode: host
    cap_add:
      - NET_ADMIN # BANに必須
      - NET_RAW   # BANに必須
    volumes:
      - ./config/jail.local:/config/fail2ban/jail.d/jail.local
      - ./config/filter.d/nginx-404.conf:/config/fail2ban/filter.d/nginx-404.conf
      - ./config/action.d/slack.conf:/config/fail2ban/action.d/slack.conf
      - ./nginx/log:/remotelogs/nginx:ro  # nginxログを読み取り専用で監視
    environment:
      - PUID=1000  # 自身のユーザIDとグループIDに合わせて変更 (config/ 配下の権限がコンテナ側のユーザIDで変更されてしまうため編集ができなくなる)
      - PGID=122
      - TZ=Asia/Tokyo
      - VERBOSITY=-vv
    restart: unless-stopped

./config/jail.conf

nginx-404 サービスの action に slack を追加します

[DEFAULT]

# "bantime.increment" allows to use database for searching of previously banned ip's to increase a
# default ban time
bantime.increment = true

# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further)
bantime.maxtime = 5w

# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier
bantime.factor = 24

# "bantime" is the number of seconds that a host is banned.
bantime = 1h

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 24h

# "maxretry" is the number of failures before a host get banned.
maxretry = 5

# Prevents banning LAN subnets
ignoreip    = 127.0.0.1/8 ::1
              10.0.0.0/8
              172.16.0.0/12
              192.168.0.0/16

# The ban action "iptables-multiport" (default) should work for most
# The ban action "iptables-allports" can be used if multiport causes issues
banaction = iptables-multiport
banaction_allports = iptables-allports

# Read https://github.com/sebres/PoC/blob/master/FW.IDS-DROP-vs-REJECT/README.md before changing block type
# The block type "REJECT --reject-with icmp-port-unreachable" (default behavior) should respond to, but then instantly reject connection attempts
# The block type "DROP" should not respond to connection attempts, resulting in a timeout
#banaction = iptables-multiport[blocktype=DROP]

# Add additional actions
action  = %(action_)s

# [sshd]
# configuration inherits from jail.conf
# enabled = true
# chain   = INPUT
# action  = %(action_)s

[nginx-http-auth]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-badbots]
# configuration inherits from jail.d/nginx-badbots.conf 
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-botsearch]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-deny]
# configuration inherits from jail.d/nginx-deny.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-unauthorized]
# configuration inherits from jail.d/nginx-unauthorized.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-404]
# Custom filter to detect excessive 404 errors
enabled  = true
chain    = INPUT
port     = http,https
filter   = nginx-404
logpath  = /remotelogs/nginx/access.log
maxretry = 3
findtime = 10s
bantime  = 1h
action   = %(action_)s
           slack

config/filter.d/nginx-404.conf

前回と同じです

config/action.d/slack.conf

各種アクション時に通知するようにします
webhook_url は変更してください

# Fail2Ban configuration file
#
# Action to send notifications to Slack
#

[Definition]

# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
actionstart = curl -X POST -H 'Content-type: application/json' \
              --data '{"text":"[Fail2Ban] <name> jail has started"}' \
              <slack_webhook_url>

# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
actionstop = curl -X POST -H 'Content-type: application/json' \
             --data '{"text":"[Fail2Ban] <name> jail has stopped"}' \
             <slack_webhook_url>

# Option: actioncheck
# Notes.: command executed once before each actionban command
actioncheck =

# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
#         command is executed with Fail2Ban user rights.
actionban = curl -X POST -H 'Content-type: application/json' \
            --data '{"text":"[Fail2Ban] :no_entry: <name> jail banned IP: *<ip>*\nFailures: <failures>\nTime: <time>"}' \
            <slack_webhook_url>

# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
#         command is executed with Fail2Ban user rights.
actionunban = curl -X POST -H 'Content-type: application/json' \
              --data '{"text":"[Fail2Ban] :white_check_mark: <name> jail unbanned IP: *<ip>*"}' \
              <slack_webhook_url>

[Init]

# Slack webhook URL
slack_webhook_url = https://hooks.slack.com/services/xxx/xxx/xxx

動作確認

  • docker compose up -d

あとは localhost/test などで 404 を発生させると通知が来ます
unbanip で ban 解除しても通知が来ます

トラブルシューティング

起動時にちゃんと追加したアクションが読み込まれていることを確認しましょう
ここで読み込まれていないとうまく動作しません

  • docker compose logs | grep ‘action’
fail2ban  |  2025-12-12 10:00:26,132 79986F9C2B28 INFO  Loading configs for action.d/iptables-multiport under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 DEBUG Reading configs for action.d/iptables-multiport under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 DEBUG Reading config files: /etc/fail2ban/action.d/iptables-multiport.conf
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/iptables-multiport.conf']
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/iptables.conf']
fail2ban  |  2025-12-12 10:00:26,134 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/iptables.conf', '/etc/fail2ban/action.d/iptables-multiport.conf']
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 INFO  Loading configs for action.d/slack under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 DEBUG Reading configs for action.d/slack under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 DEBUG Reading config files: /etc/fail2ban/action.d/slack.conf
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/slack.conf']
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/slack.conf']

最後に

ban になったかどうかをいちいち確認する必要がなくなるので便利です
現在の ban 状況を定期的に送信してくれる機能があると更に便利かなと思います

2025年12月14日日曜日

fail2ban を docker で動かす

fail2ban を docker で動かす

概要

fail2ban をコンテナで動作させる方法を紹介します
よく紹介されているのはホストに直接インストールする方法かなと思います
今回は nginx の不正アクセスを ban する方法を紹介します

環境

  • Ubuntu 24.04
  • docker
  • linuxserver/fail2ban 1.1.0-r2-ls26
  • fail2ban 1.1.0

compose.yaml

fail2ban は linuxserver/fail2ban を使います
fail2ban は仕組み上 iptables を使うのでホストネットワークを使う必要があります

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    network_mode: host
    volumes:
      - ./nginx/log:/var/log/nginx  # このログをfail2banで監視
    restart: unless-stopped

  fail2ban:
    image: linuxserver/fail2ban:latest
    container_name: fail2ban
    network_mode: host
    cap_add:
      - NET_ADMIN # BANに必須
      - NET_RAW   # BANに必須
    volumes:
      - ./config/jail.local:/config/fail2ban/jail.d/jail.local
      - ./config/filter.d/nginx-404.conf:/config/fail2ban/filter.d/nginx-404.conf
      - ./nginx/log:/remotelogs/nginx:ro  # nginxログを読み取り専用で監視
    environment:
      - TZ=Asia/Tokyo
    restart: unless-stopped

./config/jail.local

[DEFAULT]

# "bantime.increment" allows to use database for searching of previously banned ip's to increase a
# default ban time
bantime.increment = true

# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further)
bantime.maxtime = 5w

# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier
bantime.factor = 24

# "bantime" is the number of seconds that a host is banned.
bantime = 1h

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 24h

# "maxretry" is the number of failures before a host get banned.
maxretry = 5

# Prevents banning LAN subnets
ignoreip    = 127.0.0.1/8 ::1
              10.0.0.0/8
              172.16.0.0/12
              192.168.0.0/16

# The ban action "iptables-multiport" (default) should work for most
# The ban action "iptables-allports" can be used if multiport causes issues
banaction = iptables-multiport
banaction_allports = iptables-allports

# Read https://github.com/sebres/PoC/blob/master/FW.IDS-DROP-vs-REJECT/README.md before changing block type
# The block type "REJECT --reject-with icmp-port-unreachable" (default behavior) should respond to, but then instantly reject connection attempts
# The block type "DROP" should not respond to connection attempts, resulting in a timeout
#banaction = iptables-multiport[blocktype=DROP]

# Add additional actions
action  = %(action_)s
          apprise-api[host="127.0.0.1", tag="fail2ban"]
          cloudflare[cfuser="YOUR-EMAIL", cftoken="YOUR-TOKEN"]

# [sshd]
# configuration inherits from jail.conf
# enabled = true
# chain   = INPUT
# action  = %(action_)s

[nginx-http-auth]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-badbots]
# configuration inherits from jail.d/nginx-badbots.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-botsearch]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-deny]
# configuration inherits from jail.d/nginx-deny.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-unauthorized]
# configuration inherits from jail.d/nginx-unauthorized.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-404]
# Custom filter to detect excessive 404 errors
enabled  = true
chain    = INPUT
port     = http,https
filter   = nginx-404
logpath  = /remotelogs/nginx/access.log
maxretry = 3
findtime = 10s
bantime  = 1h

banaction などは適切なものを設定しましょう

ポイントは一番下の nginx-404 です
これで 404 を連発する IP を ban にできます
それ以外も nginx の ban ルールですが認証エラーなどで発生させるのが難しいので今回 404 を追加しています

config/filter.d/nginx-404.conf

# Fail2Ban filter to detect excessive 404 errors from nginx
# Detects clients making too many requests to non-existent pages

[Definition]

failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" 404

ignoreregex =

datepattern = {^LN-BEG}%%d/%%b/%%ExY:%%H:%%M:%%S

nginx のログで 404 を正規表現でマッチさせます

起動

  • docker compsoe up -d

動作確認

今回は10秒間に3回404発生でbanなのですぐにbanになります
localhost/test など存在しないページにアクセスしましょう

そして ban になっているか確認します

  • docker compose exec fail2ban fail2ban-client status nginx-404
Status for the jail: nginx-404
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     6
|  `- File list:        /remotelogs/nginx/access.log
`- Actions
   |- Currently banned: 1
   |- Total banned:     1
   `- Banned IP list:   xxx.xxx.xxx.xxx

ban されると i-Filter のお馴染みの画面が表示されるようになります
また iptables -L でホスト側を確認するとちゃんと REJECT ルールがあることが確認できると思います

sudo iptables -L f2b-nginx-404

Chain f2b-nginx-404 (1 references)
target     prot opt source               destination
REJECT     all  --  xxx.xxx.xxx.xxx      anywhere             reject-with icmp-port-unreachable
RETURN     all  --  anywhere             anywhere

ban解除

  • docker compose exec fail2ban fail2ban-client set nginx-404 unbanip xxx.xxx.xxx.xxx

テストなどですぐに解除したい場合は IP を指定して ban 解除できます
iptables にもないことが確認できます

最後に

今回使った linuxserver/fail2ban は /config/fail2ban/filter.d に大量のプリセットがあるのでログをセットするだけでも簡単に ban 監視を開始することができます
今回は 404 ルールだけなかったので手動で追加しています

fail2ban は iptables を操作するツールなので iptables を使ってアクセス制御しているケースとは親和性は高い気がします
逆にクラウドなどのファイアウォールでアクセス制御している場合は fail2ban とは少し相性が悪いので使い分けが必要かなと思います

結局 iptables なのでホストにインストールしても変わりませんが docker compose でアプリを管理していたりホストにはなるべくアプリをインストールしたくないという場合にはやはり docker で fail2ban を動かしたくなるかなと思います

参考サイト