3

I am setting up a selection of S3 buckets and wish to restrict access to them to a VPC while still allowing access to the buckets from the AWS console.

As proposed here I have created an S3 endpoint and also added it to the main routing table. The policy on the endpoint allows full access to all resources.

I have created an S3 policy (see below) and added it to the bucket. As soon as I save the policy, access to the bucket from the console is no longer possible.

I have also tried to specifically add a user to the condition "StringNotEquals" in the form of "aws:username": "user1", to no avail.

{
"Version": "2012-10-17",
"Id": "Policy-S3-Bucket-myBucket",
"Statement": [

    {
        "Sid": "Access-via-VPC-only",
        "Principal": "*",
        "Action": "s3:*",
        "Effect": "Deny",
        "Resource": [
            "arn:aws:s3:::myBucket",
            "arn:aws:s3:::myBucket/*"
        ],
        "Condition": {
            "StringNotEquals": {
                "aws:sourceVpc": "vpc-01c9d66c12345"
            }
        }
    },
    {
        "Sid": "Allow-console-access",
        "Action": [
          "s3:*"
        ],
        "Effect": "Allow",
        "Resource": [
          "arn:aws:s3:::myBucket",
          "arn:aws:s3:::myBucket/*"
      ],
        "Principal": {
          "AWS": [
            "arn:aws:iam::<account-id>:user/user1", "arn:aws:iam::<account-id>:user/user2"
          ]
        }
      }
]
}

The expected result would be that the S3 bucket is only accessible by the aforementioned VPC and via the AWS console.

The actual result is:

The bucket overview shows an "Error: Access Denied" and the permissions page(public access settings) shows: "You don't have access to view this configuration. Contact your account administrator to request access."

I have to login using the root user and delete the policy to regain access to the bucket.

  • When you say that you want to allow access from the console, do you actually mean that or do you mean that you want the S3 bucket to be accessible to a specific IAM user (regardless of the mechanism used, be it console, SDK, AWSCLI)? Your attempted policy suggests the latter. – jarmod Jan 18 '19 at 19:34
  • Specifically, I want to make the S3 buckets available to administrative (IAM) users using the AWS console. That means editing the bucket cconfiguration, changing the policy, etc directly in the console. Currently this is not possible. Access via SDK and AWSCLI would be good too, although I currently don't know how to make that happen. On the SDK/CLI front, I would use roles for that (Again, I need to figure out what the correct way to do that is) – Michael Boeni Jan 18 '19 at 19:59
  • Could you test a policy containing just the first 'deny' statement (removing the 'allow' statement that you currently have) and modify the condition to: "StringNotEquals": { "aws:sourceVpce": "vpc-01c9d66c12345", "aws:username": "user1", "aws:username": "user2" }. Ideally test this on a new bucket that you don't care about, just in case. – jarmod Jan 18 '19 at 23:07
  • I have tried that. It seems that this would result in an AND statement, not an OR. As far as I have understood it, you can have multiple values to a single key these values are evaluated as 'OR'. Two keys, on the other hand, are evaluated as an AND. So the above would require both statements "aws:sourceVpce" = "vpc-01c9d66c12345" AND "aws:username" = "user1" to be true. – Michael Boeni Jan 18 '19 at 23:59
  • Yes, it is an AND. However, the policy I proposed wouldn't work anyway as you cannot have multiple aws:username elements in the same condition. – jarmod Jan 19 '19 at 17:03

4 Answers4

0

I found a solution which seems to work. I have tested it in the policy simulator and it also appears to work correctly in the live environment. The following policy does the trick:

{
    "Version": "2012-10-17",
    "Id": "Policy-S3-Bucket-myBucket",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetBucketLocation",
                "s3:ListAllMyBuckets"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:username": ["user1", "user2"]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": ["s3:ListBucket"],
            "Resource": ["arn:aws:s3:::myBucket"],
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpc": "vpc-01c9d66c12345"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": ["s3:ListBucket"],
            "Resource": ["arn:aws:s3:::myBucket"],
            "Condition": {
                "StringEquals": {
                    "aws:username": ["user1", "user2"]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": ["arn:aws:s3:::myBucket/*"],
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpc": "vpc-01c9d66c12345"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": ["arn:aws:s3:::myBucket/*"],
            "Condition": {
                "StringEquals": {
                    "aws:username": ["user1", "user2"]
                }
            }
        }
    ]
}

The policy requires for either the sourceVpc string OR the username to be as listed in the condition.

Admittedly, the policy is verbose and there is a substantial amount of replication. If anyone has an idea to solve this more efficiently, I'm all ears.

  • Note that this policy allows access from vpc-01c9d66c12345 but does not prevent access from other VPCs. So in that sense it does not "restrict access to [the S3 buckets] to a VPC" which was one of your stated requirements. That might be acceptable, unless you genuinely need to prevent access from other VPCs - as written, anyone with s3:*, for example, can access this bucket from a different VPC. – jarmod Jan 19 '19 at 17:02
  • Hmm, that is not the intention then. What does indicate to you that the buckets can be accessed from other VPCs? This should only work if you are one of the specified users, doesn't it? So, if you are either in the mentioned VPC OR you are user1 or user2 you have access, otherwise you do not...right? – Michael Boeni Jan 19 '19 at 20:30
  • The effective policy at runtime is a combination of the S3 bucket policy and whatever IAM policy the client has. If the client has an IAM policy allowing s3:* against resource:*, for example, then they can write to this S3 bucket - nothing in the bucket policy will deny that IAM access. The S3 bucket policy doesn’t explicitly allow it, but it doesn’t explicitly deny it either, and the IAM policy will allow it. Policy evaluation is explicit deny > explicit allow > implicit deny. By default, in the absence of policies, everything is implicit deny, as you’d expect. – jarmod Jan 19 '19 at 21:45
0

This policy was tested and gives exactly what you need:

 Statement": {        
        "Sid": "Allow-anonymous-access-from-specific-VPC",
        "Effect": "Allow",
        "Principal": "*",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::bucket_name/*",
        "Condition": {
            "StringEquals": {
                "aws:SourceVpc": "some-vpc-id"
            }
        }
    }

This will allow anonymous access from requests coming from "some-vpc-id", while yet allowing access from AWS console.

Your VPC has to have S3 endpoint configured for it to work.

Eric Abramov
  • 462
  • 4
  • 19
0

I had this same issue. When I tried the strict deny option from examples online, I lost console access to my bucket (I am guessing that it happens to a lot of people). I had to delete the bucket from the aws console on my bastion host in the VPC.

This is the version that worked for me in the end (with a fresh bucket). Note that the bucket has:

Block all public access
✅ On

So it seems to be to deny by default, and then this allow policy makes sense.

{
    "Version": "2012-10-17",
    "Id": "Policy-S3-Bucket-myBucket",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::bucket_name",
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpce": "some-vpce-id"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::bucket_name/*",
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpce": "some-vpce-id"
                }
            }
        }
    ]
}

I tested the download link and it fails from my computer, but succeeds from the bastion host inside the VPC, so it seems to be still denying public traffic correctly.

Jayd
  • 880
  • 1
  • 12
  • 16
-1

The trick seems to be on denying everything except if it comes from the user or from the VPC but it has to be in the same condition. The way policies work is that Deny rules precede over every other rule, so if you deny, you can't then allow on a subsequent rule; it's already denied and that's it.

By the way, the aws:userid of the root user is the Account Id. Probably a bad practice to use this user but oh well :P

So my bucket now only accepts traffic from the VPC and from the user I log into via the AWS web console (so I don't get access denied errors in the web console)

{
"Version": "2012-10-17",
"Id": "Policy154336817545456388",
"Statement": [
    {
        "Sid": "Block-if-not-from-VPC-or-Me",
        "Effect": "Deny",
        "Principal": "*",
        "Action": "s3:*",
        "Resource": [
            "arn:aws:s3:::bucket-name",
            "arn:aws:s3:::bucket-name/*"
        ],
        "Condition": {
            "StringNotEquals": {
                "aws:SourceVpce": "vpce-4598ujfjrhc",
                "aws:userid": "576767373466"
            }
        }
    }
    ]
}