diff --git a/README.md b/README.md index 148ca35..37c6eff 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,19 @@ nearly all AWS services. ## Example ```nginx + +#You're limited by your max_body_size (and memory it may take up) +client_max_body_size 1G; +client_body_buffer_size 1024M; +client_body_in_single_buffer on; + +map $request_uri $request_uri_no_parameters { + "~^(?P.*?)(\?.*)*$" $path; +} + location / { set $s3_host s3.amazonaws.com; - set $s3_uri $request_uri; + set $s3_uri $request_uri_no_parameters; access_by_lua "local aws = require 'resty.aws'; aws.s3_set_headers(ngx.var.access_key, ngx.var.secret_key, ngx.var.s3_host, ngx.var.s3_uri)"; proxy_pass https://$s3_host$s3_uri; } diff --git a/lib/resty/aws.lua b/lib/resty/aws.lua index 03c7acb..610c137 100644 --- a/lib/resty/aws.lua +++ b/lib/resty/aws.lua @@ -4,7 +4,7 @@ local resty_hmac = require('resty.hmac') local resty_sha256 = require('resty.sha256') local str = require('resty.string') -local _M = { _VERSION = '0.1.2' } +local _M = { _VERSION = '0.1.3' } local function get_iso8601_basic(timestamp) return os.date('!%Y%m%dT%H%M%SZ', timestamp) @@ -17,15 +17,15 @@ 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 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 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 k_service = h_service:final() local h = resty_hmac:new(k_service, resty_hmac.ALGOS.SHA256) h:update('aws4_request') @@ -49,12 +49,40 @@ local function get_sha256_digest(s) return str.to_hex(h:final()) end +local function get_canonical_query_string() + local args = ngx.req.get_uri_args() + local query_string = '' + for key, val in pairs(args) do + if query_string ~= '' then + query_string = '&' .. query_string + end + + if type(val) == "table" then + query_string = ngx.escape_uri(key) .. '=' .. ngx.escape_uri(val[0]) .. query_string --Get the first instance of said argument, ignore the others. (Note: Maybe we should just include all instances of said parameter?) + else + if val == true then + query_string = ngx.escape_uri(key) .. '=' .. query_string + else + query_string = ngx.escape_uri(key) .. '=' .. ngx.escape_uri(val) .. query_string + end + end + end + + return query_string +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' + local digest + if ngx.var.request_body == nil and ngx.var.request_method == 'PUT' then + digest = 'UNSIGNED-PAYLOAD' + else + digest = get_sha256_digest(ngx.var.request_body) + end + local canonical_request = + ngx.var.request_method .. '\n' + .. uri .. '\n' + .. get_canonical_query_string() .. '\n' + .. 'host:' .. host .. '\n' .. 'x-amz-content-sha256:' .. digest .. '\n' .. 'x-amz-date:' .. get_iso8601_basic(timestamp) .. '\n' .. '\n' @@ -106,13 +134,15 @@ local function get_service_and_region(host) return nil, nil end -function _M.aws_set_headers(access_key, secret_key, host, uri) +function _M.aws_set_headers(access_key, secret_key, host, uri, region, service) local creds = { access_key = access_key, secret_key = secret_key } local timestamp = tonumber(ngx.time()) - local service, region = get_service_and_region(host) + if region == nil or service == nil then + service, region = get_service_and_region(host) + end local auth = get_authorization(creds, timestamp, region, service, host, uri) ngx.req.set_header('Authorization', auth) @@ -120,9 +150,13 @@ function _M.aws_set_headers(access_key, secret_key, host, uri) ngx.req.set_header('x-amz-date', get_iso8601_basic(timestamp)) end -function _M.s3_set_headers(access_key, secret_key, host, uri) - _M.aws_set_headers(access_key, secret_key, host, uri) - ngx.req.set_header('x-amz-content-sha256', get_sha256_digest(ngx.var.request_body)) +function _M.s3_set_headers(access_key, secret_key, host, uri, region, service) + _M.aws_set_headers(access_key, secret_key, host, uri, region, service) + if ngx.var.request_body == nil and ngx.var.request_method == 'PUT' then + ngx.req.set_header('x-amz-content-sha256', 'UNSIGNED-PAYLOAD') + else + ngx.req.set_header('x-amz-content-sha256', get_sha256_digest(ngx.var.request_body)) + end end -return _M +return _M \ No newline at end of file