Jumpアカウント環境でIPアドレス制限を行う方法

マルチアカウントの IAM ユーザーを一つのアカウントに集約した Jump アカウントを利用している環境において、マネジメントコンソールの IP アドレス制限を実現する方法を 2 つ紹介します。
2022.04.28

IAM ユーザー/グループを一つのアカウントに集約する Jump アカウント(別名、踏み台アカウント)を利用している環境において、AWS の利用を送信元 IP アドレスにより制限する方法を 2 つ紹介します。

Jump アカウントとは、複数の AWS アカウントを運用している場合において、ユーザー管理の効率化のために IAM ユーザー/グループを集約したアカウントです。AWS Single Sign-On を利用するための条件である AWS Organizations を利用していない環境でも構築することができます。

https://d0.awsstatic.com/events/jp/2017/summit/slide/D4T2-2.pdf より引用

マネジメントコンソールの IP アドレス制限については次のブログで詳しく紹介されていますので、合わせて読むことで理解が深まります。


IP アドレス制限の設定イメージ

IP アドレス制限を行ういくつかの方法のうち、本ブログでは次の 2 つを試してみます。

  • Jump アカウントで IP 制限を行う方法
  • スイッチロール先 IAM ロールで IP 制限を行う方法

2 つの方法のイメージ図を示します。

Jump アカウントで IP 制限を行う方法では、ユーザーは Jump アカウントのマネジメントコンソールにサインインすることはできますが、サインイン後に許可されたアクションがない状態とすることで実現します。

スイッチロール先 IAM ロールで IP 制限を行う方法では、ユーザーが Jump アカウントから別の AWS アカウントにスイッチロールすることを拒否することで実現します。


IP 制限なしの Jump アカウント設定

まずは、IP アドレス制限なしで Jump アカウントを設定してみます。

Jump アカウントの IAM ユーザー/グループ設定

今回の設定では、Jump アカウントの IAM ユーザーと IAM グループの構成は次の通りとしています。

  • IAM グループ名:test-group
  • IAM ユーザー名:test-user
    • IAM グループ test-group に所属

test-group には、2 つの IAM ポリシーをアタッチします。

1 つ目は、他の AWS アカウントにスイッチロールすることを許可するポリシーをアタッチします。

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "*"
    }
}

2 つ目は、IAM ユーザーが自身の MFA 認証の設定とマネジメントコンソールのパスワード変更を許可する IAM ポリシーをアタッチします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowListActions",
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowIndividualUserToListOnlyTheirOwnMFA",
            "Effect": "Allow",
            "Action": [
                "iam:ListMFADevices"
            ],
            "Resource": [
                "arn:aws:iam::*:mfa/*",
                "arn:aws:iam::*:user/${aws:username}"
            ]
        },
        {
            "Sid": "AllowIndividualUserToManageTheirOwnMFA",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::*:mfa/${aws:username}",
                "arn:aws:iam::*:user/${aws:username}"
            ]
        },
        {
            "Sid": "AllowIndividualUserToDeactivateOnlyTheirOwnMFAOnlyWhenUsingMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::*:mfa/${aws:username}",
                "arn:aws:iam::*:user/${aws:username}"
            ],
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        },
        {
            "Sid": "AllowGetAccountPasswordPolicy",
            "Effect": "Allow",
            "Action": "iam:GetAccountPasswordPolicy",
            "Resource": "*"
        },
        {
            "Sid": "AllowChangePassword",
            "Effect": "Allow",
            "Action": "iam:ChangePassword",
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "BlockMostAccessUnlessSignedInWithMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ListUsers",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "iam:GetAccountPasswordPolicy",
                "iam:ChangePassword"
            ],
            "Resource": [
                "arn:aws:iam::*:mfa/${aws:username}",
                "arn:aws:iam::*:user/${aws:username}"
            ],
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

ポリシーは次のドキュメントを参考としました。

IAM: IAM ユーザーに MFA デバイスの自己管理を許可する

IAM ユーザーに自分のパスワードを変更する権限を付与する

スイッチロール先アカウントの IAM ロール設定

スイッチロール先の AWS アカウントでは、IAM ロールを作成します。

IAM ロールにアタッチする許可ポリシーには AdministratorAccess をアタッチするものとします。

信頼関係ポリシーの設定では Jump アカウントの AWS アカウントを信頼し、スイッチロールできる IAM ユーザーを指定します。その際に MFA 認証が行われている場合のみスイッチロールを許可する Condition も含めています。

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

なお、2022 年 4 月 28 日時点では、Principal として IAM グループを指定することはできず、Jump アカウントへの IAM ユーザーの追加に合わせてスイッチロール先 IAM ロールの信頼関係ポリシーを更新するか、次のように root を指定して Jump アカウントのすべての IAM ユーザーや IAM ロール等からのアクセスを許可する方法があります。

            "Principal": {
                "AWS": "arn:aws:iam::111122223333:root"
            }

スイッチロールを試してみる

Jump アカウントにサインインした後は、IAM における一部の権限しかない状態です。

スイッチロールをするために、MFA 認証を設定した後に「ロールの切り替え」の設定を行います。

スイッチロール先のアカウント ID と IAM ロール名を指定します。表示名には任意の名前を入力します。

スイッチロールできました。

以降は、送信元 IP アドレス制限の設定を試してみます。

Jump アカウントで IP 制限を行う方法

Jump アカウントで IP 制限を行う場合は、指定した送信元 IP アドレス以外からのアクセスの場合にすべてのアクションを拒否するポリシーを設定します。

Jump アカウントの IAM グループ test-group に対して、追加で次のポリシーを設定した IAM ポリシーをアタッチします。aws:SourceIp にはアクセスを許可したい IP アドレスを指定します。

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Deny",
        "Action": "*",
        "Resource": "*",
        "Condition": {
            "NotIpAddress": {
                "aws:SourceIp": [
                    "xxx.xxx.xxx.xxx/32",
                    "xxx.xxx.xxx.xxx/32"
                ]
            },
            "Bool": {
                "aws:ViaAWSService": "false"
            }
        }
    }
}

ポリシーは次のドキュメントを参考にしました。

AWS: 送信元 IP に基づいて AWS へのアクセスを拒否する

では、ポリシーに記載した IP アドレス以外の IP アドレスから Jump アカウントへのサインインを試してみます。

サインインはできますが、すべてのアクションが拒否されているため、IP 制限の設定前には操作できていた MFA 認証の設定ができなくなっています。


スイッチロール先 IAM ロールで IP 制限を行う方法

スイッチロール先 IAM ロールで IP 制限を行う場合は、IAM ロールの信頼関係ポリシーにおいて、指定した送信元 IP アドレス以外のアクセスからのスイッチロールを拒否します。

IAM ロールの信頼関係ポリシーを変更して IP アドレスによる条件を追加してみます。aws:SourceIp にはアクセスを許可したい IP アドレスを指定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/test-user"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        },
        {
            "Effect": "Deny",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/test-user"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/32",
                        "xxx.xxx.xxx.xxx/32"
                    ]
                }
            }
        }
    ]
}

ポリシーは次のドキュメントを参考にしました。

IAM ロールを使用して特定の IP アドレスからの API コールを制限する方法を教えてください。

また、別のパターンとして次の信頼関係ポリシーでも実現できます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/test-user"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                },
                "IpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/32",
                        "xxx.xxx.xxx.xxx/32"
                    ]
                }
            }
        }
    ]
}

では、ポリシーに記載した IP アドレス以外の IP アドレスから Jump アカウントにアクセスし、スイッチロールを試してみます。

スイッチロールの設定画面から「ロールの切り替え」をクリックしてもスイッチロール先のマネジメントコンソールへ遷移しなくなりました。

CloudTrail のイベントにおいても、MFA 認証を行っているにもかかわらずアクセスが拒否されていることが記録されています。

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDAWLQZGIQFRP2W3KFKE",
        "arn": "arn:aws:iam::111122223333:user/test-user",
        "accountId": "111122223333",
        "accessKeyId": "XXXXXXXXXXXXXXXXXXXXX",
        "userName": "test-user",
        "sessionContext": {
            "sessionIssuer": {},
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2022-04-26T16:45:46Z",
                "mfaAuthenticated": "true"
            }
        },
        "invokedBy": "AWS Internal"
    },
    "eventTime": "2022-04-26T16:45:56Z",
    "eventSource": "sts.amazonaws.com",
    "eventName": "AssumeRole",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "AWS Internal",
    "errorCode": "AccessDenied",
    "errorMessage": "User: arn:aws:iam::111122223333:user/test-user is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::444455556666:role/test-role-admin",
    "requestParameters": null,
    "responseElements": null,
    "requestID": "32711c3d-80e9-4edb-9c60-91bca5bf4e24",
    "eventID": "eb79c53a-6ad6-4452-8879-381a1f231904",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "111122223333",
    "eventCategory": "Management"
}


IP 制限方法の使い分け

「Jump アカウントで IP 制限を行う方法」はすべての AWS アカウントに対して一律で送信元 IP アドレスによる制限を行う場合に適しており、「スイッチロール先 IAM ロールで IP 制限を行う方法」は AWS アカウント毎に送信元 IP アドレスによるアクセス可否の条件を変更したい場合に適しています。

運用負荷とのトレードオフとなりますが、設定ミス等の対策として両方の方法で設定して多段的に制限を行う方法もあると思います。


さいごに

IAM ユーザー/グループを一つのアカウントに集約する Jump アカウントの設定に加えて、IP アドレス制限を行う場合の設定を試してみました。

セキュリティ要件として送信元 IP アドレスによるマネジメントコンソールへのアクセス制限がある場合などのご参考になれば幸いです。