Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(aws-lambda): aws lambda service cache by service related fields #11805

Merged
merged 3 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/aws_lambda_service_cache.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: Cache the AWS lambda service by those lambda service related fields
type: bugfix
scope: Plugin
35 changes: 31 additions & 4 deletions kong/plugins/aws-lambda/handler.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
-- Copyright (C) Kong Inc.

local fmt = string.format
local ngx_var = ngx.var
local ngx_now = ngx.now
local ngx_update_time = ngx.update_time
local md5_bin = ngx.md5_bin
local fmt = string.format
local buffer = require "string.buffer"
local lrucache = require "resty.lrucache"

local kong = kong
local meta = require "kong.meta"
Expand All @@ -22,7 +25,7 @@ local AWS_REGION do
AWS_REGION = os.getenv("AWS_REGION") or os.getenv("AWS_DEFAULT_REGION")
end
local AWS
local LAMBDA_SERVICE_CACHE = setmetatable({}, { __mode = "k" })
local LAMBDA_SERVICE_CACHE


local function get_now()
Expand All @@ -32,11 +35,34 @@ end


local function initialize()
LAMBDA_SERVICE_CACHE = lrucache.new(1000)
AWS_GLOBAL_CONFIG = aws_config.global
AWS = aws()
initialize = nil
end

local build_cache_key do
-- Use AWS Service related config fields to build cache key
-- so that service object can be reused between plugins and
-- vault refresh can take effect when key/secret is rotated
local SERVICE_RELATED_FIELD = { "timeout", "keepalive", "aws_key", "aws_secret",
"aws_assume_role_arn", "aws_role_session_name",
"aws_region", "host", "port", "disable_https",
"proxy_url", "aws_imds_protocol_version" }
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved

build_cache_key = function (conf)
local cache_key_buffer = buffer.new(100):reset()
for _, field in ipairs(SERVICE_RELATED_FIELD) do
local v = conf[field]
if v then
cache_key_buffer:putf("%s=%s;", field, v)
end
end

return md5_bin(cache_key_buffer:get())
end
end


local AWSLambdaHandler = {
PRIORITY = 750,
Expand All @@ -62,7 +88,8 @@ function AWSLambdaHandler:access(conf)
local scheme = conf.disable_https and "http" or "https"
local endpoint = fmt("%s://%s", scheme, host)

local lambda_service = LAMBDA_SERVICE_CACHE[conf]
local cache_key = build_cache_key(conf)
local lambda_service = LAMBDA_SERVICE_CACHE:get(cache_key)
if not lambda_service then
local credentials = AWS.config.credentials
-- Override credential config according to plugin config
Expand Down Expand Up @@ -132,7 +159,7 @@ function AWSLambdaHandler:access(conf)
http_proxy = conf.proxy_url,
https_proxy = conf.proxy_url,
})
LAMBDA_SERVICE_CACHE[conf] = lambda_service
LAMBDA_SERVICE_CACHE:set(cache_key, lambda_service)
end

local upstream_body_json = build_request_payload(conf)
Expand Down
93 changes: 93 additions & 0 deletions spec/03-plugins/27-aws-lambda/99-access_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local fixtures = require "spec.fixtures.aws-lambda"
local TEST_CONF = helpers.test_conf
local server_tokens = meta._SERVER_TOKENS
local null = ngx.null
local fmt = string.format



Expand Down Expand Up @@ -1182,4 +1183,96 @@ for _, strategy in helpers.each_strategy() do
end)
end)
end)

describe("Plugin: AWS Lambda with #vault [#" .. strategy .. "]", function ()
local proxy_client
local admin_client

local ttl_time = 1

lazy_setup(function ()
helpers.setenv("KONG_VAULT_ROTATION_INTERVAL", "1")

local bp = helpers.get_db_utils(strategy, {
"routes",
"services",
"plugins",
"vaults",
}, { "aws-lambda" }, { "random" })

local route1 = bp.routes:insert {
hosts = { "lambda-vault.com" },
}

bp.plugins:insert {
name = "aws-lambda",
route = { id = route1.id },
config = {
port = 10001,
aws_key = fmt("{vault://random/aws_key?ttl=%s&resurrect_ttl=0}", ttl_time),
aws_secret = "aws_secret",
aws_region = "us-east-1",
function_name = "functionEcho",
},
}

assert(helpers.start_kong({
database = strategy,
prefix = helpers.test_conf.prefix,
nginx_conf = "spec/fixtures/custom_nginx.template",
vaults = "random",
plugins = "bundled",
log_level = "error",
}, nil, nil, fixtures))
end)

lazy_teardown(function()
helpers.unsetenv("KONG_VAULT_ROTATION_INTERVAL")

helpers.stop_kong()
end)

before_each(function()
proxy_client = helpers.proxy_client()
admin_client = helpers.admin_client()
end)

after_each(function ()
proxy_client:close()
admin_client:close()
end)

it("lambda service should use latest reference value after Vault ttl", function ()
local res = assert(proxy_client:send {
method = "GET",
path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
headers = {
["Host"] = "lambda-vault.com"
}
})
assert.res_status(200, res)
local body = assert.response(res).has.jsonbody()
local authorization_header = body.headers.authorization
local first_aws_key = string.match(authorization_header, "Credential=(.+)/")

assert.eventually(function()
proxy_client:close()
proxy_client = helpers.proxy_client()

local res = assert(proxy_client:send {
method = "GET",
path = "/get?key1=some_value1&key2=some_value2&key3=some_value3",
headers = {
["Host"] = "lambda-vault.com"
}
})
assert.res_status(200, res)
local body = assert.response(res).has.jsonbody()
local authorization_header = body.headers.authorization
local second_aws_key = string.match(authorization_header, "Credential=(.+)/")

return first_aws_key ~= second_aws_key
end).ignore_exceptions(true).with_timeout(ttl_time * 2).is_truthy()
end)
end)
end
13 changes: 13 additions & 0 deletions spec/fixtures/custom_vaults/kong/vaults/random/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
local utils = require "kong.tools.utils"

local function get(conf, resource, version)
-- Return a random string every time
kong.log.err("get() called")
return utils.random_string()
end


return {
VERSION = "1.0.0",
get = get,
}
19 changes: 19 additions & 0 deletions spec/fixtures/custom_vaults/kong/vaults/random/schema.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
local typedefs = require "kong.db.schema.typedefs"

return {
name = "random",
fields = {
{
config = {
type = "record",
fields = {
{ prefix = { type = "string" } },
{ suffix = { type = "string" } },
{ ttl = typedefs.ttl },
{ neg_ttl = typedefs.ttl },
{ resurrect_ttl = typedefs.ttl },
},
},
},
},
}
Loading