Spotlight
ABAC vs RBAC for Access Control in AWS
Explore how Access Controls can protect your sensitive information from unauthorized access.
Fri, 27 Jul 2018
Access management is a key attribute to the security of any enterprise IT infrastructure. Amazon Web Services (AWS) offers a variety of tools to address access management. Having a wide variety of options available provides flexibility and agility to the customer but can also add confusion as it is sometimes unclear how these access management services interact. Specifically, S3 access management can get quite overwhelming. Object ACLs, Bucket ACLs, IAM Policies, Bucket Policies, Bucket Ownership, and Object Ownership all effect who has access to an object stored in S3 and it can be unclear how they interact.
When interacting with s3 permissions, this AWS blog post is my goto for a basic understanding of the interactions between the three access controls (IAM policies, bucket policies, ACLs), but it doesn’t cover every use case, notably it does not mention that the object owner can have an effect on the permissions.
In an attempt to add some clarity to the s3 permissions, we created a script to test all relevant combinations of IAM policy, bucket policy, object owner and ACL. The results are summarized in the table at the end of this post.
For simplicity we only looked at the GetObject
api operation. We created multiple buckets in one account, each with a different bucket policy. In each bucket, we created multiple objects with different object owners and ACLs. We created IAM users in two seperate accounts (one account also holds the bucket) to test cross account access. We created objects owned by three accounts (two of the previously mentioned accounts and a third because sometimes objects are owned by an AWS controlled account, billing reports and some access logs are notable examples of this). We used three IAM policies: one with an explicit deny, one with an explicit allow, and one with no policy. (Policy statements are detailed at the end). We also had four bucket policies: one with no policy, one with an explicit deny for everyone, one that allowed access to everyone, and one that allowed access to either of the two user accounts. Again, policies are detailed a the end of the post. We set four different object ACLs: private, public-read, authenticated-read, and bucket-owner-read. We initially started with different bucket ACLs but they had no effect (since we only test the GetObject api). Thus they are not included in the results. Finally, we also made unauthenticated get requests for each of the objects.
The table shows the results every combination of the above resources: every combination of AWS account and IAM policy made a getObject
call on every combination of bucket policy, object acl, and object owner.
The results are recorded in the following table. We have reduced the results using wildcards where appropriate.
Symbol | Meaning |
---|---|
* | Any possible value for the given column |
! VALUE | Any possible value for the given column except the given VALUE |
This first table demonstrates how deny policies completely deny access, regardless of other factors.
User Account | User Policy | Bucket Policy | Object Owner Account | Object ACL | Caller Owns Bucket | Caller Owns Object | Bucket Owner Owns Object | Has Access | |
---|---|---|---|---|---|---|---|---|---|
* | * | deny | * | * | * | * | * | AccessDenied | |
* | deny | * | * | * | * | * | * | AccessDenied |
This table shows calls made from the first account, which is the same account as the bucket owner.
User Account | User Policy | Bucket Policy | Object Owner Account | Object ACL | Caller Owns Bucket | Caller Owns Object | Bucket Owner Owns Object | Has Access | |
---|---|---|---|---|---|---|---|---|---|
1st Acct | allow | ! deny | * | ! private | Yes | * | * | Success | |
1st Acct | allow | ! deny | 1st Acct | private | Yes | Yes | Yes | Success | |
1st Acct | allow | * | 2nd Acct | private | Yes | No | No | AccessDenied | |
1st Acct | none | allow-any | 1st Acct | * | Yes | Yes | Yes | Success | |
1st Acct | none | allow-any | 2nd Acct | ! private | Yes | No | No | Success | |
1st Acct | none | allow-any | 2nd Acct | private | Yes | No | No | AccessDenied | |
1st Acct | none | allow-calling-accounts OR none | 1st Acct | authenticated-read OR public-read | Yes | Yes | Yes | Success | |
1st Acct | none | allow-calling-accounts OR none | 1st Acct | bucket-owner-read OR private | Yes | Yes | Yes | AccessDenied | |
1st Acct | none | allow-calling-accounts OR none | 2nd Acct | * | Yes | No | No | AccessDenied |
This table shows calls made from the second account, which demonstrates cross account access.
User Account | User Policy | Bucket Policy | Object Owner Account | Object ACL | Caller Owns Bucket | Caller Owns Object | Bucket Owner Owns Object | Has Access | |
---|---|---|---|---|---|---|---|---|---|
2nd Acct | allow | ! deny | 2nd Acct | * | No | Yes | No | Success | |
2nd Acct | allow | allow-any OR allow-calling-accounts | 1st Acct | * | No | No | Yes | Success | |
2nd Acct | allow | allow-any OR allow-calling-accounts | 3rd Acct | authenticated-read OR public-read | No | No | No | Success | |
2nd Acct | allow | allow-any OR allow-calling-accounts | 3rd Acct | bucket-owner-read OR private | No | No | No | AccessDenied | |
2nd Acct | allow | none | 1st Acct OR 3rd Acct | authenticated-read OR public-read | No | No | Yes | Success | |
2nd Acct | allow | none | 1st Acct OR 3rd Acct | bucket-owner-read OR private | No | No | Yes | AccessDenied | |
2nd Acct | none | ! deny | 2nd Acct | authenticated-read OR public-read | No | Yes | No | Success | |
2nd Acct | none | ! deny | 2nd Acct | bucket-owner-read OR private | No | Yes | No | AccessDenied | |
2nd Acct | none | * | 1st Acct | * | No | No | Yes | AccessDenied | |
2nd Acct | none | * | 3rd Acct | * | No | No | No | AccessDenied |
This table shows calls made from an unauthenticated user.
User Account | User Policy | Bucket Policy | Object Owner Account | Object ACL | Caller Owns Bucket | Caller Owns Object | Bucket Owner Owns Object | Has Access | |
---|---|---|---|---|---|---|---|---|---|
Unauth User | none | ! deny | * | public-read | No | No | * | Success | |
Unauth User | none | allow-any | 1st Acct | * | No | No | Yes | Success | |
Unauth User | none | allow-any | 2nd Acct | ! public read | No | No | No | AccessDenied | |
Unauth User | none | allow-calling-accounts | * | ! public read | No | No | * | AccessDenied | |
Unauth User | none | none | * | ! public read | No | No | * | AccessDenied |
The following are the possible values we used for each coloumn.
User Account
User Policy
]
Bucket ACLs
Bucket Policy
GetObject
operation (there is a base bucket policy affecting the put operation).{ "Sid":"Deny s3 get object", "Effect":"Deny", "Principal": "*", "Action":["s3:GetObject"], "Resource":["arn:aws:s3:::*"]}
Object Owner Account
Object ACL (these are just the canned ACLs provided by AWS)
Every possible combination of these features were tested in the script. The code and the complete, unreduced results can be found here.
Explore how Access Controls can protect your sensitive information from unauthorized access.