I've been trying to get active storage working with direct uploads in my react project. Keep getting a 403 because of the 'Deny' in my IAM policy.
I have active storage setup with the following storage.yml file
production:
service: S3
bucket: <bucketname>
region: "us-east-1"
access_key_id: <access_key>
secret_access_key: <secret_access_key>
upload:
server_side_encryption: "AES256"
I am using the direct upload mentioned here in the rails docs using this package
The IAM policy looks like this
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::<bucket>",
"arn:aws:s3:::<bucket>/*"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": [
"arn:aws:s3:::<bucket>",
"arn:aws:s3:::<bucket>/*"
],
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
}
]
}
My bucket's CORS settings
[
{
"AllowedHeaders": [
"*",
"x-amz-server-side-encryption"
],
"AllowedMethods": [
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"http://localhost:8000/",
"http://localhost:8000",
"http://localhost:8010/"
]
}
]
I've tried every suggestion I can find on google and finally tracked down the issue.
If I remove
{
"Sid": "VisualEditor1",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": [
"arn:aws:s3:::<bucket>",
"arn:aws:s3:::<bucket>/*"
],
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
}
from the IAM policy, it works!
But I want the extra security.
My presigned URL I am getting from the rails/active_storage/direct_uploads endpoint looks like this.
https://.s3.amazonaws.com/1fhy070cp9l4oegb3mcq5mta0rgx?x-amz-server-side-encryption=AES256&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...
EDIT: here's the FE code that does the uploading via the rails/activestorage package
const getLogoBlob = (logo: File, callback: (blob: Blob) => void) => {
const upload = new DirectUpload(
logo,
`${env.baseUrl}/rails/active_storage/direct_uploads`,
);
upload.create(async (error, blob) => {
if (error) {
throw new Error('Failed to upload logo');
}
callback(blob);
});
};