BackWPup: Amazon S3 Access Denied

I’m working on setting up a nightly backup addon for my hosting clients, and since most of my clients are on WordPress, I decided to first focus on setting up the backup to occur via WordPress plugin. Backups are going to an Amazon S3 bucket, and because there is a limit on the number of buckets per account, I wanted to make sure all client backups go to a single bucket.

The challenge with that, of course, is security. I don’t want all my clients to have access to or even view each other’s backups and if the keys are lost or compromised, I don’t want all of my clients’ data to be vulnerable.

Thankfully, the AWS blog has my back and there is a wonderful blog post describing exactly how to set up user permissions so that users only have access to their own home directories. Having done that (at this point, I am using the exact code at the bottom of the AWS blog post), all that was left was to create the users themselves, plug their keys into a plugin and we’re done.

After experimenting with multiple plugins, I settled on BackWPup. I plugged in the keys, annnnd…

[27-May-2015 20:00:10] 1. Trying to send backup file to S3 Service …
[27-May-2015 20:00:10] Connected to S3 Bucket "rainworks-hosting-backups" in us-west-2
[27-May-2015 20:00:10] Checking for not aborted multipart Uploads …
[27-May-2015 20:00:10] ERROR: S3 Service API: Access Denied
[27-May-2015 20:00:10] 2. Trying to send backup file to S3 Service …
[27-May-2015 20:00:11] Connected to S3 Bucket "rainworks-hosting-backups" in us-west-2
[27-May-2015 20:00:11] Checking for not aborted multipart Uploads …
[27-May-2015 20:00:11] ERROR: S3 Service API: Access Denied
[27-May-2015 20:00:11] 3. Trying to send backup file to S3 Service …
[27-May-2015 20:00:11] Connected to S3 Bucket "rainworks-hosting-backups" in us-west-2
[27-May-2015 20:00:11] Checking for not aborted multipart Uploads …
[27-May-2015 20:00:11] ERROR: S3 Service API: Access Denied

Googling on the exact BackWPup messaging brought unhelpful results, but after enabling access logging on my bucket, I got better error messaging (which has been trimmed for clarity):

REST.HEAD.BUCKET - "HEAD / HTTP/1.1" 403 AccessDenied 243 - 16
REST.GET.UPLOADS - "GET /?uploads&prefix=rainworkswebdevelopment.com%2F HTTP/1.1" 403 AccessDenied 243 - 6

So permissions aren’t quite what they need to be to allow the plugin to do what it needs to do. After more reading and research (the comments on the afore-linked AWS blog post were quite helpful!), this is the final version of my policy document (the lines I added or modified from Amazon’s code are highlighted):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowGroupToSeeBucketListInTheConsole",
            "Action": [
                "s3:ListAllMyBuckets",
                "s3:GetBucketLocation",
                "s3:ListBucketMultipartUploads"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::*"
            ]
        },
        {
            "Sid": "AllowRootListingWithoutPrefix",
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::rainworks-hosting-buckets"
            ],
            "Condition": {
                "Null": {
                    "s3:prefix": "true"
                },
                "StringEquals": {
                    "s3:delimiter": [
                        "/"
                    ]
                }
            }
        },
        {
            "Sid": "AllowRootAndHomeListingOfCompanyBucket",
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::rainworks-hosting-backups"
            ]
        },
        {
            "Sid": "AllowListingOfUserFolder",
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::rainworks-hosting-backups"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "${aws:username}/*"
                    ]
                }
            }
        },
        {
            "Sid": "AllowAllS3ActionsInUserFolder",
            "Action": [
                "s3:*"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::rainworks-hosting-backups/${aws:username}/*"
            ]
        }
    ]
}

Everything works like a charm now! Hopefully this saves someone else some trouble!

About Kelly Carter

I'm a freelance web developer, doing business under the name Rainworks Web Development. I'm a skeptical technophile, voracious reader, softcore gamer, and haphazard tinkerer. I have a long-term partner, a cat, and no time for glass ceilings.

Comments

  1. Thank you SO MUCH! You seem to be the only person on the internet who describes how to fix this problem. AWS access policies are so complicated. I appreciate your help. Below is the policy I ended up using. It’s a bit shorter than yours for some reason. I think you know more about this stuff than I do. `{“Version”:”2012-10-17″,”Statement”:[{“Effect”:”Allow”,”Action”:[“s3:ListAllMyBuckets”],”Resource”:”arn:aws:s3:::*”},{“Effect”:”Allow”,”Action”:[“s3:ListBucket”,”s3:GetBucketLocation”,”s3:ListBucketMultipartUploads”],”Resource”:”arn:aws:s3:::my-user-backwpup”},{“Effect”:”Allow”,”Action”:[“s3:*”],”Resource”:”arn:aws:s3:::my-user-backwpup/*”}]}`

  2. Thanks a lot – you saved me a lot of pain!
    BTW, I only needed to add “s3:ListBucketMultipartUploads” permission to the bucket it self (arn:aws:s3:::MY-BUCKET) and did NOT need to add it to the contents of the bucket (arn:aws:s3:::MY-BUCKET/*)

Speak Your Mind

*