今日からNetBSD Advent Calendar 2019が始まります。1日目はipfのパケットフィルタログを集計する手順を紹介してみようと思います。
ipf
NetBSDにはipfというパケットフィルタ機能が搭載されています。フィルタ状況のログは以下のようにipmonコマンドで確認することができます(IPアドレス等は AAA
や BBB
に置き換えています)。
$ sudo ipmon
...
01/12/2019 11:34:41.268129 pppoe0 @0:7 b AAA.AAA.AAA.AAA,58501 -> BBB.BBB.BBB.BBB,22 PR tcp len 20 44 -S IN
...
この例では、 AAA.AAA.AAA.AAA
からssh(22番ポート)へのアクセスをブロックしたというログになっています。このログの内容をうまいこと集計できるようにしてみます。
集計の前に、まずはipfの設定手順を概観する
今回紹介するipfのログ集計は、以前のアドベントカレンダーにおいて、NetBSD+Raspberry PiでPCルータを作る(ユーザランド設定編)の記事で紹介したネットワーク構成の環境で行っています。
ご家庭LAN-------+------ 192.168.10.0/24
|
|usmsc0
+-------+--------+
| NetBSD |
| (Raspberry Pi) |
+-------+--------+
|pppoe0(axe0)
|
-----+-------インターネット
前述の記事ではipfのフィルタ設定内容を紹介していなかったので、ここで簡単に設定内容を紹介します。
まずは /etc/rc.conf
でipfを有効化します。
ipfilter=YES
/etc/ipf.conf
には、パケットフィルタルールを列挙する形で設定します。先述した ipmon
のsshをフィルタするルールは block in log quick on pppoe0 from any to pppoe0/peer port = ssh
になります。
block in log quick on pppoe0 proto icmp from any to pppoe0/peer icmp-type echo
block in log quick on pppoe0 from any to pppoe0/peer port = 25 # mail
block in log quick on pppoe0 from any to pppoe0/peer port = 53 # DNS
block in log quick on pppoe0 from any to pppoe0/peer port = 67 # DHCP
block in log quick on pppoe0 from any to pppoe0/peer port = ftp
block in log quick on pppoe0 from any to pppoe0/peer port = www
block in log quick on pppoe0 from any to pppoe0/peer port = ssh
block in log quick on pppoe0 from any to pppoe0/peer port = ldap
block in log quick on pppoe0 from any to pppoe0/peer port = ldaps
block in log quick on pppoe0 from any to pppoe0/peer port = 443
ipfのログを集計する
ipfの設定手順とipmonによるパケットフィルタログの確認手順が把握できたので、いよいよipfのログを集計してみます。とりあえず、集計に必要な項目として以下の内容をログから抽出できればよさそうです。
- タイムスタンプ
- 送信元IPアドレス
- 受信先IPアドレス(自IPアドレス)
- 送信元ポート番号
- 受信先ポート番号(自サービスのポート番号)
- block/pass状態
集計用データベースを作成する
ログ集計を柔軟に行いたいので、これらの項目をデータベース(DB)に投入し、SQLで集計できるようにしておきます。DBは手軽に用意できるSQLiteを使用し、集計用のテーブル定義を以下のように作成します。
CREATE TABLE ipmon(
timestamp VARCHAR(32) PRIMARY KEY, -- タイムスタンプ
sip VARCHAR(16) NOT NULL, -- 送信元IPアドレス
dip VARCHAR(16) NOT NULL, -- 受信先IPアドレス(自IPアドレス)
sport VARCHAR(5) NOT NULL, -- 送信元ポート番号
dport VARCHAR(5) NOT NULL, -- 受信先ポート番号(自サービスのポート番号)
status VARCHAR(1) NOT NULL -- block/pass状態
);
以下の手順でテーブルを作成します。
$ sqlite3 ipmon.sqlite3
sqlite> .read ipmon_table.sql
ipmonの内容からデータを抽出する
ipmon
コマンドが出力するログデータを処理したいので、出力内容を以下のように適当なファイルにリダイレクトしておきます。
$ sudo bash
# ipmon | tee /tmp/_ipmon.log
ログファイルが /tmp/_ipmon.log
に書き出されるようになったので、以下のシェルスクリプトでログ内容をDBに投入するようにします。
#!/bin/sh
# ipmonのフォーマットを変換する。
# before) 03/11/2019 02:10:58.901477 pppoe0 @0:5 b 74.82.47.10,60311
# after) 153.232.11.74,21 PR tcp len 20 40 -S IN
IPMON_LOG=/tmp/_ipmon.log
cat <<_EOF
INSERT INTO
ipmon(timestamp,sip,dip,sport,dport, status)
VALUES
_EOF
for line in `cat $IPMON_LOG | sed -e "s/ /_SP_/g" | grep '/11/2019'`
do
timestamp=`echo $line | sed -e "s/_SP_/ /g" -e "s/,/ /g" | awk '{ print $1" "$2 }'`
sip=` echo $line | sed -e "s/_SP_/ /g" -e "s/,/ /g" | awk '{ print $6 }'`
dip=` echo $line | sed -e "s/_SP_/ /g" -e "s/,/ /g" | awk '{ print $9 }'`
sport=` echo $line | sed -e "s/_SP_/ /g" -e "s/,/ /g" | awk '{ print $7 }'`
dport=` echo $line | sed -e "s/_SP_/ /g" -e "s/,/ /g" | awk '{ print $10 }'`
status=` echo $line | sed -e "s/_SP_/ /g" -e "s/,/ /g" | awk '{ print $5 }'`
echo -n "('$timestamp','$sip','$dip','$sport','$dport','$status'),"
done | tee out.txt | sed -e "s/,$/;/"
上記のシェルスクリプトを実行し、DBにデータを投入します。
$ ./ipmon_analyze.sh > out.sql
$ sqlite3 ipmon.sqlite3
sqlite> .read out.sql
SQLで集計する
DBにデータを投入し、集計準備が整いました。あとはSQLを投入して集計結果を得るだけです。
集計結果を見やすくするため、あらかじめ以下の設定をSQLiteに投入しておきます。
$ sqlite3 ipmon.sqlite3
sqlite> -- SELECT結果でのカラム名表示と見やすい形に整形する設定。
sqlite> .headers on
sqlite> .mode column
アクセスをブロックしたポート番号毎に集計する
アクセスをブロックしたポート番号毎に集計してみます。具体的なSQLは以下になります。
SELECT
MAX(status) AS status,
MAX(dport) AS protocol,
COUNT(status) as count
FROM
ipmon
GROUP BY
dport
ORDER BY
count DESC
;
集計結果は以下です。ssh(22),http(80),https(443)等へのアクセスが多くブロックされていることが把握できます。
status protocol count
---------- ---------- ----------
b 22 64
b 80 32
b 443 22
b icmp 16
b 389 12
b 5900 11
b 53 9
...
ポート番号389はパッと頭に浮かばなかったので /etc/services
から調べてみました。どうやらLDAPへのアクセスのようですね。
$ grep -w 389 /etc/services
ldap 389/tcp # Lightweight Directory Access [Tim_Howes] [Tim_Howes]
ldap 389/udp # Lightweight Directory Access [Tim_Howes] [Tim_Howes]
送信元IPアドレス毎に集計する
次に送信元IPアドレス毎のフィルタ結果を集計してみます。集計用SQLは以下になります。
SELECT
MAX(status) AS status,
MAX(dport) AS protocol,
MAX(sip) as sip,
COUNT(status) as count
FROM
ipmon
GROUP BY
sip
ORDER BY
count DESC
;
集計結果は以下です(IPアドレスは一部マスクしています)。個人用のPCルータへのアクセスなので、それほど多くはないにせよ同一のIPアドレスから何度かsshでの接続が試行されているようです。
status protocol sip count
---------- ---------- ------------- ----------
b 22 80.XXX.XXX.XXX 12
b 22 185.XXX.XXX.XXX 5
b 53 146.XXX.XXX.XXX 4
b 22 185.XXX.XXX.XXX 4
b 22 88.XXX.XXX.XXX 4
...
まとめ
ipfでのパケットフィルタ結果を集計する手順を紹介してみました。パケットフィルタの結果を定期的に集計できるようにしておくと、特定ポートへのアクセス増加といった日常と異なる状態の検出にも応用できそうです。
(あと単にパケットフィルタ結果を集計するだけでも楽しいという感じもします...)