Hina-Mode

とある呑んだくれエンジニアの気が向いた時に書く戯言

AWSのEC2+ELBで、ロードバランサへのアクセス時点で特定IPからのアクセスを弾く

つい先日、どこぞのサーバからEC2のサーバに対してDosアタックを受けました。
別にある程度の攻撃を受けること自体は想定してサーバを公開していましたし、
Apache側ではmod_dosdetectorというDos攻撃をある程度受けた場合、検知出来る仕組みを用意していたので特に問題ない、、、と思っていました。

stanaka/mod_dosdetector · GitHub

が、しかし。
Apache側で検知するということは、WEBサーバのリソースを結構食っちゃうんですよね…。

実際、普段5%以内で収まっていたCPU Usageが、攻撃を食らっている間50%オーバーをずっとマークしていました。

f:id:hinashiki:20150219153950p:plain

真ん中の跳ねてるグラフが攻撃受けてた期間ですね。

さすがに運営に支障が出るレベルだったので、出来ればWEBサーバの前に立っているELBにアクセス来た時点で、対象の攻撃者IPを弾いて欲しかったんですが
AWS側で用意されているSecurity Groupsはホワイトリスト形式での指定は可能なのですが、ブラックリスト形式での指定が出来ないため途方に暮れていました。

Google先生に「aws ロードバランサ ip拒否」とか聞いてみたんですが、
求めていた一発回答は存在せずに結構悩みました。
が、上記で出てきたDevelopers.io様の記事の中に

[Apache] 非VPCのELBでX-Forwarded-Forを利用したアクセス制限 | Developers.IO

VPC環境で、ELB配下のWebサーバがインターネットからのDoS攻撃にさらされた場合に、特定のIPアドレスからのアクセスを拒否(403)します。
VPC環境であればNetworkACLやSecurityGroupで制限をかけることができますが、非VPCではELBへのアクセスを制限することができません。この場合httpdレベルで制限する必要があります。

という内容が書かれていたのを見つけました。

…ん?NetworkACL、かけれるの?

という今更知りました感満載の内容を見つけ、今度は「AWS ACL」で検索。

http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_ACLs.html

ありました。

今まで全く気にしてもいなかったんですが、かけれるんですね。

ということでまずはELBのVPC-IDを確認。

f:id:hinashiki:20150219155629p:plain

みつけました。これを今度はVPC画面で確認します。

f:id:hinashiki:20150219155836p:plain

で、左のナビゲーションからNetwork ACLsを選択。

f:id:hinashiki:20150219160311p:plain

恥ずかしながら今まで、
VPC-IDって何やねんそれ、おまじないか?」
ぐらいにしか思ってなかったのでVPC-IDは1つしか無いです。

Editを押して編集に入ります。

f:id:hinashiki:20150219160605p:plain

ルール番号は100がデフォルトでAll RangeのAllowを行っているみたいです。
番号が若いほど優先処理をされるようなので、今回はとりあえず直前の99で該当IPの拒否処理をしようと思います。
※画像にIPおもいっきり乗っかってますが、攻撃者のIPなんて晒されても何も文句ないと思うので放置です。海外みたいですし。

最後にSaveを押せば、特にELBの再起動とか必要なく、リアルタイムでACLがかかりました。


とりあえず一時しのぎで手動で対応しましたが、最終的にはDosアタック検知したら自動でACLで弾いてしまうようにしたいですね。。。

というかこれ以外でスマートなやり方をご存知な方がいらっしゃったら是非ご教授願いたいです。

FuelPHPでメンテナンスモードパッケージを作成してみた

hinashiki/fuelphp-maintenance · GitHub

とある複数サイトで独自の503ルールが必要になったため、せっかくだからとパッケージにしてみました。

fuel/app/config/maintenance.php

<?php
return array(
  "maintenance_mode" => true
);

を作成し、Contoller::before()へ

\MaintenanceMode::check();

を入れれば、アプリケーション全体がメンテナンスモードになります。
メンテナンスモード中はステータス503を常に返し、返されるビューは固定されます。

