#はじめに
AWSのIAMユーザーは、AWS CLIやAWS APIにプログラムからアクセスする場合、アクセスキーの設定が必要になります。
このアクセスキーを定期的に変更しなければセキュリティ上リスクがあるため、その古さ(アクセスキー作成日からの日数)を監視するLambdaを作成しました。
#構成
CloudWatchEventsで定期的にLambdaを動かし、アクセスキーが作成されてから半年(180日)以上経過している場合、Slackに通知します。また、半年からさらに30日経過した場合はアクセスキーを削除してSlackに通知します。
分かりにくいのでまとめると、
アクセスキー作成日から180日以内の場合 :Slack通知なし。
アクセスキー作成日から180日以上180+30日以内の場合 :キーは削除しない。削除までの日数をSlack通知。
アクセスキー作成びから180+30日以上の場合 :キー削除。削除したことをSlack通知。
なお、2019年8月現在東京リージョンでは1つのIAMユーザーにつき2つのアクセスキーが作成できますが、Lambdaによる監視はどちらも行います。
また、今回は監視対象外とするIAMユーザーも設定しました。
#事前準備
##Slack WebhookURLの取得
Slack通知するため、SlackWebhookのURLを取得しておいてください。
##IAMロールの作成
Lambda関数に付与するIAMロールを作成します。
今回のLambdaでは、以下の権限が必要となります。
- IAMについて
- アクセスキー削除
- アクセスキー一覧取得
- ユーザー一覧取得
- ユーザー情報取得
- CloudWatch Logsについて
- ログストリームの作成
- ロググループの作成
- ログイベントの書き込み
以下のIAMポリシーを作成しLambda用のIAMロールにアタッチし、Lambdaにそのロールを使用してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"iam:DeleteAccessKey",
"iam:ListUsers",
"iam:GetUser",
"logs:CreateLogGroup",
"logs:PutLogEvents",
"iam:ListAccessKeys"
],
"Resource": "*"
}
]
}
#Lambda設定
##環境変数
今回は、以下の環境変数を設定しました。
CHANNEL_LIST : 通知するSlackのチャンネルをカンマ区切りで設定
DELETE_DAYS :削除する日数を設定(90日から○日経過した時に削除)
EXCEPT_USERS :監視対象外とするIAMユーザー名をカンマ区切りで設定
##Lambda Layer
今回はslackwebライブラリを使用しました。
Lambdaはデフォルトの状態ではslackwebは使用できないので、LambdaLayerを作成しLambda関数にLayerの設定を行ってください。
##Lmabda関数のコード
import os
import datetime
import time
import ast
import slackweb
import boto3
from datetime import timezone
at_color = "danger"
client = boto3.client('iam')
channel_list = os.environ.get('CHANNEL_LIST').split(',')
delete_days = int(os.environ.get('DELETE_DAYS'))
except_users = os.environ.get('EXCEPT_USERS').split(',')
slack_api = "Slack WebhookのURLを指定"
def lambda_handler(event, context):
user_keyinfo = get_accesskey_expand()
for key in user_keyinfo:
create_day = datetime.datetime.strptime(key[1], '%Y-%m-%d %H:%M:%S%z')
if create_day + datetime.timedelta(days = 180) < datetime.datetime.now(timezone.utc):
#指定した日数経過した場合キーを削除
if create_day + datetime.timedelta(days = 180 + delete_days) < datetime.datetime.now(timezone.utc):
delete_access_key(key[0], key[2])
text = f"{key[0]} 's accesskey is deleted(id = {key[2]})"
for channel in channel_list:
post_slack(channel, text)
#半年経過した場合通知
else:
days = (create_day + datetime.timedelta(days = 180 + delete_days) - datetime.datetime.now(timezone.utc)).days
text = key[0] + "'s accesskey is old. \naccesskey will be deleted after " + str(days) + " days."
for channel in channel_list:
post_slack(channel, text)
#[IAMユーザー名, アクセスキーID, アクセスキーを作成した日]を取得
def get_accesskey_expand():
##ユーザー名を取得
get_users_info = client.list_users(
PathPrefix='/'
)
user_names = []
for index,users_info in enumerate(get_users_info['Users']):
user_name = get_users_info['Users'][index]['UserName']
if user_name not in except_users:
user_names.append(user_name)
##アクセスキーの情報取得
user_keyinfo = []
for user in user_names:
get_accesskey_list = client.list_access_keys(
UserName=user
)
if len(get_accesskey_list['AccessKeyMetadata']) >= 1:
for index,metadata in enumerate(get_accesskey_list['AccessKeyMetadata']):
create_data = get_accesskey_list['AccessKeyMetadata'][index]['CreateDate']
accesskey_id = get_accesskey_list['AccessKeyMetadata'][index]['AccessKeyId']
user_keyinfo.append([user, str(create_data), accesskey_id])
return user_keyinfo
def post_slack(channel,text):
slack = slackweb.Slack(url=slack_api)
slack.notify(
username="IAM-accesskey",
channel=channel,
attachments = [{
"color" : at_color,
"text" : text
}]
)
def delete_access_key(user_name, accesskey_id):
response = client.delete_access_key(
UserName=user_name,
AccessKeyId=accesskey_id
)
#CloudWatch Eventsの設定
CloudWatch Eventsで作成したLambda関数を定期的に動かすよう設定を行います。
今回は、毎週月曜日の10時にLambdaを動かすよう設定しましたので、以下のような設定となります。