Skip to content

Commit e44349e

Browse files
committed
added doubleUrlEncode parameter to AWS V4 signature (#11)
* added doubleUrlEncode parameter to AWS V4 signature as per AWS requirements (useful to fix LambdaService)
1 parent 77984d5 commit e44349e

File tree

4 files changed

+41
-11
lines changed

4 files changed

+41
-11
lines changed

src/lua/api-gateway/aws/AwsService.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ end
9595
-- o.security_credentials_host - optional. the AWS URL to read security credentials from and figure out the iam_user
9696
-- o.security_credentials_port - optional. the port used when connecting to security_credentials_host
9797
-- o.shared_cache_dict - optional. AWSIAMCredentials uses it to store IAM Credentials.
98+
-- o.doubleUrlEncode - optional. Whether to double url-encode the resource path
99+
-- when constructing the canonical request for AWSV4 signature.
98100
--
99101
-- NOTE: class inheirtance inspired from: http://www.lua.org/pil/16.2.html
100102
function _M:new(o)
@@ -205,7 +207,7 @@ function _M:getAuthorizationHeader(http_method, path, uri_args, body)
205207
local credentials = self:getCredentials()
206208
credentials.aws_region = self.aws_region
207209
credentials.aws_service = self.aws_service
208-
local awsAuth = AWSV4S:new(credentials)
210+
local awsAuth = AWSV4S:new(credentials, self.doubleUrlEncode)
209211
local authorization = awsAuth:getAuthorizationHeader(http_method,
210212
path, -- "/"
211213
uri_args, -- ngx.req.get_uri_args()

src/lua/api-gateway/aws/AwsV4Signature.lua

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ function HmacAuthV4Handler:new(o)
2121
self.aws_region = o.aws_region
2222
self.aws_secret_key = o.aws_secret_key
2323
self.aws_access_key = o.aws_access_key
24+
---
25+
-- Whether to double url-encode the resource path when constructing the
26+
-- canonical request. By default, double url-encoding is true.
27+
-- Different sigv4 services seem to be inconsistent on this. So for
28+
-- services that want to suppress this, they should set it to false.
29+
self.doubleUrlEncode = o.doubleUrlEncode or true
2430
end
2531
-- set amazon formatted dates
2632
local utc = ngx.utctime()
@@ -93,10 +99,14 @@ local function urlEncode(inputString)
9399
inputString = string.gsub (inputString, "([^%w %-%_%.%~])",
94100
function (c) return string.format ("%%%02X", string.byte(c)) end)
95101
inputString = ngx.re.gsub (inputString, " ", "+", "ijo")
96-
-- AWS workarounds
102+
-- AWS workarounds following Java SDK
103+
-- see https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-core/src/main/java/com/amazonaws/util/SdkHttpUtils.java#L80-87
97104
-- replace '+' ( %2B ) with ( %20 )
98105
inputString = ngx.re.gsub(inputString, "%2B", "%20", "ijo")
99-
106+
-- replace %2F with "/"
107+
inputString = ngx.re.gsub(inputString, "%2F", "/", "ijo")
108+
-- replace %7E with "~"
109+
inputString = ngx.re.gsub(inputString, "%7E", "~", "ijo")
100110
end
101111
return inputString
102112
--[[local s = ngx.escape_uri(inputString)
@@ -151,6 +161,10 @@ function HmacAuthV4Handler:getSignature(http_method, request_uri, uri_arg_table,
151161
headers.host = self.aws_service .. "." .. self.aws_region .. ".amazonaws.com"
152162
headers["x-amz-date"] = date2
153163

164+
local encoded_request_uri = request_uri
165+
if (self.doubleUrlEncode == true) then
166+
encoded_request_uri = urlEncode(request_uri)
167+
end
154168
-- ensure parameters in query string are in order
155169
local sign = _sign( get_derived_signing_key( self.aws_secret_key,
156170
date1,
@@ -160,7 +174,7 @@ function HmacAuthV4Handler:getSignature(http_method, request_uri, uri_arg_table,
160174
date2,
161175
date1 .. "/" .. self.aws_region .. "/" .. self.aws_service .. "/aws4_request",
162176
get_hashed_canonical_request(
163-
http_method, request_uri,
177+
http_method, encoded_request_uri,
164178
uri_args,
165179
headers, request_payload) ) )
166180
return sign

test/integration/kms.t

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# vim:set ft= ts=4 sw=4 et fdm=marker:
22
use lib 'lib';
3+
use strict;
4+
use warnings;
35
use Test::Nginx::Socket::Lua;
46
use Cwd qw(cwd);
57

@@ -143,14 +145,18 @@ __DATA__
143145
}
144146
145147
location = /latest/meta-data/iam/security-credentials/test-iam-user {
148+
set_by_lua $expiration '
149+
local offset = os.time() - os.time(os.date("!*t"))
150+
return os.date("%Y-%m-%dT%H:%M:%SZ", os.time() + math.abs(offset) + 20)
151+
';
146152
return 200 '{
147153
"Code" : "Success",
148154
"LastUpdated" : "2014-11-03T01:56:20Z",
149155
"Type" : "AWS-HMAC",
150156
"AccessKeyId" : "$TEST_NGINX_AWS_CLIENT_ID",
151157
"SecretAccessKey" : "$TEST_NGINX_AWS_SECRET",
152158
"Token" : "$TEST_NGINX_AWS_SECURITY_TOKEN",
153-
"Expiration" : "2014-11-03T08:07:52Z"
159+
"Expiration" : "$expiration"
154160
}';
155161
}
156162
location /test-with-iam {
@@ -179,9 +185,6 @@ __DATA__
179185
local KeyId = list.Aliases[1].AliasName
180186
ngx.say("KEY-ALIAS:" .. tostring(KeyId))
181187
182-
local KeyId = "alias/GW-CACHE-MK"
183-
ngx.say("KEY ALIAS:" .. tostring(KeyId))
184-
185188
-- generate a data key
186189
local cipher = service:generateDataKey(KeyId, "AES_256")
187190
local blob = cipher.CiphertextBlob
@@ -202,12 +205,13 @@ __DATA__
202205
end
203206
';
204207
}
208+
--- timeout: 60
205209
--- more_headers
206210
X-Test: test
207211
--- request
208212
GET /test-with-iam
209213
--- response_body_like eval
210-
[".*KEY\\sALIAS\\:.*BLOB\\:.*"]
214+
[".*KEY-ALIAS\\:.*BLOB\\:.*"]
211215
--- error_code: 200
212216
--- no_error_log
213217
[error]

test/integration/lambda.t

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
# vim:set ft= ts=4 sw=4 et fdm=marker:
55
use lib 'lib';
6+
use strict;
7+
use warnings;
68
use Test::Nginx::Socket::Lua;
79
use Cwd qw(cwd);
810

@@ -130,8 +132,16 @@ __DATA__
130132
key1 = "value-1",
131133
key2 = "value-2"
132134
}
133-
local invokeResult, code, headers, status, body = service:invoke(functionName, payload)
134-
ngx.say("EXECUTION RESULT:" .. tostring(body))
135+
local context = {}
136+
context.identity = {}
137+
context.identity.accountId = tostring(ngx.var.oauth_token_user_id)
138+
local context_json = cjson.encode(context)
139+
local invokeResult, code, headers, status, body = service:invoke(functionName, payload,context_json)
140+
if (code == 200) then
141+
ngx.say("EXECUTION RESULT:" .. tostring(body))
142+
else
143+
ngx.say(tostring(body))
144+
end
135145
136146
-- TODO: delete the hello-world function
137147

0 commit comments

Comments
 (0)