2
votes

Can you write an s3 bucket policy that will deny access to all principals except a particular IAM role and AWS service role (e.g. billingreports.amazonaws.com).

I have tried using 'Deny' with 'NotPrincipal', but none of the below examples work as I don't think the ability to have multiple types of principals is supported by AWS?

This allows you to save the policy but locks out the bucket (warning: only root user can then update policy to unlock)

"Effect": "Deny",
     "NotPrincipal": {
          "AWS": [
          "arn:aws:iam::<account_id>:root",
          "arn:aws:iam::<account_id>:role/specialBillingRole"
     ],
     "Service": "billingreports.amazonaws.com"
}

Therefore I am trying to use conditions but can't find the right combinations that will work. Here is an example policy.

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "billingreports.amazonaws.com"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::my-bucket/*"
        },
        {
            "Sid": "",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::my-bucket/*",
                "arn:aws:s3:::my-bucket"
            ],
            "Condition": {
                "StringNotLike": {
                    "aws:PrincipalArn": [
                        "arn:aws:iam::<account_id>:role/specialBillingRole",
                        "billingreports.amazonaws.com",
                        "<account_id>"
                    ]
                }
            }
        }
    ]
}

UPDATED the question as per some comment suggestions.

2nd UPDATE Also tried the below, which still gives access to all roles/users in the account (can't use wildcards in the Principal).

{
     "Effect": "Deny",
     "Principal": {
          "AWS": "arn:aws:iam::<account_id>:root"
     },
     "Action": "s3:*",
     "Resource": [
          "arn:aws:s3:::my-bucket/*",
          "arn:aws:s3:::my-bucket"
     ],
     "Condition": {
          "ArnNotEquals": {
               "aws:PrincipalArn": [
                    "arn:aws:iam::<account_id>:role/specialBillingRole",
                    "<account_id>"
               ]
          }
     }
}
1
Why can't you specify the role, the role has a trust policy that limits which services can use itChris Williams
When you have NotPrincipal with Deny you " must also specify the account ARN of the not-denied principal. " from here. I don't know if this helps or not, but you don't have account principle provided in your first two attempts.Marcin
@ChrisWilliams Do you mean specify a role as a Principal with an Allow? This won't work for me because other roles in the account will have access to the bucket via their IAM policy (i.e roles with PowerUserAccess, ReadOnly policies etc).playdoz
@Marcin you are correct about needing the the account principal. I just tried that with multiple types of principals (IAM role + AWS Service Role) and it locked out the bucket. So looks like even though I am able to save the policy, it is in fact invalid and voids all the other roles therefore it's Deny all.playdoz
@playdoz Deny and NonPrinciple policies are difficult to manage. Are you sure there is no other way to do it, like through roles like Chris mentioned? Docs (same link as before) also write "recommend that you explore other authorization options before you decide to use NotPrincipal. "Marcin

1 Answers

0
votes

You can certainly create a bucket policy that grants access only to a service role and an IAM role, but to be clear, a service role will still begin with "arn:aws:iam:::role...".

Are you instead trying to create a bucket policy that grants access both to a particular service and a service role? I'm asking because if you have a role created with billingreports.amazonaws.com as its trusted entity, and if that role is what's intended to access the bucket, then you do not need to list the service separately in your bucket policy (if the scenario is as I imagine).

Please do note, also, that you can indeed use wildcards with the principal, combined with a condition - I do so all the time (see my example policy below). When I want to restrict bucket access to a specific role, I simply include an Allow statement with a Principal of just the role I want to allow, and then a Deny statement with a Principal of "AWS": "*", followed by a condition, like so:

{
    "Version": "2008-10-17",
    "Id": "PolicyScopedToSecurity",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::[accountID]:role/[roleName]",
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::[bucketName]",
                "arn:aws:s3:::[bucketName]/*"
            ]
        },
        {
            "Effect": "Deny",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::[bucketName]",
                "arn:aws:s3:::[bucketName]/*"
            ],
            "Condition": {
                "StringNotLike": {
                    "aws:PrincipalArn": [
                        "arn:aws:iam::[accountID]:role/[roleName]",
                        "[accountID]"
                    ]
                }
            }
        }
    ]
}

Let me know if you truly need the service itself to access the bucket, as my response assumes just the service role needs access. :)