FreeBSD14 + SSHGuard + PF(Packet Filter)で不正な攻撃を自動ブロック

SSHGuardを導入した方がssh攻撃を放っておくより圧倒的にリソース消費を抑えるとのことなので導入したときの覚書。
Gemini 2.5 Proに教えてもらっている。

環境: FreeBSD 14.3-RELEASE-p2, SSHGuard 2.5.1,1


1. SSHへの攻撃を放置しておいた場合のリソース消費

仮想マシン提供側のファイアウォール(ConoHaだとセキュリティグループ)だけだとリソースを消費し続ける。

  • ネットワークリソースの消費:
    攻撃者が送りつけてくる不正なパケットを、ネットワークインターフェースがすべて受信します。これだけでネットワーク帯域とCPUの割り込み処理リソースを消費します。
  • sshdプロセスの生成:
    SSHへの接続試行があるたびに、OSは新しいsshdのプロセスをフォーク(生成)します。プロセスの生成自体がCPUとメモリを消費します。
    ログにはsshd[19312], sshd[19314], sshd[19317]... と大量のプロセスIDが記録されており、これが頻繁に発生している証拠です。
  • 認証処理の実行:
    生成されたsshdプロセスは、ユーザー名が存在するかどうかを確認し、パスワード認証の準備をします。これには、/etc/passwdへのアクセス、暗号化関連のライブラリの呼び出しなどが含まれ、CPUリソースを消費します。
  • ログの書き込み:
    認証失敗のたびに、sshdはログ(/var/log/auth.logなど)に詳細を書き込みます。頻繁なディスクI/Oが発生し、ディスク性能やCPUリソースを消費します。


PFとsshguardを導入すると、攻撃元IPアドレスごとに最初の数回の試行に対してはリソースを消費するが、ブロックされた後はそのIPからの攻撃による負荷はほぼゼロ。
定常的に動作するPFとsshguard自体のリソース消費量は、放置した場合の負荷に比べて圧倒的に軽微


2. SSHGuardとは

sshdなどのログファイルをリアルタイムで監視し、短時間に繰り返される認証失敗を検知すると、その攻撃元IPアドレスをOSのファイアウォール(PF, IPFWなど)と連携して動的にブロックする。
公式サイト: SSHGuard

特徴

  • 動作原理: ログファイル(例: /var/log/auth.log)をtail -fのように監視。正規表現で攻撃パターンを検知。
  • 軽量: 常駐するデーモンは単機能で、リソース消費は極めて少ない。
  • 汎用性: SSHだけでなく、vsftpd, Postfix/Dovecot, Eximなど、ログを出力する様々なサービスに対応。
  • 依存関係: OSネイティブのファイアウォール機能が必須。sshguard自体はパケットフィルタリングを行わず、ファイアウォールへのIPリスト追加/削除命令を発行するだけ。


3.PFとIPFWの違い

IPFW (IP Firewall) と PF (Packet Filter) は、どちらもFreeBSDで利用できる高機能なファイアウォール。

PF (Packet Filter) を推奨する理由

  • 現在、FreeBSDコミュニティではPFが主流になりつつある。
  • 設定ファイルの可読性が高く、特にNATやsshguardのようなツールとの連携が非常に簡単。
  • ドキュメントやWeb上の情報もPFの方が豊富で見つけやすい傾向。

一昔前(FreeBSD 4.x〜7.xあたり)、特にシンプルなパケット転送(ルーティング)性能においては、IPFWの方がPFよりも高速であると言われていた。

まとめ

  • 性能差は無視できるレベル:
     一般的なサーバー用途において、IPFWとPFの性能差を気にする必要はほとんどない。
  • ボトルネックは他にあり:
    性能問題を考えるなら、アプリケーション、ディスクI/O、ハイパーバイザーのネットワーク設定など、他の要因を先に疑うべき。
  • 選択基準は「使いやすさ」と「機能」:
    設定ファイルが書きやすく、情報も豊富な PF を選ぶのが、管理の手間や設定ミスを防ぐ観点から最も合理的。
    sshguardとの連携もPFの方がドキュメントが多く、スムーズに進められる。


4. PF(Packet Filter)の導入と設定

PF (Packet Filter) はデフォルトでインストール済み。
確認。
# service pf status

pf.ko is not loaded

「PFのカーネルモジュールが現在メモリに読み込まれていません」という意味らしい。

起動ファイルでPFを有効にする。
# less /etc/rc.conf

# PF(Packet Filter)
pf_enable="YES"         # PFを有効にする
pf_rules="/etc/pf.conf" # PFのルールセットファイルを指定
pflog_enable="YES"      # ブロックしたパケットなどを記録する(推奨)

PFの設定ファイルを編集。
# vim /etc/pf.conf

# ループバックインターフェースはブロックしない
set skip on lo0

# --- テーブル ---
# sshguardがブロック対象IPを追加するためのテーブルを定義
table <sshguard> persist

# --- ルール ---
# デフォルトですべての通信をブロックし、必要なものだけ許可する(より安全)
block all

# sshguardテーブルに含まれるIPアドレスからの全ての通信をブロック
# "label" はpflogでどのルールでブロックされたか分かりやすくするためのものです
block in quick from <sshguard> label "sshguard-block"

# 外部からのSSH, HTTP, HTTPSを許可
pass in proto tcp to any port { ssh, http, https}

