Over a million developers have joined DZone.

Cross-Account Access Control With Amazon STS for DynamoDB

Given that AWS' DynamoDB doesn't provide for built-in access control, here's a way to get around it so you can open your work to other users.

· Database Zone

Build fast, scale big with MongoDB Atlas, a hosted service for the leading NoSQL database. Try it now! Brought to you in partnership with MongoDB.

In this post, we'll be talking about creating cross-account access for DynamoDB. DynamoDB is a NoSQL Database in the cloud provided by Amazon Web Services.

Whether you're creating a production deployment pipeline that leverages a shared Keystore or deploying an application in multiple accounts with shared resources, you may find yourself wondering how to provide access to your AWS resources from multiple AWS accounts.

Keystore is an open source pipeline secret management tool from Stelligent that is backed by DynamoDB and encrypted with Amazon's Key Management System. Check it out on GitHub.

Although we will focus on DynamoDB, the concepts discussed in this post are not necessarily limited to DynamoDB and have many other uses for a variety of AWS services where multi-account access control is desired.

DynamoDB does not provide any built-in access control; however, it does provide an interface to fine-grained access control for users. If you're looking to provide access to DynamoDB from a web app, mobile app, or federated user, check out the documentation in AWS to get started with AWS' Identity and Access Management (IAM).

This post will focus on leveraging IAM Roles and AWS' Security Token Service (STS) to provide more advanced access control to our DynamoDB tables.

In our use-case, we needed to provide a second account access to our DynamoDB tables for a Keystore implementation. The goal was to provide this second account access to our secrets without duplicating the data or the storage costs.

The plan is to leverage the features of IAM and STS to provide the access control, this works by creating two roles:

  • The role created on Account A will provide access to DynamoDB and KMS and allow Account B to assume it.
  • The role created on Account B will provide access to STS' AssumeRole action against our role in Account A. Any host or user with this role will be able to acquire temporary API credentials from STS for Account A.

For more information on how this works under the hood, check out the AWS Documentation on Cross-Account Access Delegation.

As a security best practice, you'll want to ensure the access provided is as specific as possible. You should limit access to specific actions, DynamoDB tables, and keys in KMS.

When creating resources in your account, it's always a good idea to use a configuration management tool. For our examples, we will be using CloudFormation to configure and provision the IAM and STS resources.

Step 1: Create a Role in Account A

  • Allow STS to assume it from Account B.
  • Attach a policy to allow access to DynamoDB and KMS.

cloudformation-launch-stack

Click here to review the CloudFormation template in the button above.

Step 2: Create a Role in Account B

  • Allow STS AssumeRole from Amazon EC2.
  • Allow access to only your Account A assumable ARN.
    • You'll need the ARN from Step 1.

cloudformation-launch-stack

Click here to review the CloudFormation template in the button above.

Step 3: Try it out!

  • You can use the AWS CLI to retrieve a temporary set of credentials from STS to use for access to Account A's DynamoDB!

Our very own Jeff Bachtel has adapted a snippet for acquiring and implementing temporary STS credentials into your shell. Here it is:

iam-assume-role.sh:

#!/bin/bash -e
#
# Adapted from https://gist.github.com/ambakshi/ba0fe456bb6da24da7c2
#
# Clear out existing AWS session environment, or the awscli call will fail
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_SECURITY_TOKEN

ROLE_ARN="${1:-arn:aws:iam::123456789:role/AccountARole}"
DURATION="${2:-900}"
NAME="${3:-$LOGNAME@`hostname -s`}"

# KST=access*K*ey, *S*ecretkey, session*T*oken
KST=(`aws sts assume-role --role-arn "${ROLE_ARN}" \
                          --role-session-name "${NAME}" \
                          --duration-seconds ${DURATION} \
                          --query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' \
                          --output text`)

echo 'export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-us-east-1}'
echo "export AWS_ACCESS_KEY_ID='${KST[0]}'"
echo "export AWS_SECRET_ACCESS_KEY='${KST[1]}'"
echo "export AWS_SESSION_TOKEN='${KST[2]}'"      # older var seems to work the same way
echo "export AWS_SECURITY_TOKEN='${KST[2]}'"


From an EC2 instance launched with the role created in step 2, we can use this script to test our cross-account access.

$ eval $(./iam-assume-role.sh arn:aws:iam::123456789:role/AccountARole)


When you're ready to go back to your own instance profile credentials, you can unset the temporary token:

$ unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_SECURITY_TOKEN


Wrapping Up

As you can see, using the power of IAM and STS to bridge the gap between two accounts to share resources is quite easy and secure. There are tons of possibilities here that go beyond that of DynamoDB and KMS to allow you to reduce costs and technical debt.

Now it's easier than ever to get started with MongoDB, the database that allows startups and enterprises alike to rapidly build planet-scale apps. Introducing MongoDB Atlas, the official hosted service for the database on AWS. Try it now! Brought to you in partnership with MongoDB.

Topics:
pipeline ,access ,sts ,access control ,aws ,deployment ,dynamodb

Published at DZone with permission of Robert Murphy. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}