概要
AWSコンソール、AWS CLIでの作業はオペミスがつきものです。
IAMで実行権限を制限する、手作業を減らすなどの対策も大切ですが、不正な変更をいち早く検知する仕組みがあるとより安心です。
今回は、次のような仕組みを実現してみたいと思います。
IAMユーザーのAWSコンソール・CLIでの操作をSlackに通知する
次の記事を参考にさせてもらいました。
セキュリティーグループが変更されたらSlackに通知する -ハンズラボエンジニアブログ
CloudTrailの設定
有効化
まずは、CloudTrailを有効化します。
AWSコンソールにログインし、CloudTrailのメニューを開きます。
まだCloudTrailの設定を行っていな環境では、以下のような画面が表示されるので、「今すぐ始める」を選択します。
CloudTailの有効化では、任意の「追跡名」を設定します。
※今回はすべてのリージョンを対象に適用します
保存先の設定
CloudTrailで取得した追跡情報はログファイルとしてS3に保存されます。次のように保存先バケットを設定を行います。
ストレージの場所で、新しいS3バケットを作成しますかを「はい」にし「S3バケット」の名称を設定します。
※今回はプレフィックスの設定は省略し、その他の項目はデフォルトのまま進めます
作成
最後に「作成」を選択してCloudTrailの作成は完了です。
CloudWatch Logsの設定
前記までで作成した追跡情報をCloudWatch Logsへ配信するように設定します。
設定
CloudTrailのメニューを開き、先ほど作成した追跡情報を選択します。
CloudWatch Logsの項目にある「設定」を選択します。
IAMロールの設定を求められるので、適切なロール選択するか新規で作成します。
「許可」を選択してCloudWatch Logsの設定は完了です。
Lambdaの設定
CloudWatchのログストリームを受けて、Slackに内容を通知するLambda functionを用意します。
今回は、Serverless Frameworkを使ってLambda functionのデプロイを行いました。
開発環境構築は→ serverlessでLambdaのローカル開発環境を整える
プロジェクトの作成
次のコマンドでプロジェクトを初期化します。
$ sls create --template aws-nodejs --path CloudTrail-Slack
必要パッケージもインストールしておきましょう。
$ npm install async
$ npm install request
functionの作成
プロジェクト作成の手順で、プロジェクトフォルダに作成されたhandler.js
を編集します。
'use strict';
const zlib = require('zlib');
const async = require('async');
const request = require('request');
// set slack setting
const slack_endpoint = 'https://slack.com/api/chat.postMessage';
const slack_token = process.env.SLACK_TOKEN;
const slack_channel = process.env.SLACK_CHANNEL;
const slack_user = process.env.SLACK_USERNAME;
module.exports.notify = (event, context, callback) => {
const payload = new Buffer(event.awslogs.data, 'base64');
zlib.gunzip(payload, (err, res) => {
if (err) {
return callback(err);
}
const parsed = JSON.parse(res.toString('utf8'));
console.log('Decoded payload:', JSON.stringify(parsed));
async.map(parsed.logEvents, (data, callback) => {
notifySlack(data, callback);
}, (err, results) =>{
if (!err) {
console.log(results);
callback(null, 'success');
}
});
});
}
let notifySlack = (data, callback) => {
let log = JSON.parse(data.message);
let attachment = {
"fallback": log.eventName + " by " + log.userIdentity.userName,
"color": "#36a64f",
"author_name": log.userIdentity.userName,
"title": log.eventName,
"fields": [
{
"title": "awsRegion",
"value": log.awsRegion,
"short": false
},
{
"title": "eventSource",
"value": log.eventSource,
"short": false
},
{
"title": "eventId",
"value": log.eventID,
"short": false
}
],
"thumb_url": "https://d0.awsstatic.com/security-center/KMS_Benefit_100x100_Compliance.png",
"ts": new Date(log.eventTime).getTime() / 1000
}
let payload = {
form: {
token: slack_token,
channel: slack_channel,
username: slack_user,
attachments: JSON.stringify([attachment])
}
}
// post slack message
request.post(slack_endpoint, payload, (error, response, body) => {
if (error) {
console.log(error)
} else {
callback(null, 'slack api call success');
}
}
)
}
serverlessの設定
プロジェクト作成の手順で、プロジェクトフォルダに作成されたserverless.yaml
を編集します。
Slackの投稿先に関する設定は環境変数から取得するようになっています。(AWSのコンソール上から設定変更可能)
また、必要に応じてリージョンの設定なども行います。
service: cloudtail-lambda # NOTE: update this with your service name
provider:
name: aws
runtime: nodejs6.10
functions:
notify:
handler: handler.notify
# Define function environment variables here
environment:
SLACK_TOKEN: <すらっくのAPIとーくん>
SLACK_CHANNEL: cloudtrail-test
SLACK_USERNAME: CloudTrail
デプロイ
次のコマンドでデプロイします。
$ sls deploy
AWS コンソール上からもデプロイしたLambda functionが確認できました。
Slackの設定
API Tokenの取得
以下の記事を参考にさせてもらいました。
Slack APIのTokenの取得・場所
チャンネルの作成
「serverlessの設定」でSLACK_CHANNEL
に設定したチャンネルを事前に作成しておきます。
SNSとの連携
ストリームの開始
AWSコンソールから、CloudWatchのメニューを開きます。
「ログ」のメニューを開き、ロググループ一覧から今回作成したロググループを選択し、「アクション」から「Lambdaサービスへのストリーミングの開始」を選択します。
ストリーム先の選択
今回作成したLambda functionを選択した後、「次へ」を選択します。
フィルタ設定
ログの形式には「AWS CloudTrail」を選択した後、サブスクリプションフィルターのパターンを設定します。
{ ($.userIdentity.type = "IAMUser") && (($.eventSource = "ec2.amazonaws.com") || ($.eventSource = "s3.amazonaws.com") || ($.eventSource = "iam.amazonaws.com") || ($.eventSource = "rds.amazonaws.com") || ($.eventSource = "signin.amazonaws.com"))}
$.userIdentity.type = "IAMUser"
では、通知するユーザーの範囲をIAMユーザーに絞っています。これにより、Rootユーザー、IAMロールからのAPIコールは対象外となります。
$.eventSource = "<サービス名>.amazonaws.com"
では、通知するサービスの範囲を絞っています。上記フィルタ設定では
- EC2
- S3
- IAM
- RDS
- ログイン関連
のみに絞っています。
確認
IAMユーザーでAWSコンソールにログイン。S3バケットの作成を行いました。
Slackには次のような通知が確認できました。
以上です。
簡単に実現できました