# 外部からのSAMBAを許可
#pass in proto tcp to any port { 139, 445}
#pass in proto udp to any port { 137, 138}

# 外部への通信は基本的にすべて許可
# このルールは、サーバー自身が起点となる通信(例: OSアップデート、外部APIへのアクセス)のために必要です。
pass out all keep state

サービス名は/etc/servicesに記載されているのが使える。
# less /etc/services

構文チェック。
-n: No-op. ルールを実際には読み込まず、チェックだけを行います。
# pfctl -nf /etc/pf.conf

ファイヤーウォールの設定を間違えるとSSHで接続できなくなるので、仮想マシンのコンソール画面を開いてログインしておく。

pflogとpfサービスの起動。
# service pflog start
# service pf start

今のSSHは一旦遮断される。
再度SSHで接続する。
接続できない場合はコンソールでpfを停止
# service pf stop

再度SSHで接続できたら状態確認。
# service pf status
# service pflog status

適用されているルールセットを確認表示。
# pfctl -s rules

block drop all
block drop in quick from <sshguard> to any label "sshguard-block"
pass in proto tcp from any to any port = ssh flags S/SA keep state
pass in proto tcp from any to any port = http flags S/SA keep state
pass in proto tcp from any to any port = https flags S/SA keep state
pass out all flags S/SA keep state

サーバーから外向きは全許可されているはずなので、メール送信して確認。
# mail hoge@gmail.com


5.SSHGuardの導入と設定

SSHGuardをインストール。
# pkg install sshguard

Message from sshguard-2.5.1,1:

--
To enable SSHGuard at startup, add the following line to your 'rc.conf':
sshguard_enable="YES"

Starting SSHGuard through syslogd(8) is discouraged and not supported.

Configure SSHGuard by editing BACKEND in '/usr/local/etc/sshguard.conf'. See
sshguard-setup(7) for instructions on setting up your firewall.

Please note that a few rc script parameters have been renamed to
better reflect the documentation:

sshguard_safety_thresh -> sshguard_danger_thresh
sshguard_pardon_min_interval -> sshguard_release_interval
sshguard_prescribe_interval -> sshguard_reset_interval

起動ファイルの設定。
# less /etc/rc.conf

SSHGuardの設定ファイルを確認。
PFの指定とホワイトリストファイルを有効にする。
# less /usr/local/etc/sshguard.conf 

BACKEND="/usr/local/libexec/sshg-fw-pf"
WHITELIST_FILE=/usr/local/etc/sshguard.whitelist

ホワイトリストファイルを編集。
# vim /usr/local/etc/sshguard.whitelist

# 1行に1つのIPアドレスまたはCIDR形式のネットワークを記述
192.xxx.xx.xxx     # Webrec Office in Kawaguchi

サービスを起動。
# service sshguard start

ログを確認する。
# tail -f /var/log/messages

Aug 11 15:52:25 hoge01 sshguard[24734]: Attack from "187.35.169.81" on service SSH with danger 2.
Aug 11 15:52:25 hoge01 sshguard[24734]: Attack from "45.135.232.92" on service SSH with danger 2.
Aug 11 15:52:32 hoge01  sshguard[24734]: Attack from "187.35.169.81" on service SSH with danger 2.
Aug 11 15:52:34 hoge01 sshguard[24734]: Attack from "45.229.102.69" on service SSH with danger 2.

ブロックしているIPアドレスを確認。
# pfctl -t sshguard -T show

これはテーブルはカーネルのメモリ上に保持している。
ディスクI/Oがなく高速だが、再起動するとリストは全て消去される。

悪意のあるIPはすぐにまたブロックされるから永続化する必要はない。


最後に再起動してもPFとSSHGuardサービスが起動しているか確認。
# reboot

# service pf status
# less /var/log/messages


6.PFLogを確認するコマンド

pflogを確認する。
pflogはバイナリ形式なのでtcpdumpを利用する。
# tcpdump -n -e -r /var/log/pflog

コマンドの解説

  • tcpdump: ネットワークパケットをキャプチャ・表示するための強力なツールですが、pflogのような保存されたファイルを読み込む機能も持っています。
  • -n: IPアドレスやポート番号を名前に解決(例: 8.8.8.8 → dns.google)しようとせず、数値のまま表示します。これにより表示が速くなります。
  • -e: データリンク層のヘッダ(MACアドレスなど)も表示します。
  • -r /var/log/pflog: リアルタイムのパケットキャプチャではなく、指定したファイル (/var/log/pflog) を読み込んで表示します。


ブロックされたログだけを見る。
# tcpdump -n -r /var/log/pflog action block

リアルタイムでpflogを監視する
# tcpdump -n -i pflog0

リアルタイムでブロックしているIPアドレスを確認。
(「pfctl -t sshguard -T show」を1秒ごとにループして再描画)
# while true; do clear; echo "Monitoring sshguard block list (Ctrl+C to exit):"; date; echo; pfctl -t sshguard -T show; sleep 1; done


pflogがログローテーションに登録されているか確認。
# less /etc/newsyslog.conf.d/pf.conf

/var/log/pflog                          600  3     1000 *     JB    /var/run/pflogd.pid


翌日以降に仮想マシンの管理画面でリソースの変化を確認する。
その後、MaxMindを利用した日本以外からのSSHアクセスをブロックする方法も組み合わせようと思う。


▼ 関連記事