脆弱性スキャナVulsでAmazon EC2をスキャンし脆弱性深刻度をタグ付けする

# tl;dr

* Vuls -> S3 -> Lambda -> EC2に深刻度をタグ付けしてみたよ
* 他にも強制的にパッチ当てるとかセキュリティグループ変更して隔離するとかConfig Rulesと連携するとか色々できそうだよね

# はじめに

日々発見される脆弱性の根本対策は、ソフトウェアアップデートです。[1日に百万種類のマルウェアが作成されいてる](http://blog.trendmicro.com/malware-1-million-new-threats-emerging-daily/)現在、シグニチャベースのアンチウィルスやIDS, IPSを入れているから大丈夫、とは言い切れません。パッケージマネージャの自動アップデート適用が可能であれば楽なのですが、アップデートが原因でサービス停止するリスクを恐れて手動アップデートで運用するケースが多く見受けられます。手動アップデートでの運用は非常に運用コストがかかります。運用者はJVNやNVDなどで日々情報収集をして、日々発見される脆弱性が自分の管理するどのサーバに該当するのかを判断しなければなりません。たまたま見逃したり、影響調査で漏れがあった場合は、システムに脆弱性を含んだ状態で運用することになります。
このように、手動アップデートでの運用は困難であり、リスクも高いと言えます。

このようなシステム管理者のアップデート作業の苦悩を軽減するツールとして、アップデートに関する情報収集、影響調査を省略化できる[Amazon Inspector](https://aws.amazon.com/jp/inspector/)というサービスがAWSで一般利用可能となりました。マネージドサービスでセットアップも簡単なのでInspectorはAWS上で運用されている方は是非使った方が良いでしょう。Inspectorは脆弱性のスキャンだけではなく、その他たくさんの機能があります。詳細は[ドキュメントを参照](https://aws.amazon.com/jp/documentation/inspector/?nc1=h_ls)してください。


このように便利なInspectorですが、2016年6月現在では以下の領域がカバーされていません。（私の勘違いであればコメントください）

* パッケージ以外の方法でインストールしたソフトウェア
* プログラミング言語のライブラリ、フレームワーク
* Dockerコンテナ

Ruby On RailsやJavaライブラリの脆弱性も教えて欲しいっ！
というわけで、最近噂のイケてる脆弱性スキャナ[Vuls](https://github.com/future-architect/vuls)を使ってInspectorを補完する方法を模索します。具体的には、VulsでAmazon EC2をスキャンし、含まれる脆弱性の深刻度を該当するEC2のタグに設定する方法を試したので紹介します。

## Vulsとは

- https://github.com/future-architect/vuls

Linuxに存在する脆弱性を検知し、Slackやメールなどで**日本語で**通知できるツールです。（FreeBSDにも現在対応準備中）。例えばCRONなどで定期的実行することで、新着の脆弱性を自動で検知して、該当するサーバを通知してくれます。なお、Webの脆弱性ではなくOSパッケージや、プログラミング言語のフレームワーク、ライブラリ、自分でコンパイルしたソフトウェアなどに含まれる脆弱性が対象です。

![hogehoge](https://github.com/future-architect/vuls/blob/master/img/vuls-motivation.png?raw=true)

詳細は　[README](https://github.com/future-architect/vuls)を参照してください。[日本語版README](https://github.com/future-architect/vuls/blob/master/README.ja.md#abstract)もあります。

# やりたいこと

![vuls-s3-lambda.png](https://qiita-image-store.s3.amazonaws.com/0/22462/ec34bac8-a267-4124-f6b5-da030d808653.png)

以下のようなイメージです。

* MacBook上にVulsをセットアップ
* VulsでAWS上のAmazon Linuxをスキャンし、S3にJSONを出力する
* S3のPUTイベントトリガーでLambdaをキックする
* Lambda内で結果のJSONに含まれる、どのinstance-idのEC2が、どの脆弱性を含むのか、の情報を元に該当のEC2にタグをつける

今回はAWSの外からSSH経由でスキャンしますが、実際の運用時はAWS上にVulsをセットアップする方がよいでしょう（初回のNVD, JVNの脆弱性DB取得後であればnanoインスタンスで十分なはず）

# 詳細

## MacBook上にVulsをセットアップ

* [README](https://github.com/future-architect/vuls#hello-vuls)などを参考にしてセットアップ

## スキャン結果格納用のS3バケットとラムダの設定

* S3バケットを準備(今回はap-northeast-1にvulsという名前で作成）
* Lambda関数の作成（roleはS3, EC2関連を割り当てる）

``````python
from __future__ import print_function

import json
import urllib
import boto3

print('Loading function')

# https://boto3.readthedocs.io/en/latest/
s3 = boto3.client('s3')

def lambda_handler(event, context):
    #print("Received event: " + json.dumps(event, indent=2))

    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key']).decode('utf8')
    
    if key.endswith('all.json'):
        return
    
    try:
        response = s3.get_object(Bucket=bucket, Key=key)
        body = response['Body'].read()
        result = json.loads(body.decode('utf-8'))
        if result['Platform']['Name'] != 'aws':
            return
        
        if 0 == len(result['KnownCves']):
            return
        
        highest = result['KnownCves'][0]['CveDetail']['Nvd']['Score']
        iid = result['Platform']['InstanceID']
        
        value = ""
        if 7 <= highest:
            value = 'High'
        elif 4 <= highest:
            value = 'Middle'
        elif 0 <= highest:
            value = 'Low'
            
        # http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_tags
        ec2 = boto3.resource('ec2')
        ec2.create_tags(Resources=[iid], Tags=[{ 'Key': 'vuls', 'Value': value}])

    except Exception as e:
        print(e)
        raise e
```

# Scan実行


スキャン対象サーバの情報を設定します。
cpeNamesのところに、OSパッケージ以外でスキャンしたいものを配列形式で指定します。
[README:Usage: Scan vulnerability of non-OS package](https://github.com/future-architect/vuls/blob/master/README.md#usage-scan-vulnerability-of-non-os-package)

```toml:config.toml
[servers.amazon1]
host     = "52.196.***.***"
port     = "22"
user     = "ec2-user"
keyPath  = "/Users/kotakanbe/.ssh/private.pem"
cpeNames = [
  "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
  "cpe:/a:phusionpassenger:phusion_passenger:5.0.20"
]
```

* 実行
 
```bash
$ vuls scan --config $HOME/Desktop/config.toml \
-report-s3 \
-aws-region=ap-northeast-1 \
-aws-s3-bucket=vuls \
-aws-profile=vuls \
-ignore-unscored-cves \
-cve-dictionary-dbpath=$GOPATH/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3 amazon1
```

* 結果

以下の通り、自動でタグをつけることができました。

![after_EC2_Management_Console.png](https://qiita-image-store.s3.amazonaws.com/0/22462/b0c33e03-d1ad-cadb-d561-6c89b0115221.png)


# 最後に

* Vulsを使ってEC2をスキャンし、S3とLambdaを使って、その結果をEC2のタグにセットすることができました。
* Lambda内でスキャン結果をJSONで取得可能です。今回はタグでしたが、例えば重大な脆弱性が見つかった場合は、EC2のセキュリティグループを変更して他と隔離するといったこともできそうです。
* 今回はVulsをMacBook上で動かしましたが、AWSであれば、初回セットアップ完了後はnanoインスタンスでも動作すると思います。
* nanoであれば月に500円程度なので、安価に運用が可能です。（もしくはOpsWorksなどでスケジュール起動すればさらに節約できそう）
* Slackへの通知も簡単にできるので、試してみてはいかがでしょうか。
* (6/9追記)VulsはSSHで正常に接続してコマンド発行するだけであり、DoSやポートスキャンはしないので、事前にAWSへ脆弱性診断テスト申請は必要ないです
 
![slack](https://raw.githubusercontent.com/future-architect/vuls/master/img/vuls-slack-ja.png)