16

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:*"
                    ]
                }
            }
        }
    ]
}
  1. I'm not able to find an aws-wide key -- http://docs.aws.amazon.com/AmazonS3/latest/dev/amazon-s3-policy-keys.html -- for the OAI, which I could plug into the "aws:userId" array along with MY_IAM_ROLE_USER_ID. I tried plugging in the OAI's CanonicalUserId but it didn't work.

My question: How can I combine (1) aws:userid of IAM roles/users and (2) OAI IDs in a single policy statement?

ktrace
  • 525
  • 1
  • 5
  • 17
  • so you are wanting to allow your Cloudfront Origin Access Identity to the S3 bucket, but also allow your IAM Role(s) access? – Nah Sep 10 '17 at 14:15
  • @MuhammadHannan Yes, allow *only* specific IAM role and Cloudfront Origin Access Identity, and deny everyone else. – ktrace Sep 10 '17 at 14:26
  • I have two ways you can do this. It just depends on how many people access your IAM Role you are whitelisting. I will add it through the case for you, so you can review. It breaks down both, I'll post in answer shortly. – Nah Sep 10 '17 at 14:28

5 Answers5

16

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/*"
    ]
}
Ale Felix
  • 379
  • 5
  • 7
8

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:

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.

http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#private-content-creating-oai-api

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.

Nah
  • 1,690
  • 2
  • 26
  • 46
  • 2
    @MuhammadHannad thanks for posting the above response but it doesn't solve my problem. Cloudfront Origin Access Identity is **not** based on IAM and does not have a corresponding "aws:userid" value, so I need a different solution than the one you're suggesting. – ktrace Sep 10 '17 at 16:21
  • Have just added an important note, please check that. Hopefully this will guide you in right direction. – Nah Sep 11 '17 at 04:25
  • 1
    I read your note. While the note suggests how to write a policy statement with just the OAI UserID/S3CanonicalID, it doesn't solve the central problem of how to combine (1) aws:userid of IAM roles/users and (2) OAI IDs in a single policy statement. – ktrace Sep 12 '17 at 13:03
  • Didn't understand what you meant. The note at end of my answer has the link to describe the process. Moreover this link might also help: https://stackoverflow.com/questions/22668121/creating-an-s3-bucket-policy-that-allows-access-to-cloudfront-but-restricts-acce – Nah Sep 12 '17 at 14:05
  • https://stackoverflow.com/questions/22668121/creating-an-s3-bucket-policy-that-allows-access-to-cloudfront-but-restricts-acce and http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#private-content-creating-oai-api suggest how to allow access to just OAI in the policy statement. My problem is to do with allowing access to OAI **and** IAM, while explicitly denying everyone else. – ktrace Sep 12 '17 at 16:12
  • 2
    @ktrace was my any of solution helpful? Didn't receive any feedback. – Nah Dec 28 '17 at 08:39
4

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/*"
        }
    ]
}
Nah
  • 1,690
  • 2
  • 26
  • 46
  • This should probably be the accepted answer. I nearly almost duplicated it because it wasn't accepted, but it handles denying everything except for the OAI and a given role name, and explicitly allowing the OAI. My solution used 'StringNotLike: aws:userId', and the explicit user id for the user that Jenkins used, but was otherwise the same. In the process of figuring this out, I was reminded of the policy generator tool: https://awspolicygen.s3.amazonaws.com/policygen.html . – Cognitiaclaeves Nov 18 '21 at 15:03
0

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.

Michael - sqlbot
  • 169,571
  • 25
  • 353
  • 427
0

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.

  • Sorry, I seem to recall obtaining this solution from AWS Enterprise Support, so there is no public link. – Robby Morgan Sep 16 '19 at 01:36
  • ****Be careful with the above solution*** The above solution denies access to all users except the CloudFront OAI. – Karthik Jun 17 '20 at 02:25