I have an S3 bucket on which I am trying to apply the bucket policy via CloudFormation. I want to allow two IAM roles to access the bucket and is achieved by specifying the ARN of the roles in the bucket policy in the CloudFormation template.
Below is the CloudFormation template:
LandingBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref LandingBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
# yamllint disable-line rule:line-length
- Sid: "Allow s3 permission"
Action:
- s3:PutObject
- s3:GetObject
- s3:ListBucket
Effect: "Allow"
Resource:
- !GetAtt LandingBucket.Arn
- !Sub "${LandingBucket.Arn}/*"
Principal:
AWS:
- !Ref IamRoleArn1
- !Ref IamRoleArn2
Parameters are: IamRoleArn1: arn:aws:iam::1234:role/xyz, IamRoleArn2: arn:aws:iam::1234:role/abc
The final policy from the console looks like below
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "file drop permission",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::1234:role/xyz",
"AROxxIECxx"
]
},
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
]
}
]
}
The first Principal is an IAM role, however, the second one looks like an API key even though in the CloudFormation template I have mentioned the second IAM role ARN just like the first IAM role.
Why is the second role ARN not showing up in the bucket policy?
That is the unique identifier of that particular resource. In this case it is called a RoleId and ARN is just a readable format of the same. Both representation points to the same resource in AWS.
Try running
aws iam get-role --role-name "<you role here>"
the output of this command will have a field named RoleId and should that should clear things out for you.
The unique identifier that starts with AROA represents that it is a role related resource.
Related
I have an IAM role (MyRole) with S3 and CloudFormation full access policies attached, and a trust relationship like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::1234567890:root",
"Service": [
"cloudformation.amazonaws.com",
"s3.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
I have a group defined with this policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::1234567890:role/MyRole"
}
]
}
I created a user in that group and a config and credentials file to assume the role:
$ cat ~/.aws/config
[profile MyRole_role]
role_arn = arn:aws:iam::1234567890:role/MyRole
source_profile = profile1
$ cat ~/.aws/credentials
[profile1]
aws_access_key_id = A*********E7NT
aws_secret_access_key = 1a**************lVa
In a CloudFormation template I try to add a bucket policy:
Resources:
MyS3Bucket:
Type: 'AWS::S3::Bucket'
Description: Chris test bucket and policy
Properties:
BucketName: !Ref BucketName
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
MyS3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref MyS3Bucket
PolicyDocument:
Statement:
-
Action:
- s3:*
Effect: Allow
Resource:
- !Sub arn:aws:s3:::${MyS3Bucket}
- !Sub arn:aws:s3:::${MyS3Bucket}/*
Principal:
AWS:
- '*'
I kick off the CloudFormation template with "--profile MyRole_role", and the bucket gets created but I always get a "API: s3:PutBucketPolicy Access Denied" error and the stack rolls back. In the IAM policy sim I never find any problems with being able to use S3:PutBucketPolicy. With the cli I can do something like a 's3 ls --profile MyRole_role" and it works fine, which makes me think my user is assuming the role. If I use "--profile profile1" I get an expected access denied. What could I be missing? Thanks for any insight.
Most likely the issue is due you still have enabled the "Block all Public Access" on your S3 Account.
UnCheck it, and try again, it should work
I have S3 bucket "cross-bucket" in Account say B.Now i want EC2 which is present in Account A to access this bucket "cross-bucket" in Account B.
I need to achieve this using IAM roles as we are not allowed to create users.
I have used below template to create role in Account B
AWSTemplateFormatVersion : '2010-09-09'
Description: 'Cross account role for S3'
Parameters:
AccountId:
Type: String
Description: Account ID of admin account (containing user to allow)
Resources:
CrossAccountRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
AWS:
- !Sub arn:aws:iam::${AccountId}:root
Path: /
Policies:
- PolicyName: my-s3-delegate
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetObject
Resource: "*"
RootInstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
Roles:
-
Ref: "CrossAccountRole"
After creating this role how should i attach this to instance present in Account A?
Or i am missing something here?
Your situation is:
Amazon EC2 instance in Account-A
Amazon S3 bucket in Account-B
You would like to allow the EC2 instance to access the bucket
There are two ways to do this:
Option 1: Bucket Policy
Simply add a bucket policy to the bucket in Account-B that grants access to the IAM Role used by the EC2 instance:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Principal": {
"AWS": [
"arn:aws:iam::ACCOUNT-A:role/my-ec2-role"
]
}
}
]
}
The EC2 instance will use its normal IAM Role credentials to access the bucket. Also make sure the IAM Role has given permission to use Amazon S3 to access the bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::bucket-b",
"arn:aws:s3:::bucket-b/*"
]
}
]
}
Option 2: Assume Role
Create an IAM Role in Account-B that has permission to access the bucket
Code on the EC2 instance calls AssumeRole() on the IAM Role
Use the returned credentials to access the bucket
After creating this role how should i attach this to instance present in Account A?
You are not attaching it to an instance in Acc A. Instead, you create an instance role in Acc A. The role will have permissions to assume the role from Acc B.
Thus, the instance role would have a policy similar to the following one:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "<ARN-of-ROLE-in-ACC-B>"
}
}
]
}
Then any application running on the instance would have to use sts assume-role to actually assume the role and perform actions in Acc B.
I have recently started to use Control Tower from AWS to manage my multiple account environment.
My current question is:
I have a bucket belonging to the master account that I would like to share console access with some of the accounts of the organization. How can I do that? I have tried adding a bucket policy specifying the accounts and an SSO permission set attached to that account granting access to the bucket but when accessing with that role to s3 I can't see that bucket.
I am able to access the bucket through CLI but not through console, though. I.e. When accessing with the assigned role through CLI I am able to do aws s3 ls s3://mybucket and it shows the folders inside it (other commands work as well). But when doing aws s3 ls the bucket is not listed.
bucket policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Example permissions",
"Effect": "Allow",
"Principal": {
"AWS": [
"123456789101",
"112131415161",
]
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::mybucket"
}
]
}
permission set:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Example",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::mybucket",
"arn:aws:s3:::mybucket/*"
]
}
]
}
Does anyone know how to allow the users to list it with the rest of the account buckets and through the console on the s3 page?
Thank you!!
Daiana
As I understand ControlTower, you are not supposed to do anything meaningful in the root account.
Also, there is no shared Console access unless you allow other users to "federate" into the very same account where the bucket was created. Using the ControlTower this is usually done via Single-Sign-On (SSO)
My suggestion is: Create a Shared Services/Resources account and allow access to those resources to any member of your organization. Do this by making use of the new AWS:PrincipalOrgID. For example, see this CloudFormation Snippet for a central SNS queue with sns:Publish permission from within the AWS organization.:
Resources:
Topic:
Type: AWS::SNS::Topic
Properties:
DisplayName: Name
TopicName: name
TopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
Topics:
- !Ref Topic
PolicyDocument:
Version: '2012-10-17'
Statement:
# default permission allow same account: https://www.terraform.io/docs/providers/aws/r/sns_topic_subscription.html
- Sid: __default_statement_ID
Effect: Allow
Principal:
AWS: "*"
Action:
- SNS:GetTopicAttributes
- SNS:SetTopicAttributes
- SNS:AddPermission
- SNS:RemovePermission
- SNS:DeleteTopic
- SNS:Subscribe
- SNS:ListSubscriptionsByTopic
- SNS:Publish
- SNS:Receive
Resource: !Ref Topic
Condition:
StringEquals:
AWS:SourceOwner: !Sub ${AWS::AccountId}
- Sid: SnsTopicPolicy
Effect: Allow
Principal:
AWS: "*"
Condition:
StringEquals:
# allow access from within your organization
AWS:PrincipalOrgID: "o-xxxxxxxxxx"
Action: sns:Publish
Resource: !Ref Topic
Policy definition of AWS managed policy(AWSLambdaExecute) is:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [ "logs:*" ],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [ "s3:GetObject", "s3:PutObject" ],
"Resource": "arn:aws:s3:::*"
}
]
}
But the AWS_documentation gives a sample serverless function using the same policy name AWSLambdaExecute, as shown below:
Type: AWS::Serverless::Function
Properties:
Handler: index.js
Runtime: nodejs8.10
CodeUri: 's3://my-code-bucket/my-function.zip'
Description: Creates thumbnails of uploaded images
MemorySize: 1024
Timeout: 15
Policies:
- AWSLambdaExecute # Managed Policy
- Version: '2012-10-17' # Policy Document
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectACL
Resource: 'arn:aws:s3:::my-bucket/*'
that does not match with the above definition.
Edit:
Below is the sample function's execution role... I do not see AWS mananged execution role names(such as AWSLambdaBasicExecutionRole). Because my understanding is, AWSLambdaBasicExecutionRole role should be assigned to Lambda, by default
Are we overriding the policy definition of AWSLambdaExecute in this example?
When you are specifying policies, you are basically building an execution role your lambda function.
Policies is a list of policies because role can include multiple policies in it.
This line
- AWSLambdaExecute # Managed Policy
states that the lambda function that you are creating should include this AWS managed policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [ "logs:*" ],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [ "s3:GetObject", "s3:PutObject" ],
"Resource": "arn:aws:s3:::*"
}
]
}
Following lines:
- Version: '2012-10-17' # Policy Document
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectACL
Resource: 'arn:aws:s3:::my-bucket/*'
are specifying next policy that you want to include in your lambda execution role.
Are we overriding the policy definition of AWSLambdaExecute in this example?
No, we are adding multiple policies to lambda execution role, one of them is AWS managed policy and one is our own custom policy. So the lambda function will have permissions defined in both of them. Or more precisely, union of those policies will be made and lambda function will have permissions defined by that union, meaning that if one of the policies allows lambda function to do something and the other denies the same thing, the result will be that the action will be denied.
I think what your Policies attribute does, is:
attaches the managed policy AWSLambdaExecute and then
creates an inline policy for your execution role which grants the s3 permissions s3:GetObject and s3:PutObject. There is another SO post which indicates that SAM now supports defining inline policies. [1]
Defining inline policies does not overwrite anything.
You can have multiple different types of policies attached to a single identity (e.g. IAM user or role). [2]
References
[1] https://stackoverflow.com/a/52719165/10473469
[2] https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html
Here's my preferred approach (omitting other fields for clarity):
MyLambdaFunction:
Type: 'AWS::Serverless::Function'
Properties:
Policies:
- CloudWatchLambdaInsightsExecutionRolePolicy # AWS Managed Policy
- AWSXrayWriteOnlyAccess # AWS Managed Policy
- AWSLambdaExecute # AWS Managed Policy
- Version: '2012-10-17' # Policy Document to allow S3 access
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectACL
Resource: 'arn:aws:s3:::my-bucket/*'
I have a main account user that I want to allow access to a subaccount S3 bucket. I have setup the following stack in my subaccount
AWSTemplateFormatVersion : '2010-09-09'
Description: 'Skynet stack to allow admin account deploying user to access S3'
Parameters:
AccountId:
Type: String
Description: Account ID of admin account (containing user to allow)
Username:
Type: String
Description: Username to be allowed access
BucketPrefix:
Type: String
Description: Bucket to be allowed (prefix appended with -{AccountId}-{Region})
Resources:
CrossAccountRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
AWS:
- !Sub arn:aws:iam::${AccountId}:user/${Username}
Path: /
Policies:
- PolicyName: skynet-s3-delegate
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetObject
Resource: "*"
But I find that I still get an error when I try to assume the role:
aws s3 cp skynet-lambda.zip s3://skynet-lambda-TARGET_ACCOUNT_ID-ap-southeast-1 --profile skynetci-cross-account
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::MAIN_ACCOUNT_ID:user/circleci-skynet is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::TARGET_ACCOUNT_ID:role/StackSet-df0e85b0-d6fd-47bf-a0bb-CrossAccountRole-1EW45TXEFAY0D
Why is this so considering I already have the following policy for the user
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": "arn:aws:iam::TARGET_ACCOUNT_ID:role/StackSet-df0e85b0-d6fd-47bf-a0bb-CrossAccountRole-1EW45TXEFAY0D"
}
You need to have Bucket Policy update to allow cross account access a sample policy would be like:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Example permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::AccountB-ID:root"
},
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::examplebucket"
]
}
]
}
Also make sure IAM user who is trying to access has this inline policy attached:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Example",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::examplebucket"
]
}
]
}
You can refer AWS Documentation
I am attaching a working example that I tested using two of my accounts.
STEP 1: CloudFormation YAML template:
AWSTemplateFormatVersion : '2010-09-09'
Description: 'Skynet stack to allow admin account deploying user to access S3'
Parameters:
AccountId:
Type: String
Description: Account ID of admin account (containing user to allow)
Username:
Type: String
Description: Username to be allowed access
BucketPrefix:
Type: String
Description: Bucket to be allowed (prefix appended with -{AccountId}-{Region})
Resources:
CrossAccountRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
AWS:
- !Sub arn:aws:iam::${AccountId}:user/${Username}
Path: /
Policies:
- PolicyName: skynet-s3-delegate
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetObject
Resource: "*"
RootInstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
Roles:
-
Ref: "CrossAccountRole"
STEP 2: Create the cross account profile
Modify ~/.aws/credentials. Add a new profile called "skynetci-cross-account". Modify based upon your parameters created in STEP 1. You will need the role arn to replace the one below. You will also need the profile name for the account that you are giving permission to. In this example the profile name is "default".
Example here:
[skynetci-cross-account]
role_arn = arn:aws:iam::191070ABCDEF:role/Test-CrossAccountRole-IZDDLRUMABCD
source_profile = default
STEP 3: Test cross access
aws --profile skynetci-cross-account s3 ls s3://bucket-name
To accomplish your goal, you need to set a bucket policy in your target S3 bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DelegateS3Access",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::MAIN_ACCOUNT_ID:USER_NAME"
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::BUCKET_NAME/*",
"arn:aws:s3:::BUCKET_NAME"
]
}
]
}
And allow S3 permissions to this user.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Example",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"*"
]
}
]
}
In this case you do not need assume a role on the target account. The user itself will be able to access to bucket in another account.