概要
新規サービスの開発に較べて、どうしても後回しになりがちな脆弱性のテストについて、ついに、ちゃんとやらねばならぬ時が来てしまいました。
AWS Inspector を使えば、手軽に、かつ、網羅的に診断を行うことが可能になっています。
AWS Inspector ユーザガイド
毎回、手動で実行していては忘れてしまうことがあるため、以下のように、Jenkins で定期的に自動実行し、かつ、結果を Slack に通知してくれる仕組みを構築しました。
構築手順について、忘れないうちに Qiita化 しておきます。
設定
準備
まず、EC2に Agent をインストールして実行しておく必要があります。
Amazon Linux (2015.03 以降)
Ubuntu (14.04 LTS)
Red Hat Enterprise Linux (7.2)
CentOS (7.2)
Windows Server 2008 R2 および Windows Server 2012
# wget https://d1wk0tztpsntt1.cloudfront.net/linux/latest/install
# bash install
# /etc/init.d/awsagent start
Inspector
AWS Inspector では、以下の4種類のルールパッケージから【複数】選択することができます。
名前 | 内容 |
---|---|
一般的な脆弱性と曝露 | 評価ターゲット内の EC2 インスタンス が一般的な脆弱性や曝露 (CVE) に曝露されているかどうかを確認 例えば OpenSSLの脆弱性「ハートブリード」 CVE-2014-0160 が含まれます |
CIS オペレーティングシステムのセキュリティ設定ベンチマーク | 組織がセキュリティを評価して強化できるよう明確に定義された、公平でコンセンサスベースの業界のベストプラクティスを提供 |
セキュリティのベストプラクティス | システムが安全に設定されているかどうかの判断 |
実行時の動作の分析 | インスタンスの動作を分析し、EC2 インスタンスのセキュリティを高めるためのガイダンスを提供 |
ルールパッケージ、対象となるEC2のタグ、評価時間 を選択して、一つの「評価テンプレート」を作成します。
イベントをSNSに発火するように設定しておきます。
注意
- 評価テンプレートは、作成後は編集はできません。
(SNSトピックだけは、後から追加変更ができるようです) - 対象となるEC2インスタンスは、タグによって、評価テンプレート作成後でも増減できます。
- 評価中のEC2インスタンスは、同時に他の評価をすることはできません。
SNS
以下のイベントがSNSに向けて発火されてきます。
No | event | newstate |
---|---|---|
1 | ASSESSMENT_RUN_STATE_CHANGED | CREATED |
2 | ASSESSMENT_RUN_STATE_CHANGED | START_DATA_COLLECTION_IN_PROGRESS |
3 | ASSESSMENT_RUN_STATE_CHANGED | START_DATA_COLLECTION_PENDING |
4 | ASSESSMENT_RUN_STATE_CHANGED | COLLECTING_DATA |
5 | ASSESSMENT_RUN_STARTED | |
6 | ASSESSMENT_RUN_STATE_CHANGED | STOP_DATA_COLLECTION_PENDING |
7 | ASSESSMENT_RUN_STATE_CHANGED | DATA_COLLECTED |
8 | ASSESSMENT_RUN_STATE_CHANGED | EVALUATING_RULES |
9 | FINDING_REPORTED | |
10 | ASSESSMENT_RUN_COMPLETED | |
11 | ASSESSMENT_RUN_STATE_CHANGED | COMPLETED |
以下に対応しているようです。
イベント | event |
---|---|
実行開始 | ASSESSMENT_RUN_STARTED |
実行完了 | ASSESSMENT_RUN_COMPLETED |
実行ステータスが変更されました | ASSESSMENT_RUN_STATE_CHANGED |
報告された結果 | FINDING_REPORTED |
Slack通知で ASSESSMENT_RUN_STATE_CHANGED は必要ないですね。開始/完了/結果 を通知するようにします。
Lambda
Slackに通知する、以下の Lambda 関数を作成しました。
Node.js 4.3 を選択しています。
AWS API で Promise が使えますね。
外部 npm モジュールを使わないので、インラインで Lambda を構築できます。
// https://nodejs.org/docs/v4.3.2/api/
// Set your slack url here.
const slack = "/services/T0DD9AQER/B0DDBKWSX/xxxxxxxxxxxxxxxxxxxxxxxx";
const AWS = require("aws-sdk");
AWS.config.apiVersions = {
inspector: "2016-02-16"
};
const inspector = new AWS.Inspector();
const https = require('https');
//
// Slack message format.
//
function build(template, target, finding, message) {
var events = {
"ASSESSMENT_RUN_STARTED": "実行開始",
"ASSESSMENT_RUN_COMPLETED": "実行完了",
"ASSESSMENT_RUN_STATE_CHANGED": "実行ステータス変更",
"FINDING_REPORTED": "結果報告"
};
var buff = [
[
target.name,
template.name,
events[message.event]
].join(":")
];
switch (message.event) {
case "FINDING_REPORTED":
buff.push("```");
buff.push("[" + finding.severity + "] " + finding.id);
buff.push(finding.title);
buff.push("```");
break;
case "ASSESSMENT_RUN_STARTED":
case "ASSESSMENT_RUN_COMPLETED":
break;
case "ASSESSMENT_RUN_STATE_CHANGED":
buff.push(message.newstate);
break;
default:
buff = null; // 通知しない
break;
}
return buff && buff.join("\n");
}
//
// Lambda entry point
//
exports.handler = function (event, context, callback) {
console.log("inspector lambda start : " + new Date());
var messages = event.Records.reduce(function (memo, v) {
if (v.Sns && v.Sns.Message) {
memo.push(JSON.parse(v.Sns.Message));
}
return memo;
}, []);
Promise.all([
inspector.describeAssessmentTemplates({
assessmentTemplateArns: pluck(messages, "template")
}).promise(),
inspector.describeAssessmentTargets({
assessmentTargetArns: pluck(messages, "target")
}).promise(),
inspector.describeFindings({
findingArns: pluck(messages, "finding", "")
}).promise()
]).then(function (result) {
var templates = result[0].assessmentTemplates;
var targets = result[1].assessmentTargets;
var findings = result[2].findings;
return Promise.all(messages.map(function (message, idx) {
var text = build(templates[idx], targets[idx], findings[idx], message);
console.log(text);
return text && request({text: text});
}));
}).then(function () {
callback();
}).catch(function (err) {
console.log(err);
callback(err);
});
};
//
// Slack
//
function request(data) {
return new Promise(function (resolve, reject) {
var body = JSON.stringify(data);
var req = https.request({
hostname: "hooks.slack.com",
port: 443,
path: slack,
method: "POST",
headers: {
'Content-Type': 'application/json; charser=UTF-8',
"Content-Length": Buffer.byteLength(body)
}
}, function (res) {
res.statusCode === 200 ? resolve(res) : reject(res);
});
req.end(body);
});
}
// utility
function pluck(array, propertyName, _default) {
return array.map(function (v) {
return v[propertyName] !== undefined ? v[propertyName] : _default;
});
}
SNS でトリガーされるように設定します。
Slack
何か問題があれば「報告された結果」として通知されます。
詳細(対処方法)は Inspector の「結果」画面で確認してください。
上記の例では、次のような指摘をされています。
- node.js が古い
- ポート 80番 で HTTP が動作している
- root で ssh ログイン可能な設定になっている
たしかに。ごもっとも。
Jenkins
手持ちの Jenkins で定期的に実行するようにしておくと、安心ですね。
Jenkins では 「シェルの実行」を選択し、
Inspector 操作権限のあるアカウントのIDとパスワードを環境変数に設定してから、「評価テンプレート」を指定して inspector start-assessment-run を実行します。
シェルの例
# IAM : InspectorJenkins
export AWS_ACCESS_KEY_ID=AKIAIXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export AWS_DEFAULT_REGION=ap-northeast-1
# テンプレート
TEMPLATE="arn:aws:inspector:ap-northeast-1:663889673734:target/0-FcroYIWk/template/0-0h8lMLYY"
/opt/pyenv/shims/aws inspector start-assessment-run --assessment-template-arn ${TEMPLATE}
おわりに
何台かのEC2インスタンスで本番サービスを運用していますが、いままで後手後手になっていた脆弱性診断が自動化されて、ほっと一安心しています。
さらに、ペネトレーション的な診断をしてくれるサービスが出てくることを、強く期待しています!!