Demystifying AWS IAM Policies: Unraveling De Morgan’s Laws and S3 Buckets Policy¶
Condition Evaluation¶
Before we get into it we need to review how Condition evaluation works:
The images on this page does a nice job of summarizing.


New IAM Deny reason Error Messages¶
As of September 2021 You will now get error messages that detail the source of a IAM access block for the following policy types:
SCP
VPC Endpoint policy
Permission Boundaries
Session Policies
Resource Based policy
Identity Based policy
This saves a lot of time especially if you do not have access to all of these.
When there is no additional context¶
For S3 if you do not have permission on the default KMS key that is configured
on the bucket you will get an AccessDenied with no further information.
A good indicator of this is if you can list_objects which requires no KMS
permission. If that works, but PutObject or GetObject give AccessDenied
check very closely that say your lambda role has permission to use the default
KMS key of the bucket.
De Morgan’s laws and IAM¶
We all know that multiple conditions in a IAM Condition statement are joined by logical AND. Having an understanding of using Not operator conditions in AWS IAM policy by applying De Morgans laws can give you powerful flexibility, that can be mind boggling to read and understand.
Lets take a look at this example policy. Have a think about what will happen if you access it using the named role via the internet S3 endpoint.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyNonVPCAccessExceptNamedRole",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
],
"Condition": {
"StringNotEquals": {
"aws:sourceVpc": ["vpc-123", "vpc-456"]
},
"ArnNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::12345678902:role/role_that_cannot_use_vpc"
}
}
}
]
}
This Bucket policy will deny all access unless it is from the named VPC - If the
role is arn:aws:iam::12345678902:role/role_that_cannot_use_vpc and not from a
VPC allow it.
Lets break it down:
The StringNotEquals condition for aws:sourceVpc ensures that the request’s
source VPC is not equal to vpc-123. The ArnNotEquals condition for
aws:PrincipalArn checks if the request’s principal ARN is not equal to
arn:aws:iam::12345678902:role/role_that_cannot_use_vpc. if you access the
bucket using the role called role_that_cannot_use_vpc and do not access it
from the VPCs listed in the policy:
If bucket is accessed from a VPC other than vpc-123 using the role
role_that_cannot_use_vpc the StringNotEquals condition for aws:sourceVpc
evaluates to true since the VPC ID does not match. However, the ArnNotEquals
condition for aws:PrincipalArn evaluates to false since the principal ARN
matches the specified role ARN. As a result, the deny statement is not
triggered, and access to the bucket is allowed.
If bucket is accessed from VPC vpc-123 but not using the role
role_that_cannot_use_vpc, the StringNotEquals condition for aws:sourceVpc
evaluates to false since the VPC ID matches. The ArnNotEquals condition for
aws:PrincipalArn evaluates to true since the principal ARN does not match the
specified role ARN. The deny statement is not triggered, and access to the
bucket is allowed.
If bucket is accessed from a VPC other than vpc-123 and using a role other
than role_that_cannot_use_vpc, both conditions (StringNotEquals for
aws:sourceVpc and ArnNotEquals for aws:PrincipalArn) evaluate to true.
Consequently, the deny policy statement is not triggered, and access to the
bucket is allowed.
Other S3¶
VPC Endpoint Policy does not allows listing objects in bucket:
{
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "*",
"Resource": "arn:aws:s3:::aws-logs-003422198502-ap-southeast-1/*"
}
]
}
ubuntu@ip-10-0-9-197:~$ aws s3 ls aws-logs-003422198502-ap-southeast-1/elasticmapreduce
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: arn:aws:sts::003422198502:assumed-role/adminrole/i-0bf2c5ae5ae2b566c is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::aws-logs-003422198502-ap-southeast-1" because no VPC endpoint policy allows the s3:ListBucket action
But when we remove the /* from the resource from the endpoint policy:
{ “Version”: “2008-10-17”, “Statement”: [ { “Effect”: “Allow”, “Principal”: “”, “Action”: “”, “Resource”: “arn:aws:s3:::aws-logs-003422198502-ap-southeast-1” } ] }
It works:
ubuntu@ip-10-0-9-197:~$ aws s3 ls aws-logs-003422198502-ap-southeast-1 PRE bucet2brentvc/ PRE elasticmapreduce/
``
750263812530 org child brent-orgchild bukcet name
“aws:PrincipalOrgPaths”: “o-r2rjrevijr/r-3hbo/ou-3hbo-nmya1n36/*”
Consider the following:
{
"Action": [
"s3:GetObject*",
"s3:ListBucket"
],
"Condition": {
"StringEquals": {
"aws:ResourceOrgId": [
"o-r2rjrevijr"
],
"aws:PrincipalOrgPaths": [
"o-r2rjrevijr/r-3hbo/*"
],
"aws:ResourceAccount": [
"750263812530"
]
}
},
"Effect": "Allow",
"Principal": "*",
"Resource": "*"
},
aws:PrincipalOrgPaths is a multi valued key, and must be used with a set
condition key operator such as ForAnyValue:StringLike it will resolve to
false if used with only StringEquals
Now consider the incorrect use in the following policy:
This following is allowed is because the statement-1 evaluates to false but is
not a Deny statement, and statement-2 evaluates to true and is an Allow:
{
"Version": "2008-10-17",
"Statement": [
{
"sid": "Statement-1",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject*",
"s3:ListBucket"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgPaths": "o-r2rjrevijr/r-3hbo/*",
"aws:ResourceAccount": "750263812530",
"aws:ResourceOrgId": "o-r2rjrevijr"
}
}
},
{
"sid": "Statement-2",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject*",
"s3:ListBucket"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceOrgId": "o-r2rjrevijr"
}
}
}
]
}
However if we remove statement-2 it is denied:
This is because the
statement-1evaluates to false but is not aDenystatement , since there is no other policy to allow this it is denied.
{
"Version": "2008-10-17",
"Statement": [
{
"sid": "Statement-1",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject*",
"s3:ListBucket"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgPaths": "o-r2rjrevijr/r-3hbo/*",
"aws:ResourceAccount": "750263812530",
"aws:ResourceOrgId": "o-r2rjrevijr"
}
}
}
]
}
Comments
comments powered by Disqus