From 28685e84df933c4df2d3d2d8f3a2860958a11cb6 Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Mon, 30 May 2016 16:59:50 +0000 Subject: [PATCH] Extend AwsService:getAuthorizationHeader() / AwsV4Signature:getAuthorizationHeader() so that the caller can pass the arbitrary host name for signer to calculate the hash with. --- src/lua/api-gateway/aws/AwsService.lua | 24 ++++++++----- src/lua/api-gateway/aws/AwsV4Signature.lua | 42 ++++++++++++++-------- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/lua/api-gateway/aws/AwsService.lua b/src/lua/api-gateway/aws/AwsService.lua index 1ad82e6..b3e55f7 100644 --- a/src/lua/api-gateway/aws/AwsService.lua +++ b/src/lua/api-gateway/aws/AwsService.lua @@ -187,7 +187,12 @@ function _M:getHttpClient() end function _M:getAWSHost() - return self.aws_service .. "." .. self.aws_region .. ".amazonaws.com" + -- FIXME: the endpoint cannot always be determined mechanically; http://docs.aws.amazon.com/general/latest/gr/rande.html + if self.aws_service == "s3" then + return self.aws_service .. "-" .. self.aws_region .. ".amazonaws.com" + else + return self.aws_service .. "." .. self.aws_region .. ".amazonaws.com" + end end function _M:getCredentials() @@ -201,16 +206,18 @@ function _M:getCredentials() return return_obj end -function _M:getAuthorizationHeader(http_method, path, uri_args, body) +function _M:getAuthorizationHeader(http_method, path, uri_args, body, host_override) local credentials = self:getCredentials() + credentials.aws_endpoint = self:getAWSHost() credentials.aws_region = self.aws_region credentials.aws_service = self.aws_service local awsAuth = AWSV4S:new(credentials) - local authorization = awsAuth:getAuthorizationHeader(http_method, + local authorization, payload_hash = awsAuth:getAuthorizationHeader(http_method, path, -- "/" uri_args, -- ngx.req.get_uri_args() - body) - return authorization, awsAuth, credentials.token + body, + host_override) + return authorization, awsAuth, credentials.token, payload_hash end --- @@ -278,7 +285,7 @@ function _M:performAction(actionName, arguments, path, http_method, useSSL, time end - local authorization, awsAuth, authToken = self:getAuthorizationHeader(request_method, request_path, uri_args, request_body) + local authorization, awsAuth, authToken, payloadHash = self:getAuthorizationHeader(request_method, request_path, uri_args, request_body) local t = self.aws_service_name .. "." .. actionName local request_headers = { @@ -287,7 +294,8 @@ function _M:performAction(actionName, arguments, path, http_method, useSSL, time ["Accept"] = "application/json", ["Content-Type"] = content_type, ["X-Amz-Target"] = t, - ["x-amz-security-token"] = authToken + ["x-amz-security-token"] = authToken, + ["x-amz-content-sha256"] = payloadHash } if ( extra_headers ~= nil ) then for headerName, headerValue in pairs(extra_headers) do @@ -329,4 +337,4 @@ function _M:performAction(actionName, arguments, path, http_method, useSSL, time return ok, code, headers, status, body end -return _M \ No newline at end of file +return _M diff --git a/src/lua/api-gateway/aws/AwsV4Signature.lua b/src/lua/api-gateway/aws/AwsV4Signature.lua index d4eb601..8e9d4e4 100644 --- a/src/lua/api-gateway/aws/AwsV4Signature.lua +++ b/src/lua/api-gateway/aws/AwsV4Signature.lua @@ -17,10 +17,12 @@ function HmacAuthV4Handler:new(o) setmetatable(o, self) self.__index = self if ( o ~= nil) then + self.aws_endpoint = o.aws_endpoint self.aws_service = o.aws_service self.aws_region = o.aws_region self.aws_secret_key = o.aws_secret_key self.aws_access_key = o.aws_access_key + self.token = o.token end -- set amazon formatted dates local utc = ngx.utctime() @@ -52,7 +54,8 @@ local function get_hashed_canonical_request(method, uri, querystring, headers, r -- add canonicalHeaders local canonicalHeaders = "" local signedHeaders = "" - for h_n,h_v in pairs(headers) do + for _, p in ipairs(headers) do + local h_n, h_v = p[1], p[2] -- todo: trim and lowercase canonicalHeaders = canonicalHeaders .. h_n .. ":" .. h_v .. "\n" signedHeaders = signedHeaders .. h_n .. ";" @@ -63,13 +66,14 @@ local function get_hashed_canonical_request(method, uri, querystring, headers, r hash = hash .. canonicalHeaders .. "\n" .. signedHeaders .. "\n" - hash = hash .. _hash(requestPayload or "") + requestPayloadHash = _hash(requestPayload or "") + hash = hash .. requestPayloadHash ngx.log(ngx.DEBUG, "Canonical String to Sign is:\n" .. hash) local final_hash = _hash(hash) ngx.log(ngx.DEBUG, "Canonical String HASHED is:\n" .. final_hash .. "\n") - return final_hash + return final_hash, signedHeaders, requestPayloadHash end local function get_string_to_sign(algorithm, request_date, credential_scope, hashed_canonical_request) @@ -141,17 +145,28 @@ function HmacAuthV4Handler:formatQueryString(uri_args) return uri end -function HmacAuthV4Handler:getSignature(http_method, request_uri, uri_arg_table, request_payload ) +function HmacAuthV4Handler:getSignature(http_method, request_uri, uri_arg_table, request_payload, host_override) local uri_args = self:formatQueryString(uri_arg_table) local utc = ngx.utctime() local date1 = self.aws_date_short local date2 = self.aws_date + local host = self.aws_endpoint + if host_override ~= nil then + host = host_override + end local headers = {} - headers.host = self.aws_service .. "." .. self.aws_region .. ".amazonaws.com" - headers["x-amz-date"] = date2 + table.insert(headers, {"host", host}) + table.insert(headers, {"x-amz-date", date2}) + if self.token ~= nil then + table.insert(headers, {"x-amz-security-token", self.token}) + end -- ensure parameters in query string are in order + local hashed_canonical_request, signed_headers, request_payload_hash = get_hashed_canonical_request( + http_method, request_uri, + uri_args, + headers, request_payload) local sign = _sign( get_derived_signing_key( self.aws_secret_key, date1, self.aws_region, @@ -159,18 +174,15 @@ function HmacAuthV4Handler:getSignature(http_method, request_uri, uri_arg_table, get_string_to_sign("AWS4-HMAC-SHA256", date2, date1 .. "/" .. self.aws_region .. "/" .. self.aws_service .. "/aws4_request", - get_hashed_canonical_request( - http_method, request_uri, - uri_args, - headers, request_payload) ) ) - return sign + hashed_canonical_request) ) + return sign, signed_headers, request_payload_hash end -function HmacAuthV4Handler:getAuthorizationHeader(http_method, request_uri, uri_arg_table, request_payload ) - local auth_signature = self:getSignature(http_method, request_uri, uri_arg_table, request_payload) +function HmacAuthV4Handler:getAuthorizationHeader(http_method, request_uri, uri_arg_table, request_payload, host_override) + local auth_signature, signed_headers, request_payload_hash = self:getSignature(http_method, request_uri, uri_arg_table, request_payload, host_override) local authHeader = "AWS4-HMAC-SHA256 Credential=" .. self.aws_access_key.."/" .. self.aws_date_short .. "/" .. self.aws_region - .."/" .. self.aws_service.."/aws4_request,SignedHeaders=host;x-amz-date,Signature="..auth_signature - return authHeader + .."/" .. self.aws_service.."/aws4_request,SignedHeaders="..signed_headers..",Signature="..auth_signature + return authHeader, request_payload_hash end return HmacAuthV4Handler