Search code examples
ruby-on-railsrubyamazon-web-servicesamazon-s3amazon-cloudfront

How does one sign cloudfront urls for files stored on s3?


I will attempt to answer this question with the hope of helping others who come across the same concern. Also with the hope of someone pointing out what I am missing (if any) or improvements along the way.


Solution

  • The first thing you will need is to create a key pair at https://portal.aws.amazon.com/gp/aws/securityCredentials (Key Pairs tab). Which will provide you with a key pair id and a pem file (save the pem file in your project dir).

    In your AWS Cloudfront portal, create a distribution. Select yes for Restrict Bucket Access.

    Click yes for Grant Read Permissions on Bucket. This will add a bucket policy allowing your Cloudfront distribution to read files on your S3 bucket.

    Create the distribution.

    In your S3 bucket, have a file that is not public. i.e. no read permissions. In this case, we will test this against test.png.

    url = "https://actual_cdn_id_here.cloudfront.net/test.png"
    key_pair_id = 'your_aws_key_pair_id'
    expires_in = 1.minute
    
    expires = (Time.now.getutc + expires_in).to_i.to_s
    
    private_key = OpenSSL::PKey::RSA.new(File.read('private_key_file.pem'))
    
    policy = %Q[{"Statement":[{"Resource":"#{url}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires}}}}]}]
    signature = Base64.strict_encode64(private_key.sign(OpenSSL::Digest::SHA1.new, policy))
    
    "#{url}?Expires=#{expires}&Signature=#{signature}&Key-Pair-Id=#{key_pair_id}"
    

    Hope this helps someone. If there is a need for improvement / suggestions for this solution, do mention.