OK, I figured out my original issue of every item in a bucket getting the Public ACL was not because of my policies but because I am using the presigned URL JavaScript upload and had acl:public-read
in the upload. But I don't want that. I want the heroku app to be able to access the photos but not the rest of the world. Can this be done in Rails? Can the concept of pre-signed URLs be used for access as well upload? I have CORS setup for the upload as:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>http://mylocalhost</AllowedOrigin>
<AllowedOrigin>http://www.myherokuapp.com</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>ETag</ExposeHeader>
<ExposeHeader>x-amz-server-side-encryption</ExposeHeader>
<ExposeHeader>x-amz-request-id</ExposeHeader>
<ExposeHeader>x-amz-id-2</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
<AllowedHeader>x-amz-acl</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Would adding GET to my CORS be the solution to my problem? Does it consider the original request for the photo when embedded in the web page to be coming from heroku? or from the browser?
------- old question -----
I have an AWS S3 bucket that I am trying to keep private except for access through my Heroku app. The only access should be from an IAM user or a CORS upload using a presigned URL. I already have a bunch of images in the bucket. Those were uploaded when the ACL was set to Public:READ. I would like to undo that but first I need a policy that will allow my Heroku app to access every image in the bucket. So my object is to:
1) Remove the ACL of Public from all sub items in the bucket
2) Set a bucket permission that denies ALL, then allows all access from my root user and my heroku IAM user (already set up).
I have a bucket policy of:
{
"Id": "Policy1525547050154",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1525546474878",
"Action": "s3:*",
"Effect": "Allow",
"Resource": "arn:aws:s3:::my-bucket",
"Principal": {
"AWS": [
"arn:aws:iam::xxxxxxxxxxx:user/heroku-app"
]
}
},
{
"Sid": "Stmt1525547042878",
"Action": "s3:*",
"Effect": "Allow",
"Resource": "arn:aws:s3:::my-bucket",
"Principal": {
"AWS": [
"arn:aws:iam::xxxxxxxxxxx:root"
]
}
}
]
}
I have applied this and it will apply to every NEW item uploaded into the bucket by the Heroku IAM user. But items that were previously uploaded do not seem to have this applied. The web console is very confusing and IMHO poorly written. I get that most big users will be using a command line console to do all of these things. I am a Ruby/Rails dev with a fair bit of systems management experience (pre-AWS) and I don't mind using the command line if it is in fact more useful than the web console.
EDIT: OK, looking at the doc again, I'm thinking my explicit deny of * at the beginning is never going to be overridden by my allows that follow. So I need to rethink my policy but it still doesn't change how to override the ACLs of previously saved items. I would do them manually except here are hundreds of them and I don't want to have to click through each one. It just doesn't make sense to me that an ACL at the top level wouldn't have an option of "apply to all child items recursively". And shouldn't my explicit policy override that?
Latest: I deleted the explicit deny on my policy. I then went to my Heroku app, uploaded a new photo and am able to do all of the actions the app should. BUT even the new upload is publicly available from a private window session in another browser just by copying the web address. I even tried the web address in an anonymous proxy and I can view the photo. I then removed the public ACL from the photo I uploaded and when I try to view it through my Heroku app it gets access denied.