DoS攻撃/DDoS攻撃からサーバーを守る方法(fail2banのススメ)

dosddoseyecatch

短時間に大量の通信リクエストトラフィックを発生させてサーバーに負荷をかけ、アクセス不能にして被害を与えるDoS/DDoS攻撃。

小規模サーバーは一瞬で落とせるDoS/DDoS攻撃

個人ブログや企業サイトなどの小規模サーバーには関係のない話かと思われますが、攻撃者はそんなことお構いなしに面白半分で攻撃してきます。
当然そういった所はサーバーに高額な費用をかけられないので、ちょっとしたDoS/DDoS攻撃で簡単にダウンしてしまいます。
ここに来られた皆さんもおそらくその被害に遭われたことでしょう。(だからこの記事を読んでるわけですよね)

DoS/DDoS攻撃はなぜ厄介か?

サーバーのログをジッと見ている人はそんなにいないと思いますが、DoS/DDoS攻撃というのは厳密なルールがあるわけではなく、ユーザーエージェント(ブラウザの種類)を見ても普通のユーザーのアクセスに見えます。
それが頻繁に来るイメージです。 だから判別が厄介なのです。

高度な技術で攻撃する方法を知らない人でも、F5キーを押しっぱなしにするだけの「F5アタック」でDoS攻撃が出来てしまいます。

せめて「これだけの時間内に同じIPアドレスからこれだけのアクセスがあったらそのIPアドレスを1日拒否する!」という仕組みを作ることができれば良いと思いませんか?

DoS/DDoS対策にfail2banを導入

DoS/DDoS対策にうってつけのものに fail2ban というサーバー用のツールがあります(pythonで書かれています)。
本来はWebサーバーだけでなく、メールサーバーやSSH、FTPなど、あらゆるサーバーソフトのセキュリティをログファイルをもとに監視するための汎用的なツールなのですが、DoS/DDoS対策にも威力を発揮してくれるので大変便利です。

CentOSであれば、以下のyumコマンドで簡単にインストールできます。(※サーバー管理者権限が必要です)

# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
# yum -y install fail2ban
コマンドのポイント・注意点
このコマンドはCentOS6.xの例です。
また、epelリポジトリをインストール(1行目)するとyumコマンドで常に有効になるので、無効にしたい場合は/etc/yum.repos.d/epel.repo を編集して「enabled=0」にしておくと良いでしょう。

[epel]
-enabled=0
+enabled=1

また、ファイアウォール(iptables)のバージョンが古いとうまく動作しないことがあるようなので、/etc/fail2ban/action.d/iptables-common.conf も編集してlockingoptオプションを無効にしておきます。

# Option:  lockingopt
# Notes.:  Option was introduced to iptables to prevent multiple instances from
#          running concurrently and causing irratic behavior.  -w was introduced
#          in iptables 1.4.20, so might be absent on older systems
#          See https://github.com/fail2ban/fail2ban/issues/1122
# Values:  STRING
-lockingopt = -w
+#lockingopt = -w

# Option:  iptables
# Notes.:  Actual command to be executed, including common to all calls options
# Values:  STRING
-iptables = iptables <lockingopt>
+#iptables = iptables <lockingopt>
+iptables = iptables
ポイント
iptables –helpコマンドを実行して「-w」オプションがあればこの作業は必要ありません。

fail2banのしくみ

では、このfail2banというものがどういう仕組みでDoS/DDoS攻撃をブロックするのかを見てみましょう。

fail2ban-map

このように、Webサーバーのログファイルに対して「Filter(フィルター)」と「Jail(檻)」をセットで定義します。
具体的には、Filterのほうに正規表現を使って「どのようなアクセスを違反とみなすか」を定義し、Jailのほうに「どれくらい短時間で」「何回違反があった」ら「どれくらいの時間、Jail(檻)にぶち込む(=ブロックする)」かを定義します。

自分用設定ファイル「jail.local」を作成

フィルターは後で作成するとして、まずDOS攻撃のあったIPアドレスをどう扱うか(=Jail)を定義しましょう。
fail2banにはデフォルトで色々なJail+Filterがセットで定義されていますが、設定ファイルで全て無効になっています。

