My last post was about a simple script I wrote to start up an EC2 instance and verify the SSH fingerprint. It did the trick but left me itching for something that was a little easier to extend and generalize.
I've also been itching to learn some Python, so for my next experiment I wrote a Python script (my first) that generates the security signature for an EC2 API request. Google pointed me to several solutions, but they were all either outdated (version 1 of the signature specification), incomplete, inaccurate or all of the above.
The following script works for me. It is still hard coded to perform a single type of simple request, but it should be a useful example for anybody that just wants to sign an EC2 request in Python.
The boto project also provides what looks like a fairly complete Python interface to most of the Amazon web services.
#!/usr/bin/python3 import base64,hashlib,hmac,datetime,urllib.parse,urllib.request endpoint = 'ec2.us-west-1.amazonaws.com' aws_secret_access_key = b'YOUR_KEY_SECRET' params = {} params['Version'] = '2011-01-01' params['AWSAccessKeyId'] = 'YOUR_ACCESS_KEY' now = datetime.datetime.utcnow() params['Timestamp'] = now.strftime("%Y-%m-%dT%H:%M:%S.000Z") # this can be omitted expires = now + datetime.timedelta(minutes=10) params['Expires'] = expires.strftime("%Y-%m-%dT%H:%M:%SZ") params['SignatureMethod'] = 'HmacSHA256' params['SignatureVersion'] = '2' params['Action'] = 'DescribeInstances' keys = sorted(params.keys()) values = map(params.get, keys) query_string = urllib.parse.urlencode( list(zip(keys,values)) ) # construct the string to sign string_to_sign = '\n'.join(['GET', endpoint, '/', query_string]) # sign the request signature = hmac.new( key=aws_secret_access_key, msg=bytes(string_to_sign, 'UTF-8'), digestmod=hashlib.sha256).digest() # base64 encode the signature. URL safe base64 encoding does NOT work signature = base64.b64encode(signature) signature = urllib.parse.quote(signature) url = 'https://' + endpoint + '/?' + query_string + '&Signature=' + signature response = urllib.request.urlopen(url) xml = response.read() # learning a little about xml parsing in Python is my next project print(xml)