capistranoなどのdeployで利用したい場合はfalseのファイルも持っておいてファイルを入れ替えれば素敵なメンテナンス切り替えが可能になります。

一応パッケージ内にデフォルトのビューを用意していますが、自分で作成したい場合は
fuel/app/views/503.php
を作成していただければ大丈夫です。

また、503.php以外をビューに利用したい場合は

fuel/app/config/maintenance.php

<?php
return array(
  "maintenance_mode" => true,
  "view" => "hogehoge"
);

という感じでhogehogeにビューパスを指定していただければ変更できるようになっています。

また、メンテナンスモードに移行するのにいちいちdeployカマしたくないというずぼらな方の為に、 503を強制throwできるようにしています。

throw new \HttpServerMaintenanceException();

を投げれば、メンテナンスモードの画面を返します。

外部DBやAPIへの接続に失敗した場合にthrowするようにすれば、該当APIなどを停止するだけでサイト側も自動でメンテナンスモードへ移行し、HTTPステータス503を返すことが出来ます。

メンテナンスモードを実装するには他にも

FuelPHP でシンプルメンテナンスモード - Qiita

上記のような方法があるのですが、

コントローラーまで処理が渡らないので、コントローラーが必要な処理は出来ない

ということでしたので、まぁ使い分けが出来ればいいかなー位にサクッと作りました。

どこかで役に立てば幸いです。

AWSにBounceHammerをインストールする

ググったけど有用な記事が見つからなかったので自分で頑張っていれてみた。
結果ちょこっとばかしハマってしまったので記載。
メールサーバをAWSで自作していて、バウンスメールの解析をしたくなったので、ついやってみた。

ちなみにBounceHammerとはオープンソースのバウンスメール集積ソフトです。

bounceHammer | an open source software for handling email bounces

AMIはAmazon Linux, 2014/7月頃に立ち上げた時点で最新でしたがyum updateしてなかったんで色んなモノが古いので厳密にこうだ、という保証はないです。念のため。

手順書

$ su
$ yum install -y perl-core gcc
$ cd /usr/local/bin
$ wget -O- 'http://cpanmin.us/' > ./cpanm
$ chmod +x ./cpanm
$ cd /usr/local/src
$ wget http://dist.bouncehammer.jp/bouncehammer-2.7.12.tar.gz
$ tar xfz bouncehammer-2.7.12.tar.gz
$ export PERL5LIB=/usr/local/bouncehammer/lib/perl5
$ mkdir -p /usr/local/bouncehammer
$ cd bouncehammer-2.7.12
$ perl ./Modules.PL missing | cut -d' ' -f4 | cpanm -l /usr/local/bouncehammer
# テストエラーで入らなかったモジュールを強制追加
$ cpanm -l /usr/local/bouncehammer/ --force DBIx::Skinny
# WARNになっているモジュールのupdate
$ cpanm -l /usr/local/bouncehammer Getopt::Long
$ ./configure --disable-webui --with-perl-extlib=/usr/local/bouncehammer/lib/perl5
$ make
$ make test
$ make install
$ cp /usr/local/bouncehammer/etc/bouncehammer.cf-example /usr/local/bouncehammer/etc/bouncehammer.cf

無駄な事してる部分もあるかもしれませんが、一応これでインストール出来ました。
事前にperl-coreもそうなんですが、gccが入ってないとperl系のライブラリをcpanmでインストールするときにコンパイルできないでインストール中断していまイます。
※そもそもgccが入ってないことに全く気づかなかった自分がアホといえばアホなのですが。。。

あと、BounceHammer公式ではcpanmに渡すのは-Lオプションにしていますが、一応-lオプションに切り替えています。

cpanm - CPANからモジュールを取得、アンパック、インストールする - perldoc.jp

上記でオプションの意味を調べたのですが、-Lだとperl-coreを先にyum installしても、
「コアでない依存が、それらがインストール済みであっても、 インストールされ」るみたいです。
ExtUtilsなんかが該当して、最初何回cpanmにモジュールリストをわたしても依存関係を解決できずにインストールをスキップしていました。


