概要
自分用メモ。
背景と目的
AWSリソースを使いながら動くLambda関数をローカルでテストしておきたい、というような場合に、IAMユーザーに紐づくアクセスキーをローカル保存して使用することがある。しかし、アクセスキーの漏洩の危険性からただ単にローカル保存することは望ましくなく、
- ①目的の権限を持ったロールを引き受けるようにする
- ②MFA認証を強制する
- ③IAM Identity CenterのSSOを使用する
ということが対策として言われたりする。
①は単独ではほとんど意味をなさないと思う。なぜなら、結局のところ権限を引き受けられるので、もともと権限があることとあまり変わらないから。②と併用することで効果的といえる。①で当該ユーザーそのものには権限を与えないようにしておき、MFA認証したうえでロールを切り替えて初めてAWSの操作ができるようになるから。(ただ、個人的には①は要るのだろうか?と思っていたりする。)ただ、②を導入すると使用するたびにMFA認証を要求され使い勝手が悪い。
③は、近年最も推奨される方法だが、会社などで組織としてAWSアカウントが管理されている場合に自由に使えなかったりする。
①+②+認証キャッシュ使用
の方法で一定期間再認証抑制で安全と使い勝手を確保する方法を調べ、メモしておく。
詳細
0. 前提条件
- IAMユーザーにMFAデバイス登録済み
1. 流れの確認
ここでは、AWS SDK(Python)すなわちboto3での活用を目指す。
目的の権限を持つロールを作成 ↓ ユーザーのポリシー編集 ↓ CLIのプロファイル設定 ↓ プロファイルを参照、認証キャッシュを利用
2. 目的の権限を持つロールを作成
IAMコンソールで、目的の権限(S3がいじりたければS3FullAccessなど)を付与したロールを作成しておく。その際、信頼ポリシーには、MFA認証済みであることを条件としてロール引き受けを許可するように設定。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Statement1", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{accound_id}:user/{iam_user_name}" }, "Action": "sts:AssumeRole", "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } } } ] }
3. ユーザーのポリシー
IAMユーザー側のインラインポリシーで、目的の権限を持つロールARNの引き受けができるようにしておく。それ以外のロールは一切アタッチしない。つまり、AWSサービスの操作はできない状態。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::{accound_id}:role/{目的の権限を持つロールARN}" } ] }
4. CLIのプロファイル設定
base-userプロファイルと目的のロール引き受け用設定を保持するtargetプロファイルを記載。
4.1 ~/.aws/config
[profile base-user]
output = json
region = ap-northeast-1
[profile target]
output = json
region = ap-northeast-1
source_profile = base-user
mfa_serial = {登録済みMFAデバイスのARN}
role_arn = {引き受けたい目的の権限を持つロールARN}
4.2 ~/.aws/credentials
base-userプロファイルとして、IAMユーザーのアクセスキーを記入。このアクセスキーが万が一漏洩しても、MFA認証を強制されるため、ローカルに保存しても安全。
[base-user]
aws_access_key_id = {アクセスキーID}
aws_secret_access_key = {シークレットアクセスキー}
5. boto3でプロファイルを参照、認証キャッシュを利用
以下は、S3のバケット一覧を取得する例。 boto3では、targetプロファイルを使ってセッションを作成する。ここで、MFA認証後一定期間再認証を要求されないようにするには、認証キャッシュを読み出せるようにしておく必要がある。そこで、一連の処理を行ってboto3.Sessionを取得する専用の関数boto3_session_from_cli_cacheを用意しておく。
import os from pathlib import Path import boto3 import botocore.session def boto3_session_from_cli_cache(profile_name: str, cache_dir: str | None = None) -> boto3.Session: botocore_session = botocore.session.Session() botocore_session.set_config_variable('profile', profile_name) if cache_dir is None: cache_dir = Path.home() / '.aws' / 'cli' / 'cache' cache = botocore.credentials.JSONFileCache(cache_dir) resolver = botocore_session.get_component('credential_provider') assume_role_provider = resolver.get_provider('assume-role') assume_role_provider.cache = cache return boto3.Session(botocore_session=botocore_session) if __name__ == "__main__": session = boto3_session_from_cli_cache("target") s3_client = session.client("s3") print(s3_client.list_buckets())
6. 結果
上記のスクリプトを実行すると、以下のようにMFAのコード入力を要求される。
Enter MFA code for arn:aws:iam::{account_id}:mfa/{登録MFAデバイス名}:
正しく入力すると、ロール引き受けが走り、スクリプトにおいて目的の権限を持つ操作が可能。今回ではS3のバケット一覧が無事取得できた。
さらに、もう一度スクリプトを実行すると、今度はキャッシュを読み出すことでMFA認証の要求をされずそのままスクリプトが実行された。
ということで、「安全と使い勝手を確保」することができた。
まとめと今後の課題
目的の動作をさせることができた。簡易な処理なので、スクリプトに差し込んで使おうと思う。