AWS LambdaでVulsを使おうと試みた話

投稿者: | 2016/12/18

本記事はVuls Advent Calendar 2016 18日目の投稿記事です。

概要

本記事は、脆弱性検知ツールVulsをサーバレスで実行しようと試みた内容です。
結果としては、AWS Lambda(以下、Lambda)で実行するには様々な制約があって難しいことがわかりましたが、他の方がさらに発展させてくれると信じてここに公開します。

構成

以下のような構成を考えていました。

  • Amazon CloudWatch Events ・・・ スケジューラ(いつ実行するかを決める)
  • Lambda ・・・ 脆弱性情報取得 & スキャン実行
  • S3 ・・・ 脆弱性情報(SQLite)やスキャン結果を保存

VulsはGo言語で書かれていますが、LambdaはNode.js、Java、C# および Pythonでしか動かないため、Vulsをビルドしてバイナリ化することで、Lambda上で実行しようと考えました。

Lambdaで実行するうえでの制約条件

ここで、Lambdaを使ううえでの制約を見ていきましょう。

AWS Lambda の制限

ドキュメントを読むと、問題となるのが一時的に保存できるディスク容量と最大実行時間であることがわかります。

  • 一時ディスク容量 (“/tmp” スペース) ・・・ 512MB
  • 最大実行時間 ・・・ 300秒

一回の実行につき、300秒(=5分間)しか実行できないので、長時間にわたるスキャン実行はできません。また、脆弱性データベースファイル(SQLite)は500MBを超えると取得ができないため、それを超えない範囲で取得しなければなりません。

前準備

さて、ここから設定に入りますが、いくつか準備が必要です。

  • 秘密鍵・公開鍵の作成
  • スキャン用ユーザの作成(vulsユーザ)
  • VPC、サブネット、セキュリティグループの設定
    • Lambdaから各サーバにSSHできるように設定しておく

秘密鍵の作成

パスフレーズなしの秘密鍵を生成します。

スキャン用ユーザの作成(vulsユーザ)

スキャン対象サーバにスキャン専用のユーザを作成します。

CentOS

Ubuntu

Amazon Linux

IAMロールの作成

Lambda Functionに付与するIAMロールを作成します。
権限は以下のとおり。lambda_vuls_executionという名前で作成します。
VPC内で実行するためENI関係の権限が必要なので付与しています。
また、後述しますが、AWS Key Management Service (KMS)で秘密鍵を復号するのでその権限も必要です。

S3バケットの作成

脆弱性情報ファイルやスキャン結果を格納するS3バケットを作成します。
各々セキュリティ要件や環境に応じてアクセス制限をかけてください。

秘密鍵をKMSに登録

秘密鍵をLambda Function内に仕込むのは危険なので、KMSで暗号化して、実行時のみ復号するようにします。
KMSへの暗号化手順は以下の記事を参考にしました。
KMSで認証情報を暗号化しLambda実行時に復号化する | Developers.IO

CiphertextBlobをメモっておいてください。

Lambdaの実行環境構築

Vulsをバイナリ化したり、Lambda Functionのパッケージ化をするサーバを作ります。
Lambda 実行環境と利用できるライブラリにAMI名(amzn-ami-hvm-2016.03.3.x86_64-gp2)が記載されているのでそれを使います。インスタンスタイプはt2.microで十分です。

lambda-uploaderのインストール

Lambdaで実行するデプロイパッケージを作成するために今回はlambda-uploaderを使いました。シンプルに使えるのがとても良いです。

Go言語のインストール

今日時点(2016/12/18)で最新版の 1.7.4 をインストールします。

go-cve-dictionaryのインストール、バイナリ化

Deploy go-cve-dictionaryを見ながらインストールします。

ビルド

Vulsのインストール、バイナリ化

v0.1.7をダウンロードし、ビルドします。

設定ファイル

NVD、JVDから脆弱性情報を取得する

