manekineko倉金家ホームページ

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

アタック対策fail2ban

2010年10月28日 2016年1月2日 更新
サーバーに不正にログインしようと試みるホストを検知し、接続拒否するためのソフトfail2banを導入しました。効果絶大、お薦めです。


2016年1月2日 追記
最近fail2banがアップグレードされ、特にフィルター部分などのファイルや書き方が変更になっています。
この記事は古いfail2banのものですので、あまり参考にはなりません。
新しいfail2banは設定がとてもわかりにくくなってしまい、英語堪能、正規表現得意という方でないと少し苦労するかもしれません。私もよくわからず、そこそこ動けばいいやというところで使っています。

参考に、最近のfail2banの設定はこちら。
→ アタック対策 fail2ban (2016.1)





時折サーバーの負荷が非常に増大していることがあり、調べてみると何者かがSSHやFTPを使ってサーバーに不正ログインをしようとしています。
すべて海外からのアクセスでいくら辞書攻撃をやっても、普通の辞書にはない間違っても他人には言えないくらい上品な日本語をそのままローマ字にしたような私のパスワードが破られるとは思いませんが、気持ちよくありません。

このような不正アタックをログから検知して接続を拒否するソフトがあり、今回そのどれかを使ってみようと思います。
有名どころではdenyhosts,blockhosts,fail2banなどですが、いろいろ比較検討した結果、fail2banを使うことにしました。

ソフトの簡単な比較
denyhosts
 ・hosts.denyを使いTCPWrapper で制御。
 ・TCPWrapperを使用するサービスに適用される。
 ・sshがメインのようでそれ以外もできるらしいが情報不足。
 ・denyhosts-2.6-5 がEPELにあり。
  denyhosts-2.6-3 がRPMForgeにあり。

blockhosts
 ・hosts.allowを使いTCPWrapper で制御、
  あるいはファイアウォールを使うことも可能。
 ・sshがメインのようでそれ以外もできるらしいが情報不足。
 ・CentosPlus,EPEL,RPMForgeのいずれにもパッケージはない。

fail2ban
 ・ファイアウォールとTCPWrapper両方使える。
 ・(ログがあってフィルターが書ければ)原理的にはすべてのサービスに適用可能。
 ・sshd httpd proftpd vsftpdなどのフィルタが標準で準備されている。
  postfix,dovecotなど他のフィルターも何とか自分で書けそう。
 ・gamin, shorewallが必要。
   gaminはなぜかすでにインストールされてる。
   shorewall-4.0.15-1 がEPELにあり。
 ・fail2ban-0.8.2-3 がRPMForge にあり。
  fail2ban-0.8.4-23 がEPELにあり。
 ・情報が他に比べわりと多い。

インストール
まずはEPELよりshorewallをインストール。shorewall-4.0.15-1.el5.noarch.rpmと依存関係で他にいくつか。
shorewallはfail2banのインストール依存関係で必要なだけで特に使わなくてもいいようです。(単に作者の好みでshorewall推奨ということのようだ。)
rpmに--nodepsオプションをつければインストールしなくてもいいようですが、ちょっと興味があるのでインストールしておいて、あとでゆっくりいじってみます。

引き続きfail2banをインストール。fail2ban-0.8.4-23.el5.noarch.rpm。

設定
設定ファイル /etc/fail2bam/fail2ban.conf はそのままでOK。
設定ファイル /etc/fail2ban/jail.conf編集。
ざっとみてみると、ssh, proftpd, sasl, apache, named, ...といろいろフィルターが入っています。またTCPWrapperを使うサービスについては、個々にiptablesだけでなく、hosts.denyを使う(denyhostsと同じような)設定もできるようです。

あとは必要に応じて有効化、ログの場所確認、通知メールの確認などをやればいいようです。
以下、/etc/fail2ban/jail.conf 変更箇所。
[default]
maxretry = 5 …自分でも3回くらい間違えることはよくあるんだわ。
logtarget = /var/log/fail2ban.log
…出力log位置は好みで変更。合わせてloglotateの方も変えておく。

