アクセスキーの使用をやめて、SSO認証に変更する

## はじめに
以前からAWS CLIなどで設定していたアクセスキーに懸念がありました。今回、その使用をやめてIAM Identity CenterによるSSO認証に変更してみました。また、有効なアクセスキーが残っているかをチェックして、メールで通知する仕組みを導入してみました。利用されているAWSアカウントのセキュリティ対策の参考にして頂けると幸いです。

## きっかけ
マネジメントコンソールのIAM User設定画面で、アクセスキーを追加しようとしていた時でした。推奨される代替案として、IAM Identity Centerのユーザーによる認証を使用することが表示されていることに気づき、調べたのがきっかけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/d24f258c-b8f0-d0fc-a0d1-dff09e595d48.png)

:::note info
コンソールに表示されているメッセージは、重要なことや役立つ情報であるかもしれないので、気づけばチェックしよう。
:::

## アクセスキーをやめたかった理由
とにかく漏洩することが怖いです。2要素認証などの仕組みがないため、キーが漏洩してしまうと簡単に利用されてしまいます。ベストプラクティスでは、90日に1回のローテーションが推奨されています。特に個人で使用しているようなAWSアカウントでは、キーを作成したまま放置ということが起こりがちなため、なるべく使用を避けたいと思っていました。

↓の記事にアクセスキーについて詳しく解説されていました。

https://qiita.com/Neville/items/b1a00cb18292b0f18846

## 設定
ここからSSO認証のための設定について説明していきます。

### IAM Identity Center
IAM Identity Centerのユーザーで、AWSマネジメントコンソールへのログインやリソースへのアクセスをさせる場合、マルチアカウントのアクセス許可機能を利用します。IAM Identity Centerは「組織のインスタンス」である必要があります。
Organizationsを利用し複数のアカウントを管理している場合、マスターアカウントで設定することになります。

### 許可セット
IAM Identity Centerのユーザーと、AWSアカウントへのアクセス権の割当を紐付けします。私の場合は、AWS CLIを利用するユーザーはPowerUserの操作に加え、IAMの操作も行いたかったので、以下のようにしました。

IAM Identity Center > 許可セット > AWSPowerUserAccess > ポリシーをアタッチ
マネージドポリシーから、「IAMFullAccess」を追加しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/d0b4d983-697a-ba0d-c195-ca94762b873f.png)

IAM Role を確認すると、許可セットに対応するロールが自動的に作成されています。「AWSPowerUserAccess」に対応するロール「AWSReservedSSO_AWSPowerUserAccess_〜」へアタッチされたポリシーを確認します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/7a9bfe29-ccfc-b71e-1f30-1d758dd7fd35.png" width=50%>

許可セットの設定画面で割り当てたポリシーが、IAM Roleにアタッチされていることが確認できます。
※カスタマー管理ポリシーもアタッチ可能です。カスタマー管理ポリシーの場合は、同名のポリシーを各AWSアカウントに作成しておく必要があります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/520f2a8d-1028-b690-5ec0-3d68f047bac8.png" width=50%>

### グループとユーザーの設定
IAM Identity Centerのグループと許可セットを割当します。
私の場合は、グループ「Developer」と許可セット「AWSPowerUserAccess」を割当ました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/0063b2e7-c5f1-bada-5a85-c59de96e8327.png" width=50%>

AWS CLIで利用するユーザーをグループ「Developer」に所属させました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/8697991c-c20a-0444-25db-4119e1fc6d66.png" width=50%>

### AWS CLI
続いてAWS CLIの設定を行います。以下のサイトに記載のとおり、aws configure ssoコマンドで、IAM Identity Center用のプロファイルを作成します。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-sso.html

``` shell
mhukmtd@mhukmtd-virtualbox:~$ aws configure sso
SSO session name (Recommended):
WARNING: Configuring using legacy format (e.g. without an SSO session).
Consider re-running "configure sso" command and providing a session name.
SSO start URL [None]: https://d-xxxxxxxxxxxxx.awsapps.com/start
SSO region [None]: ap-northeast-1
```

上記の「SSO start URL」は、IAM Identity Centerのダッシュボードで確認できる、「AWS access portal URL」を入力します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/532e7226-fee5-9a3a-3d17-b140f7306c53.png" width=50%>

ブラウザーが起動し、ログインページが表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/295ab7ee-4064-a0b9-2891-de80e8404250.png" width=50%>

