Reputation: 565
My overall objective: I tried several things and read relevant AWS documentation but am unable to figure how to write an S3 bucket policy to allow access only to specific IAM role and Cloudfront Origin Access Identity(OAI), and deny everyone else.
What I've tried so far: 1. I found this blog, which shows how to restrict s3 bucket access to specific IAM roles:https://aws.amazon.com/blogs/security/how-to-restrict-amazon-s3-bucket-access-to-a-specific-iam-role/ 2. Based on the above blog, I wrote the following bucket policy. This policy allows only MY_IAM_ROLE to allow all operations on a bucket called 'myBucket':
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::myBucket",
"arn:aws:s3:::myBucket/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": [
"MY_IAM_ROLE_USER_ID:*"
]
}
}
}
]
}
My question: How can I combine (1) aws:userid of IAM roles/users and (2) OAI IDs in a single policy statement?
Upvotes: 17
Views: 44986
Reputation: 407
I was having the same problem, I needed to restrict access to my bucket only to Cloudfront (OAI), MediaConvert (Role) and certain users (IAM).
The NotPrincipal
directive worked for Cloudfront and individual users, but as @MuhammadHannad said, the problem is that for a role you must list every session name associated to it and you can't solve this by combining wildcards with strings because this element doesn't support it.
Using "Condition": {"StringNotLike": {"aws:userid" [...]}}
didn't work either because, as you said in a comment, the Cloudfront Origin Access Identity doesn't have a corresponding aws:userid
.
I finally found a solution, instead of using StringNotLike
, we can use "Condition": {"ArnNotLike": {"aws:PrincipalArn": [...]}}
, which behaves in the same way as NotPrincipal
, but with the possibility of using wildcards in the resource names.
{
"Sid": "1",
"Effect": "Deny",
"Principal": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": [
"arn:aws:iam::111122223333:user/S3-USER",
"arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity AAAABBBBCCCC1",
"arn:aws:iam::111122223333:role/MediaConvertRole",
"arn:aws:sts::111122223333:assumed-role/MediaConvertRole/*",
"arn:aws:iam::111122223333:root"
]
}
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::myBucket",
"arn:aws:s3:::myBucket/*"
]
}
Upvotes: 18
Reputation: 51
Your problem can be solved by replacing Principal: "*"
in your Deny
statement with an appropriate NotPrincipal
referencing the CloudFront OAI, and adding an appropriate Allow
statement for the CloudFront OAI:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"NotPrincipal": {
"CanonicalUser": "..."
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::myBucket",
"arn:aws:s3:::myBucket/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": [
"MY_IAM_ROLE_USER_ID:*"
]
}
}
},
{
"Effect": "Allow",
"Principal": {
"CanonicalUser": "..."
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::myBucket/*"
}
]
}
This way your IAM role is excluded from the Deny
statement by the aws:userId
condition, while your CloudFront OAI is excluded by the NotPrincipal
condition. All other principals will still be denied access, and the CloudFront OAI will only have the permissions you grant to it directly in the bucket policy.
As simple as it seems in retrospect, this solution was certainly not obvious, and credit for this answer should go to AWS Support, who recently answered this same question for me.
Upvotes: 0
Reputation: 1768
My older answer would support some people but may not yours precisely. So, I am posting another answer which will address your need more precisely. The following role policy will allow access to IAM role and OAI and will deny every one else:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EXXXXXXXXX"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucketname/*"
},
{
"Sid": "2",
"Effect": "Deny",
"NotPrincipal": {
"AWS": [
"arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EXXXXXXXXX",
"arn:aws:iam::AWS-account-ID:role/role-name"
]
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucketname/*"
}
]
}
Upvotes: 4
Reputation: 1768
To recap, you were needing a bucket policy that restricted access to your S3 bucket and contents, but allow access to your Cloudfront Origin Access Identity as well as your IAM Role(s) you wanted to specify. I have a few ways you can do this, one with the NotPrincipal element and the other with the Principal element. It depends on how many people are using your IAM Role, or if you plan on scaling the amount of access to the Role. You can prevent Amazon Identify and Access Management (IAM) entities from accessing your Amazon S3 buckets by designating permissions in a bucket policy using the NotPrincipal element and explicit Deny. However, NotPrincipal does not support wildcards.
In this policy example, you must list the role session name of every user and every instance ID that will be assuming the role:
"Effect": "Deny",
"NotPrincipal": {
"AWS": [
"arn:aws:sts::444455556666:assumed-role/cross-account-read-only-role/cross-account-audit-app",
"arn:aws:sts::444455556666:assumed-role/cross-account-read-only-role/instanceID",
"arn:aws:iam::444455556666:role/cross-account-read-only-role",
"arn:aws:iam::444455556666:root"
]
}
To cover all of those users and instances, you would need a wildcard in the statement to represent the assumed role:
"arn:aws:sts::444455556666:assumed-role/cross-account-read-only-role/*"
However, wildcards are not supported with the NotPrincipal element. This will work if you only have yourself using the Role, or one Instance for example, but as you scale, you need to add the ARN for user/instance assuming the role.
In this example, instead of NotPrincipal
, use "Principal": "*"
as the target entity in each statement block, which includes the Condition for each Allow block. Wildcards are used in "aws:userid": ["ROLE-ID:*"]
to include all names that are passed by the calling process (such as application, service, or instance ID) when it makes a call to get temporary credentials. For more information, see Information Available in All Requests. The root account is included to prevent lockout:
"Condition": {
"StringLike": {
"aws:userid": [
"AROAID2GEXAMPLEROLEID:*",
"444455556666"
]
}
}
StringNotLike in the Deny block:
"Condition": {
"StringNotLike": {
"aws:userid": [
"AROAID2GEXAMPLEROLEID:*",
"444455556666"
]
}
}
Here is the complete policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::myExampleBucket",
"Condition": {
"StringLike": {
"aws:userid": [
"AROAID2GEXAMPLEROLEID:*",
"444455556666"
]
}
}
},
{
"Sid": "",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::myExampleBucket/*",
"Condition": {
"StringLike": {
"aws:userid": [
"AROAID2GEXAMPLEROLEID:*",
"444455556666"
]
}
}
},
{
"Sid": "",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::myExampleBucket/*",
"arn:aws:s3:::myExampleBucket"
],
"Condition": {
"StringNotLike": {
"aws:userid": [
"AROAID2GEXAMPLEROLEID:*",
"444455556666"
]
}
}
}
]
}
Note:
- Be sure that you replace the sample names with your own role IDs and bucket names.
- In the above example, I am using the unique ID for the IAM entity. http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-unique-ids
To get information about an IAM role The following get-role command gets information about the role named Test-Role:
aws iam get-role --role-name Test-Role
Output:
{
"Role": {
"AssumeRolePolicyDocument": "<URL-encoded-JSON>",
"RoleId": "AIDIODR4TAW7CSEXAMPLE",
"CreateDate": "2013-04-18T05:01:58Z",
"RoleName": "Test-Role",
"Path": "/",
"Arn": "arn:aws:iam::123456789012:role/Test-Role"
}
}
Important Note
Basically when you create the Origin Access Identity using the CloundFront API, you get the UserID and S3 canonical ID and you will have to note it down to use this in the condition string.If you currently don't have the ID for the Origin Access Identity of your CloudFront, please create a new one and you can save the ID element and S3 Canonical ID as suggested.
Upvotes: 8
Reputation: 179364
It seems like a bit of a long shot, but find the Origin Access Identity's ID (not the canonical user ID... find the one that starts with E
and is approximately 12 characters long) and add it to the policy like this:
"StringNotLike": {
"aws:userId": [
"MY_IAM_ROLE_USER_ID:*",
"arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EXXEXAMPLEXX"
]
}
Note that the words "CloudFront Origin Access Identity" including the spaces, are part of this quoted string. Insert it exactly as shown, changing only the 12 character ID.
That might prevent this policy from denying access to the OAI. If there is a way to include the OAI's user in a condition key, this seems like the only possible solution.
Separately, you need a second policy statement, the normal statement that will grant the necessary access to the OAI.
Upvotes: 0