Pete Cheslock

DevOps, RelEng, DevTools, Automation, Randomness

How to Setup AWS S3 Access From Specific IPs

Recently we were testing with AWS VPC, and a requirement for our project was that we needed to allow nodes within a VPC access to S3 buckets, but deny access from any other IP address. Specifically this was accessing of data that was going to be secured using AWS IAM keys. We needed to make sure that even with the AWS access key and secret key, data could only be retrieved while inside the VPC. Adding yet another layer of security to our existing model.

I started by reviewing the AWS documentation but found that their example for restricting access to a specific IP didn’t seem to work for me. After some back and forth with the AWS support team - we were able to nail down the problem and get this working. I figured I would share what we learned for anyone else trying to do something similar.

The way that we have decided to configure our VPC is such that when the VPC nodes connect to S3 - they will connect out to the internet via the “gateway” nodes that we control. Since all traffic from a VPC to S3 needs to traverse the public internet (over https of course), it must route outbound from your VPC to get to S3 (there is not currently a direct, internal to VPC access to S3). This design allows our nodes to connect to S3 from a single IP address, an elastic IP attached to our gateway node.

So - based on this design, we needed a way to only allow access to a set of buckets from this single IP address. For example purposes we are using the IP of 72.309.38.2.

IF you were to follow the initial example laid out by the AWS documentation - you’ll end up with a policy that probably looks similar to this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
    "Id": "S3PolicyId1",
    "Statement": [
        {
            "Sid": "IPAllow",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::my-wicked-awesome-bucket/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "72.309.38.2/32"
                }
            }
        }
    ]
}

What you’re going to find, after banging your head on the table a few times, is that this policy does not work. There does not appear to be an implied deny rule with S3 buckets (similar to how IAM access policies are setup). I did my testing with the s3-curl command - When you have that tool setup - you can make a query like the following:

1
./s3curl.pl --head --id=AKIAYOURACCESSKEY --key=YourSecretKey -- https://s3.amazonaws.com/my-wicked-awesome-bucket/even-more-awesome.file

Using the policy above - returned this:

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
x-amz-id-2: BLPqeibX8nZGnSDNi9zRhb+6a8fDiOW6Ij1OXhadWknJKCX9WAb7x1xNETvdXAEv
x-amz-request-id: F6AF36D912E6003B
Date: Fri, 27 Apr 2012 00:25:52 GMT
Last-Modified: Tue, 17 Apr 2012 17:50:11 GMT
ETag: "8553074962ba71d8b2b600b971ba80a8"
Accept-Ranges: bytes
Content-Type: application/pdf
Content-Length: 1451065
Server: AmazonS3

After troubleshooting this with the AWS support team - they let me know of the following:

By default accounts are restricted from accessing S3 unless they have been given access via policy. However, S3 is designed by default to allow any IP address access. So to block IP's you would have to specify denies explicitly in the policy instead of allows.

Once I learned this - the policy was easy to adjust. I flipped around the policy from allowing access from only my IP address to denying access from everywhere that was NOT my IP address.

My new policy looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
    "Id": "S3PolicyId1",
    "Statement": [
        {
            "Sid": "IPDeny",
            "Effect": "Deny",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::my-wicked-awesome-bucket/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": "72.309.38.2/32"
                }
            }
        }
    ]
}

And when I ran my s3-curl command again - I now received the following response.

1
2
3
4
5
6
7
HTTP/1.1 403 Forbidden
x-amz-request-id: 3B90483F655AA692
x-amz-id-2: 0XEO2miEzZEwbU2N5zPwzLtX4J7BJx/LLXpvSLPpQ7e2wuocx49katsD+ZcqwYA7
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Fri, 27 Apr 2012 00:27:14 GMT
Server: AmazonS3

Success!

This could be used as well for added layers of security with your existing applications that use/access S3 - not just nodes within a VPC. I hope this helps someone out there from any undue stress when trying to securing your S3 access.