-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(vault): resurrect positive results in lru cache for ttl + resurre…
…ct ttl (#11815) ### Summary The vault is rotating secrets on every minute which updates the shared dictionary cache with new values, both negative and positive results. This commit changes the Negative results handling on LRU. Previously the LRU was cleared for negative results, and we just used to cache for config.ttl amount of time. This commit changes it so that LRU values are deleted, and we cache things config.ttl + config.resurrect_ttl amount of time in lru cache too. It was reported by @Hayk-S on KAG-2833. Signed-off-by: Aapo Talvensaari <[email protected]> (cherry picked from commit 1b6c394)
- Loading branch information
Showing
5 changed files
with
271 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
message: Vault resurrect time is respected in case a vault secret is deleted from a vault | ||
type: bugfix | ||
scope: Core |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
local helpers = require "spec.helpers" | ||
|
||
-- using the full path so that we don't have to modify package.path in | ||
-- this context | ||
local test_vault = require "spec.fixtures.custom_vaults.kong.vaults.test" | ||
|
||
local CUSTOM_VAULTS = "./spec/fixtures/custom_vaults" | ||
local CUSTOM_PLUGINS = "./spec/fixtures/custom_plugins" | ||
|
||
local LUA_PATH = CUSTOM_VAULTS .. "/?.lua;" .. | ||
CUSTOM_VAULTS .. "/?/init.lua;" .. | ||
CUSTOM_PLUGINS .. "/?.lua;" .. | ||
CUSTOM_PLUGINS .. "/?/init.lua;;" | ||
|
||
local DUMMY_HEADER = "Dummy-Plugin" | ||
local fmt = string.format | ||
|
||
|
||
|
||
--- A vault test harness is a driver for vault backends, which implements | ||
--- all the necessary glue for initializing a vault backend and performing | ||
--- secret read/write operations. | ||
--- | ||
--- All functions defined here are called as "methods" (e.g. harness:fn()), so | ||
--- it is permitted to keep state on the harness object (self). | ||
--- | ||
---@class vault_test_harness | ||
--- | ||
---@field name string | ||
--- | ||
--- this table is passed directly to kong.db.vaults:insert() | ||
---@field config table | ||
--- | ||
--- create_secret() is called once per test run for a given secret | ||
---@field create_secret fun(self: vault_test_harness, secret: string, value: string, opts?: table) | ||
--- | ||
--- update_secret() may be called more than once per test run for a given secret | ||
---@field update_secret fun(self: vault_test_harness, secret: string, value: string, opts?: table) | ||
--- | ||
--- setup() is called before kong is started and before any DB entities | ||
--- have been created and is best used for things like validating backend | ||
--- credentials and establishing a connection to a backend | ||
---@field setup fun(self: vault_test_harness) | ||
--- | ||
--- teardown() is exactly what you'd expect | ||
---@field teardown fun(self: vault_test_harness) | ||
--- | ||
--- fixtures() output is passed directly to `helpers.start_kong()` | ||
---@field fixtures fun(self: vault_test_harness):table|nil | ||
--- | ||
--- | ||
---@field prefix string # generated by the test suite | ||
---@field host string # generated by the test suite | ||
|
||
|
||
---@type vault_test_harness[] | ||
local VAULTS = { | ||
{ | ||
name = "test", | ||
|
||
config = { | ||
default_value = "DEFAULT", | ||
default_value_ttl = 1, | ||
}, | ||
|
||
create_secret = function(self, _, value) | ||
-- Currently, create_secret is called _before_ starting Kong. | ||
-- | ||
-- This means our backend won't be available yet because it is | ||
-- piggy-backing on Kong as an HTTP mock fixture. | ||
-- | ||
-- We can, however, inject a default value into our configuration. | ||
self.config.default_value = value | ||
end, | ||
|
||
update_secret = function(_, secret, value, opts) | ||
return test_vault.client.put(secret, value, opts) | ||
end, | ||
|
||
delete_secret = function(_, secret) | ||
return test_vault.client.delete(secret) | ||
end, | ||
|
||
fixtures = function() | ||
return { | ||
http_mock = { | ||
test_vault = test_vault.http_mock, | ||
} | ||
} | ||
end, | ||
}, | ||
} | ||
|
||
|
||
local noop = function(...) end | ||
|
||
for _, vault in ipairs(VAULTS) do | ||
-- fill out some values that we'll use in route/service/plugin config | ||
vault.prefix = vault.name .. "-ttl-test" | ||
vault.host = vault.name .. ".vault-ttl.test" | ||
|
||
-- ...and fill out non-required methods | ||
vault.setup = vault.setup or noop | ||
vault.teardown = vault.teardown or noop | ||
vault.fixtures = vault.fixtures or noop | ||
end | ||
|
||
|
||
for _, strategy in helpers.each_strategy() do | ||
for _, vault in ipairs(VAULTS) do | ||
|
||
|
||
describe("vault resurrect_ttl and rotation (#" .. strategy .. ") #" .. vault.name, function() | ||
local client | ||
local secret = "my-secret" | ||
|
||
|
||
local function http_get(path) | ||
path = path or "/" | ||
|
||
local res = client:get(path, { | ||
headers = { | ||
host = assert(vault.host), | ||
}, | ||
}) | ||
|
||
assert.response(res).has.status(200) | ||
|
||
return res | ||
end | ||
|
||
|
||
lazy_setup(function() | ||
helpers.setenv("KONG_LUA_PATH_OVERRIDE", LUA_PATH) | ||
helpers.setenv("KONG_VAULT_ROTATION_INTERVAL", "1") | ||
|
||
helpers.test_conf.loaded_plugins = { | ||
dummy = true, | ||
} | ||
|
||
vault:setup() | ||
vault:create_secret(secret, "init") | ||
|
||
local bp = helpers.get_db_utils(strategy, | ||
{ "vaults", "routes", "services", "plugins" }, | ||
{ "dummy" }, | ||
{ vault.name }) | ||
|
||
|
||
assert(bp.vaults:insert({ | ||
name = vault.name, | ||
prefix = vault.prefix, | ||
config = vault.config, | ||
})) | ||
|
||
local route = assert(bp.routes:insert({ | ||
name = vault.host, | ||
hosts = { vault.host }, | ||
paths = { "/" }, | ||
service = assert(bp.services:insert()), | ||
})) | ||
|
||
|
||
-- used by the plugin config test case | ||
assert(bp.plugins:insert({ | ||
name = "dummy", | ||
config = { | ||
resp_header_value = fmt("{vault://%s/%s?ttl=%d&resurrect_ttl=%d}", | ||
vault.prefix, secret, 2, 2), | ||
}, | ||
route = { id = route.id }, | ||
})) | ||
|
||
assert(helpers.start_kong({ | ||
database = strategy, | ||
nginx_conf = "spec/fixtures/custom_nginx.template", | ||
vaults = vault.name, | ||
plugins = "dummy", | ||
log_level = "info", | ||
}, nil, nil, vault:fixtures() )) | ||
|
||
client = helpers.proxy_client() | ||
end) | ||
|
||
|
||
lazy_teardown(function() | ||
if client then | ||
client:close() | ||
end | ||
|
||
helpers.stop_kong(nil, true) | ||
vault:teardown() | ||
|
||
helpers.unsetenv("KONG_LUA_PATH_OVERRIDE") | ||
end) | ||
|
||
|
||
it("resurrects plugin config references when secret is deleted (backend: #" .. vault.name .. ")", function() | ||
local function check_plugin_secret(expect, ttl, leeway) | ||
leeway = leeway or 0.25 -- 25% | ||
|
||
local timeout = ttl + (ttl * leeway) | ||
|
||
assert | ||
.with_timeout(timeout) | ||
.with_step(0.5) | ||
.eventually(function() | ||
local res = http_get("/") | ||
local value | ||
if expect == "" then | ||
value = res.headers[DUMMY_HEADER] or "" | ||
if value == "" then | ||
return true | ||
end | ||
|
||
else | ||
value = assert.response(res).has.header(DUMMY_HEADER) | ||
if value == expect then | ||
return true | ||
end | ||
end | ||
|
||
return nil, { expected = expect, got = value } | ||
end) | ||
.is_truthy("expected plugin secret to be updated to '" .. tostring(expect) .. "' " | ||
.. "within " .. tostring(timeout) .. " seconds") | ||
end | ||
|
||
vault:update_secret(secret, "old", { ttl = 2, resurrect_ttl = 2 }) | ||
check_plugin_secret("old", 5) | ||
vault:delete_secret(secret) | ||
ngx.sleep(2.5) | ||
check_plugin_secret("old", 5) | ||
check_plugin_secret("", 5) | ||
end) | ||
end) | ||
|
||
|
||
end -- each vault backend | ||
end -- each strategy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters