-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathRemoteCredentials.lua
172 lines (129 loc) · 4.97 KB
/
RemoteCredentials.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
--- RemoteCredentials class.
-- @classmod RemoteCredentials
-- This code is reverse engineered from the original AWS sdk. Specifically:
-- https://github.com/aws/aws-sdk-js/blob/c175cb2b89576f01c08ebf39b232584e4fa2c0e0/lib/credentials/remote_credentials.js
local log = ngx.log
local DEBUG = ngx.DEBUG
local DEFAULT_SERVICE_REQUEST_TIMEOUT = 5000
local url = require "socket.url"
local http = require "resty.luasocket.http"
local json = require "cjson"
local readfile = require("pl.utils").readfile
local FullUri
local AuthToken
local AuthTokenFile
local function initialize()
-- construct the URL
local function makeset(t)
for i = 1, #t do
t[t[i]] = true
end
return t
end
local aws_config = require("resty.aws.config")
local FULL_URI_UNRESTRICTED_PROTOCOLS = makeset { "https" }
local FULL_URI_ALLOWED_PROTOCOLS = makeset { "http", "https" }
local FULL_URI_ALLOWED_HOSTNAMES = makeset { "localhost", "127.0.0.1", "169.254.170.23" }
local RELATIVE_URI_HOST = '169.254.170.2'
local function getFullUri()
if aws_config.global.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI then
return 'http://' .. RELATIVE_URI_HOST .. aws_config.global.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
elseif aws_config.global.AWS_CONTAINER_CREDENTIALS_FULL_URI then
local parsed_url = url.parse(aws_config.global.AWS_CONTAINER_CREDENTIALS_FULL_URI)
if not FULL_URI_ALLOWED_PROTOCOLS[parsed_url.scheme] then
return nil, 'Unsupported protocol, must be one of '
.. table.concat(FULL_URI_ALLOWED_PROTOCOLS, ',') .. '. Got: '
.. parsed_url.scheme
end
if (not FULL_URI_UNRESTRICTED_PROTOCOLS[parsed_url.scheme]) and
(not FULL_URI_ALLOWED_HOSTNAMES[parsed_url.host]) then
return nil, 'Unsupported hostname: AWS.RemoteCredentials only supports '
.. table.concat(FULL_URI_ALLOWED_HOSTNAMES, ',') .. ' for '
.. parsed_url.scheme .. '; ' .. parsed_url.scheme .. '://'
.. parsed_url.host .. ' requested.'
end
return aws_config.global.AWS_CONTAINER_CREDENTIALS_FULL_URI
else
return nil, 'Environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI or '
.. 'AWS_CONTAINER_CREDENTIALS_FULL_URI must be set to use AWS.RemoteCredentials.'
end
end
local err
FullUri, err = getFullUri()
if not FullUri then
log(DEBUG, "Failed to construct RemoteCredentials url: ", err)
else
-- parse it and set a default port if omitted
FullUri = url.parse(FullUri)
FullUri.port = FullUri.port or
({ http = 80, https = 443 })[FullUri.scheme]
end
-- get auth token/file
AuthToken = aws_config.global.AWS_CONTAINER_AUTHORIZATION_TOKEN
AuthTokenFile = aws_config.global.AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
initialize = nil
end
-- Create class
local Super = require "resty.aws.credentials.Credentials"
local RemoteCredentials = setmetatable({}, Super)
RemoteCredentials.__index = RemoteCredentials
--- Constructor, inherits from `Credentials`.
-- @function aws:RemoteCredentials
-- @param opt options table, no additional fields to the `Credentials` class.
function RemoteCredentials:new(opts)
local self = Super:new(opts) -- override 'self' to be the new object/class
setmetatable(self, RemoteCredentials)
return self
end
-- updates credentials.
-- @return success, or nil+err
function RemoteCredentials:refresh()
if initialize then
initialize()
end
if not FullUri then
return nil, "No URI environment variables found for RemoteCredentials"
end
local headers = {}
if AuthToken then
headers["Authorization"] = AuthToken
end
if AuthTokenFile then
local token, err = readfile(AuthTokenFile)
if not token then
return nil, "Failed reading AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE: " .. err
end
headers["Authorization"] = token
end
local client = http.new()
client:set_timeout(DEFAULT_SERVICE_REQUEST_TIMEOUT)
local ok, err = client:connect {
scheme = FullUri.scheme,
host = FullUri.host,
port = FullUri.port,
}
if not ok then
return nil, "Could not connect to RemoteCredentials metadata service: " .. tostring(err)
end
local response, err = client:request {
method = "GET",
path = FullUri.path,
headers = headers,
}
if not response then
return nil, "Failed to request RemoteCredentials request returned error: " .. tostring(err)
end
if response.status ~= 200 then
return nil, "Unable to request RemoteCredentials request returned status code " ..
response.status .. " " .. tostring(response:read_body())
end
local credentials = json.decode(response:read_body())
log(DEBUG, "Received temporary IAM credential from RemoteCredentials " ..
"service with session token: ", credentials.Token)
self:set(credentials.AccessKeyId,
credentials.SecretAccessKey,
credentials.Token,
credentials.Expiration)
return true
end
return RemoteCredentials