Best Practices for Granting Application Permissions to AWS Resources

Alan Zhao
6 min readJun 30, 2023

--

As a cloud-focused engineer, it’s inevitable that I have to consume cloud resources locally from my computer. Let it be running some CLI commands or standing up a working application environment that talks to the cloud.

Setting up the correct permissions with boundaries and security in mind is nothing of an easy task.

In this article, I’ll demonstrate how I use a dedicated SSO user to assume an application role in order to access AWS resources.

I created a Terraform script that can provision all the resources described below here: https://gist.github.com/alanzhaonys/244f418d063583b56c5572875a9fe870

Skip to SSO CLI Login section, once you have all the resource.

IAM Role and Policies

In IAM console, let’s create a role and two customer managed policies.

The Role

Create a placeholder IAM role my-application-dev-role. Select Trusted entity type: AWS service and Use case: EC2 for now. We will modify it later.

The Policies

Create two customer managed policies.

The first policy my-application-dev-policies defines all the permissions that the application will need. My sample policy looks like below.

{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ssm:GetParameter"
],
"Resource": "arn:aws:ssm:us-east-1:1234567890:parameter/MY-APPLICATION/*",
"Effect": "Allow"
},
{
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:Query",
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:BatchWriteItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:TransactGetItems",
"dynamodb:TransactWriteItems"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:1234567890:table/my_application_*"
],
"Effect": "Allow"
}
]
}

Don’t forget to practice the least privilege principle!

The second policy my-application-assume-dev-role-policy has permission to assume the role my-application-dev-role that we just created. This is the policy that gets attached to the SSO user.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::1234567890:role/my-application-dev-role"
}
]
}

One more step, go back to the my-application-dev-role IAM console. Attach my-application-dev-policies (the first policy) to the role, then leave it for now.

SSO User

First, create a new user in IAM Identity Center Users, don’t assign it to any group yet.

AWS will send a new user invitation email. Go ahead to open the email and create user password. Enable MFA if it’s required.

If you log into the new account, it’s going to say “You do not have any applications.”, that’s expected because the new SSO user is not yet associated to any account and permission.

Group

Next, create a group MyApplicationDevGroup and add the new user to it.

The group will be very convenient when adding more users later on. All the users will inherit the same access.

Permission Set

Go to Permission sets, create a new Custom permission set MyApplicationDevPermissions, attach the second policy my-application-assume-dev-role-policy to it.

Account Assignment

Go to AWS accounts, select the account, and click on Assign users or groups. Select MyApplicationDevGroup and MyApplicationDevPermissions in the following screens. The screenshot below demonstrates what should be seen.

SSO CLI Login

To login the SSO user from CLI, you have to configure SSO first.

AWS has a comprehensive tutorial here: https://docs.aws.amazon.com/cli/latest/userguide/sso-configure-profile-token.html

In a nutshell, run the aws configure sso command. It has a wizard interface similar to below.

aws configure sso
SSO session name (Recommended): MyApplicationDevSession
SSO start URL [None]: https://MyApplication.awsapps.com/start
SSO region [None]: us-east-1
SSO registration scopes [sso:account:access]:
Attempting to automatically open the SSO authorization page in your default browser.
If the browser does not open or you wish to use a different device to authorize this request, open the following URL:

https://device.sso.us-east-1.amazonaws.com/

Then enter the code:

XXXX-XXXX

The only AWS account available to you is: 1234567890
Using the account ID 1234567890
The only role available to you is: MyApplicationDevPermissions
Using the role name "MyApplicationDevPermissions"
CLI default client Region [us-east-1]:
CLI default output format [json]:
CLI profile name [MyApplicationDevPermissions-1234567890]: MyApplicationDevProfile

To use this profile, specify the profile name using --profile, as shown:

aws s3 ls --profile MyApplicationDevProfile

I have a SSO profile created at the end of the setup above, we will call it MyApplicationDevProfile.

Role Modification

First, make sure login from CLI using the user profile works with aws sso login --profile MyApplicationDevProfile.

It will open a browser page and prompt user to enter username, password and MFA, then return a successful message about the login in CLI.

Next, find out the SSO user ARN with command aws sts get-caller-identity --profile MyApplicationProfile. The console should return a JSON blob similar to below, go ahead copy the ARN.

{
"UserId": "XXXXXXXX:your-email@here.com",
"Account": "1234567890",
"Arn": "arn:aws:sts::1234567890:assumed-role/AWSReservedSSO_....."
}

Go back to the my-appplication-dev-role IAM console, edit its Trust policy so it looks like below and save.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:sts::1234567890:assumed-role/AWSReservedSSO_.....",
]
},
"Action": "sts:AssumeRole"
}
]
}

If yow followed along this far, congratulations. You now have a SSO user that can assume the role my-application-dev-role from CLI, but we’re not done yet.

Assume Role

Remember, the SSO user doesn’t have direct access to any AWS resources. It needs to assume the role my-application-dev-role first. This is a safeguard to prevent user to have direct access to the AWS resources.

By assuming the role programmatically, you get a set of temporary credential as AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN that can be used to construct a AWS credential profile. With it, you’re able to access the AWS resources permitted in the my-application-dev-policies.

I have the following block of Bash script that I implement in my Docker workflow to containerize the application, hence the application will have all the permissions it needs. I also implement the script in the CI/CD pipelines to run unit tests. You can modify the script to run CLI commands after all the export, for example aws s3 ls.

#!/bin/bash

# AWS Account ID
ACCOUNT_ID=1234567890
# Your SSO user profile
SOURCE_PROFILE=MyApplicationDevProfile
# Your application role
ROLE_TO_ASSUME=my-application-dev-role
# Assume role session name
SESSION=${ROLE_TO_ASSUME}-assumed-role-session

# You can also pass in --duration-seconds to limit the session duration
ASSUMED_ROLE=$(aws sts assume-role \
--role-arn "arn:aws:iam::${ACCOUNT_ID}:role/$ROLE_TO_ASSUME" \
--role-session-name $SESSION \
--profile $SOURCE_PROFILE \
--output text)

if [ -z "$ASSUMED_ROLE" ]; then
echo The role $ROLE_TO_ASSUME cannot be assumed
exit
fi

export AWS_REGION=$(aws configure get region)
export AWS_ACCESS_KEY_ID=$(echo $ASSUMED_ROLE | awk '{print $5}')
export AWS_SECRET_ACCESS_KEY=$(echo $ASSUMED_ROLE | awk '{print $7}')
export AWS_SESSION_TOKEN=$(echo $ASSUMED_ROLE | awk '{print $8}')

# Test if I have access to S3
aws s3 ls

The script can also be improved to populate/update a profile entry in ~/.aws/credentials, so AWS CLI can be ran straight from command console. The temporary security credentials will look like the following.

[profile my-application-assumed-profile]
aws_access_key_id=xxxxx
aws_secret_access_key=xxxxx
aws_session_token=xxxx

The assumed role has a Max session duration of one hour. This is a hard limit and can’t be increased.

Final Words

With this practice in place, the single set of IAM policy my-application-dev-policies can be shared. Locally, permissions are inherited by assuming the role. In the AWS environment, it can be attached to the container execution role. All that means the same set of permissions can used locally and in production.

AWS SSO login can be replaced with any compatible identity provider that your company uses. You should assume the role defined for the application to gain access to AWS resources, instead of using the role and permissions directly attached to the user. This ensures the highest security standard, enforces permission consistency between environments, and isolates permissions between user and application.

--

--

Alan Zhao
Alan Zhao

Written by Alan Zhao

Solutions Architect & Software Engineer

No responses yet