manekineko倉金家ホームページ

趣味の部屋/サーバー構築メモ その4

アタック対策 fail2ban (2016.1)

2016年1月2日 2016年2月6日 更新
サーバーへのアタックを検知し拒絶するソフトとしてFail2banを使用していますが、最近のfail2banでの設定を記しておきます。


最近のバージョンアップに伴いfail2banの設定は、監視対象が増えたのとファイルがいろいろ分離されて少しわかりにくくなりました。
で、めんどうだったのと少し自信がなかったので書いてなかったのですが、検索サイトからわざわざ来てくださる方が多いので参考までに私の設定(やりかた)を記載しておきます。
ただしfilterなどの設定はログを見ながらそのときそのときのアタックの流行の状況を反映させて流動的に変える場合があります。

現在のCentos7サーバーにインストールされているパッケージとバージョンは
fail2ban-0.9.3-1.el7.noarch
fail2ban-systemd-0.9.3-1.el7.noarch
fail2ban-firewalld-0.9.3-1.el7.noarch
fail2ban-server-0.9.3-1.el7.noarch
fail2ban-sendmail-0.9.3-1.el7.noarch
fail2ban-mail-0.9.3-1.el7.noarch

一方CentOS6サーバーの方は、
fail2ban-0.9.3-1.el6.1.noarch
のみでした。

いずれもEPELよりインストールしていますが、CentOS7ではsystemd,firewalldへ変わったため構成が変更されたようです。

以下参考に各設定について記しておきます。

全体の設定
大元の動作の設定ファイルは /etc/fail2ban/fail2ban.conf です。
/etc/fail2ban/fail2ban.conf …変更箇所のみ。以下同様。
....
[Definition]
...
loglevel = NOTICE …元はINFOだが"Found ..."というログがたくさん出て鬱陶しい。
....
dbpurgeage = 864000 …たちの悪いのは少し長い日数banするので延ばしている。
その他必要なら
/etc/fail2ban/action.d/iptables-common.conf
....
# Option: blocktype
....
blocktype = DROP …アタックアクセスにいちいち拒否を返答する必要もないと思うので私は黙ってDROP(無視)。
....

監視対象(jail)の設定
監視対象とその設定は /etc/fail2ban/jail.local というファイルをつくり、ここに書きます。
内容はjail.confを参考にして自分勝手にアレンジしたもの。たとえば最近なんと数十分に一度ですごく長期間にわたって気長にアタックするなど手のこんだアタックがあり、findtime を変えたりしています。

fail2banはバージョンアップのたびにjailとそのフィルターファイルが増えて少しわかりにくくなってきています。
そのため今は自分が使っているのとその設定がすぐわかるようにjailの名前を変えたりしています。頭にMY-のついているのがそれです。
ただしjail名を変えているので設定が必ずしも踏襲されないのと必要に応じて変えやすいよう各項目皆書いておきます。

参考に今のもの。
/etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 192.168.0.0/16
usedns = yes

[MY-apache-fakegoogle] …filter.d/ignorecommands/apache-fakegooglebotもあわせて修正のこと。(後述)
enabled = true
port = http,https
logpath = %(apache_access_log)s
bantime = 3600
findtime = 60
maxretry = 1
ignorecommand = %(ignorecommands_dir)s/MY-apache-fakegooglebot <ip>

[MY-apache-noscript]
enabled = true
port = http,https
logpath = %(apache_error_log)s
bantime = 864000
findtime = 3600
maxretry = 3

[MY-dovecot]
enabled = true
port = pop3,pop3s,imap,imaps,submission,465,sieve
logpath = %(dovecot_log)s
bantime = 10800
findtime = 3600
maxretry = 4

[MY-postfix-sasl]
enabled = true
port = smtp,465,submission,imap3,imaps,pop3,pop3s
logpath = %(postfix_log)s
bantime = 86400
findtime = 3600
maxretry = 4

[MY-postfix]
enabled = true
port = smtp,465,submission
logpath = %(postfix_log)s
bantime = 86400
findtime = 3600
maxretry = 4

[MY-sshd]
enabled = true
#port = ssh
banaction = iptables-allports …全ポートをbanするならportの代わりにこれを書きます。sshは他の人は使わないのでエラーはすべてアタック。
logpath = %(sshd_log)s
bantime = 864000
findtime = 6000
maxretry = 2

[MY-vsftpd]
enabled = true
port = ftp,ftp-data,ftps,ftps-data
logpath = %(vsftpd_log)s
bantime = 10800
findtime = 3600
maxretry = 3

各jail毎のファイルの設定
上記jail.localで指定した各項目の設定ファイルです。
前述したようにわかりやすいよう名前を変更しています(頭に"MY-"をつけている)。
名前を変更すると場合によっては名前が長すぎると文句を言われることがあり、その場合は変更して少し短くします。

