ニフクラ ブログ

ニフクラやクラウドの技術について、エンジニアが語るブログです。

fail2banでサービスを死守せよ

こんにちは。株式会社ドリーム・アーツの石田です。

ニフティクラウドなどのパブリッククラウドを利用するときにとにかく不安なのはセキュリティですね。

最近、ニフティクラウドではファイヤウォール機能が追加されたり、PFUからはVPN接続でニフティクラウドのサーバーにアクセスできるサービスが提供されたりと、自社でサーバーを構築する場合は当たり前だったセキュリティレベルがようやくパブリッククラウドでも実現できるようになりました。

企業内で利用するようなパターンだとこれでも十分かもしれません。

ところが、インターネットから利用可能なサービスを作るにはこれではまだ不安です。

なぜなら、インターネットに対してサービスを公開するということは、当然サービスを提供するためのhttpなどのポートを開けておかないといけないわけで、ファイヤウォールだけではこれらのポートに対する悪意のあるアクセスを拒絶することはできないからです。

今日はインターネットに公開するポートをブルートフォース攻撃やDoS攻撃から守るための賢いファイヤウォールである fail2ban をご紹介します。

ブルートフォース攻撃?

ブルートフォース攻撃という攻撃手法をご存じでしょうか。

この攻撃は、サービスに対して何回もパスワード認証を試みパスワードを奪取する攻撃手法です。

パスワードとしてよくつかわれる文字列を集めた辞書なども出回っており、現在の性能の高いサーバーと攻撃ツールの組み合わせだと、数分のうちにパスワードが破られたりしてしまいます。

たとえ、使用しているサーバーソフトやミドルウェアの脆弱性、WebアプリケーションならばSQLインジェクションやクロスサイトスクリプティングなどのアプリレベルでの脆弱性をきちんと対応していたとしてもです。

一般に、ブルートフォース攻撃に対しては、特定のアカウントに対して連続して認証に失敗したときにアカウントをロックしたり、認証に失敗したら結果を返すまでにウェイトを入れることで連続攻撃を防止したりといったアプリレベルでの予防線を張ります。

これらの対応は諸刃の剣で、ターゲットにされてしまったアカウントは自分に非はないのにロックされてしまったり、ウェイトで待つスレッドが大量に発生することでサービスのパフォーマンスが低下するDoS攻撃を引き込んでしまったりします。

そこで登場するのが、fail2ban です。

fail2ban の役割

fail2ban は iptables などのファイヤウォールの仕組みのフロントエンドとして実装されていて、アプリのログを監視して特定のパターンに引っかかる動作が記録されたらファイヤウォールに動的にルールを追加して通信をブロックするというソフトです。

つまり、アプリのログを監視して10分間に3回認証に失敗したクライアントをIPレベルでブロックするといったことが可能になります。

もちろん、ブロックの解除も自動的に行うことができますから、運用時に手を煩わせることなくブルートフォース攻撃やDoS攻撃を回避できるようになります。

それでは早速、ニフティクラウドの CentOS サーバーに fail2ban をインストールして設定してみましょう。

fail2ban のインストール

CentOS用の fail2ban のパッケージはデフォルトではそのまま利用可能でなく、EPELという拡張リポジトリで配布されているものを利用します。

インストールの前に EPEL リポジトリを利用できるようにすることから始めます。64ビット版のCentOSイメージを使って作ったサーバーの場合は、以下のコマンドで使えるようになります。

# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm

EPELをセットアップした後で、fail2ban をインストールします。これは以下のように1行で完了です。

# yum -y install fail2ban

依存関係がある shorewall なども一緒に入ってしまいますが、問題はありませんので安心してください。

インストール直後の状態だと、SSHに対する不正なアクセスを監視するように設定されています。

早速、起動して動作を確認してみます。

# service fail2ban start
Starting fail2ban:                                         [  OK  ]