何かの参考になれば幸いです。

AWSのec2インスタンス上にcapistranoとaws-sdk(ruby)をインストールしようとしたらnokogiriに阻まれた話

capistranoのinstallは非常に簡単だったのですが、
aws-sdkを突っ込むときにちょっと引っかかったので備忘録としてエントリーします。

  • 使用するec2インスタンスAmazon Linux AMI 2014.03.2 (HVM)
    ※エントリー記載現在の最新版です
  • VMは立ち上げた初期の状態で特に何もインストールしていません。
  • capistranoで自分が立ち上げたec2インスタンスをデプロイ先として自動認識出来るようにするため、aws-sdkを必要としていました。

先に実行コマンドのまとめ

$ su
$ yum install -y gcc libxml2-devel libxslt-devel ruby-devel
$ gem install capistrano
$ gem install capistrano_colors
$ gem install capistrano-ext
$ gem install railsless-deploy
$ gem install nokogiri -- --use-system-libraries
$ gem install aws-sdk

※railsless-deployは、私がPHPerの為rails使わないので突っ込んでます。

補足

yum-installでgcc, libxml...などをインストールする理由

まずはaws-sdkだけインストールしようとした結果を見てみましょう。

$ gem install aws-sdk
Fetching: mini_portile-0.6.0.gem (100%)
Successfully installed mini_portile-0.6.0
Fetching: nokogiri-1.6.2.1.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing aws-sdk:
        ERROR: Failed to build gem native extension.
    /usr/bin/ruby2.0 extconf.rb
mkmf.rb can't find header files for ruby at /usr/share/ruby/include/ruby.h
 
Gem files will remain installed in /usr/local/share/ruby/gems/2.0/gems/nokogiri-1.6.2.1 for inspection.
Results logged to /usr/local/share/ruby/gems/2.0/gems/nokogiri-1.6.2.1/ext/nokogiri/gem_make.out

ダメでした。
aws-sdkはnokogiriの依存性があるみたいですね。
で、色々調べた結果、最低でもnokogiriをインストールするために

  • libxml2-devel
  • libxslt-devel
  • ruby-devel
  • gcc

の4種が必要なことがわかりました
gccは--use-system-librariesでインストールする際に必要なんでしょうかね。
自分で必要とか言っておきながら、詳しくは分かりませんでした。すいません。

nokogiriを単体でインストールする理由

さて、この状態でもう一度aws-sdkインストールしようとしてみます。

$ gem install aws-sdk
/usr/bin/ruby2.0 extconf.rb
Building nokogiri using packaged libraries.
checking for iconv.h... yes
checking for iconv_open() in iconv.h... yes
Building libxml2-2.8.0 for nokogiri with the following patches applied:
        - 0001-Fix-parser-local-buffers-size-problems.patch
        - 0002-Fix-entities-local-buffers-size-problems.patch
        - 0003-Fix-an-error-in-previous-commit.patch
        - 0004-Fix-potential-out-of-bound-access.patch
        - 0005-Detect-excessive-entities-expansion-upon-replacement.patch
        - 0006-Do-not-fetch-external-parsed-entities.patch
        - 0007-Enforce-XML_PARSER_EOF-state-handling-through-the-pa.patch
        - 0008-Improve-handling-of-xmlStopParser.patch
        - 0009-Fix-a-couple-of-return-without-value.patch
        - 0010-Keep-non-significant-blanks-node-in-HTML-parser.patch
        - 0011-Do-not-fetch-external-parameter-entities.patch
************************************************************************
IMPORTANT!  Nokogiri builds and uses a packaged version of libxml2.
If this is a concern for you and you want to use the system library
instead, abort this installation process and reinstall nokogiri as
follows:
    gem install nokogiri -- --use-system-libraries
If you are using Bundler, tell it to use the option:
    bundle config build.nokogiri --use-system-libraries
    bundle install
