silverstripe / silverstripe-s3

Silverstripe module to store assets in S3 rather than on the local filesystem (SS4/SS5 only)
BSD 3-Clause "New" or "Revised" License
20 stars 25 forks source link

Unable to PUT objects in bucket #29

Closed heytrav closed 5 years ago

heytrav commented 5 years ago

Attempts to upload images via the SilverStripe GUI always fail with a 403 error when attempting to upload the objects to my s3 bucket.

Expected Result

File is uploaded and appears in my s3 bucket

Actual result

Uncaught Exception Aws\S3\Exception\S3Exception: "Error executing "PutObject" on "https://my-bucket.s3.ap-southeast-2.amazonaws.com/protected/Uploads/56aa048962/changing-stuff.png"; 
AWS HTTP error: Client error: `PUT https://my-bucket.s3.ap-southeast-2.amazonaws.com/protected/Uploads/56aa048962/changing-stuff.png` resulted in a `403 Forbidden` response: <?xml version="1.0" encoding="UTF-8"?> <Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>364A2C (truncated...)  AccessDenied (client): Access Denied - <?xml version="1.0" encoding="UTF-8"?> <Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>364A2C02BB38CC88</RequestId><HostId>QmjcW22l1PCP4H6cwaL41uH9qd2nfI36jBVBBpIDzUhtlMraqwfBuP6DqLNq7CuPX+gSgkLDdJc=</HostId></Error>" at /var/www/silverstripe/my-app/current/vendor/aws/aws-sdk-php/src/WrappedHttpHandler.php line 195

Config

In my .env file:

# S3 config
AWS_REGION="ap-southeast-2"
AWS_BUCKET_NAME="my-bucket"
AWS_ACCESS_KEY_ID="*******"
AWS_SECRET_ACCESS_KEY="********************************************"

My S3 policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::#######:user/my-user"
            },
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::my-bucket/*"
        },
        {
            "Sid": "statement3",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-bucket/public/*"
        }
    ]
}

If I try to upload objects using the aws command line interface it works fine:

user@frontend:~# export AWS_ACCESS_KEY_ID="*******"
user@frontend:~# export AWS_SECRET_ACCESS_KEY="****************************"

user@frontend:~# echo "hello" > text.txt
user@frontend:~# aws s3 cp text.txt s3://my-bucket/protected/
upload: ./text.txt to s3://my-bucket/protected/text.txt

Any suggestions what I am doing wrong would be highly appreciated.

obj63mc commented 5 years ago

I would make sure that your s3 client is actually using your key and secret. If this isn't a global environment variable on your machine and just in your .env file you have to configure the s3 client in your yaml config. Checkout https://github.com/silverstripe/silverstripe-s3/issues/26#issuecomment-505525388 for an example.

phptek commented 5 years ago

I guess this is the confusing thing for us (I work with @heytrav), I'd expect the module to pickup the ENV var, from .env itself. So on first glance, it seems redundant to have to re-state the config in YML. Regardless, it works from my dev env, when previously it didn't.

obj63mc commented 5 years ago

The module was designed primarily to work on amazon infrastructure directly, so you wouldn't be specifying anything other than the region and your bucket name. The reason you have to setup the yml config is that the s3 client class that is used is injected dynamically with SS Injector class from the yaml config.

If we set it up the default config to always look for the key and secret then it wouldn't work on amazon directly as the key and secret aren't in their default environment.

Checkout - https://github.com/silverstripe/silverstripe-s3/blob/master/_config/assets.yml#L8 to see how we instantiate the AWS PHP SDK's s3 client. You are basically telling the injector to use different constraints on the constructor of the clients instantiation.