Browse code

Merge pull request #127 from dudyk/IAM

Add support for IAM roles and temp tokens

Michal Ludvig authored on 2013/03/04 07:32:50
Showing 4 changed files
... ...
@@ -534,6 +534,10 @@ class CloudFront(object):
534 534
         if not headers.has_key("x-amz-date"):
535 535
             headers["x-amz-date"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
536 536
 
537
+        if len(self.config.access_token)>0:
538
+            self.config.refresh_role()
539
+            headers['x-amz-security-token']=self.config.access_token
540
+
537 541
         signature = self.sign_request(headers)
538 542
         headers["Authorization"] = "AWS "+self.config.access_key+":"+signature
539 543
 
... ...
@@ -7,8 +7,11 @@ import logging
7 7
 from logging import debug, info, warning, error
8 8
 import re
9 9
 import os
10
+import sys
10 11
 import Progress
11 12
 from SortedDict import SortedDict
13
+import httplib
14
+import json
12 15
 
13 16
 class Config(object):
14 17
     _instance = None
... ...
@@ -16,6 +19,7 @@ class Config(object):
16 16
     _doc = {}
17 17
     access_key = ""
18 18
     secret_key = ""
19
+    access_token = ""
19 20
     host_base = "s3.amazonaws.com"
20 21
     host_bucket = "%(bucket)s.s3.amazonaws.com"
21 22
     simpledb_host = "sdb.amazonaws.com"
... ...
@@ -103,7 +107,73 @@ class Config(object):
103 103
 
104 104
     def __init__(self, configfile = None):
105 105
         if configfile:
106
-            self.read_config_file(configfile)
106
+            try:
107
+                self.read_config_file(configfile)
108
+            except IOError, e:
109
+                if 'AWS_CREDENTIAL_FILE' in os.environ:
110
+                    self.env_config()
111
+            if len(self.access_key)==0:
112
+                self.role_config()  
113
+
114
+    def role_config(self):
115
+        conn = httplib.HTTPConnection(host='169.254.169.254',timeout=0.1)
116
+        try:
117
+            conn.request('GET', "/latest/meta-data/iam/security-credentials/")
118
+            resp = conn.getresponse()
119
+            files = resp.read()
120
+            if resp.status == 200 and len(files)>1:               
121
+                conn.request('GET', "/latest/meta-data/iam/security-credentials/%s"%files)
122
+                resp=conn.getresponse()
123
+                if resp.status == 200:
124
+                    creds=json.load(resp)
125
+                    Config().update_option('access_key', creds['AccessKeyId'].encode('ascii'))
126
+                    Config().update_option('secret_key', creds['SecretAccessKey'].encode('ascii'))
127
+                    Config().update_option('access_token', creds['Token'].encode('ascii'))
128
+                else:
129
+                    raise IOError
130
+            else:
131
+                raise IOError
132
+        except:
133
+            raise
134
+
135
+    def role_refresh(self):
136
+        try:
137
+            self.role_config()
138
+        except:
139
+            warning("Could not refresh role")
140
+
141
+    def env_config(self):
142
+        cred_content = ""
143
+        try:
144
+            cred_file = open(os.environ['AWS_CREDENTIAL_FILE'],'r')
145
+            cred_content = cred_file.read()
146
+        except IOError, e:
147
+            debug("Error %d accessing credentials file %s" % (e.errno,os.environ['AWS_CREDENTIAL_FILE']))
148
+        r_data = re.compile("^\s*(?P<orig_key>\w+)\s*=\s*(?P<value>.*)")
149
+        r_quotes = re.compile("^\"(.*)\"\s*$")
150
+        if len(cred_content)>0:
151
+            for line in cred_content.splitlines():
152
+                is_data = r_data.match(line)
153
+                is_data = r_data.match(line)
154
+                if is_data:
155
+                    data = is_data.groupdict()
156
+                    if r_quotes.match(data["value"]):
157
+                        data["value"] = data["value"][1:-1]
158
+                    if data["orig_key"]=="AWSAccessKeyId":
159
+                        data["key"] = "access_key"
160
+                    elif data["orig_key"]=="AWSSecretKey":
161
+                        data["key"] = "secret_key"
162
+                    else:
163
+                        del data["key"]                    
164
+                    if "key" in data:
165
+                        Config().update_option(data["key"], data["value"])
166
+                        if data["key"] in ("access_key", "secret_key", "gpg_passphrase"):
167
+                            print_value = (data["value"][:2]+"...%d_chars..."+data["value"][-1:]) % (len(data["value"]) - 3)
168
+                        else:
169
+                            print_value = data["value"]
170
+                        debug("env_Config: %s->%s" % (data["key"], print_value))
171
+                
172
+        
107 173
 
108 174
     def option_list(self):
109 175
         retval = []
... ...
@@ -92,6 +92,9 @@ class S3Request(object):
92 92
         # Add in any extra headers from s3 config object
93 93
         if self.s3.config.extra_headers:
94 94
             self.headers.update(self.s3.config.extra_headers)
95
+        if len(self.s3.config.access_token)>0:
96
+            self.s3.config.role_refresh()
97
+            self.headers['x-amz-security-token']=self.s3.config.access_token
95 98
         self.resource = resource
96 99
         self.method_string = method_string
97 100
         self.params = params
... ...
@@ -131,6 +131,9 @@ class SimpleDB(object):
131 131
     def create_request(self, Action, DomainName, parameters = None):
132 132
         if not parameters:
133 133
             parameters = SortedDict()
134
+        if len(self.config.access_token) > 0:
135
+            self.config.refresh_role()
136
+            parameters['Signature']=self.config.access_token
134 137
         parameters['AWSAccessKeyId'] = self.config.access_key
135 138
         parameters['Version'] = self.Version
136 139
         parameters['SignatureVersion'] = self.SignatureVersion