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

#はじめに
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ユーザーも設定しました。
<img width="638" alt="スクリーンショット 2019-08-10 12.06.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429159/0a2eee5e-9bf7-ea25-bf97-c8d2307e0963.png">

#事前準備
##Slack WebhookURLの取得
Slack通知するため、SlackWebhookのURLを取得しておいてください。
##IAMロールの作成
Lambda関数に付与するIAMロールを作成します。
今回のLambdaでは、以下の権限が必要となります。

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

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

```json: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関数のコード

```Python: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を動かすよう設定しましたので、以下のような設定となります。
<img width="1461" alt="スクリーンショット 2019-08-10 12.54.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429159/9d5fb578-2b2c-a428-2650-0571919a30e6.png">

#参考サイト
[Boto3 Docs1.9.205 documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/logs.html#CloudWatchLogs.Client.start_query)