MFA トークンを利用して別アカウントのIAM ロールをAssumeRole する際のGetSessionToken とAssumeRole の違い

以下のような前提で考えます

  • 対象アカウントには、Organization のSCP でMFA必須のポリシーが設定されいている
  • 開発者は認証アカウントのアクセスキーを利用して、対象アカウントにAssumeRole でアクセスする
  • AssumeRole を呼び出す際は必ずMFA が必須とする

図示すると以下のような場合です。

MFA 必須SCPとは具体的には以下のようなポリシーです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllDenyWithoutMFA",
            "Effect": "Deny",
            "Action": [
                "*"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

対象アカウントのIAM ロールの信頼ポリシーには、MFA 必須の設定がされています。

以下、信頼ポリシー

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<auth_account_id>:user/<iam_username>"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]

また、IAM ロールのアクセス権限には AdministratorAccess ポリシーを設定していて、AssumeRole したら何でもできるといった前提とします。

以上がシナリオの前提です。

AssumeRole にMFA トークンを指定する場合

上記のシチュエーションでは、以下のようにAssumeRole を呼び出すことができ一時的認証情報を得ることができます。

aws configure set aws_access_key_id <auth_account_access_key>
aws configure set aws_secret_access_key <auth_account_secret>

aws sts assume-role --role-arn <target_account_role_arn> \
    --serial-number <your_mfa_device_arn> \
    --role-session-name <any_name> \
    --token-code <your_token_code>  \

一時的認証情報の例

{
    "Credentials": {
        "AccessKeyId": "xxxx",
        "SecretAccessKey": "xxxx",
        "SessionToken": "xxxxxx",
        "Expiration": "2020-04-04T02:54:37+00:00"
    },
    ・・・略・・・
}

では、今回の前提で上記一時的認証情報を使ってAWS API を実行するとどうなるでしょか?

例えば以下のようなコマンドを実行します。(一時的認証情報を dev プロファイルに設定しているとします。)

$ aws s3 ls --profile dev

An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

今回の前提ではIAM ロールのアクセス権限に AdministratorAccess ポリシーを設定しているので呼び出しは成功しても良いはずですが、Access Denied になってしまいました。

GetSessionToken + AssumeRole をする場合

以下のようなスクリプトを組みます。これはまずGetSessionToken を実行してMFA 認証済みの一時的認証情報を入手してから、その認証情報を利用してAssumeRole をするという流れです。

ファイル名: assume-role.sh

以下のように実行します。

$ aws configure set aws_access_key_id <auth_account_access_key>
$ aws configure set aws_secret_access_key <auth_account_secret>

$ assume-role.sh <your_token_code>

$ aws s3 ls --profile dev

… 実行がうまくいく …

この場合は、今回の前提においても成功します。

2つの挙動の違い

以下のドキュメントに記載があります。

GetSessionToken によって返される一時的な認証情報には MFA 情報が含まれているので、認証情報によって実行される各 API オペレーションで MFA を確認できます。

AssumeRole によって返される一時的な認証情報のコンテキストには MFA 情報が含まれていないので、MFA に対する個別の API オペレーションを確認できません。

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

AssumeRole にMFA トークンを指定して呼び出すだけの場合、取得できる一時的認証情報には、MFA 情報が含まれません。CloudTrail のログを見るとリクエストのuserIdentity の情報としては以下のようになっています。

"userIdentity": {
    "type": "AssumedRole",
    "principalId": "xxx:xxx",
    "arn": "arn:aws:sts::xxxxx:assumed-role/xxxx/xxxxx",
    "accountId": "xxxxx",
    "accessKeyId": "xxxxxxx",
    "sessionContext": {
        "attributes": {
            "mfaAuthenticated": "false",
            "creationDate": "2020-04-04T01:58:27Z"
        },
        "sessionIssuer": {
            "type": "Role",
            "principalId": "xxxxxx",
            "arn": "arn:aws:iam::xxxxxx:role/xxxxxx",
            "accountId": "xxxxxx",
            "userName": "xxxxxxx"
        }
    }
}

“mfaAuthenticated” : “false” となっています。

次に、GetSessionToken -> AssumeRole の場合の、CloudTrail のログを見てみます。同じように”userIdentity” の内容です。

"userIdentity": {
    "type": "AssumedRole",
    "principalId": "xxxxx",
    "arn": "arn:aws:sts::xxxxx:assumed-role/xxxx/xxxxx",
    "accountId": "xxxxx",
    "accessKeyId": "xxxxxx",
    "sessionContext": {
        "sessionIssuer": {
            "type": "Role",
            "principalId": "xxxxx",
            "arn": "arn:aws:iam::xxxxxxx:role/xxxxx",
            "accountId": "xxxxxxx",
            "userName": "xxxxxx"
        },
        "webIdFederationData": {},
        "attributes": {
            "mfaAuthenticated": "true",
            "creationDate": "2020-04-04T00:53:53Z"
        }
    }

この場合、”mfaAuthenticated” : “true” となっており、MFA 認証情報が含まれていることがわかります。

今回の前提では、SCP にて”mfaAuthenticated” : “true” 以外のリクエストはすべてDeny する設定になっているため、GetSessionToke + AssumeRole の場合だけ、正常に実行できたということになります。

以上です。