rlcabral
rlcabral

Reputation: 1546

S3 with Cloudflare disallow direct access

I am trying to have Cloudflare to act as CDN for files hosted on S3, in a way that nobody can access the files directly. For example:

S3 bucket: cdn.mydomain.com.s3.amazonaws.com

CDN (Cloudflare): cdn.mydomain.com

What I want is to be able to access cdn.mydomain.com/file.jpg (Cloudflare) but not cdn.mydomain.com.s3.amazonaws.com/file.jpg (S3).

Right now I have a CNAME configured on Cloudflare that points to my bucket, and the following CORS:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

If I try to access any file, via S3 or CDN, I get permission denied. If I make a file public (aka grantee Everyone), I can then access that file via S3 and CDN.

I have tried changing the AllowedOrigin with *.mydomain.com, but no luck.

Upvotes: 12

Views: 6388

Answers (3)

tu4n
tu4n

Reputation: 4430

It's better to use your domain name as the entry guard to s3, rather than a list of IPs.

Go to your site's S3 Console
Select the Properties Panel
Under Permission
Choose Add CORS Configuration
Add your CORSRules for your domain names

S3-CORS-Config example

More CORS configuration can be found here


My mistake, this answer only forbids other sites from downloading your assets via legitimate web browsers, cannot stop people from downloading them via different user agents.

Upvotes: 2

Ben Cooper
Ben Cooper

Reputation: 1495

The accepted solution doesn't exactly work. It just allows access to CloudFlare. For that solution to work, you must explicitly deny everything elsewhere in the policy. This bucket policy is updated for Cloudflare's most recent IP addresses (including IPv6) and it also denies all access not from a Cloudflare IP address out of the box.

{
    "Id": "Policy1517260196123",
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "A string ID here",
            "Action": "s3:*",
            "Effect": "Deny",
            "Resource": "arn:aws:s3:::yourbucket.example.com/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "103.21.244.0/22",
                        "103.22.200.0/22",
                        "103.31.4.0/22",
                        "104.16.0.0/12",
                        "108.162.192.0/18",
                        "131.0.72.0/22",
                        "141.101.64.0/18",
                        "162.158.0.0/15",
                        "172.64.0.0/13",
                        "173.245.48.0/20",
                        "188.114.96.0/20",
                        "190.93.240.0/20",
                        "197.234.240.0/22",
                        "198.41.128.0/17",
                        "2400:cb00::/32",
                        "2405:8100::/32",
                        "2405:b500::/32",
                        "2606:4700::/32",
                        "2803:f800::/32",
                        "2c0f:f248::/32",
                        "2a06:98c0::/29"
                    ]
                }
            },
            "Principal": {
                "AWS": "*"
            }
        }
    ]
}

Upvotes: 3

rlcabral
rlcabral

Reputation: 1546

I found the solution. The article at CloudFlare's support center doesn't mention this.

You have to edit the bucket policy, not the CORS. And instead of allowing your domain, like that article says, to have access to the bucket, you have to allow CloudFlare IP's. For the reference, here is the list of IP's: https://www.cloudflare.com/ips

Here is the bucket policy sample to work with CloudFlare:

    {
        "Sid": "SOME_STRING_ID_HERE",
        "Effect": "Allow", // or deny
        "Principal": {"AWS": "*"}, // or whatever principal you want
        "Action": "s3:GetObject", // or whatever action you want
        "Resource": "arn:aws:s3:::cdn.mydomain.com/*", // or whatever resource you want
        "Condition": {
            "IpAddress": {
                "aws:SourceIp": [
                    "103.21.244.0/22",
                    "103.22.200.0/22",
                    "103.31.4.0/22",
                    "104.16.0.0/12",
                    "108.162.192.0/18",
                    "131.0.72.0/22",
                    "141.101.64.0/18",
                    "162.158.0.0/15",
                    "172.64.0.0/13",
                    "173.245.48.0/20",
                    "188.114.96.0/20",
                    "190.93.240.0/20",
                    "197.234.240.0/22",
                    "198.41.128.0/17",
                    "199.27.128.0/21"
                ]
            }
        }
    }

Upvotes: 19

Related Questions