はじめに
本記事では、Cognito で認証したユーザーに対して、専用の CMK を構築し、他のユーザーが触れないようにアクセス制御する方法をご紹介します。
珍しい要件であり、同様の記事がみつからなかったため、備忘として記事化しました。
実現したいこと
実現したいことは、以下の通りです。
以下のイメージ図をご覧ください。
注意点として、ID プールに紐づけられる認証済みユーザー用の IAM ロールは1つのみです。
上記構成では、全 Cognito ユーザーで IAM ロールを共有するため、IAM ロールで CMK のアクセス先制御を行うのは難しそうです。
結論
キーポリシーの Condition 句で、cognito-identity.amazonaws.com:sub を使用することで、上記を実現可能です。以下をご参照ください。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM policies",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${ACCOUNT_ID}:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow a specific cognito user to use the key",
"Effect": "Allow",
"Principal": "*",
"Action": [
"kms:Describe*",
"kms:Get*",
"kms:List*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:sub": "${IDENTITY_ID}",
"aws:userid": "${AUTH_ROLE_ID}:CognitoIdentityCredentials"
}
}
}
]
}
cognito-identity.amazonaws.com:sub で IdentityId を指定することにより、特定の Cognito ユーザーのみに CMK アクセスを制限することが可能です。2
アクセス元の IAM ロールは限定されているので、aws:userid では認証済みユーザー用の IAM ロール ID を指定しています。
なお、キーポリシーで KMS 権限を付与しているため、IAM ロールに KMS 権限は不要です。Sid「Enable IAM policies」ではデフォルトのキーポリシーを使用していますが、必要に応じて権限を絞ってください。3
構築方法
前提
事前に、Cognito ユーザープール・ID プール作成済であることを前提とします。
Amplify でのCognito関連構築方法に関しては、以下をご参照ください。
amplify add auth
で簡単に認証機能を実装できます。
以下、Admin 相当の権限を保有する Cloud9 のターミナルより、AWS CLI で Cognito ユーザと専用の CMK を作成します。4
- OS : Amazon Linux 2
- AWS CLI : v2.10.1
共通値の設定
まずは、アカウント・リージョン情報や事前作成済みの Cognito に関する値を設定します。
# 共通値
$ ACCOUNT_ID=012345678901
$ REGION=ap-northeast-1
$ USER_POOL_ID=${REGION}_123xyABCD
$ CLIENT_ID=abcdefghijklmnop1234567890
$ IDENTITY_POOL_ID=${REGION}:11111111-1111-1111-1111-111111111111
$ COGNITO_USER_POOL=cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}
$ AUTH_ROLE_NAME=amplify-react-dev-01234-authRole
Cognito ユーザー作成
次に、テスト用の Cognito ユーザーとして、「test_user1」を作成します。
# ユーザー情報
$ USER_NAME=test_user1
$ PASSWORD="Password@1"
# 「test_user1」を作成
$ aws cognito-idp admin-create-user \
--user-pool-id ${USER_POOL_ID} \
--username ${USER_NAME} \
--user-attributes Name=email,Value="test@sample.com" Name=email_verified,Value=true \
--message-action SUPPRESS > /dev/null
$ aws cognito-idp admin-set-user-password \
--user-pool-id ${USER_POOL_ID} \
--username ${USER_NAME} \
--password ${PASSWORD} \
--permanent
CMKの作成
専用 CMK 作成の下準備として、test_user1 の IdentityId を取得します。
# トークン取得(IdentityId取得に必要)
$ ID_TOKEN=$(aws cognito-idp admin-initiate-auth \
--user-pool-id ${USER_POOL_ID} \
--client-id ${CLIENT_ID} \
--auth-flow ADMIN_NO_SRP_AUTH \
--auth-parameters "USERNAME=${USER_NAME},PASSWORD=${PASSWORD}" \
--query "AuthenticationResult.IdToken" \
--output text)
# IdentityId取得
$ IDENTITY_ID=$(aws cognito-identity get-id \
--identity-pool-id ${IDENTITY_POOL_ID} \
--logins "${COGNITO_USER_POOL}=${ID_TOKEN}" \
--query "IdentityId" \
--output text) && echo ${IDENTITY_ID}
ap-northeast-1:1234abcd-a123-b234-c345-222222222222
次に、キーポリシーで許可する IAM ロールを絞るため、Cognito 認証後のロール ID を取得します。
# Cognito 認証済みユーザー用の IAM ロール IDを取得
$ AUTH_ROLE_ID=$(aws iam get-role \
--role-name ${AUTH_ROLE_NAME} \
--query "Role.RoleId" \
--output text) && echo ${AUTH_ROLE_ID}
AROA1234567890EXAMPLE
上記で CMK 作成のための下準備は完了です。
以下コマンドで、test_user1 専用の CMK を作成します。前述の通りに キーポリシーを指定します。
# CMK・キーポリシーを作成
$ KEY_ID=$(aws kms create-key \
--policy \
'{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM policies",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::'${ACCOUNT_ID}':root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow a specific cognito user to use the key",
"Effect": "Allow",
"Principal": "*",
"Action": [
"kms:Describe*",
"kms:Get*",
"kms:List*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:sub": "'${IDENTITY_ID}'",
"aws:userid": "'${AUTH_ROLE_ID}':CognitoIdentityCredentials"
}
}
}
]
}' \
--query "KeyMetadata.KeyId" \
--output text) && echo ${KEY_ID}
b8a9477d-836c-491f-857e-07937918959b
以上で、Cognito ユーザと専用の CMK 作成は完了です。
アクセステスト
想定通りの制御ができているか、以下2種のテストをします。
- 正常系:
Cognito ユーザー「test_user1」が、専用の CMK にアクセスできることの確認 - 異常系:
他の Cognito ユーザーが、test_user1 専用の CMK にアクセスできないことの確認
1. 正常系
test_user1 が専用の CMK にアクセスできることを確認します。
まずは、test_user1 のクレデンシャルを取得・設定します。
※トークンなどは、構築時に取得したコマンドと同じなので省略
# クレデンシャル取得
$ CREDENTIALS=$(aws cognito-identity get-credentials-for-identity \
--identity-id ${IDENTITY_ID} \
--logins "${COGNITO_USER_POOL}=${ID_TOKEN}")
# クレデンシャルを設定
$ export AWS_ACCESS_KEY_ID=`echo "${CREDENTIALS}" | jq -r ".Credentials.AccessKeyId"`
$ export AWS_SECRET_ACCESS_KEY=`echo "${CREDENTIALS}" | jq -r ".Credentials.SecretKey"`
$ export AWS_SECURITY_TOKEN=`echo "${CREDENTIALS}" | jq -r ".Credentials.SessionToken"`
$ aws sts get-caller-identity
{
"UserId": "AROA1234567890EXAMPLE:CognitoIdentityCredentials",
"Account": "012345678901",
"Arn": "arn:aws:sts::012345678901:assumed-role/amplify-react-dev-01234-authRole/CognitoIdentityCredentials"
}
上記クレデンシャルで、test_user1 専用の CMK にアクセスしてみます。
# CMKの情報を取得
$ aws kms describe-key --key-id ${KEY_ID}
{
"KeyMetadata": {
"AWSAccountId": "012345678901",
"KeyId": "b8a9477d-836c-491f-857e-07937918959b",
"Arn": "arn:aws:kms:ap-northeast-1:012345678901:key/b8a9477d-836c-491f-857e-07937918959b",
"CreationDate": "2023-03-13T10:14:42.260000+00:00",
"Enabled": true,
"Description": "",
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
"KeySpec": "SYMMETRIC_DEFAULT",
"EncryptionAlgorithms": [
"SYMMETRIC_DEFAULT"
],
"MultiRegion": false
}
}
無事にアクセスすることができました。
2. 異常系
他の Cognito ユーザーを作って、前述の CMK にアクセスできないことを確認してみます。
まずは、「test_user2」を作成します。
# クレデンシャルのリセット
$ export -n AWS_ACCESS_KEY_ID
$ export -n AWS_SECRET_ACCESS_KEY
$ export -n AWS_SECURITY_TOKEN
# 他ユーザー情報を定義
$ USER_NAME=test_user2
$ PASSWORD="Password@2"
# 他ユーザー「test_user2」を作成
$ aws cognito-idp admin-create-user \
--user-pool-id ${USER_POOL_ID} \
--username ${USER_NAME} \
--user-attributes Name=email,Value="test2@sample.com" Name=email_verified,Value=true \
--message-action SUPPRESS > /dev/null
$ aws cognito-idp admin-set-user-password \
--user-pool-id ${USER_POOL_ID} \
--username ${USER_NAME} \
--password ${PASSWORD} \
--permanent
次に、test_user2 のクレデンシャルを取得・設定します。
# 認証・認可
$ ID_TOKEN=$(aws cognito-idp admin-initiate-auth \
--user-pool-id ${USER_POOL_ID} \
--client-id ${CLIENT_ID} \
--auth-flow ADMIN_NO_SRP_AUTH \
--auth-parameters "USERNAME=${USER_NAME},PASSWORD=${PASSWORD}" \
--query "AuthenticationResult.IdToken" \
--output text)
$ IDENTITY_ID=$(aws cognito-identity get-id \
--identity-pool-id ${IDENTITY_POOL_ID} \
--logins "${COGNITO_USER_POOL}=${ID_TOKEN}" \
--query "IdentityId" \
--output text)
$ CREDENTIALS=$(aws cognito-identity get-credentials-for-identity \
--identity-id ${IDENTITY_ID} \
--logins "${COGNITO_USER_POOL}=${ID_TOKEN}")
# クレデンシャルを設定
$ export AWS_ACCESS_KEY_ID=`echo "${CREDENTIALS}" | jq -r ".Credentials.AccessKeyId"`
$ export AWS_SECRET_ACCESS_KEY=`echo "${CREDENTIALS}" | jq -r ".Credentials.SecretKey"`
$ export AWS_SECURITY_TOKEN=`echo "${CREDENTIALS}" | jq -r ".Credentials.SessionToken"`
$ aws sts get-caller-identity
{
"UserId": "AROA1234567890EXAMPLE:CognitoIdentityCredentials",
"Account": "012345678901",
"Arn": "arn:aws:sts::012345678901:assumed-role/amplify-react-dev-01234-authRole/CognitoIdentityCredentials"
}
get-caller-identity の実行結果は、test_user1 と同じです。この点については後ほど触れます。
上記クレデンシャルで、test_user1 専用の CMK にアクセスしてみます。
# CMKの情報を取得
$ aws kms describe-key --key-id ${KEY_ID}
An error occurred (AccessDeniedException) when calling the DescribeKey operation: User: arn:aws:sts::012345678901:assumed-role/amplify-react-dev-01234-authRole/CognitoIdentityCredentials is not authorized to perform: kms:DescribeKey on resource: arn:aws:kms:ap-northeast-1:012345678901:key/b8a9477d-836c-491f-857e-07937918959b because no identity-based policy allows the kms:DescribeKey action
想定通り、アクセス拒否されました。
考察
test_user1 と test_user2 で get-caller-identity の結果が同じなのに、CMK のアクセス結果が異なるのはなぜでしょうか。
まずは、test_user1 のリクエストに関する CloudTrail ログをみてみます。
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROA1234567890EXAMPLE:CognitoIdentityCredentials",
"sessionContext": {
"sessionIssuer": {
"arn": "arn:aws:iam::012345678901:role/amplify-react-dev-01234-authRole"
},
"webIdFederationData": {
"federatedProvider": "cognito-identity.amazonaws.com",
"attributes": {
"cognito-identity.amazonaws.com:aud": "ap-northeast-1:11111111-1111-1111-1111-111111111111",
"cognito-identity.amazonaws.com:sub": "ap-northeast-1:1234abcd-a123-b234-c345-222222222222"
}
}
}
},
"eventSource": "kms.amazonaws.com",
"eventName": "DescribeKey"
}
上記の通り、Cognito 経由のリクエストには、WebIdFederationData が含まれています。
次に、AccessDenied されたtest_user2 のリクエストに関する CloudTrail ログをみてみます。
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROA1234567890EXAMPLE:CognitoIdentityCredentials",
"sessionContext": {
"sessionIssuer": {
"arn": "arn:aws:iam::012345678901:role/amplify-react-dev-01234-authRole"
},
"webIdFederationData": {
"federatedProvider": "cognito-identity.amazonaws.com",
"attributes": {
"cognito-identity.amazonaws.com:aud": "ap-northeast-1:11111111-1111-1111-1111-111111111111",
- "cognito-identity.amazonaws.com:sub": "ap-northeast-1:1234abcd-a123-b234-c345-222222222222"
+ "cognito-identity.amazonaws.com:sub": "ap-northeast-1:9876abcd-x000-y111-z222-333333333333"
}
}
}
},
"eventSource": "kms.amazonaws.com",
"eventName": "DescribeKey",
+ "errorCode": "AccessDenied"
}
test_user1 と test_user2 の各リクエストで、cognito-identity.amazonaws.com:sub の値が異なっています。
この値は、Cognito ID プールに紐づく IdentityId であり、キーポリシーでリクエスト元の Cognito ユーザーを識別することができます。
ちなみに、cognito-identity.amazonaws.com:aud は、Cognito ID プール自体の ID になります。
この値は、認証済みユーザー用の IAM ロールにおける信頼ポリシーでも使用されています。
イメージ図の通り、ID プールと IAM ロールがきちんと紐づいている証ですね。
信頼ポリシー例
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "ap-northeast-1:11111111-1111-1111-1111-111111111111"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}
おわりに
本記事では、Cognito で認証したユーザーに対して、キーポリシーを使って専用 CMK へアクセス許可する方法をご紹介しました。
CLI では Cognito の挙動が分かりやすく、本記事の執筆を通じて、筆者自身も知識の整理ができました。本記事がどなたかのお役に立てれば、幸いです。
参考情報
- AWS CLIで動かして学ぶCognito IDプールを利用したAWSの一時クレデンシャルキー発行
- AWS Cognitoで、AWS CLI でユーザを作りパスワードを設定してCONFIRMEDにする
- フルスタック React アプリケーションを構築する
-
本記事ではCognito ユーザープールで認証したユーザーのことを、Cognito ユーザーと呼んでいます。 ↩
-
記事用に Action 句で幅広い権限を付与していますが、実開発では権限を絞っています。 ↩
-
IAM ポリシーが AWS KMS の KMS キーへのアクセスをユーザーまたはロールに許可しないようにするにはどうすればよいですか? ↩
-
各入力値・実行結果は、記事用に仮値へ変換していますので、ご留意ください。 ↩