Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to s3 object URL that works with cloudfront?

I'm currently storing files privately on S3. In my Rails app, in the attachment.rb model I can obtain a public URL for the private file like so:

def cdn_url ( style='original' )
  attachment.s3_object(style).url_for( :read, secure: true, response_content_type: self.meta['file_content_type'], expires: 1.hour ).to_s
end

The problem is this is providing a URL to S3 and rewriting the URL to use my Cloudfront origin url is erroring with:

The request signature we calculated does not match the signature you provided. Check your key and signing method.

How can I get a public URL asset like below but serve the asset via Cloudfront?

like image 921
AnApprentice Avatar asked Dec 04 '25 14:12

AnApprentice


1 Answers

First Way (Easy)

Just use aws_cf_signer gem. Put it in you bundler.

WIth this you can do something like

def cdn_url (options = {})
  style = options[:style] || 'original'
  cloudfront_domain =  options[:cloudfront_domain] || 'example.cloudfront.net'
  cloudfront_pem_key_path = options[:cloudfront_pem_key_path]
  cloudfront_key_paid_id = options[:cloundfrount_key_paid_id] 
  path = attachment.path(style)  #path of the file
  # you can get this values from your aws a/c , most probably by going int 
  # https://console.aws.amazon.com/iam/home?#security_credential      
  signer = AwsCfSigner.new(cloudfront_pem_key_path, cloudfront_key_paid_id)
  # this configuration may vary. 
  # visit https://github.com/dylanvaughn/aws_cf_signer 
  # and check all available settings/options   
  url = signer.sign(path, :ending => Time.now + 3600)
  cloudfront_domain + url
end 

With this you can access the url with something like this

  cdn_url(cloudfront_pem_key_path: '/users/downloads/pri.pem' , cloudfront_key_paid_id: '33243424XXX')

Second way

# A simple function to return a signed, expiring url for Amazon Cloudfront. 
# This will require openssl, digest/sha1, base64 and maybe other libraries. 
 
module CloudFront
  def get_signed_expiring_url(domain,path, expires_in, private_key_filename, key_pair_id)
 
    # AWS works on UTC, so make sure you are not using local time
    expires = (Time.now.getutc + expires_in).to_i.to_s
 
    private_key = OpenSSL::PKey::RSA.new(File.read(private_key_filename))
 
    # path should be your S3 path without a leading slash and without a file extension.
    # e.g. files/private/52
    policy = %Q[{"Statement":[{"Resource":"#{path}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires}}}}]}]
    signature = Base64.strict_encode64(private_key.sign(OpenSSL::Digest::SHA1.new, policy))
 
    # I'm not sure exactly why this is required, but it's in Amazon's perl script and seems necessary
    # Different base64 implementations maybe? 
    signature.tr!("+=/", "-_~")
    
    "#{domain}#{path}?Expires=#{expires}&Signature=#{signature}&Key-Pair-Id=#{key_pair_id}"
  end
end
 

With this you can do something like

def cdn_url ( style='original',cloudfront_pem_key_path,key_pair_id)
  path = attachment.path(style)  #path of the file
  # you can get this values from your aws a/c , most probably by going int 
  CloudFront.get_signed_expiring_url 'example.cloudfront.net', path, 45.seconds ,'/users/downloads/pri.pem', 'as12XXXXX')
end
 

Give a try, may be it will work. Be sure to properly set properly bucket access policy. check this out if you are seeing accessDenied error http://www.jppinto.com/2011/12/access-denied-to-file-amazon-s3-bucket/

like image 186
Paritosh Piplewar Avatar answered Dec 07 '25 03:12

Paritosh Piplewar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!