Multi-account setup in cloud offers several advantages like grouping of workloads based on business purpose and ownership, applying distinct security controls by environment, constraining access to sensitive data, reducing blast radius, supporting multiple IT operating models, managing costs and distributing AWS service quotas and API request rate limits. However, setting up multi-account environment requires understanding of services and its access across account boundaries.
For example, organizations are often required to grant external users or users from other accounts within their organization access to certain Amazon S3 buckets. In this article lets explore the possible options to enable cross account access to AWS S# buckets and their objects.
Depending on the use case there are couple of ways to grant cross-account access to objects:
Lets look at the scenario where IAM user “developer” is in account “A” and S3 bucket “test-app-bucket-1” is in account “B”
For better understanding and to practically observe this scenario, let's put CloudFormation Script to create user with required permissions in account “A”.
In this setup we'll define the following:-
- Who can access the objects inside the bucket (using the Principal element)?
- Objects they can access (using the Resource element).
- How they can access the objects inside the bucket (using the Action element)?
CloudFormation template in account "A"
Description: CloudFormation Template for developer IAM user creation in account"A"
Parameters:
ForEnvironment:
Type: String
Default: dev
Description: Environment name for which this developer user is created
AllowedValues:
- sit
- uat
- prod
- dev
- test
Resources:
#IAM user in account A#
AccAdeveloperIAMUser:
Type: 'AWS::IAM::User'
Properties:
UserName: !Join # give a name to this user
- '-'
- - 'developer'
- !Ref ForEnvironment
- "iam-user-01"
Policies: # list of inline policy documents embedded in the user
- PolicyName: !Join # give a name to this user
- '-'
- - 'developer'
- !Ref ForEnvironment
- "iam-policy-01"
PolicyDocument: # JSON policy document
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:ListBucketMultipartUploads
- s3:DeleteObjectVersion
- s3:ListBucketVersions
- s3:ListBucket
- s3:DeleteObject
- s3:GetObjectVersion
- "s3:ListAccessPoints"
Resource: "arn:aws:s3:::test-dev-app-bucket-1/*"
Outputs:
AccAdeveloperIAMUser:
Description: ARN to be supplied in developer template for environments
Value: !GetAtt AccAdeveloperIAMUser.Arn'
Now lets create an S3 bucket in account with required bucket policies to grant access to this user "Developer" from account "A".
AWSTemplateFormatVersion: '2010-09-09
Description: CloudFormation Template for S3 bucket in account "B"
Parameters:
EnvName:
Type: String
Default: "dev"
Description: The name of the S3 Bucket to be created for FrontEnd
AllowedValues:
- sit
- uat
- prod
- dev
Resources:
S3Bucket1:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Join
- '-'
- - 'test'
- !Ref EnvName
- "app-bucket-1"
VersioningConfiguration:
Status: Enabled
AccessControl: "Private"
PublicAccessBlockConfiguration:
BlockPublicAcls: "true"
BlockPublicPolicy: "true"
IgnorePublicAcls: "true"
RestrictPublicBuckets: "true"
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: 'AES256'
NameBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket1
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS:
- !Ref SSIamRoleArn
- !GetAtt IamUser.Arn
Action:
- s3:GetObject
- s3:PutObject
- s3:ListBucket
- s3:DeleteObject
Resource:
- !Join
- ''
- - 'arn:aws:s3:::'
- !Ref S3Bucket1
- !Join
- ''
- - 'arn:aws:s3:::'
- !Ref S3Bucket1
- /*
Outputs:
S3Bucket1:
Description: Name of the bucket created using this template.
Value: !Ref S3Bucket1'
This configuration will allow user "Developer" from Account "A" to securely access an S3 bucket in account "B".
Now lets look at other scenario where an EC2 instance called "Jenkins-server" in account "A" needs to access S3 bucket "“test-dev-app-bucket-1" in account "B". Here the EC2 instance will have the required permissions to access S3 across account.
This use case is suitable to centralize permission management when providing cross-account access to multiple services. Cross-account IAM roles simplifies provisioning cross-account access to S3 objects that are stored in multiple S3 buckets. So that we don't have to manage multiple bucket policies for S3 buckets. This IAM role based access also allows cross-account access to objects owned or uploaded by another AWS account or AWS services.
Following script will create an EC2 instance with required role association in Account "A"
AWSTemplateFormatVersion: '2010-09-09
Description: CloudFormation Template for ec2 creation in Account A
Parameters:
EnvName:
Type: String
Default: "dev"
Description: The name of the S3 Bucket to be created for FrontEnd
AllowedValues:
- sit
- uat
- prod
- dev
KeyPairName:
Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: must be the name of an existing EC2 KeyPair.
SubnetInstance:
Type: AWS::EC2::Subnet::Id
Description: Subnet ID for Instance
VPCIdEC2:
Type: AWS::EC2::VPC::Id
Description: VPC ID for Instance
Resources:
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "instance-sg-01"
GroupDescription: "security group attached to ec2 instance"
VpcId: !Ref VPCIdEC2
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 172.16.0.0/16
EC2Instance:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: "ami-0015c3ec5dfe53255"
InstanceType: "t3.small"
KeyName: !Ref KeyPairName
SubnetId: !Ref SubnetInstance
SecurityGroupIds:
- Ref: EC2SecurityGroup
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 50
Encrypted: 'true'
VolumeType: "gp3"
IamInstanceProfile: !Ref InstanceProfile
LaunchTemplate:
LaunchTemplateId: !Ref InstanceLaunchTemplate
Version: "1"
Tags:
- Key: Name
Value: !Join
- '-'
- - 'kbc-be'
- !Ref EnvName
- "-Inst-01"
- Key: Schedule
Value: -server
- Key: environment
Value: !Ref EnvName
InstanceSSMRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
CrossAccAssumeRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
InstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: /
Roles:
- !Ref InstanceSSMRole
- !Ref CrossAccAssumeRole'
Now lets create a role which will be assumed by account "A" EC2 instance. Following is the script which will create required role in account "B" and an S3 bucket for test.
RoleAccountB:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- s3:ListAllMyBuckets
Effect: Allow
Resource:
- arn:aws:s3:::*
- Action:
- s3:ListBucket
- s3:GetBucketLocation
Effect: Allow
Resource: arn:aws:s3:::AccountBBucketName
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: arn:aws:s3:::AccountBBucketName/*
AccountBS3Bucket1:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Join
- '-'
- - 'test'
- !Ref EnvName
- "app-bucket-1"
VersioningConfiguration:
Status: Enabled
AccessControl: "Private"
PublicAccessBlockConfiguration:
BlockPublicAcls: "true"
BlockPublicPolicy: "true"
IgnorePublicAcls: "true"
RestrictPublicBuckets: "true"
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: 'AES256' :
Now role in account assumes the role in Account B so that EC2 "Jenkins-server" in Account A can perform the required S3 operations.
This configuration covers most of the use cases however architectures are evolving every day. For example many organizations prefer to put restrictive rules around editing bucket policies. AWS has another solution called S3 access points which enforce granular access to S3 objects with flexible cross-account access without having to edit bucket policies. I'll cover this topic in my subsequent Articles.
-- Reference- Amazon web service
Top comments (0)