AWS Partner Network (APN) Blog

Securely Using External ID for Accessing AWS Accounts Owned by Others

By Ram Dileepan, Principal Solutions Architect – AWS
By Patrick McDowell, Sr. Partner Solutions Architect, Security – AWS

It’s often required for a partner solution running on Amazon Web Services (AWS) to access AWS accounts owned by their customers (third-party AWS accounts). This kind of access is known as cross-account access. In such scenarios, a cross-account AWS Identity and Access Management (IAM) role with external ID should be used.

In a previous blog post, we described how to use IAM roles with external ID to access a third-party AWS account. In another blog post, we described the importance of an external ID when using a cross-account IAM role to access third-party AWS accounts.

We also provided some ways to automate the creation of cross-account roles, which you can learn more about here:

In this post, we’ll focus on the best practices for using external ID to avoid the confused deputy problem it is designed to solve.

Example of Cross-Account Access

Let’s take a look at a log management solution which accesses customers’ Amazon CloudWatch logs as an example. The solution uses AWS Lambda functions running in a partner’s AWS account to access CloudWatch logs in multiple different customers’ AWS accounts.

Securely-Using-External-ID-1

Figure 1 – Log management solution which accesses customers’ CloudWatch logs.

For a partner to gain access to a customer’s Amazon CloudWatch logs, the customer must create an IAM role for the partner solution to use. The IAM role requires permissions to access CloudWatch logs and the customer must share the Amazon Resource Name (ARN) associated with the role with the partner.

However, without an external ID anyone who guesses or has the role’s ARN can use the IAM role to access that customer’s CloudWatch logs through this partner solution.

This form of permission escalation is called the confused deputy problem, and an external ID can eliminate this issue when used correctly.

During a partner solution’s onboarding process, the partner will generate an external ID and provide that to the customer. The customer then uses that external ID when creating the IAM role with access to their CloudWatch logs. When an external ID is included in creating the role, the partner solution now must use the role ARN in combination with this external ID to access the customer’s CloudWatch logs.

Below is a list of best practices for using external ID to access customer AWS accounts securely. We’ll continue to use the example above to explain these best practices.

Generate the External ID

It’s important to generate the external ID that a customer uses to create their cross-account IAM role. The external ID should not be provided by the customer. If it can be provided by the customer, a customer can access someone else’s AWS account using their role ARN and external ID.

If the external ID is owned by the solution provider (and used during role assumption) regardless of the ARN that Customer A provides (in this case, Customer B’s ARN), the solution will use Customer A’s external ID which will fail to authenticate.

Having the solution provider generate the ID ensures they are able to enforce uniqueness across all of their customers, as well as ensure the external ID format remains at a consistent level.

We recommend implementing a process that ensures a random unique value—such as a Universally Unique Identifier—is generated for the external ID a customer would use to setup a cross account role.

Prevent Customers from Changing the External ID

A partner solution should prevent customers from changing the external ID in the solution at any time. The solution provider may optionally have a feature that allows a new external ID to be generated.

However, if the customer can change the external ID, this leaves the potential for using another customer’s external ID and IAM role ARN to get access to their AWS resources through the partner solution.

Use Unique Identifier for External ID

If they are not unique, it provides a possibility to expose one customer’s data to another.

For example, let’s say both Customer A and Customer B have the same external ID. Customer B can get access to Customer A’s AWS account as long as Customer B has the ARN for the cross-account access role created by Customer A.

Test the Customer’s Configuration

Ideally, your service provides a generated AWS CloudFormation template that makes it easy for customers to create their role with a properly configured trust policy.

However, there’s always a risk your customer could make a mistake during the role configuration. If they don’t include the appropriate external ID condition in the role’s trust policy, it provides no protection against the risks discussed above.

To prevent this, your system should attempt to assume the role without specifying an external ID. If that call is successful, the system should reject the configuration and provide an error message back to the customer.

Steps to Set Up Cross-Account Access Securely

For the partner solution to access a customer’s AWS account, the partner and customers have to follow the following steps to set it up:

  1. Generate a unique external ID: The partner generates a unique external ID for the customer. They can provide this to the customer on their user portal.
  2. Create an IAM role in customer AWS account: Customer creates an IAM role with external ID provided by the partner, as well as appropriate permissions for the partner to access their account. The partner must provide all required permissions following the least privilege principle. It’s strongly recommended the partner provides a CloudFormation template to the customer to create this IAM role.
  3. Customer provides the IAM role ARN: After the IAM role is successfully created, the customer must provide the ARN to the partner. This can also be achieved through the user portal.
  4. Store the IAM role ARN: The partner must securely store the IAM role ARN so they can use it to access the customer’s AWS account
  5. Assuming role in customer account: Now, the partner can use the ARN provided by the customer and the external ID they generated to assume role in customer’s account. A sample Python code to access customers account using cross-account IAM role and external ID is shown below.
import boto3

# create an STS client object that represents a live connection to the 
# STS service
sts_client = boto3.client('sts')

# Call the assume_role method of the STSConnection object and pass the role
# ARN,external ID and a role session name.
assumed_role_object=sts_client.assume_role(
    RoleArn="<PROVIDE ROLE ARN HERE>",
    RoleSessionName="AssumeRoleSession1",
    ExternalId="<PROVIDE EXTERNAL ID HERE>"
)

# From the response that contains the assumed role, get the temporary 
# credentials that can be used to make subsequent API calls
credentials=assumed_role_object['Credentials']

# Use the temporary credentials that AssumeRole returns to make a 
# Connection to Amazon S3  
s3_resource=boto3.resource(
    's3',
    aws_access_key_id=credentials['AccessKeyId'],
    aws_secret_access_key=credentials['SecretAccessKey'],
    aws_session_token=credentials['SessionToken'],
)

# Use the Amazon S3 resource object that is now configured with the 
# credentials to access your S3 buckets. 
for bucket in s3_resource.buckets.all():
    print(bucket.name)

Conclusion

This post explained the importance of following best practices for using an external ID to protect your customers.

If you are using AWS IAM credentials provided by customers to access their accounts, we strongly recommend you implement IAM role-based access.

Please note that the sample code, software libraries, command line tools, proofs of concept, templates, or other related technology (including any of the foregoing that are provided by our personnel) is provided to you as AWS content under the AWS Customer Agreement, or the relevant written agreement between you and AWS (whichever applies).

You should not use this AWS content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the AWS content, such as sample code, as appropriate for production-grade use based on your specific quality control practices and standards. Deploying AWS content may incur AWS charges for creating or using AWS chargeable resources, such as running Amazon EC2 instances or using Amazon S3 storage.