S3 bucket policy & IAM policy

AWS S3 provide a lot of flexibility on the permission control, you can either attach the policy on the IAM user, buckets or use the pre-canned ACL. That’s no right or wrong way to attach the policy on either IAM or resource level, it’s depend on your use case and you can use both side of policy to compliment each other.

To create the bucket policy

  1. Go to the respective bucket where you want to add the policy
  2. Click on Permissions,
  3. Select the bucket policy. You will get a text area where you can enter your policy into.
How do I add an S3 Bucket policy? - Amazon Simple Storage Service

If you not familiar with the bucket policy syntax, you can just use the policy generator at the bottom of the page to help you on this, or you can just search of sample policy on the internet.

To add the policy to the IAM user

  1. Go to the IAM console
  2. on the left menu, click on users
  3. Choose the user that you wish to add the policy into
  4. On the user details page, choose the Permission tab and than choose Add inline policy
  5. Choose the JSON tab
  6. Copy the following policy and paste it into the policy text field

It’s always recommended to use the latest policy version which is “2012-10-17” in your policy JSON, this is because if you not specify the version number, AWS will use the old version as a default “2008-10-17” which may have some limitation.

In the article, I will share few of the S3 bucket policy as well as IAM policy which are commonly use for most of the public or private bucket.

1. Only allow HTTPS access policy

By default, S3 supported both HTTP and HTTPS request. In order to disable the access using HTTP but only allow HTTPS, we can create the rule to deny the http access with the bucket policy by adding the condition where “aws:SecureTransport” is false. This will explicitly deny any none https request by accessing the bucket and it’s content.

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSSLRequestsOnly",
      "Action": "s3:*",
      "Effect": "Deny",
      "Resource": [
        "arn:aws:s3:::awsexamplebucket",
        "arn:aws:s3:::awsexamplebucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      },
      "Principal": "*"
    }
  ]
}

In contrast, you may using the allow s3:GetObject policy with the condition “aws:SecureTransport”:”True” which may have the same effect. This statement allow any user to access the s3:GetObject for all object if its request uses HTTPS, but try to avoid this unless there is a special purpose for your own use case. We should always use deny to explicitly block the none https request.

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSSLRequestsOnly",
      "Action": "s3:GetObject",
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::awsexamplebucket",
        "arn:aws:s3:::awsexamplebucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "True"
        }
      },
      "Principal": "*"
    }
  ]
}

2. IP whitelisting policy

To allow only the certain IP to access the s3 bucket, just add the condition to check on the IPAddress “aws:SourceIP” is from the list of CIDR range only.

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IP whitelisting",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::awsexamplebucket",
        "arn:aws:s3:::awsexamplebucket/*"
      ],
      "Condition": {
        "IpAddress": {
           "aws:SourceIp": [
              "11.11.11.11/32",
              "22.22.22.22/32"
              ]
          }
      },
      "Principal": "*"
    }
  ]
}

3. IP blacklisting policy

In contrast, to blacklist the IP, use the condition to check NotIpAddress “aws:SourceIp” with the CIDR range

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IP Blacklist",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::awsexamplebucket",
        "arn:aws:s3:::awsexamplebucket/*"
      ],
      "Condition": {
        "NotIpAddress": {
           "aws:SourceIp": [
              "11.11.11.11/32",
              "22.22.22.22/32"
              ]
          }
      },
      "Principal": "*"
    }
  ]
}

4. Allow IAM user access to a folder in a bucket

This is the policy that attach to the IAM user, so that’s no principal needed for the policy.

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Allow only own folder",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::awsexamplebucket/Alice/*"
    }
  ]
}

5. Allow IAM user access to their own bucket with their own username

To use the AWS variable “${aws:xxxxxxx}” in the policy, the version must specific “2012-10-17”, if you not specific the version number, the default version of “2008-10-17” will be use which may not work in this case.

In the following policy, we assume that you already have the folder under the bucket name awsexamplebucket

awsexamplebucket
    Alice/
    Bob/
    Criss/
{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Allow only username folder",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::awsexamplebucket/${aws:username}/*"
    }
  ]
}

6. Allow IAM user access to their own bucket with their IAM user id

Because the username is a human readable name, which is not require by AWS to be globally unique. So to avoid this issue, you may use the “${aws:userid}” instead of “${aws:username}”

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Allow only user id folder",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::awsexamplebucket/${aws:userid}/*"
    }
  ]
}

7. All IAM user access to selected folder

This policy allow the user access to only Development folder and the content in the folder. We use the condition where to allow only Development folder inside the awsexamplebucket. Noted that “s3:prefix” is require for the folder-like access, if that’s no prefix parameter exists, than S3 will assume it’s a object keys.

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Allow only development folder",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::awsexamplebucket"
      "Condition": {
         "StringLike": {"s3:prefix":["Development/*"]}
       }
    }
  ]
}