IAM Identity CenterでMFAを設定していると、MFA Codeの入力も求められます。設定することをお勧めします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/e7b1a93d-479c-4bfd-e438-90a560848e90.png" width=50%>

ターミナル上に表示されているCodeとブラウザの画面に表示されているCodeが一致するかを確認し、「Confirm and continue」をクリックします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/44fc75ee-6577-9fe3-56e2-5f1ec08aa100.png" width=50%>

このブラウザで認証されたセッションを、AWS CLIで利用することを許可するための確認メッセージが表示されます。「Allow」をクリックします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/e2893b7d-8e17-cbfd-2a03-09da87a9db55.png" width=50%>

許可が完了したので、このブラウザーの画面は閉じてOKです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/148bdacc-4b1e-a349-4d84-b93434f9d629.png" width=50%>

ターミナルには以下のように続きのメッセージとプロンプトが表示されますので、適宜応答します。
```
The only AWS account available to you is: 123456789012
Using the account ID 123456789012
The only role available to you is: AWSPowerUserAccess
Using the role name "AWSPowerUserAccess"
CLI default client Region [None]: ap-northeast-1
CLI default output format [None]:
CLI profile name [AWSPowerUserAccess-123456789012]:

To use this profile, specify the profile name using --profile, as shown:

aws s3 ls --profile AWSPowerUserAccess-123456789012
```

ホームフォルダ\.aws\configファイルを確認すると、IAM Identity Center用のプロファイルが作成されていることが確認できました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/742801f1-57ba-877b-a828-4b92d71090d1.png" width=50%>

ためしに、ターミナルでs3バケットのリストを表示するコマンドを入力すると、バケットのリストが正しく表示されました。SSO認証でAWS CLIが使用可能となりました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/ef07a0de-1dcd-bbe2-e84f-4aad13f3306b.png" width=50%>

## ログイン通知
以前に設定したログイン通知機能を、IAM Identity Centerユーザーにも適用してみます。SSO認証した時のイベントも検知するようにして、メール送信させます。

https://qiita.com/mhuk_mtd/items/06af63c4020dc4fee6fa

### SSO認証時のCloudTrailのイベント記録内容
IAM Identity Center ユーザーでSSO認証した時間帯のCloudTrail ログを確認しました。以下のログイベントが該当すると見当をつけました。

``` JSON:CloudTrailログイベントの一部
    "eventVersion": "1.09",
    "userIdentity": {
        "type": "Unknown",
        "principalId": "123456789012",
        "arn": "",
        "accountId": "123456789012",
        "accessKeyId": "",
        "userName": "IAM Identity Centerのユーザ名",
        "onBehalfOf": {
            "userId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
            "identityStoreArn": "arn:aws:identitystore::123456789012:identitystore/d-xxxxxxxxxx"
        },
        "credentialId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
    },
    "eventTime": "2025-02-11T07:14:10Z",
    "eventSource": "signin.amazonaws.com",
    "eventName": "UserAuthentication",
    "awsRegion": "ap-northeast-1",```
```
eventNameは「UserAuthentication」で、userIdentity.typeは「Unknown」であることがわかりました。

### SSO認証のイベントをフィルタに追加
CloudTrail用に設定しているCloudWatch logs ロググループのサブスクリプションフィルターを修正します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/228118d3-f71b-5809-83fc-73f84f04e375.png" width=50%>

```JSON:サブスクリプションフィルターパターン
{ ($.eventName = "ConsoleLogin") || ($.eventName = "UserAuthentication") }
```

以前のサブスクリプションフィルターは削除します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/6ad8edd2-7f38-6ef4-7937-81d80baf3e9d.png" width=50%>

これで、SSO認証したときのログ情報も、メール通知用のLambdaへ連携されるようになりました。

### SSO認証時のユーザー名取得
通知用のLambda「ConsoleLoginNotificationLambda」を修正し、SSO認証時もユーザー名をメール本文に出力できるようにコードを修正します。userIdentity.typeが「Unknown」の場合の処理を追加します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/ef80fdda-2ce9-a1cd-abb1-7d723247d80a.png" width=50%>

```Python:index.pyの一部抜粋
    match event['userIdentity']['type']:
        case 'IAMUser':
            inputData['userName'] = event['userIdentity']['userName']
        case 'Root':
            inputData['userName'] = 'Root'
        case 'Unknown':      # 追加
            inputData['userName'] = event['userIdentity']['userName']   # 追加
        
