Provisioning AWS KMS-Encrypted Buckets with Cross Account Access

  • An S3 bucket, s3://account-a-bucket, is to be created in account-a and made accessible to an external AWS account, account-b
  • A new KMS-CMK key needs to be created, with bucket encryption enabled using this key
  • IAM user Ann, in account-a, requires access to the bucket
  • IAM user Dilip, in account-b, also requires the same level of access to the bucket
  • Level of access required includes, ListBucket/GetObject/PutObject

Overview of Activities Required for each Account

Below diagram shows an overview of tasks each account Administrator would be expected to perform.

Activities for account-a

  • Create KMS-CMK and attach Key policy document, which grants
    account-b access to cryptographic operations using this Key
  • Create the S3 bucket and attach appropriate bucket policy, granting account-b with permissions to ListBucket/GetObject/PutObject
  • Enable bucket encryption with the KMS-CMK key created
  • Attach an inline policy allowing IAM user, Ann access to the KMS-CMK key and bucket

Activities for account-b

Once account-a activities are complete, an Administrator in account-b will have access to the bucket/key, and will also have authority to delegate the access to nominated users within this account. In the scenario described earlier, the nominated IAM user is, Dilip.

  • The Administrator attaches an inline policy to user Dilip, granting him the access on the bucket and KMS-CMK key
  • The inline policy required would be identical to the one applied to IAM user, Ann

Prerequisites: AWS CLI Credentials for Admin Tasks

The AWS CLI will be used perform the tasks required in each account.

[profile account-a-admin]
region = <region>
aws_access_key_id = <access key id of account-a admin user>
aws_secret_access_key = <aws secret key for account-a admin user>
[profile account-b-admin]
region = <region>
aws_access_key_id = <access key id of account-b admin user>
aws_secret_access_key = <aws secret key for account-b admin user>

Executing Activities Required for account-a

1. Create KMS Key and Policy

We can use the following policy when creating the KMS key. The policy contains two action statements:

  • The first of these permits management of access for this key from the account itself
  • The second action grants account-b access to the key for the purposes of cryptographic operations
{
"Version": "2012-10-17",
"Id": "key-policy-account-a-bucket",
"Statement": [
{
"Sid": "Enable IAM user permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-a:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-b:root"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
]
}
  • Using the aws cli, create the key and attach corresponding policy by running:
$ aws --profile account-a-admin \
create-key \
--tags TagKey=Purpose,TagValue=AWS-KMS-Key \
--description "Key used for s3://account-a-bucket encryption" \
--key-usage "ENCRYPT_DECRYPT" \
--policy '{
"Version": "2012-10-17",
"Id": "key-policy-account-a-bucket",
"Statement": [
{
"Sid": "Enable IAM user permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-a:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-b:root"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
]
}'
  • This should return the details of the key created.
{
"KeyMetadata": {
"AWSAccountId": "account-a",
"KeyId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"Arn": "arn:aws:kms:<region>:account-a:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",

"CreationDate": "2021-04-06T18:21:45+10:00",
"Enabled": true,
"Description": "Key used for s3://account-a-bucket bucket encryption",
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
"EncryptionAlgorithms": [
"SYMMETRIC_DEFAULT"
]
}
}
  • Note down the key Arn and KeyId from the output returned
  • Create an alias alias/3-account-a-bucket-encryption for the key by specifying the KeyId value for parameter --target-key-id:
$ aws --profile account-a-admin \
kms create-alias \
--alias-name alias/s3-account-a-bucket-encryption \
--target-key-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

2. Create S3 Bucket and Policy

  • Create the target bucket, s3://account-a-bucket:
$ aws --profile account-a-admin \
s3 mb s3://account-a-bucket
  • The following shows a sample bucket policy. It contains three Actions to allow access to the bucket from account-b:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-b:root"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::account-a-bucket"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-b:root"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::account-a-bucket/*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account-b:root"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::account-a-bucket/*"
}
]
}
  • Attach the policy to the bucket:
$ aws --profile account-a-admin \
s3api put-bucket-policy \
--bucket account-a-bucket \
--policy '{ \
"Version": "2012-10-17", \
"Statement": [ \
{ \
"Effect": "Allow", \
"Principal": { \
"AWS": "arn:aws:iam::account-b:root" \
}, \
"Action": "s3:ListBucket", \
"Resource": "arn:aws:s3:::account-a-bucket" \
}, \
{ \
"Effect": "Allow", \
"Principal": { \
"AWS": "arn:aws:iam::account-b:root" \
}, \
"Action": "s3:GetObject", \
"Resource": "arn:aws:s3:::account-a-bucket/*" \
}, \
{ \
"Effect": "Allow", \
"Principal": { \
"AWS": "arn:aws:iam::account-b:root" \
}, \
"Action": "s3:PutObject", \
"Resource": "arn:aws:s3:::account-a-bucket/*" \
} \
] \
}'

3. Enable Bucket Encryption and Attach KMS Key

  • Using the key Arn noted earlier, enable bucket encryption by supplying the Arn as the KMSMasterKeyID :
$ aws --profile account-a-admin \
s3api put-bucket-encryption \
--bucket account-a-bucket \
--server-side-encryption-configuration '{
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:<region>:account-a:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"BucketKeyEnabled": true
}
]
}'

4. Attach Inline Policy to IAM User, Ann

  • Assuming IAM user, Ann has already been created, we can use the following sample policy to delegate the required access to Ann.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucketContents",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::account-a-bucket",
]
},
{
"Sid": "UploadDownloadBucketPermissions",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject""
],
"Resource": [
"arn:aws:s3:::account-a-bucket/*"
]
},
{
"Sid": "EncryptDecryptBucketContents",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": [
"arn:aws:kms:<region>:account-a:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
]
}
]
}
  • The first three actions allow Ann to ListBucket/GetObject/PutObject. The last Action allows access to the KMS key.
  • Create a file named access-s3-account-a-bucket.json containing the policy definition above
  • Attach the policy Ann’s username:
$ aws --profile account-a-admin \
iam put-user-policy \
--user-name ann \
--policy-name access-s3-account-a-bucket \
--policy-document file://access-s3-account-a-bucket.json

Executing Activities Required for account-b

Once all account-a activities have successfully completed, the account-b Administrator will have the access required to the bucket/KMS key, with authority to delegate to nominated users.

1. Attach Inline Policy to IAM User, Dilip

To grant access to IAM User, Dilip, we can use the same inline policy that was used for Ann, i.e. access-s3-account-a-bucket.json.

$ aws --profile account-b-admin \
iam put-user-policy \
--user-name dilip \
--policy-name access-s3-account-a-bucket \
--policy-document file://access-s3-account-a-bucket.json

Conclusion

By now, the requirements for the scenario we defined before commencing, should all be fulfilled, that is:

  • An encrypted S3 bucket has been created in AWS account-a
  • The bucket is accessible from account-b
  • account-a user, Ann, and account-b user, Dilip both have the access required to the bucket

--

--

Learner. Interests include Cloud and Devops technologies.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Tony Tannous

Tony Tannous

62 Followers

Learner. Interests include Cloud and Devops technologies.