各EC2インスタンスに配置したSnortのアラートをELK (ElasticSearch, Logstash, Kibana) で監視する
EC2でネットワークのパケット監視をしたい場合、どうするのがいいんでしょうか。商用だとDeep SecurityとかAlert Logicとかありそうですが、値段もそれなりにします。なんとかOSSでできないかということで、ネットワーク型IDS/IPSであるSnortと、ELK (ElasticSearch, Logstash, Kibana)の組み合わせを試してみました。
構成
Snortを使う場合、監視用のサーバにSnortを入れて、そのサーバにネットワークを流れるパケットを監視させる形がふつうなようです。ただEC2でこの形を実現するのは大変そうなので、今回は各EC2インスタンスにSnortを入れる形にしてみます。各インスタンスは自分のところに来たパケットだけを監視します。
また、これだけだとSnortの出すアラートが各インスタンスに散らばってしまいます。そこで、アラートをlogstashで集めて、Amazon Elasticsearch Serviceに送るようにします。Snortに対応したネットワーク監視WebアプリとしてSnorbyというのもあるようですが、こういうログもKibanaから見たいなーと思ってElasticsearch Serviceにしてみました。
Snortの設定
Snortの設定はまじめにやると面倒そうです。こちらの記事等に手順はまとまってますが、かなり長いです。
今回はお試しということで、こちらのシェルスクリプトを実行すると、aptでのインストール+必要な設定がされるようにしました。OSはUbuntu 14.04です。スクリプト内では、以下の設定をしています。
- 監視範囲のネットワークを絞る(自ホストだけ監視するようにする)
- アラートのフォーマットをlogstashから扱いやすいものにする
- checksumモードの変更。デフォルトのモードだと、Snortルールの
uricontent
という記法がうまく機能しなかったため。 - logstashから読めるように、ファイルとディレクトリのパーミッションを変更。
Amazon Elasticsearch Serviceの設定
AWSのコンソールからぽちぽちやって、Elasticsearch+Kibanaを構築しました。ほぼデフォルトのままですが、Access Policyは、Snortを入れたEC2インスタンスのパブリックIPアドレスと、Kibanaでログを見るマシンのIPアドレスからアクセスできるように設定しました。Elasticsearchのエンドポイントは後で使うのでメモしておいて下さい。
構築できたら、次のようなリクエストを出して、snort-alert
というインデックスを作成します。string
タイプのフィールドの値をindex化するとき、値を勝手に変更しないように設定しています。これをしないと、a-b
という値が、a
とb
に分割されたりします。また、SrcGeo.locationというプロパティの型も指定しています。これは後で位置情報を使ったグラフを作るときに役立ちます。
curl -XPUT [Elasticsearchのエンドポイント]/snort-alert -d '{ "mappings": { "logs": { "dynamic_templates": [ { "notanalyzed": { "match": "*", "match_mapping_type": "string", "mapping": { "type": "string", "index": "not_analyzed" } } } ], "properties": { "SrcGeo": { "properties": { "location": { "type": "geo_point" } } } } } } }'
Logstashの設定
Logstashは、こちらのシェルスクリプトを実行すると、elastic社のレポジトリからlogstashをインストール+必要な設定をするようにしました。スクリプト内では、Snortが/var/log/snort/alert
というファイルに出したアラートを、Elasticsearchに投げる設定をしています。ENDPOINT変数は、Elasticsearchのエンドポイントを入れて下さい。
擬似的な攻撃をしてみる
では、Snortを入れたサーバに攻撃してみて、Kibanaからログを確認できるか試してみます。と言いたいところですが、EC2インスタンスに攻撃する場合は、事前申請が必要です。
申請して許可を待つのも時間がかかるので、今回は擬似的な攻撃と、擬似的な攻撃に反応してアラートを出すSnortルールを作成します。擬似的な攻撃として、pseudo_web_attack
あるいはpseudo_dos_attack
という文字列をHTTPリクエストのURIに含める攻撃がある、とします。この攻撃に反応するSnortルールとして、次のようなルールを作成します。
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"Pseudo WEB-ATTACKS"; flow:to_server,established; uricontent:"pseudo_web_attack"; nocase; classtype:web-application-attack; sid:1000001; rev:1;) alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"Pseudo DoS attack"; flow:to_server,established; uricontent:"pseudo_dos_attack"; nocase; classtype:attempted-dos; sid:1000002; rev:1;)
パッと見はややこしそうですが、実際は、HTTPリクエストのURIに特定の文字列が含まれていないか見ているだけです。このルールは、Snortを入れたサーバの/etc/snort/rules/local.rules
に追記して下さい。追記したらSnortを再起動します。
準備ができたら、http://[Snortを入れたサーバのIPアドレス]/pseudo_dos_attack
、あるいはhttp://[Snortを入れたサーバのIPアドレス]/pseudo_web_attack
にアクセスします。
成功していれば、KibanaのDiscover画面から、Snortのアラートログが確認できると思います。例えば次のような画面です。
攻撃の様子をグラフで表示する
せっかくKibanaにログを集めたので、色々なグラフを出してみます。まずは直近の攻撃回数の折れ線グラフです。攻撃の種類ごとに線の色を分けてみました。
Web Application Attack(緑色の線)とAttempted Denial of Service(濃い青色の線)は、擬似的な攻撃によるものです。残りは何もしていないのに出ているアラートです。False Positiveなアラートだと思うので、Snortルールのチューニングが必要そうです。
次も直近の攻撃回数の折れ線グラフですが、ホストごとに線の色を分けています。よく攻撃されているホストがわかります。
最後は攻撃元の位置情報を世界地図上にマップした図です。円の色の濃さで攻撃回数の大小がわかります。
まとめて、ダッシュボード化してみました。
まとめ
Snort+ELKでネットワークのパケット監視をやってみました。今回はSnortの設定をかなり省略しましたが、実際はルールの更新とかルールのチューニングが必要そうです。また、Snortを各サーバに入れる形だと、Snortがサーバ本来の処理を阻害しないか不安です。そもそも実際の環境だと、全サーバにSnortを入れるより、入り口付近のサーバにだけ入れれば十分な気がします。このあたりはまた調べたいなーと思います。