```

Lambda関数を更新します
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/2a8389c4-2d2c-9028-10e9-abc84fb6afd5.png" width=50%>

### SSO認証のテスト
SSO認証もメール通知できるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/0bbd47e5-6cd8-b92d-ebb6-17d418e243f7.png)

## アクセスキーの存在をチェックする
最後に、アクセスキーを残さないための仕組みを構築します。SSO認証に切り替えたので、アクセスキーを使用しなくてもよい環境となりました。誤って有効なアクセスキーが残っていたらメールでお知らせする機能になります。

### チェック用LambdaのPythonコード
ほぼ、生成系AIに作ってもらったコードです。

``` Python:accesskeys_check.py
import boto3
import os

def lambda_handler(event, context):
    """
    IAM Userに作成されているすべてのAccesskeyをチェックし、
    有効なものが存在した場合、そのIAM User名をSNSで通知する。
    """

    iam = boto3.client('iam')
    sns = boto3.client('sns')

    sns_topic_arn = os.environ['SNS_TOPIC_ARN']  # 環境変数からSNS Topic ARNを取得

    try:
        # すべてのIAMユーザーを取得
        users = iam.list_users()['Users']

        for user in users:
            user_name = user['UserName']
            access_keys = iam.list_access_keys(UserName=user_name)['AccessKeyMetadata']

            # 有効なアクセスキーが存在するかチェック
            for key in access_keys:
                if key['Status'] == 'Active':
                    # SNSに通知
                    message = f"IAM User '{user_name}' has an active Access Key. KeyId: {key['AccessKeyId']}"
                    print(message)  # Lambdaのログにも出力

                    sns.publish(
                        TopicArn=sns_topic_arn,
                        Message=message,
                        Subject='Active IAM Access Key Found'
                    )
                    break  # ユーザーごとに一度だけ通知

    except Exception as e:
        print(f"Error: {e}")
        raise  # エラーを再スローして、Lambdaの実行を失敗させる

    return {
        'statusCode': 200,
        'body': 'IAM Access Key Check completed.'
    }
```

### メール通知先SNSトピックの設定
環境変数に、メール通知用のSNSトピックを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/f6f9ca2e-04c9-bbf5-f6ed-6a0204c908c3.png)

アクセスキーの取得処理には少し時間がかかるようなので、タイムアウトの時間をデフォルトから30秒へ拡張しておきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/9d9979d7-7866-e601-1d67-a71af6cdaab3.png" width=30%>

### Lambdaの権限設定
このLambda関数に設定するIAM Roleに必要な権限は以下の通りです。
* iam:ListUsers
* iam:ListAccessKeys
* sns:Publish

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/32d9cf85-b8ef-2876-ff39-ed465d5eefe3.png" width=50%>

### 検知テスト
テスト用のIAM Userを作成し、アクセスキーを有効にしておきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/59cc0010-14ed-a0b4-af5f-7a8dd46eec44.png" width=50%>

テスト実行します。有効化されているアクセスキーを発見し、メールで通知してくれました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/4b19cc1c-10ba-ed64-b099-b2c93c518799.png" width=50%>


### スケジュール登録
作成した検知処理が定期的に実行されるようにするため、EventBridge Schedulerを登録します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/04e353eb-22ec-88a6-0ac5-3230c8722886.png" width=50%>

毎日02:00時にスケジュールしました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/2ccfb8d3-ab4c-b0ed-51d8-feefd7509e0a.png" width=50%>

チェック用のLambdaがターゲットになるように設定します。ペイロードは不要です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/1debbe55-7a63-cd00-8bb8-19cd0a7a5b84.png" width=50%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/ac6cc999-54e9-16e4-9d9a-ca6403e37c12.png" width=50%>

その他は、デフォルトでスケジュールを作成します。

以上で設定完了です。

## まとめ
アクセスキーの使用をやめてIAM Identity CenterによるSSO認証を利用し、AWS CLIでアクセスできるように環境設定できました。また、IAM Identity CenterによるSSO認証のイベントをメール通知させることと、不要となったアクセスキーが残っていないかを自動的にチェックする仕組みを導入することができ、アクセスキーに関しては安心できる状態になりました。
今回作成した仕組みは複数人で利用している環境でも利用できますが、アクセスキーのチェック部分は、AWS Configを利用するほうがより安全と思います。機会があれば調べてみようと思います。