自分でJail+Filterを作成したり上記既存のJail+Filterを有効にしたりカスタマイズしたりするために「/etc/fail2ban/jail.local」ファイルを作成します。同じ階層に「jail.conf」がありますが、こちらは既存のJailが定義されており、アップデートなどで上書きされる可能性もあるので触らないようにしておきます。

今回はWebサーバーのデファクトスタンダードであるApacheを想定して設定してみます。
Jail名も「apache-dos」とします。

[DEFAULT]
maxretry = 3
destemail = hoge@colo-ri.jp
findtime = 300
bantime = 3600
apache_access_log = /var/log/httpd/access_log

# 事前によく使う変数を定義
mta = sendmail
protocol = tcp
chain = INPUT
port = 0:65535
banaction = iptables-multiport
action_ = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
            %(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
             %(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]

[apache-dos]
enabled = true
port = http,https
filter = apache-dos
logpath = %(apache_access_log)s
maxretry = 10
findtime = 3
bantime = 86400
action = %(action_mwl)s
destemail = hoge@colo-ri.jp

設定内容の説明です。

filter フィルター名。/etc/fail2ban/filter.d/(フィルター名).conf を参照します。
maxretry findtime秒間に何回記録されたらBANするか。
findtime maxretry回の攻撃が何秒間で記録されたらBANするか。
bantime BANすることになったIPアドレスをどれくらいの秒数ブロックするか。
action BANすることになったIPアドレスに対して取る行動を決めます。

%(action_)s
BANしますがメール通知はしません。BANされたことを知らなくても良い場合はこれ。
%(action_mw)s
BANしたIPをメールで通知し、そのIPをWHOISで調べて詳細な身元を調べメールに転記します。
%(action_mwl)s
%(action_mw)sに加え、そのIPが記録されたログの行を転記します。
destemail actionでメール通知を含めていた場合、IPアドレスがBANされた時にメール送信する宛先。

というわけで、この設定内容をまとめると「/etc/fail2ban/filter.d/apache-dos.conf フィルターファイルのルールに、3秒間で10回一致したら、そのIPアドレスを1日ブロックするよ」となります。

具体的に1IPあたりどれくらいのリクエスト数でDoS/DDoS判定するか?

現実的にどれくらいが攻撃判定のボーダーラインなのかは運用サイトのタイプで変わってくるんですが、一般的な生活者向けのポータルサイトでの場合ですと、だいたい月間100万PVで1ユーザーあたり平均3ページビューくらいでmaxretryを12、findtimeを3くらいにしておけば負荷をかけるリクエストを大幅に削減できるでしょう。

3秒間で12リクエスト。まぁ通常の人間には不可能なアクセスですね。

1秒間で判定したらダメよ

安易に1秒間に5リクエストというような、瞬間的な負荷に対してDoS/DDoS判定してしまうのはオススメしません。
なぜなら瞬間的なアクセスはAjaxやブラウザのアドオン、トラフィックの状態によって正常なユーザーにおいても頻繁に起こり得るリクエストになるからです。

また、GooglebotやYahoo!、Bing等の正常なクローラーにおいても弾いてしまう可能性が高まります。

注意すべき正常なアクセスとしては、大量に開いていたブラウザタブをそのままで終了し、再度開いた際にガーッとリクエストしてしまう場合や、ログインが必要なページで間違ったアカウント情報のまま何回もログインボタンを連打してしまうユーザーがいる場合ですね。

そういった正常アクセスをBANしてしまいそうな場合は以下に説明するようにフィルターで無視できるようにしておくと良いと思います。

DoS/DDoS対策フィルター「apache-dos」の作成

では、次のフィルターの定義です。以下の「/etc/fail2ban/filter.d/apache-dos.conf」を作成します。

[Definition]
failregex = ^<HOST> -.*"(HEAD|GET|POST).*
ignoreregex = \.(?i)(jpe?g|gif|png|bmp|pdf|js|css|woff|eot|ttf|ico|txt|xml|swf|xlsx?|docx?|pptx?)

設定内容の説明です。

failregex Jail判定するためのログ行の正規表現。
ignoreregex failregexの中で除外したい正規表現。
(?i)」の部分は、この正規表現では大文字と小文字を区別しないという修飾子。

この設定内容をまとめると「アセットファイル(画像ファイル、JSファイル、CSSファイルなどの静的ファイル)を除く、全GET,POST,HEADリクエストを監視する」となります。

fail2banを実行

これでDoS/DDoS用のJail+Filterができたので以下のコマンドでfail2banを起動します。

# /etc/init.d/fail2ban start

起動直後はApacheログファイルを読み込みながら分析がはじまり、過去に一致したログがあればそのIPアドレスをBANします。
BANされたIPアドレスの通知は「destemail」に設定されたメールアドレスに通知されます。

fail2banの運用

間違えてIPアドレスをBANしてしまった場合

先にも書いたようにDoS/DDoS攻撃は間違いやすいです。
メールを見て「あっ…正しいIPアドレスをBANしてしまった」という場合は急いで解除する必要があります。

# fail2ban-client set apache-dos unbanip 192.168.11.100

ルールに関係なく特定のIPをBANする

たまたまログを見てると、ルールをかいくぐって不届き者がアタックの真っ最中の場合があります。
そんな時は明示的に以下のコマンドでBANしてください。

# fail2ban-client set apache-dos banip 192.168.11.101

しかし大抵、攻撃者は間もなく異なるIPに切り替えて攻撃をしかけてきます(WordPressに対する攻撃の場合は100〜200IPをBANすることもあります)。
きりがないので攻撃のパターンをみて、すぐにjailルールに適用しましょう。

今、どれくらいのIPがBANされているのかを確認する

BANされているIPアドレスを確認するには、以下のコマンドを入力します。

# fail2ban-client status apache-dos
Status for the jail: apache-dos
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     210
|  `- File list:        /var/log/httpd/access_log
`- Actions
   |- Currently banned: 1
   |- Total banned:     8
   `- Banned IP list:   192.168.11.100

で、これをいちいちコマンドで入れるのが大変!という管理者向けに、PHPで解除できるスクリプトを作ってみたので併せて読んでいただけると良いかなと思います。

運用にはCPUを消費する?fail2ban自体の負荷は?

ここからはパフォーマンスにおける注意点です。

起動時のデータベース化により影響

ネットによく書かれているのが「fail2banのプロセスがCPUを100%消費しているぞ!」という記事ですが、これはfail2banが起動する際にログファイルを読み込んでデータベース化する際にCPUをかなり消費するためです。

ですから、監視対象のログファイルの容量が大きければ大きいほど、CPUを消費する時間が長くなります。
逆に考えると、ログファイルの容量が少なければあっという間にデータベース化が完了し、落ち着きます。

Filterのルールが複雑だと高負荷

これはなんとなく分かると思いますが、Filterのルールを複雑にすると高負荷になりやすいです。
たとえば「/etc/fail2ban/filter.d/apache-dos.conf」で定義した正規表現ですが「ignoreregex」で静的ファイルの判定がやや複雑になっています。
これも負荷をかける原因の一つです。

アクセスが多いサイトの場合、ログファイルのサイズを抑えるためには静的ファイル(画像ファイルやCSS,JSファイルなど)のアクセスログを記録させないようにして「ignoreregex」を空にするのが手っ取り早いです。

例えばApacheの設定ファイルでログファイルの設定の部分を静的ファイルを記録させないようにします。

SetEnvIfNoCase Request_URI "\.(jpe?g|gif|png|bmp|pdf|js|css|woff|eot|ttf|ico|txt|xml|csv|swf|xlsx?|docx?|pptx?|zip)" staticlog
CustomLog logs/access_log combined env=!staticlog

これでApacheのアクセスログには静的ファイルのログが記録されないので、ログファイルの容量も軽くなり、apache-dosフィルターで静的ファイルの除外判定をする必要がなくなりますので「/etc/fail2ban/filter.d/apache-dos.conf」の「ignoreregex」部分をもとに戻します。

ignoreregex =

ただしここで一つ注意点があります。

それは、過去のアクセスログには静的ファイルの記録があるので、すぐに再起動で適用してしまうと本来BANすべきでないIPアドレスもBANしてしまう事態が発生してしまいます。

適用するタイミングとしてはログファイルが新しくなって、access_logファイル内に静的ファイルのアクセスログがまったく無い状態で再起動するようにしましょう。

# /etc/init.d/fail2ban restart

このページをシェアする

1 件のコメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

2016-02-13