8. Deny IAM user access to selected folder

We also can use the same condition to deny the access to certain folder under the bucket, if you wish to deny all access to private folder in any bucket, just replace the resources to “arn:aws:s3:::*”

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Deny access to private folder",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::awsexamplebucket"
      "Condition": {
         "StringLike": {"s3:prefix":["Private/"]}
       }
    }
  ]
}

9. Allow only bucket root level access

To allow the bucket root level access only, we can change the condition to “StringLike”: {“s3:prefix”:[“”]}

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Allow root level access",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::awsexamplebucket"
      "Condition": {
         "StringLike": {"s3:prefix":[""]}
       }
    }
  ]
}

10. Must include public read for Put object request

The following policy mandate that every put object request must include the x-amz-acl public-read permission with the key-value pair of “s3:x-amz-acl”:[“public-read”].

{ 
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Public Read ACL",
      "Effect": "Allow",
      "Action": "s3:PutObject",
      "Resource": [
        "arn:aws:s3:::awsexamplebucket/*"
      ],
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "public-read"
        }
      }
    }
  ]
}

11. Mix whitelist and blacklist IP condition

Use of AWS wide condition keys with the mix of whitelist and blacklist of IP address.

{
  "Id": "ExamplePolicy",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Whitelist and Blacklist IP address",
      "Action": "s3:*",
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::awsexamplebucket",
        "arn:aws:s3:::awsexamplebucket/*"
      ],
      "Condition": {
        "IpAddress": {
           "aws:SourceIp": "11.11.11.11/32"
          },
        "NotIpAddress": {
           "aws:SourceIp": "22.22.22.22/32"
          }
      },
      "Principal": "*"
    }
  ]
}

12. Must include bucket owner full access

When you deal with cross account S3 bucket policy, whenever you grant the permission to the user in Account B to put object to S3 bucket in Account A, by default, the object only grant the object owner full access, which will be the user in Account B.

So in order to allow root account or any administrator in Account A to have access to the object, the object owner need to explicitly grant the permission to the bucket owner with the header of “s3:x-amz-grant-full-control”: “id=AccountA-CanonicalUserID” in the Put object request.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Bucket Owner Full Access",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::AccountB-ID:user/Dave"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::awsexamplebucket/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-grant-full-control": "id=AccountA-CanonicalUserID"
        }
      }
    }
  ]
}

13. Allow if grant full control, deny if not

Explicitly deny any put object request from the cross account user if the grant bucket owner full control header is not exists.

For example, Dave can belong to a group, and you grant the group s3:PutObject permission without any condition. To avoid such permission loopholes, you can write a stricter access policy by adding explicit deny. In this example, you explicitly deny the user Dave upload permission if he does not include the necessary headers in the request granting full permissions to the bucket owner.

Explicit deny always supersedes any other permission granted. The following is the revised access policy example with explicit deny added.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::AccountB-ID:user/AccountBadmin"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::awsexamplebucket/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-grant-full-control": "id=AccountA-CanonicalUserID"
                }
            }
        },
        {
            "Sid": "statement2",
            "Effect": "Deny",
            "Principal": {
                "AWS": "arn:aws:iam::AccountB-ID:user/AccountBadmin"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::awsexamplebucket/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-grant-full-control": "id=AccountA-CanonicalUserID"
                }
            }
        }
    ]
}

14. Grant bucket owner full control with canned ACL

The following policy achieve the same permission with above policy by using the pre-canned ACL by the S3, just use the condition of “s3:x-amz-acl”: “bucket-owner-full-control”

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Bucket Owner Full Access",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::AccountB-ID:user/Dave"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::awsexamplebucket/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control"
        }
      }
    }
  ]
}

When calling the s3api, you need to include the –acl “bucket-owner-full-control” in the request.

aws s3api put-object --bucket examplebucket --key HappyFace.jpg --body c:\HappyFace.jpg --acl "bucket-owner-full-control" --profile AccountBadmin

15. Must encrypt the object with S3 Server-Side Encryption

The following policy request to have the “s3:x-amz-server-side-encryption”: “AES256” header when calling for the pub object API, with this header, the object uploaded to the bucket will be encrypt with the server side encryption automatically.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Encrypt with server-side encryption",
      "Effect": "Allow",
      "Principal": "*"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::awsexamplebucket/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-server-side-encryption": "AES256"
        }
      }
    }
  ]
}

There are a lot of possibility that can be set in the s3 policy based on your company usage, the above is only some of the policy which I can think of and has been use in my working environment.

I will keep on update the list if there is any of the policy which I think is good to know and useful in real environment.

There are some good example where you can find in the following link:

https://docs.aws.amazon.com/AmazonS3/latest/dev/amazon-s3-policy-keys.html

Leave a Reply

google.com, pub-3772983857049267, DIRECT, f08c47fec0942fa0
%d bloggers like this: