同じ名前のIAMプリンシパルを区別する方法

2022.09.22

こんにちは、八木です。

IAMユーザやIAMロールは削除して、再度同じ名前のリソースを作成することができます。この場合、リソース自体は別物ですが、リソース名、ARNは同一のものになります。

しかしこれでは問題が発生するケースがあります。

例えば社内資料をS3に保存するケースです。社員はそれぞれ自分の名前のIAMユーザを使用しており、IAMユーザ名ごとにオブジェクトをアップロードできるフォルダ(プレフィックス)を分けています。

この場合のバケットポリシーは以下です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowListingOfUserFolder",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::my-bucket",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalAccount": "123456789012"
                },
                "StringLike": {
                    "s3:prefix": "${aws:username}/*"
                }
            }
        },
        {
            "Sid": "AllowReadWriteObjectInUserFolder",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::my-bucket/${aws:username}/*",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalAccount": "123456789012"
                }
            }
        }
    ]
}

このような運用をしていた場合、Aliceが退職し、その1ヶ月後に別人物のAliceが入社したときはどうなるでしょうか?

使っているIAMユーザは別のものだとしても、退職したAliceと新たに入社したAliceは同じプレフィックスを使用することになります。これでは作成者が異なるファイルが同じプレフィックス配下に混在してしまいます。

そこで、IAMユーザ名(またはロール名)が同じでも別のリソースとして区別したい場合は「一意の識別子(ID)」を利用します。

一意の識別子

IAM がユーザー、ユーザーグループ、ロール、ポリシー、インスタンスプロファイル、サーバー証明書を作成するとき、各リソースには一意の ID が割り当てられます。一意の ID は次のようになります。

AIDAJQABLZS4A3QDU576Q

IAM ID 一意の識別子

IAMユーザを一度削除し、同じ名前のIAMユーザを再度作成した場合、IAMユーザ名やARNは同一になりますが、一意のIDは別のものになります。

この一意のIDでフォルダを分けることによって、同じ名前のIAMユーザでもそれぞれのフォルダを割り当てることができます。

やってみた

先述のケースと同様、IAMユーザごとに利用するS3のプレフィックスを分ける実装をしてみます。

S3バケットに以下のバケットポリシーを設定します。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "AllowListingOfUserFolder",
			"Effect": "Allow",
			"Principal": {
				"AWS": "*"
			},
			"Action": "s3:ListBucket",
			"Resource": "arn:aws:s3:::my-bucket",
			"Condition": {
				"StringEquals": {
					"aws:PrincipalAccount": "123456789012"
				},
				"StringLike": {
					"s3:prefix": "${aws:userid}/*"
				}
			}
		},
		{
			"Sid": "AllowReadWriteObjectInUserFolder",
			"Effect": "Allow",
			"Principal": {
				"AWS": "*"
			},
			"Action": [
				"s3:GetObject",
				"s3:PutObject",
				"s3:DeleteObject"
			],
			"Resource": "arn:aws:s3:::my-bucket/${aws:userid}/*",
			"Condition": {
				"StringEquals": {
					"aws:PrincipalAccount": "123456789012"
				}
			}
		}
	]
}

aws:userid でIAMプリンシパルのIDを参照し、プレフィックスの制限を行なっています。

なお、プリンシパルがIAMロールの場合は上記ポリシーでは機能しません。IAMロールの場合、 aws:useriduniqueId:xxx のようなフォーマットになり、ロールの利用方法によって xxx の部分が変化します。1 ご注意ください。

ではIAMユーザを作成して、実際にS3へファイルのアップロードをしていきます。

まず alice という名前のIAMユーザを作成し、アクセスキーを発行します。

$ aws iam create-user --user-name alice
{
  "User": {
    "Arn": "arn:aws:iam::123456789012:user/alice",
    "CreateDate": "2022-09-20T03:06:18+00:00",
    "Path": "/",
    "UserId": "AIDA5OMIMV5WIQG7EMR67",
    "UserName": "alice"
  }
}
$ aws iam create-access-key --user-name alice
{
    "AccessKey": {
        "UserName": "alice",
        "AccessKeyId": "AIDAXXXXXXXXXXXXXXXXX",
        "Status": "Active",
        "SecretAccessKey": "xxxxxxxxxxxxxxxxxx",
        "CreateDate": "2022-09-20T03:06:35+00:00"
    }
}

別のターミナルを開いてアクセスキーとシークレットアクセスキーを環境変数に設定します。

$ export AWS_ACCESS_KEY_ID=AIDAXXXXXXXXXXXXXXXXX
$ export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxx

IAMユーザのユニークIDを確認します。

$ aws sts get-caller-identity
{
    "UserId": "AIDA5OMIMV5WIQG7EMR67",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/alice"
}

ユニークIDは AIDA5OMIMV5WIQG7EMR67 でした。1人目のユーザaliceはこのユニークIDのプレフィックス配下を自身のファイル置き場として利用します。

適当なファイルをアップロードしてみます。

$ aws s3 cp sample.txt s3://my-bucket/AIDA5OMIMV5WIQG7EMR67/
upload: ./sample.txt to s3://my-bucket/AIDA5OMIMV5WIQG7EMR67/sample.txt

問題なくアップロードできました。

さて、ここでaliceが退職してしまいました。aliceがアクセスできないよう、IAMユーザを削除します。

$ aws iam delete-access-key --user-name alice --access-key-id AIDAXXXXXXXXXXXXXXXXX
$ aws iam delete-user --user-name alice

1ヶ月ほど経つと別のaliceが入社しました。IAMユーザを作成します。

$ aws iam create-user --user-name alice
{
    "User": {
        "Path": "/",
        "UserName": "alice",
        "UserId": "AIDA5OMIMV5WF35E2L7UN",
        "Arn": "arn:aws:iam::123456789012:user/alice",
        "CreateDate": "2022-09-20T06:50:27+00:00"
    }
}
$ aws iam create-access-key --user-name alice
{
    "AccessKey": {
        "UserName": "alice",
        "AccessKeyId": "AKIAYYYYYYYYYYYYYYYY",
        "Status": "Active",
        "SecretAccessKey": "yyyyyyyyyyyyyyy",
        "CreateDate": "2022-09-20T06:50:46+00:00"
    }
}

新しく入社したaliceがS3にアクセスできるか検証します。

別のターミナルを開いてアクセスキーとシークレットアクセスキーを環境変数に設定します。

$ export AWS_ACCESS_KEY_ID=AKIAYYYYYYYYYYYYYYYY
$ export AWS_SECRET_ACCESS_KEY=yyyyyyyyyyyyyyy

IAMユーザのユニークIDを確認します。

$ aws sts get-caller-identity
{
    "UserId": "AIDA5OMIMV5WF35E2L7UN",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/alice"
}

ユニークIDは AIDA5OMIMV5WF35E2L7UN でした。以前作成したIAMユーザとユーザ名およびARNは同じですが、ユニークIDは別のものであることがわかります。

自身のプレフィックス配下にオブジェクトをアップロードしてみます。

$ aws s3 cp sample.txt s3://my-bucket/AIDA5OMIMV5WF35E2L7UN/
upload: ./sample.txt to s3://my-bucket/AIDA5OMIMV5WF35E2L7UN/sample.txt

問題なくアップロードできました。

では以前在籍していたaliceのプレフィックス配下(AIDA5OMIMV5WIQG7EMR67)にはアクセスできるでしょうか?

$ aws s3 cp sample.txt s3://my-bucket/AIDA5OMIMV5WIQG7EMR67/
upload failed: ./sample.txt to s3://my-bucket/AIDA5OMIMV5WIQG7EMR67/sample.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

権限が存在しないため、アクセスできませんでした。

同じIAMユーザ名ですが、アクセスできるプレフィックスを分けられていることが確認できました。

まとめ

IAMプリンシパル名を同じにした場合、ARNは同じになりますが、ユニークIDは異なります。

プリンシパルごとに区別したい場合はユニークIDを利用しましょう!