起動すると、以下のように iptables に fail2ban で定義されたチェインが追加されます。

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
fail2ban-SSH  tcp  --  anywhere             anywhere            tcp dpt:ssh

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain fail2ban-SSH (1 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

デフォルトの設定では、10分間のうちに5回以上、SSHの認証に失敗するとそのクライアントIPからは10分間アクセスできなくなります。

早速やってみましょう。

といってもいきなり自分の端末からこれをやると、いま繋いでいるSSH接続もブロックされ10分間はアクセスできなくなるのでテストには十分に気を付けてください。(※私はコレでハマりました・・・)

IPがブロックされると、/var/log/messages に以下のようなログが出力されます。

Jul 24 11:20:51 localhost fail2ban.actions: WARNING [ssh-iptables] Ban xxx.xxx.xxx.xxx

そして10分後、ブロックが解除されるとこうなります。

Jul 24 11:30:52 localhost fail2ban.actions: WARNING [ssh-iptables] Unban xxx.xxx.xxx.xxx

ブロックは iptables レベルで行われるので、大量の攻撃パケットが送り付けられてしまうような、途方に暮れるDoS状態でもサーバーのパフォーマンスにはあまり影響を与えずにブロックすることができます。

ブロックされている最中に、iptable の状態を見てみると、fail2ban のルールに攻撃元のIPアドレスが登録されて、すべてのパケットがドロップされている状況がわかります。

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
fail2ban-SSH  tcp  --  anywhere             anywhere            tcp dpt:ssh

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain fail2ban-SSH (1 references)
target     prot opt source               destination
DROP       all  --  xxx.xxx.xxx.xxx      anywhere  ※← 全てのパケットをドロップ
RETURN     all  --  anywhere             anywhere
ApacheのBASIC認証でban

先ほどの例は、SSHでしたが今度はApacheで動かしているアプリのBASIC認証で fail2ban をセットアップしてみます。

攻撃を受けるApacheをセットアップ

まず、攻撃を受ける側のApacheをインストールして起動します。

# yum -y install httpd
# service httpd start

つぎに、テストのためにインターネットからこのApacheにアクセスできるように iptables のルールを変更するため、 /etc/sysconfig/iptables に以下の行を追加します。

# Firewall configuration written by system-config-securitylevel
# Manual customization of this file is not recommended.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j DROP
-A RH-Firewall-1-INPUT -i lo -j ACCEPT

## DO NOT EDIT !!!!
-A RH-Firewall-1-INPUT -m tcp -p tcp --sport 67:68 -j ACCEPT
-A RH-Firewall-1-INPUT -m udp -p udp --sport 67:68 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
## DO NOT EDIT !!!!

-A RH-Firewall-1-INPUT -m tcp -p tcp --dport 80 -j ACCEPT   ※←この行を追加

-A RH-Firewall-1-INPUT -m tcp -p tcp --dport 22 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT

iptablesの設定の変更後には、とiptablesサービスの再起動が必要です。

# service iptables restart

ニフティクラウドのコントロールパネルでファイヤウォール機能を有効にしているサーバーの場合、コンパネでのファイヤウォールルールでもルール設定の変更を行う必要があります。

Cp_fwsetup

ブラウザでサーバーのApacheにアクセスできるようになったら、テストのために全てのパスに対してBASIC認証を仕掛けます。

これには、まずBASIC認証のためのID/パスワードを保存するファイルを生成します。

# htpasswd -c /etc/httpd/htpasswd test   ※← testというユーザーを利用します
New password: ※パスワードを入力します
Re-type new password: ※確認のためもう一度パスワードを入力します
Adding password for user test

つぎに、 /etc/httpd/conf.d/httpd.conf の最後に以下の設定を追加します。

    AuthUserFile    /etc/httpd/htpasswd     AuthGroupFile   /dev/null     AuthName        "Enter your ID/password"     AuthType        Basic     Require         valid-user

Apacheを再起動してブラウザからアクセスするとBASIC認証が有効になっているはずです。

ブルートフォース攻撃をシミュレートするために間違ったパスワードを入力してみると、 /var/log/httpd/error_log には以下のようなメッセージが記録されます。

[Sun Jul 24 13:13:25 2011] [error] [client a.b.c.d] user test: authentication failure for "/": Password Mismatch
fail2banの設定

fail2banの設定は、 /etc/fail2ban/jail.conf ファイルで行います。

設定ファイルを開くと、ルールごとにセクションで区切られ、各ルールにはフィルター(filter)とアクション(action)という定義があることがわかります。

フィルターとは、どのログファイルを監視してどういう文字列が記録されたらfail2banを起動するのかといった定義です。

アクションは、fail2banが起動したときにどうやってアクセスをブロックするかの定義です。

フィルターもアクションも自分で定義することもできますが、fail2banには最初から多くのフィルターとアクションが定義されており、それらを組み合わせて簡単に独自のルールを作成することができます。

どのようなフィルターがあるかは、 /etc/fail2ban/filter.d/ を、アクションは /etc/fail2ban/action.d/ を参照してください。

今回は、ApacheのBASIC認証での認証失敗をフィルタリングしてiptablesでソースIPをブロックするようにしてみます。この場合デフォルトで用意されている apache-auth.conf というフィルターと、iptables.conf というアクションを組み合わせます。

/etc/fail2ban/jail.conf に以下の設定を追加してください。

[apache-iptables]

enabled  = true
filter   = apache-auth
action   = iptables[name=Apache, port=80, protocol=tcp]
           sendmail-whois[name=Apache, dest=root, sender=fail2ban@your-domain.jp]
logpath  = /var/log/httpd/error_log
maxretry = 5

この設定は、Apacheのエラーログを監視して filter.d/apache-auth.conf の中に書かれている認証に失敗したときのログを検出する正規表現にマッチしたらそのソースIPをiptablesでブロックするという設定になります。

設定が完了したら fail2ban サービスを再起動して、ブラウザから間違ったパスワードでBASIC認証を試してみるとその動作を確認できると思います。

まとめ

いかがでしたでしょうか。fail2banを使うとアプリレベルでのログを元に、サーバーの iptables を自動的に設定して、クライアントをブロックしたり、時間が経てばブロックを解除したりといったことができることがお分かり頂けたかと思います。

この仕組みを応用すると、SQLインジェクションと思しきパラメータが指定されたURLを検知してブロックしたり、ディレクトリトラバーサル攻撃を試す攻撃者をブロックしたりといろいろアレな設定ができるようになります。

fail2banのログを眺めているとわかりますが、自分で思っている以上に攻撃はやってきます。海の向こうの顔の見えない攻撃者との間でひと時のストリートファイトをお楽しみください。

では。