あとは各セクションを編集。例:
[ssh-iptables]
enabled  = true …セクションを有効にする。
filter   = sshd …フィルターの指定。/etc/fail2ban/filter.dに.confとしてある。
action   = iptables[name=SSH, port=ssh, protocol=tcp]
           sendmail-whois[name=SSH, dest=root, sender=fail2ban]
…メールアドレス確認あるいは修正。
logpath = /var/log/secure …検査するlogの場所確認。
maxretry = 5 …好みで変更。

その他 [proftpd-iptables] などそのまま使えるのを同様に有効にしておきます。

<参考>
iptables方式を使うかtcpwrapper方式を使うかはlddコマンドで調べられます。
# which sshd
/usr/sbin/sshd
# ldd /usr/sbin/sshd
        linux-gate.so.1 =>  (0x0073f000)
        libwrap.so.0 => /lib/libwrap.so.0 (0x00448000)
        libpam.so.0 => /lib/libpam.so.0 (0x00d91000)
        ........
で、libwrap.soがあれば該当サービスがTCPWrapper(hosts.allow, hosta.deny)によるチェックをしているということで、tcpwrapperが使えます。これがない場合はiptablesを使わなくてはなりません。
オプションで使うか否かが変更されている場合があるので実際にインストールされている状態での確認が必要です。

起動
jail.confの[ssh-iptables]および[proftpd-iptables]のmaxretryを2に変えてためしに起動してみます。
# service fail2ban start
見事SELinuxに拒否されました。
起動と停止時にunixソケットにアクセスするのがいけないようです。

例によってSELinux Managementから簡単に許可しようとしましたが...該当する設定項目がありません。
しかたないので許可ポリシーとかをつくってインストールします。

許可ポリシーの作成とインストール。
以下rootでの作業。
(私の場合は)/var/log/audit/audit.logにSELInuxのログが記録されています。
まず/var/log/audit/audit.log を空にしておきます。
# > /var/log/audit/audit.log
# setenforce 0 でSELinuxをPermissiveモードにします。
問題のアクセスをやってみます。file2banを起動、停止。
# service fail2ban start; service fail2ban stop
/var/log/audit/audit.logにSELinuxのなんたらかんたらが記録されます。
読んでもよくわかりません。

audit.logからポリシー許可ファイルをつくる。
# mkdir -m 0600 SELinux/ …作業と保存用ディレクトリをなければつくる。
# cd ~/SELinux/
# audit2allow -i /var/log/audit/audit.log -m fail2ban > fail2ban.te

バイナリ形式に変換。
# checkmodule -M -m fail2ban.te -o fail2ban.mod

組込み用パッケージを作る。
# semodule_package -m fail2ban.mod -o fail2ban.pp

パッケージを組み込む。ちょっぴり時間がかかる。
# semodule -i fail2ban.pp

これでOKのはず。
ふたたびSELinuxを起動しておいてfail2banを立ち上げてみます。
# setenforce 1
# service fail2ban start

こんどはOKです!。
アクセスのソースもターゲットも固定されているのでこの方法でいいと思います。

確認
maxretryは先ほど2に変えてあります。

SSHででたらめのパスワードをいれてわざとログインを失敗してみます。
....3回パスワードを要求してきます。おかしいな。
接続を切ってまたつなぐとまたパスワードを要求してきます。
fail2banはもくろみ通りには働いていません。

ProFTPDではちゃんと3回目で応答がなくなり、
[Fail2Ban] ProFTPD: banned xxx.xxx.xxx.xxx
日付: 2010-10-30 21:13
差出人: Fail2Ban
宛先: root@mail.iroribata.net

Hi,

The IP xxx.xxx.xxx.xxx has just been banned by Fail2Ban after
2 attempts against ProFTPD.

