跨域问题解决 with Amazon CloudFront CDN
I am using Alpha SSL for SSL certificate for rubyplus.com. I did not have wildcard SSL certificate. The Chrome browser was freaking out with CORS related errors because I was accessing static resources using cloudfront.net
domain. Did you know you can get free wildcard SSL from Amazon? As long as you are in virginia region, you can get a free SSL from Amazon. I am using Linode for deploying my Rails apps. The instructions will work for any VPS.
Create CNAME in your VPS
Create a CNAME for cdn.yourdomain.com
pointing to https://your-cloudfront-cdn-id.cloudfront.net
in Linode.
Test with Curl
View page source on your site and grab a link to an image and use curl to test.
curl -I http://cdn.rubyplus.com/assets/favicon-very-long-alpha-numeric-string.ico
This failed with the output:
HTTP/1.1 403 Forbidden
Server: CloudFront
Date: Thu, 26 May 2016 22:39:47 GMT
Content-Type: text/html
Content-Length: 551
Connection: keep-alive
X-Cache: Error from cloudfront
Via: 1.1 3245d45aedf7a8621aabe6b30d2f5a48.cloudfront.net (CloudFront)
X-Amz-Cf-Id: NrdZWePG5rUVcViciQkV87rDaaus25K5LjhJd5w2q3i8neb9MUDIzg==
Speficy CNAME in CloudFront Distribution
Add cdn.yourdomain.com
as the CNAME in the CloudFront settings.
AWS Command Line Tool
Install AWS command line tool on your laptop.
sudo pip install awscli --ignore-installed six
Convert crt to pem on the server.
openssl x509 -in mycert.crt -out mycert.pem -outform PEM
Go the directory where the SSL certificate files are stored and run openssl.
openssl x509 -in intermediate_domain.crt -out intermediate_domain.pem -outform PEM
This generates the intermediate_domain.pem
file.
intermediate_domain.crt
intermediate_domain.pem
rubyplus.com.crt
rubyplus.com.key
Copy the files on the server to your laptop:
$scp deployer@rubyplus.com:/home/deployer/certs/* .
You can upload the generated SSL intermediate certificate to AWS by using the AWS command line tool from your laptop.
aws iam upload-server-certificate --server-certificate-name rubyplus.com --certificate-body file://./rubyplus.com.crt --private-key file://./rubyplus.com.key --certificate-chain file://intermediate_domain.pem --path /cloudfront/rubyplus.com/
This gave the error:
Unable to locate credentials. You can configure credentials by running "aws configure".
Run the configuration tool on your laptop to specify your credentials:
$aws configure
AWS Access Key ID [None]: Your-access-key-id-here
AWS Secret Access Key [None]: secret-access-key
Default region name [None]: us-east-1
Default output format [None]: json
My Cloudfront is in the us-east-1 region. Go to .aws directory on your laptop and check the generated files.
~/.aws $ll
total 16
drwxr-xr-x 4 bparanj staff 136B May 26 16:37 .
drwxr-xr-x+ 131 bparanj staff 4.3K May 26 16:37 ..
-rw------- 1 bparanj staff 43B May 26 16:37 config
-rw------- 1 bparanj staff 116B May 26 16:37 credentials
Run the AWS command line tool again.
aws iam upload-server-certificate --server-certificate-name rubyplus.com --certificate-body file://./rubyplus.com.crt --private-key file://./rubyplus.com.key --certificate-chain file://intermediate_domain.pem --path /cloudfront/rubyplus.com/
This gave an error:
A client error (AccessDenied) occurred when calling the UploadServerCertificate operation: User: arn:aws:iam::875721448715:user/rubyplus is not authorized to perform: iam:UploadServerCertificate on resource: arn:aws:iam::875721448715:server-certificate/cloudfront/rubyplus.com/rubyplus.com
Providing the Permission to AWS User
Login to your Cloudfront account and provide the user the permission to upload files. Create a new user in AWS.
User created successfully.
Attach Policy to allow user to upload SSL certificate.
Run the AWS command line tool again:
aws iam upload-server-certificate --server-certificate-name rubyplus.com --certificate-body file://./rubyplus.com.crt --private-key file://./rubyplus.com.key --certificate-chain file://intermediate_domain.pem --path /cloudfront/rubyplus.com/
The command ran successfully with the output:
{
"ServerCertificateMetadata": {
"ServerCertificateId": "your-server-certificate-id",
"ServerCertificateName": "rubyplus.com",
"Expiration": "2016-12-07T23:12:37Z",
"Path": "/cloudfront/rubyplus.com/",
"Arn": "arn:aws:iam::875721448715:server-certificate/cloudfront/rubyplus.com/rubyplus.com",
"UploadDate": "2016-05-26T23:41:20.713Z"
}
}
Chooose Custom SSL Certificate for the CDN in Distribution Settings.
Use curl to test.
curl -I https://cdn.rubyplus.com/assets/favicon-very-long-alphanumeric-string.ico
I got the error:
curl: (60) SSL certificate problem: Invalid certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
Let’s try with the -k switch:
curl -k -I https://cdn.rubyplus.com/assets/favicon-b73a7c5b51d68c8f821e1c0e44083c8b8762c977bd620995642c32fb074d2941.ico
It had the same problem.
HTTP/1.1 200 OK
Content-Type: image/x-icon
Content-Length: 7406
Connection: keep-alive
Date: Thu, 26 May 2016 23:42:38 GMT
Server: Apache/2.2.22 (Ubuntu)
Last-Modified: Thu, 03 Mar 2016 08:51:13 GMT
Accept-Ranges: bytes
Cache-Control: max-age=31536000
Expires: Fri, 26 May 2017 23:42:38 GMT
Age: 126
X-Cache: Hit from cloudfront
Via: 1.1 aa96a51fedae85199c643eb5c8eca4e4.cloudfront.net (CloudFront)
X-Amz-Cf-Id: 2yYMTGBdQdIyK3om0URDD9E87Ehr8xdbXGOX6EYhahdkFF9U3osprQ==
Untrusted SSL Connection
Connection is not private error in Chrome.
Amazon Wildcard SSL
AWS Certificate Manager
Amazon Wildcard SSL pending validation
Approve Amazon SSL request.
Approving the Amazon SSL request.
Successful approval of Amazon SSL certificate.
Verify Amazon wildcard SSL status
Use Asset Host in Rails App
Edit the production.rb in your Rails app:
config.action_controller.asset_host = '//cdn.rubyplus.com'
Deploy your Rails app. Go to your site and view page source. Hyperlinks without protocol.
I was getting:
Font from origin 'https://cdn.rubyplus.com' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://www.rubyplus.com' is therefore not allowed access.
I searched for this error:
Font from origin has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin is therefore not allowed access.
I found that you have to set a CORS Configuration for your CloudFront in AWS console. In CloudFront -> Distribution -> Behaviors for this origin, use the Forward Headers: Whitelist option and whitelist the ‘Origin’ header.
Choosing wildcard SSL in CloudFront Distribution settings
Edit CloudFront behavior to set the forward header and whitelist header
Wait for 20 minutes or so while CloudFront propagates the new rule. Now your CloudFront distribution should cache different responses (with proper CORS headers) for different client Origin headers. Use curl to test the cloudfront setup by hitting an icon. The output shows that it worked.
HTTP/1.1 200 OK
Content-Type: image/x-icon
Content-Length: 7406
Connection: keep-alive
Date: Thu, 26 May 2016 23:42:38 GMT
Server: Apache/2.2.22 (Ubuntu)
Last-Modified: Thu, 03 Mar 2016 08:51:13 GMT
Accept-Ranges: bytes
Cache-Control: max-age=31536000
Expires: Fri, 26 May 2017 23:42:38 GMT
X-Cache: Miss from cloudfront
Via: 1.1 2239f0bfe6d7427183a4e375c4638619.cloudfront.net (CloudFront)
X-Amz-Cf-Id: 4X9YJwlXoEALb_LzdaVP62x2_TbaDh8bHufLlH263FwO8kSJNNZySw==
Alpha SSL on the home page of rubyplus.com
The Amazon SSL takes care of the wildcard SSL. The Alpha SSL takes care of the SSL for rubyplus.com and www.rubyplus.com.
Fixing Font Loading Issue due to CORS Error
If you inspect the page, Chrome shows the following error.
Font from origin 'https://cdn.rubyplus.com' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://rubyplus.com' is therefore not allowed access.
Firefox error is better, it shows specific cause of the problem and suggestions to fix them.
downloadable font: download failed (font-family: "OpenSans-Regular" style:normal weight:normal stretch:normal src index:0): bad URI or cross-site access not allowed
source: https://cdn.rubyplus.com/assets/OpenSans-Regular-e64e508b2aa2880f907e470c4550980ec4c0694d103a43f36150ac3f93189bee.ttf
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://cdn.rubyplus.com/assets/OpenSans-Regular-e64e508b2aa2880f907e470c4550980ec4c0694d103a43f36150ac3f93189bee.ttf. This can be fixed by moving the resource to the same domain or enabling CORS.
Create a directory yourdomain.com under /var/www directory. Create a .htaccess file with:
<FilesMatch ".(ttf|otf|eot|woff)">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
You do not need to restart apache server for this change to take effect.
CloudFront Cache Behavior Settings
Default Cache Behavior Settings Screen
You must have OPTIONS also as shown here.
Make sure to whitelist the origin header.
The entire setup should look like this: