【Linux】Postfixへの不正なアクセスログを監視し自動的にiptablesで遮断する

Postfixを立ち上げていると結構な数の不正な接続が頻繁にログに記録されています。

この不正な接続に頭を悩ませている管理者も多いと思います。

個人的に運用しているPostfixではログを監視して一定回数同じ接続元から不正アクセスがあると自動的にiptablesに追加して遮断しています。

logmon

インストール

ログの監視にはlogmonというスクリプトを使用しています。

私はgithubにあるものを利用させてもらいました。

logmonの詳しいインストールの仕方や使い方は割愛しますが概ね以下のような感じでインストールできると思います。

$ git clone https://github.com/moomindani/logmon.git
$ cd logmon
$ sudo ./setup.sh

logmon.confの設定

logmon.confでPostfixの不正アクセスに絡むようなログが出力されるとスクリプトを呼んでいます。

drop_postfix_unknown_ip_packet.shは指定された閾値の回数を超えるとその接続元をiptablesにdropするように追加します。

# Monitor for messages
:/var/log/mail.log
(reject: RCPT from)
/etc/logmon/drop_postfix_unknown_ip_packet.sh 3 "<%%%%>" >> /var/log/logmon.log
:/var/log/mail.log
(non-SMTP command from)
/etc/logmon/drop_postfix_unknown_ip_packet.sh 1 "<%%%%>" >> /var/log/logmon.log
:/var/log/mail.log
(too many errors after DATA from)
/etc/logmon/drop_postfix_unknown_ip_packet.sh 1 "<%%%%>" >> /var/log/logmon.log

drop_postfix_unknown_ip_packet.sh

logmonから呼び出されるスクリプトになります。

第1引数は閾値となっています。

不正アクセスした接続元のIPアドレスは/var/cache/logmon/unknown_ip.<閾値>というファイルに記録されていきます。

そして不正アクセスの回数がIP_ADDR_CACHE_TIMEで指定された期間内に閾値を超えるとiptableに追加するようになっています。

LOG_IP_ADDRのgrep -vで除いているIPアドレスは環境に応じて192.168.から変更する必要があるかもしれません。

iptables -A INPUT -i eth1 -s $IP_ADDR -j DROP の -i eth1などは対応するインターフェースに変更して下さい。

また最後にiptables-saveによって現在のiptablesの設定を保存しています。

iptables-saveはディストリビューションによって異なる可能性があるので適宜変更して下さい。

#!/bin/sh

# unix timestamp and date now
UT_NOW=`date +'%s'`
DATE_NOW=`date +'%b %d %T'`

# attack count threshold within ip address cache time if unknown ip address is spammer
THRESHOLD_ATK_CNT=$1

# unknown ip address list file
UNKNOWN_IP_ADDR="/var/cache/logmon/unknown_ip.$THRESHOLD_ATK_CNT"

# ip address cache time
IP_ADDR_CACHE_TIME=21600

# check if 1st param is IP address
#if [[ ! $1 =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
#       echo "$DATE_NOW invalid ip address $1"
#       exit
#fi

IP_ADDR_CACHE_UT_FROM=`expr $UT_NOW - $IP_ADDR_CACHE_TIME`

if [ -e $UNKNOWN_IP_ADDR ]; then
        awk "\"$IP_ADDR_CACHE_UT_FROM\" <= \$1 && \$1 <= \"$UT_NOW\"" $UNKNOWN_IP_ADDR >$UNKNOWN_IP_ADDR.tmp
        rm $UNKNOWN_IP_ADDR
        mv $UNKNOWN_IP_ADDR.tmp $UNKNOWN_IP_ADDR
fi

LOG_IP_ADDR=`echo $2 | egrep -o "[0-9]{1,3}(\.[0-9]{1,3}){3}" | grep -v "192.168." | head -n 1`
echo "$UT_NOW $LOG_IP_ADDR" >>$UNKNOWN_IP_ADDR
echo "$DATE_NOW detected: $2"

cut -d' ' -f2 $UNKNOWN_IP_ADDR | sort | uniq -c | sed -e 's/^ *//' | awk "\"$THRESHOLD_ATK_CNT\" <= \$1" >$UNKNOWN_IP_ADDR.drop

ADDED=0
for IP_ADDR in `cut -d' ' -f2 $UNKNOWN_IP_ADDR.drop`
do
        EXISTS_RULES=`iptables -L INPUT -n | egrep -o "[0-9]{1,3}(\.[0-9]{1,3}){3}" | grep -c -w "$IP_ADDR"`
        if [ $EXISTS_RULES -eq 0 ]; then
                iptables -A INPUT -i eth1 -s $IP_ADDR -j DROP
                echo "$DATE_NOW Add $IP_ADDR to iptables"
                ADDED=1
        fi
done

if [ $ADDED -eq 1 ]; then
        iptables-save >/etc/iptables/rules.v4
fi

del_unused_iptables.sh

以下のようにcronなどに仕掛けて一定期間対象のパケットが来ていなかったらiptablesから削除するスクリプトです。

頻度や時間は環境に応じて適宜変更して下さい。

2 4 * * * /etc/logmon/del_unused_iptables.sh
#!/bin/sh

# iptables cache file
CACHE_IPTABLES='/var/cache/logmon/iptables'

# iptables delete flag
DELETED=0

iptables -L INPUT -n -v >$CACHE_IPTABLES.new

if [ -e $CACHE_IPTABLES.old ]; then
        for IP_ADDR in `fgrep -xf $CACHE_IPTABLES.{new,old} |grep DROP |sed 's/[\t ]\+/\t/g' |cut -f9`
        do
                iptables -D INPUT -i eth1 -s $IP_ADDR -j DROP
                DELETED=1
        done
        rm $CACHE_IPTABLES.old
fi

if [ $DELETED -eq 1 ]; then
        iptables -L INPUT -n -v >$CACHE_IPTABLES.new
        iptables-save >/etc/iptables/rules.v4
fi

mv $CACHE_IPTABLES.new $CACHE_IPTABLES.old

最後に

これらのスクリプトはあくまで自己責任においてご使用下さい。

そのまま導入しても正常に動作しない可能性も十分ありますので、使用の際は処理内容を十分に理解した上で各々の環境に合わせた修正をお願いいたします。

これらのスクリプトの使用や改変は使用者の自己の責任において自由に行っていただいて構いませんが、このスクリプトをそのまま他へ転載するなどの2次配布はご遠慮下さい。