Amazon S3 Bucket Policy: How to lock down access to only your EC2 Instances

Just to clarify how this is normally done. You create a IAM policy, attach it to a new or existing role, and decorate the ec2 instance with the role. You can also provide access through bucket policies, but that is less precise.

Details below:

  1. S3 buckets are default deny except for my the owner. So you create your bucket and upload the data. You can verify with a browser that the files are not accessible by trying https://s3.amazonaws.com/MyBucketName/file.ext. Should come back with error code "Access Denied" in the xml. If you get an error code of "NoSuchBucket", you have the url wrong.

  2. Create an IAM policy based on arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess. Starts out looking like the snip below. Take a look at the "Resource" key, and note that it is set to a wild card. You just modify this to be the arn of your bucket. You have to do one for the bucket and its contents so it becomes: "Resource": ["arn:aws:s3:::MyBucketName", "arn:aws:s3:::MyBucketName/*"]

  3. Now that you have a policy, what you want to do is to decorate your instances with a IAM Role that automatically grants it this policy. All without any authentication keys having to be in the instance. So go to Role, create new role, make an Amazon EC2 role, find the policy you just created, and your Role is ready.

  4. Finally you create your instance, and add the IAM role you just created. If the machine already has its own role, you just have to merge the two roles into a new one for the machine. If the machine is already running, it wont get the new role until you restart.

  5. Now you should be good to go. The machine has the rights to access the s3 share. Now you can use the following command to copy files to your instance. Note you have to specify the region

    aws s3 cp --region us-east-1 s3://MyBucketName/MyFileName.tgz /home/ubuntu

Please Note, the term "Security through obscurity" is only a thing in the movies. Either something is provably secure, or it is insecure.


I used something like

{
    "Version": "2012-10-17",
    "Id": "Allow only My VPC",
    "Statement": [
        {
            "Sid": "Allow only My VPC",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject", "s3:ListBucket",
            "Resource": [
                "arn::s3:::{BUCKET_NAME}",
                "arn::s3:::{BUCKET_NAME}/*"
            ],
            "Condition": {
                "StringLike": {
                    "aws:sourceVpc": "{VPC_ID}" OR "aws:sourceVpce": "{VPCe_ENDPOINT}"
                }
            }
        }
    ]
}