500MBを超えない範囲で取得します。

Zipに圧縮してS3にアップロードします。

デプロイパッケージの作成

ファイル構成

lambda.json(設定ファイル)

メモリが足りないとプロセスがKillされるため、設定値MAX(1,536MB)にしました。

vuls.py(実行ファイル)

S3_BUCKETは作成したS3バケット名を、ENCRYPTED_KEYはメモったCiphertextBlobの値に置き換えてください。
レポート結果をSlackに投げるようにしました。

アップロード

課題

LambdaでVulsを使って見ましたが、幾つか課題が見つかりました。

ログ

  • /var/log/vulsはLambdaの特性上、作成できません。ディレクトリが作成できないことで落ちることはありませんが、実行に失敗した場合はエラーログではなく、CloudWatch Logsから見ることになります

実行時間

  • Lambdaは5分間しか実行できません。今回は1台のみのスキャンだったので、実行時間が短かかったのですが、十数台〜数百台になると到底5分で終わらないと思います
  • 最近、リリースされたAWS Step Functionsを使うことで、go-cve-dictionaryの実行やスキャンの連続実行などを工夫してできるようになるのではないかと期待しています

ファイルサイズ

  • 制約条件でも挙げていましたが、生成できるファイルサイズが500MBまでなので、脆弱性情報(SQLite)やスキャン結果を保存する際は容量に注意しなければなりません
  • SQLiteではなく、MySQLに置き換える手もあります。RDS(MySQL)にすれば、運用する手間が大分省けますが、かなり安く使ったとしても $20/月 程度かかってしまうので予算との相談になります(t2.micro, マグネティック5GBで使った場合)。

セキュリティ的懸念

  • 秘密鍵をKMSで暗号化・復号しているのですが、これってセキュリティ的にどうなんですかね・・・?生ファイルをパッケージに入れておくよりかは安全だと思いますが、他の方ってどうしているのでしょうか
  • また、標準化したディレクトリ構成を無視しているので、本記事を試す方は自己責任でお願いします(投げやり)

スキャンに失敗する

  • vuls scanコマンドを実行する際、ホスト名を指定しないとFailed to read stdin: read /dev/stdin: resource temporarily unavailableというエラーが出て、スキャンが失敗します。

ソースコードを見ると、以下の箇所が該当します。

https://github.com/future-architect/vuls/blob/master/commands/scan.go#L297

本来であれば、パイプ(|)を使ってホスト名を渡さないと通らないはずなのですが、ここで落ちてしまいます。
きちんと調べるべきだと思いますが、今回は時間が無かったため、ホスト名を指定することでこの問題を回避しました。

ちなみに、誰かが調べてくれるかもしれないので、FileModeをデバッグした結果を貼っておきます。

最後に

LambdaでVulsを実行してスキャンできましたが、いくつか課題があったので、当面はサーバレスの夢を追いかけながら、実行サーバ(EC2)で運用することにします。

AWS LambdaでVulsを使おうと試みた話」への1件のフィードバック

  1. kotakanbe

    Vuls作者の神戸です。

    興味深いエントリありがとうございます。
    以下、最近解決した点がありますので、コメントしておきます!

    #Vuls祭り#2が3/24にあるのですが、ぜひ発表していただくことは可能でしょうか。
    # ミナサン興味津津です!

    > ログ
    ログ出力ディレクトリを指定できるようになりました
    https://github.com/future-architect/vuls/pull/301

    > 実行時間
    複数台でもパラレルでスキャンするので台数が増えても実行時間はリニアに増大しません(元から)
    スキャンしたサーバ中で実行時間が一番長いものが、スキャン実行時間となります。

    > ファイルサイズ
    CVE辞書のデータベースは、RDSがよさそうです

    > スキャンに失敗する
    直しましたー
    https://github.com/future-architect/vuls/pull/299

    返信

コメントを残す

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

*