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 allblock drop in quick from <sshguard> to any label "sshguard-block"pass in proto tcp from any to any port = ssh flags S/SA keep statepass in proto tcp from any to any port = http flags S/SA keep statepass in proto tcp from any to any port = https flags S/SA keep statepass 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'. Seesshguard-setup(7) for instructions on setting up your firewall.Please note that a few rc script parameters have been renamed tobetter reflect the documentation:sshguard_safety_thresh -> sshguard_danger_threshsshguard_pardon_min_interval -> sshguard_release_intervalsshguard_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アクセスをブロックする方法も組み合わせようと思う。