(このあとIPアドレスの所有者、国などの情報)
というメールがきます。

調整
SSHでfail2banが働かない原因はすぐにわかりました。
同じユーザで接続を切らずにパスワードだけ変えてログインを失敗してもFailedログは毎回は出力されません。
Oct 30 21:02:52 jiji sshd[4957]: Failed password for admin from ....
Oct 30 21:03:59 jiji last message repeated 2 times
....ということは、SSH側で3回、fail2banで3回として最大9回失敗しないとだめだということだな。
アタックがあると何百回もあるからまあ9回でもいいか。
で、SSHで3回、fail2banで3回に設定しなおし。
ためしに1回ごとに接続を切ってやってみたらちゃんと拒否されました。

しばらくしたらメールがきました。
[Fail2Ban] SSH: banned xxx.xxx.xxx.xxx
日付: 2010-10-30 21:39
差出人: Fail2Ban
宛先: root@mail.iroribata.net

Hi,

The IP xxx.xxx.xxx.xxx has just been banned by Fail2Ban after
3 attempts against SSH.

......
私のIPアドレスではありません。だれかがひっかかったということだな。むふふ。
これでよし!

(追記)次の日の朝メールを見たらfail2banから6通のbanメールがきていました。接続を見たらやはりすべて海外からのアクセス。
効果絶大だな。

dovecot追加
その後ログをつらつらと見ていますと、dovecotにもアタックの形跡がありました。人のメールを見てどうしようというんだ?

fail2banにはdovecotのフィルターはついていなかったので追加しましょう。

まずは/etc/dovecot.conf、認証ログの詳細を出力するよう設定を変更。
auth_verbose = yes

/etc/fail2ban/jail.confにdovecotセクション追加。
[dovecot-iptables]

enabled = true
filter = dovecot
action = iptables-multiport[name=Dovecot, port="pop3,imap,smtp", protocol=tcp]
sendmail-whois[name=Dovecot, dest=root, sender=fail2ban]
logpath = /var/log/maillog
maxretry = 10

さらに/etc/fail2ban/filter.d/にdovecot.confを作成し、認証の拒絶ログを見ながらフィルターを作成。
ログイン方法や切断再接続の仕方などによっていくつかパターンが出てくるようです。
また私の場合は認証はMySQLで行っていますので、それ以外の場合はわかりません。
[Definition]

failregex = \S+ dovecot: \S+-login: Aborted login:.* rip=<HOST>, \S+$
\S+ dovecot: \S+-login: Disconnected:.* rip=<HOST>, \S+$
\S+ dovecot: auth-worker\(default\): sql\(\S+,<HOST>\): unknown user$
\S+ dovecot: auth-worker\(default\): sql\(\S+,<HOST>\): Password mismatch$

ignoreregex =

翌日fail2banよりbanメールがきましたのでちゃんと働いています。ただかなりの数同時接続をやっているようなのと多少の遅れがあるみたいで、拒否するまで数十個のアタックログはありました。(dovecotの同時接続数を減らせばいいのかもしれない。)

http追加
httpにもあやしいアクセスがきます。adminツールをねらっており特にphpMyAdmingがいいターゲットになっているようです。
.htaccessでアクセスはlocalhostのみに制限はしてあり問題はないと思われますが、なんといってもログがうっとうしい。

/etc/fail2ban/jail.confに[apache-badbots]というセクションがあったのでそれをコピーして少々変更。
[http-iptables]

enabled = true
filter = http-scan
action = iptables-multiport[name=Http-Scan, port="http,https"]
sendmail-buffered[name=Http-Scan, lines=5, dest=root, sender=fail2ban]
logpath = /var/log/httpd/error_log
maxretry = 3
bantime = 3600
/etc/fail2ban/filter.d/http-scan.confも適当なのをコピーしてきてつくる。
[Definition]

failregex = [[]client <HOST>[]] File does not exist: /home/https?/\S*[Aa][Dd][Mm][Ii][Nn]
[[]client <HOST>[]] File does not exist: /home/https?/[Dd][Bb]
[[]client <HOST>[]] File does not exist: /home/https?/[Pp][Mm][Aa]
[[]client <HOST>[]] File does not exist: /home/https?/[Mm][Yy][Ss][Qq][Ll]
[[]client <HOST>[]] client denied by server configuration:.*

ignoreregex =
で、OK。

トラブル(というよりどじ)
その後fail2banは順調に動いておりましたが、あるときhttpによくあるphpMyAdminを狙った不正アクセスが多数あったにもかかわらず動作しないことがありました。
他のSSHとかはちゃんと動作しています。なんでだろう?
...で、思い出しました。少し前に自分のプログラムミスで大量のエラーログを吐かせてしまい、鬱陶しいのでそれを消していたのです。

もしかしたらfail2banはログの何行目あるいは何文字目まで見たかを覚えていて、ログを勝手に消した場合には覚えた数になるまでたまらないと動作しないのかもしれないと思い、fail2banを再起動。
その後はちゃんと検出するようになりました。めでたしめでたし。

failregex集(2013.6.12 追加)

 けっこう検索サイトからfail2ban用のフィルターを求めて来る方が多いので、参考までに私のを記載しておきましょう。上に記したのから少し変更したり追加したりしています。
(自動改行はしませんのでブラウザの幅をめいっぱい広げて見てください。)

まずは、jail.conf の該当部分。
/etc/fail2ban/jail.conf
........


[ssh-iptables]

enabled = true
filter = sshd
action = iptables[name=SSH, port=ssh, protocol=tcp]
sendmail-whois[name=SSH, dest=root, sender=fail2ban]
logpath = /var/log/secure
bantime = 21600
findtime = 3600
maxretry = 2


[dovecot-iptables]

enabled = true
filter = dovecot
action = iptables-multiport[name=Dovecot, port="pop3,imap,smtp", protocol=tcp]
sendmail-whois[name=Dovecot, dest=root, sender=fail2ban]
logpath = /var/log/maillog
bantime = 21600
findtime = 600
maxretry = 10


[httpd-iptables]

enabled = true
filter = httpd
action = iptables-multiport[name=Httpd, port="http,https", protocol=tcp]
sendmail-whois[name=Httpd, dest=root, sender=fail2ban]
logpath = /var/log/httpd/error_log
bantime = 21600
findtime = 600
maxretry = 3


[postfix-iptables]

enabled = true
filter = postfix
action = iptables-multiport[name=Postfix, port="pop3,imap,smtp", protocol=tcp]
sendmail-whois[name=Postfix, dest=root, sender=fail2ban]
logpath = /var/log/maillog
bantime = 21600
findtime = 3600
maxretry = 4


[proftpd-iptables]

enabled = true
filter = proftpd
action = iptables[name=ProFTPD, port=ftp, protocol=tcp]
sendmail-whois[name=ProFTPD, dest=root, sender=fail2ban]
logpath = /var/log/proftpd/proftpd.log
bantime = 21600
findtime = 600
maxretry = 7


........

以下、フィルター部分。

/etc/fail2ban/filter.d/sshd.conf
failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
^%(__prefix_line)sFailed (?:password|publickey) for .* from <HOST>(?: port \d*)?(?: ssh\d*)?$
^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
^%(__prefix_line)sUser \S+ from <HOST> not allowed because not listed in AllowUsers$
^%(__prefix_line)sauthentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
^%(__prefix_line)sAddress <HOST> .* POSSIBLE BREAK-IN ATTEMPT!*\s*$
^%(__prefix_line)sUser \S+ from <HOST> not allowed because none of user's groups are listed in AllowGroups$
^%(__prefix_line)sInvalid user\s*\S* from <HOST>
^%(__prefix_line)spam_unix(sshd:auth): authentication failure;\s*\S* rhost=<HOST>
^%(__prefix_line)sFailed password for invalid user\s*\S* from <HOST>.*
^%(__prefix_line)sConnection closed by <HOST>.*$

