Terraform - Delegate Access Across AWS Accounts Using IAM Roles
In this post, we'll see how a user who has no access can have permission to AWS resource (here, S3) by assuming a role with Trust Relationship.
The makeup of an IAM role is the same as that of an IAM user and is only differentiated by the following qualities (How to use trust policies with IAM roles):
- An IAM role does not have long term credentials associated with it; rather, a principal (an IAM user, machine, or other authenticated identity) assumes the IAM role and inherits the permissions assigned to that role.
- The tokens issued when a principal assumes an IAM role are temporary. Their expiration reduces the risks associated with credentials leaking and being reused.
- An IAM role has a trust policy that defines which conditions must be met to allow other principals to assume it. This trust policy reduces the risks associated with privilege escalation.
In the following code, the user ("random") in trusted (dev) account assumes a role that has a permission for listing S3 bucket in trusting (prod) account.
This is similar to Delegate Access Across AWS Accounts Using IAM Roles:
main.tf:
terraform { required_providers { aws = { #source = "hashicorp/aws" version = "~> 3.0" } } } data "aws_caller_identity" "dev" {} data "aws_caller_identity" "prod" { provider = aws.prod } # Utils account containing users provider "aws" { profile = "dev" region = var.region_dev } # Prod account provider "aws" { profile = "prod" region = var.region_prod alias = "prod" } # prod account resource "aws_iam_role" "prod_list_s3" { name = "s3-list-role" assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [ { Effect = "Allow", Action = "sts:AssumeRole", Principal = { "AWS" : "arn:aws:iam::${data.aws_caller_identity.dev.account_id}:root" } }] }) provider = aws.prod } resource "aws_iam_policy" "s3_list_all" { name = "s3_list_all" description = "allows listing all s3 buckets" provider = aws.prod # policy = file("role_permissions_policy.json") policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:ListAllMyBuckets", "Resource": "*" } ] } EOF } resource "aws_iam_policy_attachment" "s3_list_all" { name = "list s3 buckets policy to role" roles = ["${aws_iam_role.prod_list_s3.name}"] policy_arn = aws_iam_policy.s3_list_all.arn provider = aws.prod } # dev account resource "aws_iam_user" "random" { name = "random_user" tags = { name = "random" } } resource "aws_iam_policy" "prod_s3" { name = "prod_s3" description = "allow assuming prod_s3 role" policy = jsonencode({ Version = "2012-10-17", Statement = [ { Effect = "Allow", Action = "sts:AssumeRole", Resource = "arn:aws:iam::${data.aws_caller_identity.prod.account_id}:role/${aws_iam_role.prod_list_s3.name}" }] }) } resource "aws_iam_user_policy_attachment" "prod_s3" { user = aws_iam_user.random.name policy_arn = aws_iam_policy.prod_s3.arn }
variables.tf:
variable "region_dev" { type = string default = "us-east-1" } # AWS account region for prod account variable "region_prod" { type = string default = "us-east-1" }
$ terraform init Initializing the backend... Initializing provider plugins... - Reusing previous version of hashicorp/aws from the dependency lock file - Using previously-installed hashicorp/aws v3.43.0 Terraform has been successfully initialized! ... $ terraform apply --auto-approve ... aws_iam_user.random: Creating... aws_iam_policy.s3_list_all: Creating... aws_iam_role.prod_list_s3: Creating... aws_iam_user.random: Creation complete after 1s [id=random_user] aws_iam_policy.s3_list_all: Creation complete after 2s [id=arn:aws:iam::526262051452:policy/s3_list_all] aws_iam_role.prod_list_s3: Creation complete after 2s [id=s3-list-role] aws_iam_policy.prod_s3: Creating... aws_iam_policy_attachment.s3_list_all: Creating... aws_iam_policy_attachment.s3_list_all: Creation complete after 2s [id=list s3 buckets policy to role] aws_iam_policy.prod_s3: Creation complete after 2s [id=arn:aws:iam::526262051452:policy/prod_s3] aws_iam_user_policy_attachment.prod_s3: Creating... aws_iam_user_policy_attachment.prod_s3: Creation complete after 1s [id=random_user-20210603160902401200000001] Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
prod account:
dev account:
Now that we need to run AWS cli, we should have the following credentials (~/.aws/credentials) that has two profiles (prod and dev):
... [prod] region=us-east-1 aws_access_key_id = AKIAXVB5JUJ6CX6XZ4M6 aws_secret_access_key = wh9WeGs...a1gNicCUe [dev] region=us-east-1 aws_access_key_id = AKIAQV57YS3YJAOBU6NC aws_secret_access_key = oUH...ft3LP1
Get the s3-list-role arn:
$ aws iam list-roles --query "Roles[?RoleName == 's3-list-role'].[RoleName, Arn]" --profile prod [ [ "s3-list-role", "arn:aws:iam::526262051452:role/s3-list-role" ] ]
Request STS from AWS using the role ARN and a session name:
$ aws sts assume-role --role-arn "arn:aws:iam::526262051452:role/s3-list-role" --role-session-name "my_sts_session" --profile dev { "Credentials": { "AccessKeyId": "ASIAXVB5JUJ6CLMU6MAI", "SecretAccessKey": "wOOknb4i4ogXJq35obk8ulkEkRfEgDsy3vZC64L9", "SessionToken": "IQoJb3JpZ2luX2VjEEoaCXVzLXdlc3QtMiJGMEQCIDZ2LAaAeBrA2YXZOw3a3zPoZ1fXPcYiZe0X9BaFdpiOAiBWFO/QqgqIko0AveiHaZRwWGedKhdCz0dBGmeuxBufbyqkAgjz//////////8BEAMaDDUyNjI2MjA1MTQ1MiIMkYr++Ki41PYWSPdAKvgBd/1A6VtUAeCXYFQ8BNARuf7fiByfheLEJ0lvf8VfVQT6cv8dKtjx2KRizI/44hyJsGDdnJJ4rkJP5xJ6bJaeTq4j/NSFlB3xNzHE92Hpao2zHBMC/zGJUlIauFD6+f1X5MzFIRVm8CGGLdI9mKmhC9PbuJZ9tEVkIbZo7q2Bfwn6UNUQBjIoPRdZvgfO/d2Z2lwSeNVlMwIh8uC/CMjdylnbgsylQhcYyhVHwMWh1yXkvs3ui3mdVJEyP18z1/CBFX1YsSbegS2njXob2rHdi+RSZkHyJSE24b9szo2QRzo0SS6G+EETYN9lkmzQHmSfamXUMyOtImcwqKHkhQY6ngG5KRxvjPY3J3DVZajlWJrDx4Ket+k0nXJJWgNMt/fr74/SppAH3ciC6mHeoQ2gsg3kJXcZvfEpfpOWZUxV94FUEtCx9zBAMUjequ5z42JTUZgBRT6hVucoLj+BU7EKY13vIn3Vm8X+ZCu5ZjiPsyKSaKEpjUF95B9JyJdBt4T91IMjfNwtCgd0cj9BLAS0tUYonqi1cVFgyfct+wguNg==", "Expiration": "2021-06-03T18:26:00+00:00" }, "AssumedRoleUser": { "AssumedRoleId": "AROAXVB5JUJ6HVCMJKEKP:my_sts_session", "Arn": "arn:aws:sts::526262051452:assumed-role/s3-list-role/my_sts_session" } }
Export the temporary credentials as environment variables:
$ export AWS_ACCESS_KEY_ID=ASIAXVB5JUJ6CLMU6MAI $ export AWS_SECRET_ACCESS_KEY=wOOknb4i4ogXJq35obk8ulkEkRfEgDsy3vZC64L9 $ export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEEoaCXVzLXdlc3QtMiJGMEQCIDZ2LAaAeBrA2YXZOw3a3zPoZ1fXPcYiZe0X9BaFdpiOAiBWFO/QqgqIko0AveiHaZRwWGedKhdCz0dBGmeuxBufbyqkAgjz//////////8BEAMaDDUyNjI2MjA1MTQ1MiIMkYr++Ki41PYWSPdAKvgBd/1A6VtUAeCXYFQ8BNARuf7fiByfheLEJ0lvf8VfVQT6cv8dKtjx2KRizI/44hyJsGDdnJJ4rkJP5xJ6bJaeTq4j/NSFlB3xNzHE92Hpao2zHBMC/zGJUlIauFD6+f1X5MzFIRVm8CGGLdI9mKmhC9PbuJZ9tEVkIbZo7q2Bfwn6UNUQBjIoPRdZvgfO/d2Z2lwSeNVlMwIh8uC/CMjdylnbgsylQhcYyhVHwMWh1yXkvs3ui3mdVJEyP18z1/CBFX1YsSbegS2njXob2rHdi+RSZkHyJSE24b9szo2QRzo0SS6G+EETYN9lkmzQHmSfamXUMyOtImcwqKHkhQY6ngG5KRxvjPY3J3DVZajlWJrDx4Ket+k0nXJJWgNMt/fr74/SppAH3ciC6mHeoQ2gsg3kJXcZvfEpfpOWZUxV94FUEtCx9zBAMUjequ5z42JTUZgBRT6hVucoLj+BU7EKY13vIn3Vm8X+ZCu5ZjiPsyKSaKEpjUF95B9JyJdBt4T91IMjfNwtCgd0cj9BLAS0tUYonqi1cVFgyfct+wguNg==
Now the "random" user in the "dev" account can access the S3 in "prod" account:
$ aws s3 ls 2020-10-27 15:13:06 bogo-alb 2020-08-19 20:09:21 bogo-ddb-to-es 2021-05-04 18:04:10 bogo-production-app
Terraform
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization