LoginSignup
4
3

More than 3 years have passed since last update.

LambdaでIAMユーザーのアクセスキーを監視

Posted at

はじめに

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ユーザーも設定しました。
スクリーンショット 2019-08-10 12.06.21.png

事前準備

Slack WebhookURLの取得

Slack通知するため、SlackWebhookのURLを取得しておいてください。

IAMロールの作成

Lambda関数に付与するIAMロールを作成します。
今回のLambdaでは、以下の権限が必要となります。

  • IAMについて
    • アクセスキー削除
    • アクセスキー一覧取得
    • ユーザー一覧取得
    • ユーザー情報取得
  • CloudWatch Logsについて
    • ログストリームの作成
    • ロググループの作成
    • ログイベントの書き込み

以下のIAMポリシーを作成しLambda用のIAMロールにアタッチし、Lambdaにそのロールを使用してください。

IAMポリシー
{
    "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関数のコード

IAMアクセスキーの古さ検知
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を動かすよう設定しましたので、以下のような設定となります。
スクリーンショット 2019-08-10 12.54.40.png

参考サイト

Boto3 Docs1.9.205 documentation

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3