python - Creating Signed Cookies for Amazon CloudFront -


amazon has introduced cloudfront signed cookie in addition signed url.

a similar quesition has been signed url. apparently there support signed url in cloudfront sdk

however cannot find support of feature in aws python sdk.

how can got create signed cookie?

i created boto feature request add this, in meantime got working django python app. here's simple code i've generified own. @ bottom sample django view method can see how set cookies web page containing cloudfront content.

import time boto.cloudfront import cloudfrontconnection boto.cloudfront.distribution import distribution config import settings import logging django.template.context import requestcontext django.shortcuts import render_to_response logger = logging.getlogger('boto') logger.setlevel(logging.critical) #disable debug logging that's enabled in aws default (outside of django)  aws_access_key="akabcde1235abcdef22a"#sample aws_secret_key="a1wd2sd1a/gs8qggkxk1u8khlh+bilp0c3nbj2ww" #sample key_pair_id="apkabcdef123abcdefag" #sample download_dist_id = "e1abcdef3abcde" #sample replace id of cloudfront dist cloudfront console  ############################################ def generate_signed_cookies(resource,expire_minutes=5):     """     @resource   path s3 object inside bucket(or wildcard path,e.g. '/blah/*' or  '*')     @expire_minutes     how many minutes before expire these access credentials (within cookie)     return tuple of domain used in resource url & dict of name=>value cookies     """     if not resource:         resource = 'images/*'     dist_id = download_dist_id     conn = cloudfrontconnection(aws_access_key, aws_secret_key)     dist = signedcookiedcloudfrontdistribution(conn,dist_id)     return dist.create_signed_cookies(resource,expire_minutes=expire_minutes)  ############################################ class signedcookiedcloudfrontdistribution():      def __init__(self,connection,download_dist_id,cname=true):         """         @download_dist_id   id of cloudfront download distribution         @cname          boolean true use first domain cname, false use                          cloudfront domain name, defaults cname                         presumably matches writeable cookies ( .mydomain.com)         """         self.download_dist = none         self.domain = none         try:             download_dist = connection.get_distribution_info(download_dist_id)             if cname , download_dist.config.cnames:                 self.domain = download_dist.config.cnames[0] #use first cname if defined             else:                 self.domain = download_dist.domain_name             self.download_dist = download_dist         except exception, ex:             logging.error(ex)      def get_http_resource_url(self,resource=none,secure=false):         """         @resource   optional path and/or filename resource                     (e.g. /mydir/somefile.txt);                     defaults wildcard if unset '*'         @secure     whether use https or http protocol cloudfront url - update                       match distribution settings          return constructed url         """         if not resource:             resource = '*'         protocol = "http" if not secure else "https"         http_resource = '%s://%s/%s' % (protocol,self.domain,resource)         return http_resource      def create_signed_cookies(self,resource,expire_minutes=3):         """         generate cloudfront download distirbution signed cookies         @resource   path file, path, or wildcard pattern generate policy         @expire_minutes  number of minutes until expiration         return      tuple domain used within policy (so matches                      cookie domain), , dict of cloudfront cookies                     should set in request header         """         http_resource = self.get_http_resource_url(resource,secure=false)    #per-file access #note secure should match security settings of cloudfront distribution     #    http_resource = self.get_http_resource_url("somedir/*")  #blanket access /somedir files inside bucket     #    http_resource = self.get_http_resource_url("*")          #blanket access files inside bucket          #generate no-whitespace json policy, base64 encode & make url safe         policy = distribution._canned_policy(http_resource,signedcookiedcloudfrontdistribution.get_expires(expire_minutes))         encoded_policy = distribution._url_base64_encode(policy)          #assemble 3 cloudfront cookies         signature = signedcookiedcloudfrontdistribution.generate_signature(policy,private_key_file=settings.amazon_priv_key_file)         cookies = {             "cloudfront-policy" :encoded_policy,             "cloudfront-signature" :signature,             "cloudfront-key-pair-id" :key_pair_id #e.g, apka..... -> same value use when sign urls boto distribution.create_signed_url() function         }         return self.domain,cookies      @staticmethod     def get_expires(minutes):         unixtime = time.time() + (minutes * 60)         expires = int(unixtime)  #if not converted int causes malformed policy error , has 2 decimals in value         return expires      @staticmethod     def generate_signature(policy,private_key_file=none):         """         @policy     no-whitespace json str (not encoded yet)         @private_key_file   .pem file sign policy         return encoded signature use in cookie         """         #sign policy - code borrowed distribution._create_signing_params()         signature = distribution._sign_string(policy, private_key_file)         #now base64 encode signature & make url safe         encoded_signature = distribution._url_base64_encode(signature)         return encoded_signature  ############################################ def sample_django_view_method(request,template="mytemplate.html"):     expirelen = 30 #30 minutes     s3resource = "somepath_in_my_bucket/afile.mp4"     context = {} #variables i'm passing html template     response = render_to_response(template, context, context_instance=requestcontext(request))     domain,cookies = generate_signed_cookies(s3resource,expire_minutes=expirelen)     #troubleshooting cookies:     #note - cookie domain must domain control spans app & cloudfront cname     #note - (e.g. if webapp www.mydomain.com , aws download distribution has cname cloud.mydomain.com, cant set cookies webapp              # www.mydomain.com or localhost.mydomain.com or cloud.mydomain.com , have them work          # -> instead set cookies .mydomain.com work across sub-domains, can verify in request headers cloudfront these cookies passed.         # tip - if set_cookies page .mydomain.com suffix, don't see them set in chrome didn't set because of permissions - can't set diff subdomain or diff base domain         # tip - if set_cookies , see them in chrome don't see them in request headers cloudfront, cookie domain narrow, need widen span subdomains     base_domain = '.mydomain.com'     # note: sanity check when testing can flag gotchas - have not tested using non-cname urls inside policy vs possible domains cookie        if not domain.endswith(base_domain):         logger.warn("this won't work - resource permissions use different domain cookies")     name,value in cookies.items():         response.set_cookie(name,value=value,httponly=true,domain=base_domain)     return response  ############################################  if __name__ == '__main__':     domain,cookies = generate_signed_cookies('images/*',expire_minutes=30) 

notes setup:

  • i had make 1 change download distribution set & working signed urls: had add cname base domain matching website.
  • i used chrome web developer tools:

    • network: view request headers sent in cloudfront call , see 403 vs 200/status/response size
    • console: see 403 errors until got working
    • resources > cookies - verify [localhost or host].mydomain.com cookies show 3 cloudfront cookies populated, set domain=.mydomain.com , these values match values in request headers cloudfront (if missing, domain misconfigured)

my aws configuration

  • s3 requires cloudfront origin
  • cloudfront download distribution:
    • distribution settings:
      • cname defined: cloud.mydomain.com (new me!)
      • default cloudfront certificate
    • origins tab: 1 origin defined maps s3 bucket
    • behaviors tab - default:
      • all of these unchanged settings used signed urls
      • http , https
      • get,head
      • forward headers: none
      • use origin cache headers
      • minimum ttl: 0
      • forward cookies: none
      • forward querystrings: no
      • smooth streaming: no
      • restrict viewer access: yes (no change since signed urls)
      • trusted signers: self

trickiest parts once have cookie-generating code above:

  • making sure cookie domain right
  • making sure path/resource in policy matches request being made app

Comments

Popular posts from this blog

javascript - AngularJS custom datepicker directive -

javascript - jQuery date picker - Disable dates after the selection from the first date picker -