/etc/fail2ban/filter.d/dovecot.conf
failregex = \S+ dovecot: \S+-login: Aborted login:.* rip=<HOST>, \S+
\S+ dovecot: \S+-login: Disconnected:.* rip=<HOST>, \S+
\S+ dovecot: auth-worker\(default\): sql\(\S+,<HOST>): unknown user
\S+ dovecot: auth-worker\(default\): sql\(\S+,<HOST>\): Password mismatch
\S+ dovecot: auth\(default\): passdb\(\S+,<HOST>\): Attempted login with password having illegal chars

/etc/fail2ban/filter.d/httpd.conf
failregex = [[]client <HOST>[]] script '.*' not found or unable to stat$
[[]client <HOST>[]] client denied by server configuration:
[[]client <HOST>[]] File does not exist: .*([Aa]dmin|[Mm]anager|[Ee]ditor|[Uu]ser|login)
[[]client <HOST>[]] File does not exist: .*/([Pp][Mm][Aa]|[Mm][Yy][Ss][Qq][Ll])
[[]client <HOST>[]] File does not exist: .*/(db|scripts|forum|board|[vV][bB])
[[]client <HOST>[]] File does not exist: .*/(blog|wordpress|wp)
[[]client <HOST>[]] File does not exist: .*/(catalog|shop|oscommerce|ipb)

/etc/fail2ban/filter.d/postfix.conf
failregex = reject: RCPT from (.*)\[<HOST>\]: 550 5.1.1
reject: RCPT from (.*)\[<HOST>\]: 554 5.7.1
warning: \S+\[<HOST>\]: SASL (LOGIN|PLAIN) authentication failed:

/etc/fail2ban/filter.d/proftpd.conf
failregex = \(\S+\[<HOST>\]\)[: -]+ USER \S+: no such user found from \S+ \[\S+\] to \S+:\S+ *$
\(\S+\[<HOST>\]\)[: -]+ USER \S+ \(Login failed\): .*$
\(\S+\[<HOST>\]\)[: -]+ SECURITY VIOLATION: \S+ login attempted\. *$
\(\S+\[<HOST>\]\)[: -]+ Maximum login attempts \(\d+\) exceeded *$

出力されるログはサーバーの設定や認証方式によって変わりますので、ログを見ながら必要に応じて追加修正してください。

(参考) VineLinuxでもfail2ban (2014.5 追記)
VineLinux6.2にもfail2banを入れてみました。
VineLinuxにはfail2banのrpmはありませんでしたので本家から導入。
→ VineLinuxでもfail2ban


(参考) CentOS6 fail2ban アップデートで動かなくなる。(2015.10.17 追記)
 この次に構築して使っているCentOS6のサーバーでも現在fail2banが稼働しています。
最近やたらとアップデートが続きその度に設定やフィルターが変わっていちいち書換えていましたが、都度ホームページにアップするのも面倒で書いてはいません。
 でも今回アップデートで動かなくなる(一見動いているようだけどログにエラーが出てiptablesにも設定されない)事件があったので書いておきます。

 2015年10月17日アップデートがありfail2banを0.9.2-1.el6.noarchから0.9.3-1.el6.noarchにアップデートしました。
いくつかの設定ファイルのアップデートがあり、新しい設定(.rpmnew)を適用し、念のためfail2banを再起動してみると、fail2banのログにやたらといろんなエラーが出てしかもiptablesに拒否設定がされていません。

 調べた結果、/etc/fail2ban/action.d/iptables-common.conf に追加された設定項目
lockingopt = -w
が原因であることがわかり、
lockingopt =
に変更、OK。

最近fail2banは作っている人が自分のPCでの設定をかまわず書いてくるらしいので少し要注意です。

…10月27日のアップデートで修正されました。