philomena/docker/web/aws-signature.lua

150 lines
4.5 KiB
Lua
Raw Permalink Normal View History

--[[
Copyright 2018 JobTeaser
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local cjson = require('cjson')
local resty_hmac = require('resty.hmac')
local resty_sha256 = require('resty.sha256')
local str = require('resty.string')
local _M = { _VERSION = '0.1.2' }
local function get_credentials ()
local access_key = os.getenv('AWS_ACCESS_KEY_ID')
local secret_key = os.getenv('AWS_SECRET_ACCESS_KEY')
return {
access_key = access_key,
secret_key = secret_key
}
end
local function get_iso8601_basic(timestamp)
return os.date('!%Y%m%dT%H%M%SZ', timestamp)
end
local function get_iso8601_basic_short(timestamp)
return os.date('!%Y%m%d', timestamp)
end
local function get_derived_signing_key(keys, timestamp, region, service)
local h_date = resty_hmac:new('AWS4' .. keys['secret_key'], resty_hmac.ALGOS.SHA256)
h_date:update(get_iso8601_basic_short(timestamp))
k_date = h_date:final()
local h_region = resty_hmac:new(k_date, resty_hmac.ALGOS.SHA256)
h_region:update(region)
k_region = h_region:final()
local h_service = resty_hmac:new(k_region, resty_hmac.ALGOS.SHA256)
h_service:update(service)
k_service = h_service:final()
local h = resty_hmac:new(k_service, resty_hmac.ALGOS.SHA256)
h:update('aws4_request')
return h:final()
end
local function get_cred_scope(timestamp, region, service)
return get_iso8601_basic_short(timestamp)
.. '/' .. region
.. '/' .. service
.. '/aws4_request'
end
local function get_signed_headers()
return 'host;x-amz-content-sha256;x-amz-date'
end
local function get_sha256_digest(s)
local h = resty_sha256:new()
h:update(s or '')
return str.to_hex(h:final())
end
local function get_hashed_canonical_request(timestamp, host, uri)
local digest = get_sha256_digest(ngx.var.request_body)
local canonical_request = ngx.var.request_method .. '\n'
.. uri .. '\n'
.. '\n'
.. 'host:' .. host .. '\n'
.. 'x-amz-content-sha256:' .. digest .. '\n'
.. 'x-amz-date:' .. get_iso8601_basic(timestamp) .. '\n'
.. '\n'
.. get_signed_headers() .. '\n'
.. digest
return get_sha256_digest(canonical_request)
end
local function get_string_to_sign(timestamp, region, service, host, uri)
return 'AWS4-HMAC-SHA256\n'
.. get_iso8601_basic(timestamp) .. '\n'
.. get_cred_scope(timestamp, region, service) .. '\n'
.. get_hashed_canonical_request(timestamp, host, uri)
end
local function get_signature(derived_signing_key, string_to_sign)
local h = resty_hmac:new(derived_signing_key, resty_hmac.ALGOS.SHA256)
h:update(string_to_sign)
return h:final(nil, true)
end
local function get_authorization(keys, timestamp, region, service, host, uri)
local derived_signing_key = get_derived_signing_key(keys, timestamp, region, service)
local string_to_sign = get_string_to_sign(timestamp, region, service, host, uri)
local auth = 'AWS4-HMAC-SHA256 '
.. 'Credential=' .. keys['access_key'] .. '/' .. get_cred_scope(timestamp, region, service)
.. ', SignedHeaders=' .. get_signed_headers()
.. ', Signature=' .. get_signature(derived_signing_key, string_to_sign)
return auth
end
local function get_service_and_region(host)
local patterns = {
{'s3.amazonaws.com', 's3', 'us-east-1'},
{'s3-external-1.amazonaws.com', 's3', 'us-east-1'},
{'s3%-([a-z0-9-]+)%.amazonaws%.com', 's3', nil}
}
for i,data in ipairs(patterns) do
local region = host:match(data[1])
if region ~= nil and data[3] == nil then
return data[2], region
elseif region ~= nil then
return data[2], data[3]
end
end
return 's3', 'auto'
end
function _M.aws_set_headers(host, uri)
local creds = get_credentials()
local timestamp = tonumber(ngx.time())
local service, region = get_service_and_region(host)
local auth = get_authorization(creds, timestamp, region, service, host, uri)
ngx.req.set_header('Authorization', auth)
ngx.req.set_header('Host', host)
ngx.req.set_header('x-amz-date', get_iso8601_basic(timestamp))
end
function _M.s3_set_headers(host, uri)
_M.aws_set_headers(host, uri)
ngx.req.set_header('x-amz-content-sha256', get_sha256_digest(ngx.var.request_body))
end
return _M