Skip to content

Commit

Permalink
fix(aws-lambda): aws lambda service cache by service related fields (#…
Browse files Browse the repository at this point in the history
…11805)

Cache the aws lambda service by composing a cache key using the service related fields, so that service object can be reused between plugins and vault refresh can take effect when key/secret is rotated

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

* tests(aws-lambda): add test for checking service cache refresh when vault rotates

* style(*): lint

Fix KAG-2832
  • Loading branch information
windmgc committed Oct 24, 2023
1 parent e29e0d3 commit d775467
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 4 deletions.
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" }

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 },
},
},
},
},
}

0 comments on commit d775467

Please sign in to comment.