MFA 保護したAWS API をAWS CLI を利用してIAMロール経由で実行する方法

例えば、以下のようなシチュエーションです。

  • あるAWS アカウントPにバケット削除をMFA 保護したS3バケットが存在する
  • AWS アカウントPにアクセスするには認証用のAWS アカウントA からIAM ロールを利用する
  • この状況でAWS CLI を利用してS3 バケットを削除したい

結論:
STS::GetSessionToken API を実行 -> STS::AssumeRole を実行 -> S3バケットを削除

解説

MFA 保護したAWS API とは以下のドキュメントに記載のあるような内容です。

参考) MFA 保護 API アクセスの設定 – AWS Identity and Access Management
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_mfa_configure-api-require.html

今回のシチュエーションでは、アカウントP に以下のようなバケットポリシーが設定されたS3 バケットがあるとします。この場合、MFA 認証されていないとこのバケットを削除ことはできません。

{
  "Id": "Policy1623673100192",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1623672743376",
      "Action": [
        "s3:DeleteBucket"
      ],
      "Effect": "Deny",
      "Resource": "arn:aws:s3:::<your_bucket_name>",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      },
      "Principal": "*"
    }
  ]
}

認証用AWS アカウントAからアカウントP にアクセスするには、アカウントPのIAMロールをAssumeRole します。AWS CLI では例えば以下のようにSTS AssumeRole を実行します。

aws sts assume-role --role-arn arn:aws:iam::<account_P_id>:role/<role_name> \
    --role-session-name <session_name>

これを実行すると一時的認証情報を取得できますので、その認証情報を利用してアカウントP のDeleteBucket API を実行できます。

aws s3 rb s3://<your_bucket_name>

しかし、このときの一時的認証情報はMFA 認証されていないため失敗します。

ではどうするのかというと、まずAWS CLI にてMFA 認証されたリクエストを実行する場合は、STS GetSessionToken API を実行します。

aws sts get-session-token \
  --serial-number arn:aws:iam::<aaccount_A_id>:mfa/<your_user_name> \
  --token-code <your_mfa_token>

これを実行するとMFA認証済みの一時的認証情報を取得できます。そのMFA認証済みの一時的認証情報を利用して、先程のassume-role を実行します。そうするとassume-role から返却された一時的認証情報はMFA認証情報を含みます。さらに、assume-role により返却された認証情報を利用することで今回のS3バケットの削除のようなMFA保護されたAPI 呼び出しをCLI から実行できます。

ただ、毎回手動でget-session-token -> assume-role を実行するのは面倒なので、一つのシェルなどにまとめることができます。

間違いその1: AssumeRole を実行する際にMFAトークンコードを指定する

sts::AssumeRole はMFA トークンコードを指定して実行することもできます。例えば以下のような呼び出しです。

aws sts assume-role --role-arn arn:aws:iam::<account_P_id>:role/<role_name> \
    --serial-number arn:aws:iam::<account_A_id>:mfa/<iam_username> \
    --token-code <your_mfa_token_code>  \    
    --role-session-name <session_name> 

この場合、MFA はAssumeRole の実行に利用されるのみでSTS により生成される一時的認証情報にはMFA 情報は含まれません。

AssumeRole によって返される一時的な認証情報のコンテキストには MFA 情報が含まれていないので、MFA に対する個別の API オペレーションを確認できません。このため、リソースベースのポリシーで保護されたリソースへのアクセスを制限するために、GetSessionToken を使用する必要があります。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_mfa_configure-api-require.html

間違いその2: AWS CLI のconfig にIAM ロールの指定をして実行する

以下のようにAWS CLI のconfig ファイルにIAM ロールとそのロールをAssume する際に利用するMFAの設定が可能です。

[profile roleP]
role_arn = arn:aws:iam::<account_P_id>:role/<your_role_name>
mfa_serial = arn:aws:iam::<account_A_id>:mfa/<user_name>
source_profile = <source_profile>

このように構成するとコマンド実行時に以下のようにMFAトークンコードの入力プロンプトが表示されます。

aws s3 rb s3://<your_bucket_name> --profile roleP
Enter MFA code for arn:aws:iam::<account_A_id>:mfa/<user_name>: トークンコードをここに入力

ただ、この場合も内部的にはMFA 認証されていない一時的認証情報が生成されているだけですので、MFA保護したAPI を呼び出すことはできません。

まとめ

MFA 保護したAPI をIAM ロール経由でCLI から実行したい場合は、

STS::GetSessionToken API を実行 -> STS::AssumeRole を実行 -> MFA保護されたAPI実行

以上です。