However, note that nokogiri does not necessarily support all versions
of libxml2.
For example, libxml2-2.9.0 and higher are currently known to be broken
and thus unsupported by nokogiri, due to compatibility problems and
XPath optimization bugs.
************************************************************************
Extracting libxml2-2.8.0.tar.gz into tmp/x86_64-redhat-linux-gnu/ports/libxml2/2.8.0... OK
Running patch with /usr/local/share/ruby/gems/2.0/gems/nokogiri-1.6.2.1/ports/patches/libxml2/0001-Fix-parser-local-buffers-size-problems.patch...
Running 'patch' for libxml2 2.8.0... ERROR, review 'tmp/x86_64-redhat-linux-gnu/ports/libxml2/2.8.0/patch.log' to see what happened.
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.
Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib64
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/bin/ruby2.0
        --help
        --clean
        --use-system-libraries
        --enable-static
        --disable-static
        --with-zlib-dir
        --without-zlib-dir
        --with-zlib-include
        --without-zlib-include=${zlib-dir}/include
        --with-zlib-lib
        --without-zlib-lib=${zlib-dir}/lib
        --enable-cross-build
        --disable-cross-build
/usr/local/share/ruby/gems/2.0/gems/mini_portile-0.6.0/lib/mini_portile.rb:279:in `block in execute': Failed to complete patch task (RuntimeError)
        from /usr/local/share/ruby/gems/2.0/gems/mini_portile-0.6.0/lib/mini_portile.rb:271:in `chdir'
        from /usr/local/share/ruby/gems/2.0/gems/mini_portile-0.6.0/lib/mini_portile.rb:271:in `execute'
        from extconf.rb:282:in `block in patch'
        from extconf.rb:279:in `each'
        from extconf.rb:279:in `patch'
        from /usr/local/share/ruby/gems/2.0/gems/mini_portile-0.6.0/lib/mini_portile.rb:108:in `cook'
        from extconf.rb:253:in `block in process_recipe'
        from extconf.rb:154:in `tap'
        from extconf.rb:154:in `process_recipe'
        from extconf.rb:419:in `<main>'
 
Gem files will remain installed in /usr/local/share/ruby/gems/2.0/gems/nokogiri-1.6.2.1 for inspection.
Results logged to /usr/local/share/ruby/gems/2.0/gems/nokogiri-1.6.2.1/ext/nokogiri/gem_make.out

aws-sdkが自動でインストールしようとするnokogiriのオプションだとエラーが起きるようだったので、nokogiriだけ個別にインストールします。

$ gem install nokogiri -- --use-system-libraries
Building native extensions with: '--use-system-libraries'
This could take a while...
Building nokogiri using system libraries.
Successfully installed nokogiri-1.6.2.1
Parsing documentation for nokogiri-1.6.2.1
Installing ri documentation for nokogiri-1.6.2.1
Done installing documentation for nokogiri after 4 seconds
1 gem installed

無事、インストール出来たので、改めてaws-sdkをインストール。

$ gem install aws-sdk
Fetching: aws-sdk-1.46.0.gem (100%)
Successfully installed aws-sdk-1.46.0
Parsing documentation for aws-sdk-1.46.0
Installing ri documentation for aws-sdk-1.46.0
Done installing documentation for aws-sdk after 16 seconds
1 gem installed

これでaws-sdkcapistrano環境で利用できるようになりました。めでたしめでたし。



因みに冒頭で記述した

capistranoで自分が立ち上げたec2インスタンスをデプロイ先として自動認識出来るようにする

については、
AWS EC2 capistranoでオートスケーリングインスタンスにデプロイ - cap version2 - Qiita
こちらのエントリーを参考にしました。
ということで、何かの参考になれば幸いです。
ではでは。

FuelPHP用のGoogleMapAPIアクセスパッケージを作成しました

https://github.com/hinashiki/fuelphp-map

こういうライブラリ的なリポジトリ作るのは初めてなのでお作法的に結構間違っている部分も多々あるかと思いますが、許して下さい。
MITライセンス(FuelPHP準拠)なので適当に使ってやってください。