diff --git a/apisix/plugins/hmac-auth.lua b/apisix/plugins/hmac-auth.lua index 94916e975216..b237a2c008f0 100644 --- a/apisix/plugins/hmac-auth.lua +++ b/apisix/plugins/hmac-auth.lua @@ -15,51 +15,38 @@ -- limitations under the License. -- local ngx = ngx -local type = type local abs = math.abs local ngx_time = ngx.time local ngx_re = require("ngx.re") -local pairs = pairs +local re_gmatch = ngx.re.gmatch local ipairs = ipairs local hmac_sha1 = ngx.hmac_sha1 -local escape_uri = ngx.escape_uri local core = require("apisix.core") local hmac = require("resty.hmac") local consumer = require("apisix.consumer") -local plugin = require("apisix.plugin") local ngx_decode_base64 = ngx.decode_base64 local ngx_encode_base64 = ngx.encode_base64 - -local BODY_DIGEST_KEY = "X-HMAC-DIGEST" -local SIGNATURE_KEY = "X-HMAC-SIGNATURE" -local ALGORITHM_KEY = "X-HMAC-ALGORITHM" -local DATE_KEY = "Date" -local ACCESS_KEY = "X-HMAC-ACCESS-KEY" -local SIGNED_HEADERS_KEY = "X-HMAC-SIGNED-HEADERS" local plugin_name = "hmac-auth" -local MAX_REQ_BODY = 1024 * 512 - +local ALLOWED_ALGORITHMS = {"hmac-sha1", "hmac-sha256", "hmac-sha512"} +local resty_sha256 = require("resty.sha256") local schema = { type = "object", title = "work with route or service object", - properties = {}, -} - -local consumer_schema = { - type = "object", - title = "work with consumer object", properties = { - access_key = {type = "string", minLength = 1, maxLength = 256}, - secret_key = {type = "string", minLength = 1, maxLength = 256}, - algorithm = { - type = "string", - enum = {"hmac-sha1", "hmac-sha256", "hmac-sha512"}, - default = "hmac-sha256" + allowed_algorithms = { + type = "array", + minItems = 1, + items = { + type = "string", + enum = ALLOWED_ALGORITHMS + }, + default = ALLOWED_ALGORITHMS, }, clock_skew = { type = "integer", - default = 0 + default = 300, + minimum = 1 }, signed_headers = { type = "array", @@ -69,29 +56,24 @@ local consumer_schema = { maxLength = 50, } }, - keep_headers = { - type = "boolean", - title = "whether to keep the http request header", - default = false, - }, - encode_uri_params = { - type = "boolean", - title = "Whether to escape the uri parameter", - default = true, - }, validate_request_body = { type = "boolean", title = "A boolean value telling the plugin to enable body validation", default = false, }, - max_req_body = { - type = "integer", - title = "Max request body size", - default = MAX_REQ_BODY, - }, + hide_credentials = {type = "boolean", default = false}, + }, +} + +local consumer_schema = { + type = "object", + title = "work with consumer object", + properties = { + key_id = {type = "string", minLength = 1, maxLength = 256}, + secret_key = {type = "string", minLength = 1, maxLength = 256}, }, encrypt_fields = {"secret_key"}, - required = {"access_key", "secret_key"}, + required = {"key_id", "secret_key"}, } local _M = { @@ -126,17 +108,6 @@ local function array_to_map(arr) end -local function remove_headers(ctx, ...) - local headers = { ... } - if headers and #headers > 0 then - for _, header in ipairs(headers) do - core.log.info("remove_header: ", header) - core.request.set_header(ctx, header, nil) - end - end -end - - function _M.check_schema(conf, schema_type) core.log.info("input conf: ", core.json.delay_encode(conf)) @@ -148,9 +119,9 @@ function _M.check_schema(conf, schema_type) end -local function get_consumer(access_key) - if not access_key then - return nil, "missing access key" +local function get_consumer(key_id) + if not key_id then + return nil, "missing key_id" end local consumer_conf = consumer.plugin(plugin_name) @@ -158,10 +129,10 @@ local function get_consumer(access_key) return nil, "Missing related consumer" end - local consumers = consumer.consumers_kv(plugin_name, consumer_conf, "access_key") - local consumer = consumers[access_key] + local consumers = consumer.consumers_kv(plugin_name, consumer_conf, "key_id") + local consumer = consumers[key_id] if not consumer then - return nil, "Invalid access key" + return nil, "Invalid key_id" end core.log.info("consumer: ", core.json.delay_encode(consumer)) @@ -169,130 +140,90 @@ local function get_consumer(access_key) end -local function get_conf_field(access_key, field_name) - local consumer, err = get_consumer(access_key) - if err then - return false, err - end - - return consumer.auth_conf[field_name] -end - - -local function do_nothing(v) - return v -end - local function generate_signature(ctx, secret_key, params) - local canonical_uri = ctx.var.uri - local canonical_query_string = "" + local uri = ctx.var.request_uri local request_method = core.request.get_method() - local args = core.request.get_uri_args(ctx) - if canonical_uri == "" then - canonical_uri = "/" + if uri == "" then + uri = "/" end - if type(args) == "table" then - local keys = {} - local query_tab = {} - - for k, v in pairs(args) do - core.table.insert(keys, k) - end - core.table.sort(keys) - - local field_val = get_conf_field(params.access_key, "encode_uri_params") - core.log.info("encode_uri_params: ", field_val) - - local encode_or_not = do_nothing - if field_val then - encode_or_not = escape_uri - end - - for _, key in pairs(keys) do - local param = args[key] - -- when args without `=`, value is treated as true. - -- In order to be compatible with args lacking `=`, - -- we need to replace true with an empty string. - if type(param) == "boolean" then - param = "" - end - - -- whether to encode the uri parameters - if type(param) == "table" then - local vals = {} - for _, val in pairs(param) do - if type(val) == "boolean" then - val = "" - end - core.table.insert(vals, val) - end - core.table.sort(vals) - - for _, val in pairs(vals) do - core.table.insert(query_tab, encode_or_not(key) .. "=" .. encode_or_not(val)) - end - else - core.table.insert(query_tab, encode_or_not(key) .. "=" .. encode_or_not(param)) - end - end - canonical_query_string = core.table.concat(query_tab, "&") - end - - core.log.info("all headers: ", - core.json.delay_encode(core.request.headers(ctx), true)) - local signing_string_items = { - request_method, - canonical_uri, - canonical_query_string, - params.access_key, - params.date, + params.keyId, } - if params.signed_headers then - for _, h in ipairs(params.signed_headers) do - local canonical_header = core.request.header(ctx, h) or "" - core.table.insert(signing_string_items, - h .. ":" .. canonical_header) - core.log.info("canonical_header name:", core.json.delay_encode(h)) - core.log.info("canonical_header value: ", - core.json.delay_encode(canonical_header)) + if params.headers then + for _, h in ipairs(params.headers) do + local canonical_header = core.request.header(ctx, h) + if not canonical_header then + if h == "@request-target" then + local request_target = request_method .. " " .. uri + core.table.insert(signing_string_items, request_target) + core.log.info("canonical_header name:", core.json.delay_encode(h)) + core.log.info("canonical_header value: ", + core.json.delay_encode(request_target)) + end + else + core.table.insert(signing_string_items, + h .. ": " .. canonical_header) + core.log.info("canonical_header name:", core.json.delay_encode(h)) + core.log.info("canonical_header value: ", + core.json.delay_encode(canonical_header)) + end end end - local signing_string = core.table.concat(signing_string_items, "\n") .. "\n" + local signing_string = core.table.concat(signing_string_items, "\n") .. "\n" + return hmac_funcs[params.algorithm](secret_key, signing_string) +end - core.log.info("signing_string: ", signing_string, - " params.signed_headers:", - core.json.delay_encode(params.signed_headers)) - return hmac_funcs[params.algorithm](secret_key, signing_string) +local function sha256(key) + local hash = resty_sha256:new() + hash:update(key) + local digest = hash:final() + return digest end -local function validate(ctx, params) - if not params.access_key or not params.signature then - return nil, "access key or signature missing" +local function validate(ctx, conf, params) + if not params.keyId or not params.signature then + return nil, "keyId or signature missing" end if not params.algorithm then return nil, "algorithm missing" end - local consumer, err = get_consumer(params.access_key) + local consumer, err = get_consumer(params.keyId) if err then return nil, err end - local conf = consumer.auth_conf - if conf.algorithm ~= params.algorithm then - return nil, "algorithm " .. params.algorithm .. " not supported" + local consumer_conf = consumer.auth_conf + local found_algorithm = false + -- check supported alorithm used + if not conf.allowed_algorithms then + conf.allowed_algorithms = ALLOWED_ALGORITHMS + end + + for _, algo in ipairs(conf.allowed_algorithms) do + if algo == params.algorithm then + found_algorithm = true + break + end + end + + if not found_algorithm then + return nil, "Invalid algorithm" end core.log.info("clock_skew: ", conf.clock_skew) if conf.clock_skew and conf.clock_skew > 0 then + if not params.date then + return nil, "Date header missing. failed to validate clock skew" + end + local time = ngx.parse_http_time(params.date) core.log.info("params.date: ", params.date, " time: ", time) if not time then @@ -300,52 +231,51 @@ local function validate(ctx, params) end local diff = abs(ngx_time() - time) - core.log.info("gmt diff: ", diff) + if diff > conf.clock_skew then return nil, "Clock skew exceeded" end end -- validate headers + -- All headers passed in route conf.signed_headers must be used in signing(params.headers) if conf.signed_headers and #conf.signed_headers >= 1 then - local headers_map = array_to_map(conf.signed_headers) - if params.signed_headers then - for _, header in ipairs(params.signed_headers) do - if not headers_map[header] then - return nil, "Invalid signed header " .. header + if not params.headers then + return nil, "headers missing" + end + local params_headers_map = array_to_map(params.headers) + if params_headers_map then + for _, header in ipairs(conf.signed_headers) do + if not params_headers_map[header] then + return nil, [[expected header "]] .. header .. [[" missing in signing]] end end end end - local secret_key = conf and conf.secret_key + local secret_key = consumer_conf and consumer_conf.secret_key local request_signature = ngx_decode_base64(params.signature) local generated_signature = generate_signature(ctx, secret_key, params) - - core.log.info("request_signature: ", request_signature, - " generated_signature: ", generated_signature) - if request_signature ~= generated_signature then return nil, "Invalid signature" end - local validate_request_body = get_conf_field(params.access_key, "validate_request_body") + local validate_request_body = conf.validate_request_body if validate_request_body then local digest_header = params.body_digest if not digest_header then return nil, "Invalid digest" end - local max_req_body = get_conf_field(params.access_key, "max_req_body") - local req_body, err = core.request.get_body(max_req_body, ctx) + local req_body, err = core.request.get_body() if err then - return nil, "Exceed body limit size" + return nil, err end req_body = req_body or "" - local request_body_hash = ngx_encode_base64( - hmac_funcs[params.algorithm](secret_key, req_body)) - if request_body_hash ~= digest_header then + local digest_created = "SHA-256" .. "=" .. + ngx_encode_base64(sha256(req_body)) + if digest_created ~= digest_header then return nil, "Invalid digest" end end @@ -354,78 +284,55 @@ local function validate(ctx, params) end -local function get_params(ctx) - local params = {} - local access_key = ACCESS_KEY - local signature_key = SIGNATURE_KEY - local algorithm_key = ALGORITHM_KEY - local date_key = DATE_KEY - local signed_headers_key = SIGNED_HEADERS_KEY - local body_digest_key = BODY_DIGEST_KEY - - - local attr = plugin.plugin_attr(plugin_name) - if attr then - access_key = attr.access_key or access_key - signature_key = attr.signature_key or signature_key - algorithm_key = attr.algorithm_key or algorithm_key - date_key = attr.date_key or date_key - signed_headers_key = attr.signed_headers_key or signed_headers_key - body_digest_key = attr.body_digest_key or body_digest_key +local function retrieve_hmac_fields(ctx) + local hmac_params = {} + local auth_string = core.request.header(ctx, "Authorization") + if not auth_string then + return nil, "missing Authorization header" end - local app_key = core.request.header(ctx, access_key) - local signature = core.request.header(ctx, signature_key) - local algorithm = core.request.header(ctx, algorithm_key) - local date = core.request.header(ctx, date_key) - local signed_headers = core.request.header(ctx, signed_headers_key) - local body_digest = core.request.header(ctx, body_digest_key) - core.log.info("signature_key: ", signature_key) - - -- get params from header `Authorization` - if not app_key then - local auth_string = core.request.header(ctx, "Authorization") - if not auth_string then - return params - end - - local auth_data = ngx_re.split(auth_string, "#") - core.log.info("auth_string: ", auth_string, " #auth_data: ", - #auth_data, " auth_data: ", - core.json.delay_encode(auth_data)) - - if #auth_data == 6 and auth_data[1] == "hmac-auth-v1" then - app_key = auth_data[2] - signature = auth_data[3] - algorithm = auth_data[4] - date = auth_data[5] - signed_headers = auth_data[6] - end + if not auth_string:match("^Signature") then + return nil, "Authorization header does not start with 'Signature'" end - params.access_key = app_key - params.algorithm = algorithm - params.signature = signature - params.date = date or "" - params.signed_headers = signed_headers and ngx_re.split(signed_headers, ";") - params.body_digest = body_digest + local signature_fields = auth_string:sub(10):gmatch('[^,]+') + + for field in signature_fields do + local key, value = field:match('%s*(%w+)="(.-)"') + if key and value then + if key == "keyId" or key == "algorithm" or key == "signature" then + hmac_params[key] = value - local keep_headers = get_conf_field(params.access_key, "keep_headers") - core.log.info("keep_headers: ", keep_headers) + elseif key == "headers" then + hmac_params.headers = ngx_re.split(value, " ") + end + end + end - if not keep_headers then - remove_headers(ctx, signature_key, algorithm_key, signed_headers_key) + -- will be required to check clock skew + if core.request.header(ctx, "Date") then + hmac_params.date = core.request.header(ctx, "Date") end - core.log.info("params: ", core.json.delay_encode(params)) + if core.request.header(ctx, "Digest") then + hmac_params.body_digest = core.request.header(ctx, "Digest") + end - return params + return hmac_params end function _M.rewrite(conf, ctx) - local params = get_params(ctx) - local validated_consumer, err = validate(ctx, params) + local params,err = retrieve_hmac_fields(ctx) + if err then + core.log.warn("client request can't be validated: ", err) + return 401, {message = "client request can't be validated: " .. err} + end + + if conf.hide_credentials then + core.request.set_header("Authorization", nil) + end + local validated_consumer, err = validate(ctx, conf, params) if not validated_consumer then core.log.warn("client request can't be validated: ", err or "Invalid signature") return 401, {message = "client request can't be validated"} @@ -433,8 +340,7 @@ function _M.rewrite(conf, ctx) local consumer_conf = consumer.plugin(plugin_name) consumer.attach_consumer(ctx, validated_consumer, consumer_conf) - core.log.info("hit hmac-auth rewrite") end -return _M +return _M \ No newline at end of file diff --git a/t/node/consumer-plugin.t b/t/node/consumer-plugin.t index 89cdf68cc6ab..4a9e1220820a 100644 --- a/t/node/consumer-plugin.t +++ b/t/node/consumer-plugin.t @@ -341,18 +341,16 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/status", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /status", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) @@ -360,11 +358,8 @@ location /t { local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b diff --git a/t/plugin/consumer-restriction.t b/t/plugin/consumer-restriction.t index cc86aacdcd7d..9ba590d0530e 100644 --- a/t/plugin/consumer-restriction.t +++ b/t/plugin/consumer-restriction.t @@ -738,7 +738,7 @@ passed "username": "jack", "plugins": { "hmac-auth": { - "access_key": "my-access-key", + "key_id": "my-access-key", "secret_key": "my-secret-key" }, "consumer-restriction": { @@ -811,29 +811,24 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -934,29 +929,24 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -992,7 +982,7 @@ qr/\{"message":"The service_id is forbidden."\}/ "username": "jack", "plugins": { "hmac-auth": { - "access_key": "my-access-key", + "key_id": "my-access-key", "secret_key": "my-secret-key" }, "consumer-restriction": { @@ -1064,29 +1054,24 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -1158,29 +1143,24 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -1213,7 +1193,7 @@ passed "username": "jack", "plugins": { "hmac-auth": { - "access_key": "my-access-key", + "key_id": "my-access-key", "secret_key": "my-secret-key" }, "consumer-restriction": { @@ -1248,29 +1228,24 @@ location /t { local ngx_encode_base64 = ngx.encode_base64 local secret_key = "my-secret-key" local gmt = ngx_http_time(ngx_time) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -1303,7 +1278,7 @@ passed "username": "jack", "plugins": { "hmac-auth": { - "access_key": "my-access-key", + "key_id": "my-access-key", "secret_key": "my-secret-key" }, "consumer-restriction": { @@ -1340,29 +1315,24 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b diff --git a/t/plugin/hmac-auth-custom.t b/t/plugin/hmac-auth-custom.t deleted file mode 100644 index 6e463c9401ff..000000000000 --- a/t/plugin/hmac-auth-custom.t +++ /dev/null @@ -1,357 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. -# -use t::APISIX 'no_plan'; - -repeat_each(2); -no_long_string(); -no_root_location(); -no_shuffle(); - -add_block_preprocessor(sub { - my ($block) = @_; - - if (!$block->request) { - $block->set_value("request", "GET /t"); - } - - my $extra_yaml_config = <<_EOC_; -plugin_attr: - hmac-auth: - signature_key: X-APISIX-HMAC-SIGNATURE - algorithm_key: X-APISIX-HMAC-ALGORITHM - date_key: X-APISIX-DATE - access_key: X-APISIX-HMAC-ACCESS-KEY - signed_headers_key: X-APISIX-HMAC-SIGNED-HEADERS -_EOC_ - - $block->set_value("extra_yaml_config", $extra_yaml_config); -}); - -run_tests; - -__DATA__ - -=== TEST 1: add consumer with username and plugins ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "jack", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key", - "secret_key": "my-secret-key", - "clock_skew": 10 - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 2: add consumer with plugin hmac-auth - missing secret key ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "jack", - "plugins": { - "hmac-auth": { - "access_key": "user-key" - } - } - }]]) - - ngx.status = code - ngx.say(body) - } - } ---- error_code: 400 - - - -=== TEST 3: add consumer with plugin hmac-auth - missing access key ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "jack", - "plugins": { - "hmac-auth": { - "secret_key": "skey" - } - } - }]]) - - ngx.status = code - ngx.say(body) - } - } ---- error_code: 400 - - - -=== TEST 4: enable hmac auth plugin using admin api ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "plugins": { - "hmac-auth": {} - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uri": "/hello" - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 5: verify, missing signature ---- request -GET /hello ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: access key or signature missing ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ - - - -=== TEST 6: verify: invalid access key ---- request -GET /hello ---- more_headers -X-APISIX-HMAC-SIGNATURE: asdf -X-APISIX-HMAC-ALGORITHM: hmac-sha256 -X-APISIX-Date: Thu, 24 Sep 2020 06:39:52 GMT -X-APISIX-HMAC-ACCESS-KEY: sdf ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Invalid access key ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ - - - -=== TEST 7: verify: invalid algorithm ---- request -GET /hello ---- more_headers -X-APISIX-HMAC-SIGNATURE: asdf -X-APISIX-HMAC-ALGORITHM: ljlj -X-APISIX-Date: Thu, 24 Sep 2020 06:39:52 GMT -X-APISIX-HMAC-ACCESS-KEY: sdf ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Invalid access key ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ - - - -=== TEST 8: verify: Invalid GMT format time ---- request -GET /hello ---- more_headers -X-APISIX-HMAC-SIGNATURE: asdf -X-APISIX-HMAC-ALGORITHM: hmac-sha256 -X-APISIX-Date: adfa -X-APISIX-HMAC-ACCESS-KEY: my-access-key ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Invalid GMT format time ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ - - - -=== TEST 9: verify: ok ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key" - local time = ngx_time() - local gmt = ngx_http_time(time) - local access_key = "my-access-key" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-APISIX-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-APISIX-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-APISIX-DATE"] = gmt - headers["X-APISIX-HMAC-ACCESS-KEY"] = access_key - headers["X-APISIX-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- response_body -passed - - - -=== TEST 10: update consumer with clock skew ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "pony", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key2", - "secret_key": "my-secret-key2", - "clock_skew": 1 - } - } - }]] - ) - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 11: verify: Clock skew exceeded ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key2" - local time = ngx_time() - local gmt = ngx_http_time(time) - local access_key = "my-access-key2" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - local signing_string = "GET" .. "/hello" .. "" .. - access_key .. gmt .. custom_header_a .. custom_header_b - - ngx.sleep(2) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-APISIX-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-APISIX-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-APISIX-DATE"] = gmt - headers["X-APISIX-HMAC-ACCESS-KEY"] = access_key - - local code, body = t.test('/hello', - ngx.HTTP_GET, - core.json.encode(data), - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Clock skew exceeded ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ diff --git a/t/plugin/hmac-auth.t b/t/plugin/hmac-auth.t index 4efdae88f1b3..d6127a5336fd 100644 --- a/t/plugin/hmac-auth.t +++ b/t/plugin/hmac-auth.t @@ -35,9 +35,8 @@ __DATA__ "username": "jack", "plugins": { "hmac-auth": { - "access_key": "my-access-key", - "secret_key": "my-secret-key", - "clock_skew": 10 + "key_id": "my-access-key", + "secret_key": "my-secret-key" } } }]] @@ -67,7 +66,7 @@ passed "username": "foo", "plugins": { "hmac-auth": { - "access_key": "user-key" + "key_id": "user-key" } } }]]) @@ -84,7 +83,7 @@ qr/\{"error_msg":"invalid plugins configuration: failed to check the configurati -=== TEST 3: add consumer with plugin hmac-auth - missing access key +=== TEST 3: add consumer with plugin hmac-auth - missing key_id --- config location /t { content_by_lua_block { @@ -108,11 +107,11 @@ qr/\{"error_msg":"invalid plugins configuration: failed to check the configurati GET /t --- error_code: 400 --- response_body eval -qr/\{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin hmac-auth err: property \\"access_key\\" is required"\}/ +qr/\{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin hmac-auth err: property \\"key_id\\" is required"\}/ -=== TEST 4: add consumer with plugin hmac-auth - access key exceeds the length limit +=== TEST 4: add consumer with plugin hmac-auth - key id exceeds the length limit --- config location /t { content_by_lua_block { @@ -123,7 +122,7 @@ qr/\{"error_msg":"invalid plugins configuration: failed to check the configurati "username": "li", "plugins": { "hmac-auth": { - "access_key": "akeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakey", + "key_id": "akeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakeyakey", "secret_key": "skey" } } @@ -137,11 +136,11 @@ qr/\{"error_msg":"invalid plugins configuration: failed to check the configurati GET /t --- error_code: 400 --- response_body eval -qr/\{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin hmac-auth err: property \\"access_key\\" validation failed: string too long, expected at most 256, got 320"\}/ +qr/\{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin hmac-auth err: property \\"key_id\\" validation failed: string too long, expected at most 256, got 320"\}/ -=== TEST 5: add consumer with plugin hmac-auth - access key exceeds the length limit +=== TEST 5: add consumer with plugin hmac-auth - secret key exceeds the length limit --- config location /t { content_by_lua_block { @@ -152,7 +151,7 @@ qr/\{"error_msg":"invalid plugins configuration: failed to check the configurati "username": "zhang", "plugins": { "hmac-auth": { - "access_key": "akey", + "key_id": "akey", "secret_key": "skeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskeyskey" } } @@ -204,16 +203,16 @@ passed -=== TEST 7: verify, missing signature +=== TEST 7: verify,missing Authorization header --- request GET /hello --- error_code: 401 --- response_body -{"message":"client request can't be validated"} +{"message":"client request can't be validated: missing Authorization header"} --- grep_error_log eval qr/client request can't be validated: [^,]+/ --- grep_error_log_out -client request can't be validated: access key or signature missing +client request can't be validated: missing Authorization header @@ -221,34 +220,31 @@ client request can't be validated: access key or signature missing --- request GET /hello --- more_headers -X-HMAC-SIGNATURE: asdf +Authorization: Signature keyId="my-access-key",headers="@request-target date" ,signature="asdf" Date: Thu, 24 Sep 2020 06:39:52 GMT -X-HMAC-ACCESS-KEY: my-access-key --- error_code: 401 --- response_body {"message":"client request can't be validated"} --- grep_error_log eval -qr/client request can't be validated: [^,]+/ +qr/client request can't be validated[^,]+/ --- grep_error_log_out client request can't be validated: algorithm missing -=== TEST 9: verify: invalid access key +=== TEST 9: verify: invalid key_id --- request GET /hello --- more_headers -X-HMAC-SIGNATURE: asdf -X-HMAC-ALGORITHM: hmac-sha256 +Authorization: Signature keyId="sdf",algorithm="hmac-sha256",headers="@request-target date",signature="asdf" Date: Thu, 24 Sep 2020 06:39:52 GMT -X-HMAC-ACCESS-KEY: sdf --- error_code: 401 --- response_body {"message":"client request can't be validated"} --- grep_error_log eval qr/client request can't be validated: [^,]+/ --- grep_error_log_out -client request can't be validated: Invalid access key +client request can't be validated: Invalid key_id @@ -256,17 +252,15 @@ client request can't be validated: Invalid access key --- request GET /hello --- more_headers -X-HMAC-SIGNATURE: asdf -X-HMAC-ALGORITHM: ljlj +Authorization: Signature keyId="my-access-key",algorithm="ljlj",headers="@request-target date",signature="asdf" Date: Thu, 24 Sep 2020 06:39:52 GMT -X-HMAC-ACCESS-KEY: my-access-key --- error_code: 401 --- response_body {"message":"client request can't be validated"} --- grep_error_log eval qr/client request can't be validated: [^,]+/ --- grep_error_log_out -client request can't be validated: algorithm ljlj not supported +client request can't be validated: Invalid algorithm @@ -274,10 +268,8 @@ client request can't be validated: algorithm ljlj not supported --- request GET /hello --- more_headers -X-HMAC-SIGNATURE: asdf -X-HMAC-ALGORITHM: hmac-sha256 +Authorization: Signature keyId="my-access-key",algorithm="hmac-sha256",headers="@request-target date",signature="asdf" Date: Thu, 24 Sep 2020 06:39:52 GMT -X-HMAC-ACCESS-KEY: my-access-key --- error_code: 401 --- response_body {"message":"client request can't be validated"} @@ -292,16 +284,14 @@ client request can't be validated: Clock skew exceeded --- request GET /hello --- more_headers -X-HMAC-SIGNATURE: asdf -X-HMAC-ALGORITHM: hmac-sha256 -X-HMAC-ACCESS-KEY: my-access-key +Authorization: Signature keyId="my-access-key",algorithm="hmac-sha256",headers="@request-target date",signature="asdf" --- error_code: 401 --- response_body {"message":"client request can't be validated"} --- grep_error_log eval -qr/client request can't be validated: [^,]+/ +qr/client request can't be validated: Date header missing/ --- grep_error_log_out -client request can't be validated: Invalid GMT format time +client request can't be validated: Date header missing @@ -309,10 +299,8 @@ client request can't be validated: Invalid GMT format time --- request GET /hello --- more_headers -X-HMAC-SIGNATURE: asdf -X-HMAC-ALGORITHM: hmac-sha256 +Authorization: Signature keyId="my-access-key",algorithm="hmac-sha256",headers="@request-target date",signature="asdf" Date: adfsdf -X-HMAC-ACCESS-KEY: my-access-key --- error_code: 401 --- response_body {"message":"client request can't be validated"} @@ -337,18 +325,16 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) @@ -356,11 +342,8 @@ location /t { local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature algorithm=\"hmac-sha256\"" .. ",keyId=\"" .. key_id .. "\",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -382,88 +365,68 @@ passed -=== TEST 15: verify: ok (multiple duplicates X-HMAC-SIGNATURE header) +=== TEST 15: add route with 0 clock skew --- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": { + "clock_skew": 0 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + if code == 400 then + ngx.say(body) + end } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - local encoded_signature = ngx_encode_base64(signature) - headers["X-HMAC-SIGNATURE"] = {encoded_signature, "another-signature"} - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) } -} --- request GET /t ---- response_body -passed +-- error_code: 400 +--- response_body eval +qr/.*failed to check the configuration of plugin hmac-auth err.*/ -=== TEST 16: add consumer with 0 clock skew +=== TEST 16: add route with valid clock skew --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "username": "robin", "plugins": { "hmac-auth": { - "access_key": "my-access-key3", + "key_id": "my-access-key3", "secret_key": "my-secret-key3", - "clock_skew": 0 + "clock_skew": 1000000000000 } - } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" }]] ) - if code >= 300 then - ngx.status = code + if code == 200 then + ngx.say(body) end - ngx.say(body) } } --- request @@ -473,15 +436,28 @@ passed -=== TEST 17: verify: invalid signature +=== TEST 18: verify: invalid signature +--- request +GET /hello +--- more_headers +Authorization: Signature keyId="my-access-key",algorithm="hmac-sha256",headers="@request-target date",signature="asdf" +Date: Thu, 24 Sep 2020 06:39:52 GMT +--- error_code: 401 +--- response_body +{"message":"client request can't be validated"} +--- grep_error_log eval +qr/client request can't be validated: [^,]+/ +--- grep_error_log_out +client request can't be validated: Invalid signature + + + +=== TEST 19: verify: invalid signature --- request GET /hello --- more_headers -X-HMAC-SIGNATURE: asdf -X-HMAC-SIGNATURE: asdf -X-HMAC-ALGORITHM: hmac-sha256 +Authorization: Signature keyId="my-access-key",algorithm="hmac-sha256",headers="@request-target date",signature="asdf" Date: Thu, 24 Sep 2020 06:39:52 GMT -X-HMAC-ACCESS-KEY: my-access-key3 --- error_code: 401 --- response_body {"message":"client request can't be validated"} @@ -492,29 +468,31 @@ client request can't be validated: Invalid signature -=== TEST 18: add consumer with 1 clock skew +=== TEST 20: add route with 1 clock skew --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "username": "pony", "plugins": { "hmac-auth": { - "access_key": "my-access-key2", - "secret_key": "my-secret-key2", - "clock_skew": 1 + "clock_skew": 1 } - } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" }]] ) - - if code >= 300 then - ngx.status = code + if code == 200 then + ngx.say(body) end - ngx.say(body) } } --- request @@ -524,7 +502,7 @@ passed -=== TEST 19: verify: Invalid GMT format time +=== TEST 21: verify: Invalid GMT format time --- config location /t { content_by_lua_block { @@ -535,26 +513,23 @@ location /t { local hmac = require("resty.hmac") local ngx_encode_base64 = ngx.encode_base64 - local secret_key = "my-secret-key2" + local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key2" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" ngx.sleep(2) local signing_string = "GET" .. "/hello" .. "" .. - access_key .. gmt .. custom_header_a .. custom_header_b + key_id .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -581,7 +556,39 @@ client request can't be validated: Clock skew exceeded -=== TEST 20: verify: put ok +=== TEST 22: update route with defualt clock skew +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + if code == 200 then + ngx.say(body) + end + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 23: verify: put ok --- config location /t { content_by_lua_block { @@ -599,18 +606,16 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "PUT", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "PUT /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) @@ -618,11 +623,8 @@ location /t { local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -644,53 +646,131 @@ passed -=== TEST 21: verify: put ok (pass auth data by header `Authorization`) +=== TEST 24: update route with signed_headers +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": { + "signed_headers": ["date","x-custom-header-a", "x-custom-header-b"] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 25: verify with invalid signed header --- config location /t { content_by_lua_block { local ngx_time = ngx.time - local ngx_http_time = ngx.http_time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") local ngx_encode_base64 = ngx.encode_base64 - local data = {cert = "ssl_cert", key = "ssl_key", sni = "test.com"} - local req_body = core.json.encode(data) - req_body = req_body or "" + local secret_key = "my-secret-key" + local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) + local key_id = "my-access-key" + local custom_header_a = "asld$%dfasf" + local custom_header_c = "23879fmsldfk" + + local signing_string = "GET" .. "/hello" .. "" .. + key_id .. gmt .. custom_header_a .. custom_header_c + + local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) + core.log.info("signature:", ngx_encode_base64(signature)) + local headers = {} + headers["Date"] = gmt + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-c\",signature=\"" .. ngx_encode_base64(signature) .. "\"" + headers["x-custom-header-a"] = custom_header_a + headers["x-custom-header-c"] = custom_header_c + + local code, body = t.test('/hello', + ngx.HTTP_GET, + "", + nil, + headers + ) + + ngx.status = code + ngx.say(body) + } +} +--- request +GET /t +--- error_code: 401 +--- response_body eval +qr/{"message":"client request can't be validated"}/ +--- grep_error_log eval +qr/client request can't be validated: [^,]+/ +--- grep_error_log_out +client request can't be validated: expected header "x-custom-header-b" missing in signing + + + +=== TEST 26: verify ok with signed headers +--- config +location /t { + content_by_lua_block { + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time + local core = require("apisix.core") + local t = require("lib.test_admin") + local hmac = require("resty.hmac") + local ngx_encode_base64 = ngx.encode_base64 local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" + local custom_header_b = "asld$%dfasf" local signing_string = { - "PUT", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) - local auth_string = "hmac-auth-v1#" .. access_key .. "#" .. ngx_encode_base64(signature) .. "#" .. - "hmac-sha256#" .. gmt .. "#x-custom-header-a;x-custom-header-b" - local headers = {} - headers["Authorization"] = auth_string + headers["date"] = gmt + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b - local code, body = t.test('/hello', - ngx.HTTP_PUT, - req_body, + ngx.HTTP_GET, + "", nil, headers ) @@ -706,35 +786,176 @@ passed -=== TEST 22: hit route without auth info +=== TEST 27: add consumer with plugin hmac-auth - empty configuration +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "foo", + "plugins": { + "hmac-auth": { + } + } + }]]) + + ngx.status = code + ngx.say(body) + } + } --- request -GET /hello ---- error_code: 401 +GET /t +--- error_code: 400 +--- response_body eval +qr/\{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin hmac-auth err: property \\"(key_id|secret_key)\\" is required"\}/ + + + +=== TEST 28: add route with no allowed algorithms +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": { + "allowed_algorithms": [] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body eval +qr/validation failed: expect array to have at least 1 items/ + + + + +=== TEST 29: update route with signed_headers +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "hmac-auth": { + "hide_credentials": true + } + }, + "upstream": { + "nodes": { + "httpbin.org:80": 1 + }, + "type": "roundrobin" + }, + "uri": "/headers" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t --- response_body -{"message":"client request can't be validated"} ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: access key or signature missing +passed + -=== TEST 23: add consumer with signed_headers +=== TEST 30: verify Authorization header missing +--- config +location /t { + content_by_lua_block { + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time + local core = require("apisix.core") + local t = require("lib.test_admin") + local hmac = require("resty.hmac") + local ngx_encode_base64 = ngx.encode_base64 + + local secret_key = "my-secret-key" + local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) + local key_id = "my-access-key" + + local signing_string = { + key_id, + "GET /headers", + } + signing_string = core.table.concat(signing_string, "\n") .. "\n" + + local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) + core.log.info("signature:", ngx_encode_base64(signature)) + local headers = {} + headers["date"] = gmt + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target\",signature=\"" .. ngx_encode_base64(signature) .. "\"" + local code, _, body = t.test('/headers', + ngx.HTTP_GET, + "", + nil, + headers + ) + + if string.find(body,"Authorization") then + ngx.say("failed") + else + ngx.say("passed") + end + } +} +--- request +GET /t +--- response_body +passed + + + +=== TEST 31 : update route with signed_headers --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "username": "cook", "plugins": { "hmac-auth": { - "access_key": "my-access-key5", - "secret_key": "my-secret-key5", - "signed_headers": ["x-custom-header-a", "x-custom-header-b"] + "signed_headers": ["date","x-custom-header-a", "x-custom-header-b"] } - } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" }]] ) @@ -751,7 +972,8 @@ passed -=== TEST 24: verify with invalid signed header + +=== TEST 32: verify error with the client only sends one in the request, but there are two in the signature --- config location /t { content_by_lua_block { @@ -762,27 +984,28 @@ location /t { local hmac = require("resty.hmac") local ngx_encode_base64 = ngx.encode_base64 - local secret_key = "my-secret-key5" + local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key5" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" - local custom_header_c = "23879fmsldfk" + local custom_header_b = "asld$%dfasf" - local signing_string = "GET" .. "/hello" .. "" .. - access_key .. gmt .. custom_header_a .. custom_header_c + local signing_string = { + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b + } + signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-c" + headers["date"] = gmt + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-c"] = custom_header_c - local code, body = t.test('/hello', ngx.HTTP_GET, "", @@ -798,15 +1021,15 @@ location /t { GET /t --- error_code: 401 --- response_body eval -qr/{"message":"client request can't be validated"}/ +qr/client request can't be validated/ --- grep_error_log eval qr/client request can't be validated: [^,]+/ --- grep_error_log_out -client request can't be validated: Invalid signed header x-custom-header-c +client request can't be validated: Invalid signature -=== TEST 25: verify ok with signed headers +=== TEST 33: verify error with the client sends two in the request, but there is only one in the signature --- config location /t { content_by_lua_block { @@ -817,32 +1040,28 @@ location /t { local hmac = require("resty.hmac") local ngx_encode_base64 = ngx.encode_base64 - local secret_key = "my-secret-key5" + local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key5" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" + local custom_header_b = "asld$%dfasf" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a } signing_string = core.table.concat(signing_string, "\n") .. "\n" local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a - + headers["x-custom-header-b"] = custom_header_b local code, body = t.test('/hello', ngx.HTTP_GET, "", @@ -856,32 +1075,103 @@ location /t { } --- request GET /t ---- response_body -passed +--- error_code: 401 +--- response_body eval +qr/client request can't be validated/ +--- grep_error_log eval +qr/client request can't be validated: [^,]+/ +--- grep_error_log_out +client request can't be validated: Invalid signature -=== TEST 26: add consumer with plugin hmac-auth - empty configuration +=== TEST 34 : update route with allowed_algorithms --- config location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "username": "foo", "plugins": { "hmac-auth": { + "allowed_algorithms": ["hmac-sha256"] } - } - }]]) + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) - ngx.status = code + if code >= 300 then + ngx.status = code + end ngx.say(body) } } --- request GET /t ---- error_code: 400 +--- response_body +passed + + + +=== TEST 35: verify with hmac-sha1 algorithm, not part of allowed_algorithms +--- config +location /t { + content_by_lua_block { + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time + local core = require("apisix.core") + local t = require("lib.test_admin") + local hmac = require("resty.hmac") + local ngx_encode_base64 = ngx.encode_base64 + + local secret_key = "my-secret-key" + local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) + local key_id = "my-access-key" + local custom_header_a = "asld$%dfasf" + local custom_header_b = "asld$%dfasf" + + local signing_string = { + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b + } + signing_string = core.table.concat(signing_string, "\n") .. "\n" + + local signature = hmac:new(secret_key, hmac.ALGOS.SHA1):final(signing_string) + core.log.info("signature:", ngx_encode_base64(signature)) + local headers = {} + headers["date"] = gmt + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha1\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" + headers["x-custom-header-a"] = custom_header_a + headers["x-custom-header-b"] = custom_header_b + local code, body = t.test('/hello', + ngx.HTTP_GET, + "", + nil, + headers + ) + + ngx.status = code + ngx.say(body) + } +} +--- request +GET /t +--- error_code: 401 --- response_body eval -qr/\{"error_msg":"invalid plugins configuration: failed to check the configuration of plugin hmac-auth err: property \\"(access|secret)_key\\" is required"\}/ +qr/client request can't be validated/ +--- grep_error_log eval +qr/client request can't be validated: [^,]+/ +--- grep_error_log_out +client request can't be validated: Invalid algorithm diff --git a/t/plugin/hmac-auth2.t b/t/plugin/hmac-auth2.t index 9b78c9075ca5..7d6e8600cbc6 100644 --- a/t/plugin/hmac-auth2.t +++ b/t/plugin/hmac-auth2.t @@ -65,329 +65,7 @@ passed -=== TEST 2: keep_headers field is empty ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "james", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key4", - "secret_key": "my-secret-key4" - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 3: verify pass(keep_headers field is empty), remove http request header ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_re = require("ngx.re") - local ngx_encode_base64 = ngx.encode_base64 - - local data = {cert = "ssl_cert", key = "ssl_key", sni = "test.com"} - local req_body = core.json.encode(data) - req_body = req_body or "" - - local secret_key = "my-secret-key4" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key4" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "PUT", - "/uri", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, _, body = t.test('/uri', - ngx.HTTP_PUT, - req_body, - nil, - headers - ) - - if code >= 300 then - ngx.status = code - end - - local headers_arr = ngx_re.split(body, "\n") - for i, v in ipairs(headers_arr) do - if i ~= 4 and i ~= 6 then -- skip date and user-agent field - ngx.say(v) - end - end - } -} ---- response_body -uri: /uri -content-length: 52 -content-type: application/x-www-form-urlencoded -host: 127.0.0.1:1984 -x-custom-header-a: asld$%dfasf -x-custom-header-b: 23879fmsldfk -x-hmac-access-key: my-access-key4 -x-real-ip: 127.0.0.1 - - - -=== TEST 4: keep_headers field is false ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "james", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key4", - "secret_key": "my-secret-key4", - "keep_headers": false - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 5: verify pass(keep_headers field is false), remove http request header ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_re = require("ngx.re") - local ngx_encode_base64 = ngx.encode_base64 - - local data = {cert = "ssl_cert", key = "ssl_key", sni = "test.com"} - local req_body = core.json.encode(data) - req_body = req_body or "" - - local secret_key = "my-secret-key4" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key4" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "PUT", - "/uri", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, _, body = t.test('/uri', - ngx.HTTP_PUT, - req_body, - nil, - headers - ) - - if code >= 300 then - ngx.status = code - end - - local headers_arr = ngx_re.split(body, "\n") - for i, v in ipairs(headers_arr) do - if i ~= 4 and i ~= 6 then -- skip date and user-agent field - ngx.say(v) - end - end - } -} ---- response_body -uri: /uri -content-length: 52 -content-type: application/x-www-form-urlencoded -host: 127.0.0.1:1984 -x-custom-header-a: asld$%dfasf -x-custom-header-b: 23879fmsldfk -x-hmac-access-key: my-access-key4 -x-real-ip: 127.0.0.1 - - - -=== TEST 6: keep_headers field is true ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "james", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key4", - "secret_key": "my-secret-key4", - "keep_headers": true - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 7: verify pass(keep_headers field is true), keep http request header ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_re = require("ngx.re") - local ngx_encode_base64 = ngx.encode_base64 - - local data = {cert = "ssl_cert", key = "ssl_key", sni = "test.com"} - local req_body = core.json.encode(data) - req_body = req_body or "" - - local secret_key = "my-secret-key4" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key4" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "PUT", - "/uri", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, _, body = t.test('/uri', - ngx.HTTP_PUT, - req_body, - nil, - headers - ) - - if code >= 300 then - ngx.status = code - end - - local headers_arr = ngx_re.split(body, "\n") - for i, v in ipairs(headers_arr) do - if i ~= 4 and i ~= 6 and i ~= 11 then -- skip date, user-agent and x-hmac-signature field - ngx.say(v) - end - end - } -} ---- response_body -uri: /uri -content-length: 52 -content-type: application/x-www-form-urlencoded -host: 127.0.0.1:1984 -x-custom-header-a: asld$%dfasf -x-custom-header-b: 23879fmsldfk -x-hmac-access-key: my-access-key4 -x-hmac-algorithm: hmac-sha256 -x-hmac-signed-headers: x-custom-header-a;x-custom-header-b -x-real-ip: 127.0.0.1 - - - -=== TEST 8: get the default schema +=== TEST 2: get the default schema --- config location /t { content_by_lua_block { @@ -396,7 +74,7 @@ x-real-ip: 127.0.0.1 ngx.HTTP_GET, nil, [[ -{"properties":{},"title":"work with route or service object","type":"object"} +{"type":"object","$comment":"this is a mark for our injected plugin schema","title":"work with route or service object","properties":{"allowed_algorithms":{"type":"array","default":["hmac-sha1","hmac-sha256","hmac-sha512"],"items":{"type":"string","enum":["hmac-sha1","hmac-sha256","hmac-sha512"]},"minItems":1},"_meta":{"type":"object","properties":{"filter":{"description":"filter determines whether the plugin needs to be executed at runtime","type":"array"},"error_response":{"oneOf":[{"type":"string"},{"type":"object"}]},"disable":{"type":"boolean"},"priority":{"description":"priority of plugins by customized order","type":"integer"}}},"clock_skew":{"type":"integer","default":300,"minimum":1},"signed_headers":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50}},"hide_credentials":{"type":"boolean","default":false},"validate_request_body":{"type":"boolean","default":false,"title":"A boolean value telling the plugin to enable body validation"}}} ]] ) ngx.status = code @@ -405,7 +83,7 @@ x-real-ip: 127.0.0.1 -=== TEST 9: get the schema by schema_type +=== TEST 3: get the schema by schema_type --- config location /t { content_by_lua_block { @@ -414,7 +92,7 @@ x-real-ip: 127.0.0.1 ngx.HTTP_GET, nil, [[ -{"title":"work with consumer object","required":["access_key","secret_key"],"properties":{"clock_skew":{"default":0,"type":"integer"},"encode_uri_params":{"title":"Whether to escape the uri parameter","default":true,"type":"boolean"},"keep_headers":{"title":"whether to keep the http request header","default":false,"type":"boolean"},"secret_key":{"minLength":1,"maxLength":256,"type":"string"},"algorithm":{"type":"string","default":"hmac-sha256","enum":["hmac-sha1","hmac-sha256","hmac-sha512"]},"signed_headers":{"items":{"minLength":1,"maxLength":50,"type":"string"},"type":"array"},"access_key":{"minLength":1,"maxLength":256,"type":"string"}},"type":"object"} +{"title":"work with consumer object","required":["key_id","secret_key"],"properties":{"secret_key":{"minLength":1,"maxLength":256,"type":"string"},"key_id":{"minLength":1,"maxLength":256,"type":"string"}},"type":"object"} ]] ) ngx.status = code @@ -423,7 +101,7 @@ x-real-ip: 127.0.0.1 -=== TEST 10: get the schema by error schema_type +=== TEST 4: get the schema by error schema_type --- config location /t { content_by_lua_block { @@ -441,7 +119,7 @@ x-real-ip: 127.0.0.1 -=== TEST 11: enable hmac auth plugin using admin api +=== TEST 5: enable hmac auth plugin using admin api --- config location /t { content_by_lua_block { @@ -470,417 +148,3 @@ x-real-ip: 127.0.0.1 } --- response_body passed - - - -=== TEST 12: encode_uri_params field is true, the signature of uri enables escaping ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "james", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key6", - "secret_key": "my-secret-key6" - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 13: verify: invalid signature (Lowercase letters of escape characters are converted to uppercase.) ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key6" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key6" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "name=LeBron%2Cjames&name2=%2c%3e", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2c%3e', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Invalid signature ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ ---- error_log eval -qr/name=LeBron\%2Cjames\&name2=\%2C\%3E/ - - - -=== TEST 14: verify: ok (The letters in the escape character are all uppercase.) ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key6" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key6" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "name=LeBron%2Cjames&name2=%2C%3E", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2C%3E', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- response_body -passed ---- no_error_log - - - -=== TEST 15: encode_uri_params field is false, uri’s signature is enabled for escaping ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "james", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key6", - "secret_key": "my-secret-key6", - "encode_uri_params": false - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 16: verify: invalid signature (uri’s signature is enabled for escaping) ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key6" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key6" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "name=LeBron%2Cjames&name2=%2c%3e", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2c%3e', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Invalid signature ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ - - - -=== TEST 17: verify: ok ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key6" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key6" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "name=LeBron,james&name2=,>", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2c%3e', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- response_body -passed - - - -=== TEST 18: verify: ok, the request parameter is missing `=`. ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key6" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key6" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "age=&name=jack", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello?name=jack&age', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- response_body -passed - - - -=== TEST 19: verify: ok, the value of the request parameter is true. ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key6" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key6" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local signing_string = { - "GET", - "/hello", - "age=true&name=jack", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello?name=jack&age=true', - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- response_body -passed diff --git a/t/plugin/hmac-auth3.t b/t/plugin/hmac-auth3.t index 70c5048c89dd..8c09079f3128 100644 --- a/t/plugin/hmac-auth3.t +++ b/t/plugin/hmac-auth3.t @@ -49,9 +49,8 @@ __DATA__ "username": "robin", "plugins": { "hmac-auth": { - "access_key": "my-access-key", - "secret_key": "my-secret-key", - "validate_request_body": true + "key_id": "my-access-key", + "secret_key": "my-secret-key" } } }]] @@ -76,7 +75,9 @@ passed ngx.HTTP_PUT, [[{ "plugins": { - "hmac-auth": {} + "hmac-auth": { + "validate_request_body": true + } }, "upstream": { "nodes": { @@ -113,19 +114,17 @@ passed local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local body = "{\"name\": \"world\"}" local signing_string = { - "POST", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "POST /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) @@ -134,11 +133,8 @@ passed core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -177,19 +173,17 @@ qr/\{"message":"client request can't be validated"\}/ local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local body = "{\"name\": \"world\"}" local signing_string = { - "POST", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "POST /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) @@ -198,12 +192,9 @@ qr/\{"message":"client request can't be validated"\}/ core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-DIGEST"] = "hello" - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] ="Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" + headers["Digest"] = "hello" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -242,264 +233,37 @@ qr/\{"message":"client request can't be validated"\}/ local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local body = "{\"name\": \"world\"}" local signing_string = { - "POST", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - local body_digest = hmac:new(secret_key, hmac.ALGOS.SHA256):final(body) - - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-DIGEST"] = ngx_encode_base64(body_digest) - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello', - ngx.HTTP_POST, - body, - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 6: add consumer with max_req_body ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "robin", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key", - "secret_key": "my-secret-key", - "validate_request_body": true, - "max_req_body": 1024 - } - } - }]] - ) - - ngx.status = code - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 7: Exceed body limit size ---- config - location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - local body = ("-1Aa#"):rep(205) - - local signing_string = { - "POST", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "POST /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - local body_digest = hmac:new(secret_key, hmac.ALGOS.SHA256):final(body) - - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-DIGEST"] = ngx_encode_base64(body_digest) - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello', - ngx.HTTP_POST, - body, - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } - } ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Exceed body limit size ---- response_body eval -qr/\{"message":"client request can't be validated"}/ - - - -=== TEST 8: Test custom request body digest header name with mismatched header. ---- yaml_config -plugin_attr: - hmac-auth: - body_digest_key: "X-Digest-Custom" ---- config - location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") + local ngx_encode_base64 = ngx.encode_base64 - local secret_key = "my-secret-key" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - local body = "{\"name\": \"world\"}" - - local signing_string = { - "POST", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - local body_digest = hmac:new(secret_key, hmac.ALGOS.SHA256):final(body) + local resty_sha256 = require("resty.sha256") + local hash = resty_sha256:new() + hash:update(body) + local digest = hash:final() + local body_digest = ngx_encode_base64(digest) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" + headers["Date"] = gmt - headers["X-HMAC-DIGEST"] = ngx_encode_base64(body_digest) - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello', - ngx.HTTP_POST, - body, - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } - } ---- error_code: 401 ---- grep_error_log eval -qr/client request can't be validated: [^,]+/ ---- grep_error_log_out -client request can't be validated: Invalid digest ---- response_body eval -qr/\{"message":"client request can't be validated"\}/ - - - -=== TEST 9: Test custom request body digest header name. ---- yaml_config -plugin_attr: - hmac-auth: - body_digest_key: "X-Digest-Custom" ---- config - location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - local body = "{\"name\": \"world\"}" - - local signing_string = { - "POST", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - local body_digest = hmac:new(secret_key, hmac.ALGOS.SHA256):final(body) - - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-Digest-Custom"] = ngx_encode_base64(body_digest) - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Digest"] = "SHA-256=" .. body_digest + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -516,242 +280,3 @@ plugin_attr: } --- response_body passed - - - -=== TEST 10: Test sort table param. ---- config - location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - local body = "{\"name\": \"world\"}" - - local signing_string = { - "POST", - "/hello", - "a=&a=1&a=2&a1a=123&c=&name=123", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - local body_digest = hmac:new(secret_key, hmac.ALGOS.SHA256):final(body) - - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-DIGEST"] = ngx_encode_base64(body_digest) - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello?c=&a1a=123&name=123&a&a=2&a=1', - ngx.HTTP_POST, - body, - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 11: update consumer ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "robin", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key", - "secret_key": "my-secret-key", - "clock_skew": 10 - } - } - }]] - ) - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 12: verify that uri args are greater than 100 is ok ---- config -location /t { - content_by_lua_block { - local ngx_time = ngx.time - local ngx_http_time = ngx.http_time - local core = require("apisix.core") - local t = require("lib.test_admin") - local hmac = require("resty.hmac") - local ngx_encode_base64 = ngx.encode_base64 - - local secret_key = "my-secret-key" - local timestamp = ngx_time() - local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" - local custom_header_a = "asld$%dfasf" - local custom_header_b = "23879fmsldfk" - - local uri_args = {} - for i = 1, 101 do - uri_args["arg_" .. tostring(i)] = "val_" .. tostring(i) - end - local keys = {} - local query_tab = {} - - for k, v in pairs(uri_args) do - core.table.insert(keys, k) - end - core.table.sort(keys) - - local args_str = "" - for _, key in pairs(keys) do - args_str = args_str .. key .. "=" .. uri_args[key] .. "&" - end - -- remove the last '&' - args_str = args_str:sub(1, -2) - - local signing_string = { - "GET", - "/hello", - args_str, - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b - } - signing_string = core.table.concat(signing_string, "\n") .. "\n" - core.log.info("signing_string:", signing_string) - - local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) - core.log.info("signature:", ngx_encode_base64(signature)) - local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" - headers["x-custom-header-a"] = custom_header_a - headers["x-custom-header-b"] = custom_header_b - - local code, body = t.test('/hello' .. '?' .. args_str, - ngx.HTTP_GET, - "", - nil, - headers - ) - - ngx.status = code - ngx.say(body) - } -} ---- response_body -passed - - - -=== TEST 13: delete exist consumers ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - -- delete exist consumers - local code, body = t('/apisix/admin/consumers/robin', ngx.HTTP_DELETE) - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 14: data encryption for secret_key ---- yaml_config -apisix: - data_encryption: - enable_encrypt_fields: true - keyring: - - edd1c9f0985e76a2 ---- config - location /t { - content_by_lua_block { - local json = require("toolkit.json") - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "jack", - "plugins": { - "hmac-auth": { - "access_key": "my-access-key", - "secret_key": "my-secret-key", - "clock_skew": 10 - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - ngx.say(body) - return - end - ngx.sleep(0.1) - - -- get plugin conf from admin api, password is decrypted - local code, message, res = t('/apisix/admin/consumers/jack', - ngx.HTTP_GET - ) - res = json.decode(res) - if code >= 300 then - ngx.status = code - ngx.say(message) - return - end - - ngx.say(res.value.plugins["hmac-auth"].secret_key) - - -- get plugin conf from etcd, password is encrypted - local etcd = require("apisix.core.etcd") - local res = assert(etcd.get('/consumers/jack')) - ngx.say(res.body.node.value.plugins["hmac-auth"].secret_key) - } - } ---- response_body -my-secret-key -IRWpPjbDq5BCgHyIllnOMA== diff --git a/t/plugin/hmac-auth4.t b/t/plugin/hmac-auth4.t index 78b89b58c7ce..1bdd4707a492 100644 --- a/t/plugin/hmac-auth4.t +++ b/t/plugin/hmac-auth4.t @@ -68,7 +68,7 @@ __DATA__ "username": "jack", "plugins": { "hmac-auth": { - "access_key": "my-access-key", + "key_id": "my-access-key", "secret_key": "$secret://vault/test1/jack/secret_key" } } @@ -129,18 +129,16 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) @@ -148,11 +146,8 @@ location /t { local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b @@ -197,7 +192,7 @@ passed "username": "jack", "plugins": { "hmac-auth": { - "access_key": "my-access-key", + "key_id": "my-access-key", "secret_key": "$secret://vault/test1/jack/secret_key" } } @@ -248,18 +243,16 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() local gmt = ngx_http_time(timestamp) - local access_key = "my-access-key" + local key_id = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = { - "GET", - "/hello", - "", - access_key, - gmt, - "x-custom-header-a:" .. custom_header_a, - "x-custom-header-b:" .. custom_header_b + key_id, + "GET /hello", + "date: " .. gmt, + "x-custom-header-a: " .. custom_header_a, + "x-custom-header-b: " .. custom_header_b } signing_string = core.table.concat(signing_string, "\n") .. "\n" core.log.info("signing_string:", signing_string) @@ -267,11 +260,8 @@ location /t { local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} - headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) - headers["X-HMAC-ALGORITHM"] = "hmac-sha256" headers["Date"] = gmt - headers["X-HMAC-ACCESS-KEY"] = access_key - headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" + headers["Authorization"] = "Signature keyId=\"" .. key_id .. "\",algorithm=\"hmac-sha256\"" .. ",headers=\"@request-target date x-custom-header-a x-custom-header-b\",signature=\"" .. ngx_encode_base64(signature) .. "\"" headers["x-custom-header-a"] = custom_header_a headers["x-custom-header-b"] = custom_header_b