MY-apache-fakegoogle
けっこうgooglebotを騙ったにせのgooglebotのアクセスがあります。
放っておいてもいいけどアクセスがとてもしつこいのがいたので拒否することにしました。

ただしオリジナルの設定のままだとgoogleの検索ロボット以外の正規の各種googleのサービスのアクセスも拒否してしまいます。
すなわち以下のようなもの。
rate-limited-proxy-xxxx.google.com ... モバイル向けホームページ変換サービス
google-proxy-xxxx.google.com ... google翻訳サービスなどを使って閲覧した場合など
xxxx.bc.googleusercontent.com ... googleの接続サービスのひとつらしいがけっこうパスワードアタックなどに使われているのでほんとはやめてほしいサービス。でもその場合別途拒否するのでここではひとまずOK。

このため、とにかくホスト名にgoogleの文字があればいいことにして /etc/fail2ban/filter.d/ignorecommands/apache-fakegooglebot を MY-apache-fakegooglebot としてコピーし、変更します。

/etc/fail2ban/filter.d/ignorecommands/MY-apache-fakegooglebot
....
sys.exit(0 if (host and re.match('crawl-.*\.googlebot\.com', host)) else 1)
↓(29行目あたり変更。とにかくgoogleの文字があればいいことに。)
sys.exit(0 if (host and re.search('google', host)) else 1)
....
フィルターファイルは、 apache-fakegooglebot.conf を MY-apache-fakegoogle.conf としてコピー。特に変更はしていません。

MY-apache-noscript
これは大幅に変えており、かつアタックの流行に合わせてさらに流動的に変更します。
オリジナルは拡張子で見ていますが、私はアタックアクセスによく使われるキーワード(admin,editor,login,cgi,script,setupなど)を検出するようにしています。もちろんこういう名前は使ってはいませんので。
また apache-nohome の記述もこれに含ませてしまっています。
ただしやたら詰め込むと判定が遅くなりますし、ときに長過ぎると文句を言われます。

/etc/fail2ban/filter.d/MY-apache-noscript.conf
....
[INCLUDES]

# overwrite with apache-common.local if _apache_error_client is incorrect.
before = apache-common.conf

[Definition]

failregex = ^%(_apache_error_client)s ((AH001(28|30): )?File does not exist|(AH01264: )?script not found or unable to stat): /\S*([Aa]dmin|editor|login|cgi|script|setup)\S*(, referer: \S+)?\s*$
^%(_apache_error_client)s script '/\S*([Aa]dmin|editor|login|cgi|script|setup)\S*' not found or unable to stat(, referer: \S+)?\s*$
^%(_apache_error_client)s (AH00128: )?File does not exist: \S*([Aa]dmin|editor|login|cgi|script|setup)\S*(, referer: \S+)?\s*$

ignoreregex =

....

MY-dovecot
dovecot.conf を MY-dovecot.conf としてコピーし、backend を sysytemd に変えています。(もちろんCentOS6などsystemdを使ってない場合は変えてはいけない。以下同様。)

/etc/fail2ban/filter.d/MY-dovecot.conf
....

[Init]

backend = systemd

journalmatch = _SYSTEMD_UNIT=dovecot.service

....

MY-postfix-sasl
これも postfix-sasl.conf を MY-postfix-sasl.conf としてコピーし、backend を systemd に変えています。

/etc/fail2ban/filter.d/MY-postfix-sasl.conf
....

[Init]

backend = systemd

journalmatch = _SYSTEMD_UNIT=postfix.service

....

MY-postfix
postfix.conf を MY-postfix.conf としてコピーし、フィルター内容を変更しています。
すなわちオリジナルではソフトリジェクト(コード450 4.7.1)でも拒否しますが、私は逆に複数回の450再試行は救済(OK)するようにしていますのでこれは外します。(→ 再度のスパム対策 参照。)
またbackend を systemd に変えています。

/etc/fail2ban.filter.d/MY-postfix.conf
....
[Definition]

_daemon = postfix/(submission/)?smtp(d|s)

failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.7\.1 .*$
^%(__prefix_line)sNOQUEUE: reject: VRFY from \S+\[<HOST>\]: 550 5\.1\.1 .*$
^%(__prefix_line)simproper command pipelining after \S+ from [^[]*\[<HOST>\]:?$


ignoreregex =

[Init]

backend = systemd

journalmatch = _SYSTEMD_UNIT=postfix.service
....

MY-sshd
ssshd.conf を MY-sshd.conf としてコピーし、backend を systemd に変えています。

/etc/fail2ban/filter.d/MY-sshd.conf
....
[Init]

# "maxlines" is number of log lines to buffer for multi-line regex searches
maxlines = 10

backend = systemd

journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd

....

MY-vsftpd
vsftpd.conf を MY-vsftpd.conf としてコピーし、ignoreregex を追加しています。
クライアントによっては最初自動的にあるいは設定によってはまず anonymous での接続を試みる場合があるからです。

/etc/fail2ban/filter.d/MY-vsftpd.conf
....
[Definition]

__pam_re=\(?%(__pam_auth)s(?:\(\S+\))?\)?:?
_daemon = vsftpd

failregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=(ftp)? ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
^ \[pid \d+\] \[.+\] FAIL LOGIN: Client "<HOST>"\s*$

ignoreregex = ^%(__prefix_line)s%(__pam_re)s\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=(ftp)? ruser=anonymous rhost=<HOST>(?:\s+user=.*)?\s*$
^ \[pid \d+\] \[anonymous\] FAIL LOGIN: Client "<HOST>"\s*$


....

以上、よくわからないながらも現時点でのざっとの設定でした。


(2016.2.6 追記) サーバー再起動・停止時のエラーログ
fail2ban単独での停止や起動では問題ありませんでしたが、事前にfail2banを停止せずサーバーを停止あるいは再起動したときに、fail2ban.logに以下のようなエラーが出ていました。
....
2016-02-03 16:33:22,498 fail2ban.action         [2992]: ERROR   firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -p tcp -m multiport --dports ssh -m set --match-set fail2ban-MY-sshd src -j DROP
ipset flush fail2ban-MY-sshd
ipset destroy fail2ban-MY-sshd -- stdout: 'Not using slip\n'
2016-02-03 16:33:22,498 fail2ban.action         [2992]: ERROR   firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -p tcp -m multiport --dports ssh -m set --match-set fail2ban-MY-sshd src -j DROP
ipset flush fail2ban-MY-sshd
ipset destroy fail2ban-MY-sshd -- stderr: 'Traceback (most recent call last):\n  File "/usr/bin/firewall-cmd", line 703, in <module>\n    fw = FirewallClient()\n  File "<string>", line 2, in __init__\n  File "/usr/lib/python2.7/site-packages/firewall/client.py", line 52, in handle_exceptions\n    return func(*args, **kwargs)\n  File "/usr/lib/python2.7/site-packages/firewall/client.py", line 1594, in __init__\n    self.bus = dbus.SystemBus()\n  File "/usr/lib64/python2.7/site-packages/dbus/_dbus.py", line 194, in __new__\n    private=private)\n  File "/usr/lib64/python2.7/site-packages/dbus/_dbus.py", line 100, in __new__\n    bus = BusConnection.__new__(subclass, bus_type, mainloop=mainloop)\n  File "/usr/lib64/python2.7/site-packages/dbus/bus.py", line 122, in __new__\n    bus = cls._new_for_bus(address_or_type, mainloop=mainloop)\ndbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoServer: Failed to connect to socket /var/run/dbus/system_bus_socket: \xe6\x8e\xa5\xe7\xb6\x9a\xe3\x82\x92\xe6\x8b\x92\xe5\x90\xa6\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xbe\xe3\x81\x97\xe3\x81\x9f\nipset v6.19: Set cannot be destroyed: it is in use by a kernel component\n'
2016-02-03 16:33:22,498 fail2ban.action         [2992]: ERROR   firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -p tcp -m multiport --dports ssh -m set --match-set fail2ban-MY-sshd src -j DROP
ipset flush fail2ban-MY-sshd
ipset destroy fail2ban-MY-sshd -- returned 1
2016-02-03 16:33:22,498 fail2ban.actions        [2992]: ERROR   Failed to stop jail 'MY-sshd' action 'firewallcmd-ipset': Error stopping action
....
これについては、fail2banが停止する際に何か必要とする別のプロセスが先に停止してしまいfirewalldに投入したルールが解除できないためと思われました。

で、少し試してみた結果いちおう回避できましたので参考に記しておきます。

修正すべきは
/etc/systemd/system/multi-user.target.wants/fail2ban.service
ですが、これはリンクで、元は
/usr/lib/systemd/system/fail2ban.service
[Unit]
Description=Fail2Ban Service
Documentation=man:fail2ban(1)
After=network.target iptables.service firewalld.service
    ↓  修正
After=network.target iptables.service firewalld.service dbus.service

....
すなわちdbus.service追加です。
dbusを使ってfirewalldとやりとりしているのにdbusが先に終了してエラーになっていたということのようです。

なお、変更したら
~]# systemctl daemon-reload
で変更を反映させる必要があります。