28

I am trying to apply the following policy in order to restrict my_bucket's access to a particular VPC.

When I try to apply this as a bucket policy, I get an Policy has an invalid condition key - ec2:Vpc.

How do I correct this?

{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Effect": "Deny",
         "Principal": {
            "AWS": "*"
         },
         "Action": "*",
         "Resource": "arn:aws:s3:::my_bucket/*",
         "Condition":{
            "StringNotEquals": {
               "ec2:Vpc": "arn:aws:ec2:region:account:vpc/vpc-ccccccc"
            }
         }
      }
   ]
}
BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Lelouch Lamperouge
  • 8,171
  • 8
  • 49
  • 60

3 Answers3

48

I just got this to work. I had to do two things. 1) Create the bucket policy on the S3 bucket, 2) create a "VPC Endpoint"

My S3 bucket policy looks like this (of course put in your bucket name and VPC identifier):

{
    "Version": "2012-10-17",
    "Id": "Policy1234567890123",
    "Statement": [
        {
            "Sid": "Stmt1234567890123",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::my_bucket/*",
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpc": "vpc-12345678"
                }
            }
        }
    ]
}

The S3 bucket also has some permissions outside the bucket policy to allow access from the AWS Console. Doing the above did not give access. To get access, I also had to go to AWS Console -> VPC -> Endpoints, and then create an endpoint. I attached the newly created endpoint to the only routing policy the account has at the moment (that has all subnets attached to it) and I used the default policy of

{
    "Statement": [
        {
            "Action": "*",
            "Effect": "Allow",
            "Resource": "*",
            "Principal": "*"
        }
    ]
}

Once I created the endpoint, I was able to read from the S3 bucket from any EC2 instance in my VPC simply using wget with the right URL. I am still able to access the bucket from the AWS Console. But if I try to access the URL from outside the VPC, I get 403 forbidden. Thus, access to the S3 bucket is restricted to a single VPC, just like what you are looking for.

This is apparently a new feature. See this AWS blog entry for more information.

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Eddie
  • 53,828
  • 22
  • 125
  • 145
  • Did this work for instances in a public subnet as well? – Eleni Oct 21 '15 at 16:36
  • @Eleni: What do you mean public subnet? As long as your instance belongs to the VPC listed, that should be sufficient. I've had this work across AWS accounts. That is, S3 bucket in one account listing a VPC in another and it works. – Eddie Oct 22 '15 at 16:34
  • 2
    Thanks Man! Well written, Exactly what I needed. May your steak will always be done to perfection. – Asaf Savich Nov 30 '16 at 09:37
  • One challenge with VPC Endpoints is that they are only available for services in your region, as far as I know. This means that an s3 bucket in Us-East can't be accssed by a VPC in EU-Central through a VPC endpoint. Challenging. – Mr.Budris May 17 '17 at 18:30
  • 1
    Thanks. It works. AWS should really simplify its S3 configuration methods, rather than making fancy "new S3 console" blah blah. In Route 53 you can easily create a private hosted zone and associate it with a VPC but in S3 you need to write cryptic JSON to restrict access to a VPC... – Zhuoyun Wei Jul 18 '17 at 12:44
  • @Eddie What type of VPC endpoint are you using - gateway or interface ? – Saurabh Jul 15 '22 at 04:34
8

Two things that bit me and which might be helpful to add to Eddie's nice answer are:

First, you won't be able to view your bucket (or even modify its policy once you set the policy above) in the S3 AWS console unless you also give your AWS users permissions to manipulate the bucket. To do that, find your AWS account number (displayed in upper-right here), and add this statement to the bucket policy statements list:

    {
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::YOUR_AWS_ACCOUNT_NUMBER:root"
        },
        "Action": "s3:*",
        "Resource": [
            "arn:aws:s3:::my_bucket",
            "arn:aws:s3:::my_bucket/*"
        ]
    },

Second, if you have more than one VPC, say vpc-XXXXXX and vpc-YYYYYY to give access to, the statement in Eddie's answer needs to be tweaked to something like the following (note the "Allow" "StringEquals" and list of sourceVpc values:

... 
"Effect": "Allow",
...
"Condition": {
    "StringEquals": {
        "aws:sourceVpc": [
            "vpc-XXXXXXXX",
            "vpc-YYYYYYYY"
        ]
    }
JJC
  • 9,547
  • 8
  • 48
  • 53
1

No, you can't do that.

Here's another person asking the same: https://forums.aws.amazon.com/thread.jspa?threadID=102387

Some have gotten overly creative with the problem trying to solve it with networking: https://pete.wtf/2012/05/01/how-to-setup-aws-s3-access-from-specific-ips/

I prefer a more simple route, S3 allows you to sign urls to solve this very problem, but inside of your VPC you may wish to not have to think about signing - or you just couldn't sign, for example you might be using wget, etc. So I wrote this little micro-service for that very reason: https://github.com/rmmeans/S3-Private-Downloader

Hope that helps!

UPDATED:

AWS now has a feature for VPC endpoints: https://aws.amazon.com/blogs/aws/new-vpc-endpoint-for-amazon-s3/, you should use that and not what I previously suggested.

rmmeans
  • 283
  • 4
  • 13