Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
98a6475
feat(basic-auth): Add option to use {vault://} in username and passwo…
lordgreg Oct 13, 2025
e2064f6
feat(hmac-auth): Add option to use {vault://} in username and secret …
lordgreg Oct 13, 2025
5ef5005
feat(jwt): Add option to use {vault://} in secret field
lordgreg Oct 13, 2025
7df0b8a
feat(oauth2): Add option to use {vault://} in client_id, client_secre…
lordgreg Oct 13, 2025
7bd8bb3
feat(request-transformer): Add option to use {vault://} in fields
lordgreg Oct 13, 2025
a852681
feat(response-transformer): Add option to use {vault://} in fields
lordgreg Oct 13, 2025
c05fb9b
docs(changelog): Add changelog for vault template support
lordgreg Oct 13, 2025
6d52709
feat(vault): Add tests for vault integration in basic-auth plugin
lordgreg Oct 16, 2025
59e6de3
feat(vault): Add tests for vault integration in response-transformer …
lordgreg Oct 16, 2025
89a263c
feat(vault): Add tests for vault integration in JWT plugin
lordgreg Oct 16, 2025
f3185d8
feat(vault): Add tests for vault integration in hmac-auth plugin
lordgreg Oct 16, 2025
9f0e84c
feat(vault): Add tests for vault integration in request-transformer p…
lordgreg Oct 16, 2025
2c83103
feat(vault): Add tests for vault integration in oauth2 plugin
lordgreg Oct 16, 2025
8be60d5
fix(changelog): Fix filename for changelog
lordgreg Oct 16, 2025
33e9a90
fix(changelog): Fix typo in changelog file
lordgreg Oct 16, 2025
eebdbf1
Merge branch 'master' into master
lordgreg Oct 24, 2025
a2a110f
Merge branch 'master' into master
lordgreg Oct 28, 2025
6bdaab0
fix(test): Update linting error
lordgreg Nov 4, 2025
36828d1
Merge branch 'master' of https://github.com/lordgreg/kong
lordgreg Nov 4, 2025
cd8a648
Merge branch 'master' into master
lordgreg Nov 28, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: Added an option to use {vault://} in specific fields of plugins
type: feature
scope: Plugin
4 changes: 2 additions & 2 deletions kong/plugins/basic-auth/daos.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ return {
{ id = typedefs.uuid },
{ created_at = typedefs.auto_timestamp_s },
{ consumer = { type = "foreign", reference = "consumers", required = true, on_delete = "cascade" }, },
{ username = { type = "string", required = true, unique = true }, },
{ password = { type = "string", required = true, encrypted = true }, }, -- encrypted = true is a Kong Enterprise Exclusive feature, it does nothing in Kong CE
{ username = { type = "string", required = true, unique = true, referenceable = true }, },
{ password = { type = "string", required = true, encrypted = true, referenceable = true }, }, -- encrypted = true is a Kong Enterprise Exclusive feature, it does nothing in Kong CE
{ tags = typedefs.tags },
},
transformations = {
Expand Down
4 changes: 2 additions & 2 deletions kong/plugins/hmac-auth/daos.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ return {
{ id = typedefs.uuid },
{ created_at = typedefs.auto_timestamp_s },
{ consumer = { type = "foreign", reference = "consumers", required = true, on_delete = "cascade", }, },
{ username = { type = "string", required = true, unique = true }, },
{ secret = { type = "string", auto = true }, },
{ username = { type = "string", required = true, unique = true, referenceable = true }, },
{ secret = { type = "string", auto = true, referenceable = true }, },
{ tags = typedefs.tags },
},
},
Expand Down
2 changes: 1 addition & 1 deletion kong/plugins/jwt/daos.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ return {
{ created_at = typedefs.auto_timestamp_s },
{ consumer = { type = "foreign", reference = "consumers", required = true, on_delete = "cascade", }, },
{ key = { type = "string", required = false, unique = true, auto = true }, },
{ secret = { type = "string", auto = true }, },
{ secret = { type = "string", auto = true, referenceable = true }, },
{ rsa_public_key = { type = "string" }, },
{ algorithm = {
type = "string",
Expand Down
6 changes: 3 additions & 3 deletions kong/plugins/oauth2/daos.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ local oauth2_credentials = {
{ created_at = typedefs.auto_timestamp_s },
{ consumer = { type = "foreign", reference = "consumers", required = true, on_delete = "cascade", }, },
{ name = { type = "string", required = true }, },
{ client_id = { type = "string", required = false, unique = true, auto = true }, },
{ client_secret = { type = "string", required = false, auto = true, encrypted = true }, }, -- encrypted = true is a Kong Enterprise Exclusive feature. It does nothing in Kong CE
{ hash_secret = { type = "boolean", required = true, default = false }, },
{ client_id = { type = "string", required = false, unique = true, auto = true, referenceable = true }, },
{ client_secret = { type = "string", required = false, auto = true, encrypted = true, referenceable = true }, }, -- encrypted = true is a Kong Enterprise Exclusive feature. It does nothing in Kong CE
{ hash_secret = { type = "boolean", required = true, default = false, referenceable = true }, },
{ redirect_uris = {
type = "array",
required = false,
Expand Down
4 changes: 4 additions & 0 deletions kong/plugins/request-transformer/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ local strings_array = {
type = "array",
default = {},
required = true,
referenceable = true,
elements = { type = "string" },
}

Expand All @@ -58,6 +59,7 @@ local headers_array = {
type = "array",
default = {},
required = true,
referenceable = true,
elements = { type = "string", custom_validator = validate_headers },
}

Expand All @@ -76,6 +78,7 @@ local colon_strings_array = {
type = "array",
default = {},
required = true,
referenceable = true,
elements = { type = "string", custom_validator = check_for_value }
}

Expand All @@ -102,6 +105,7 @@ local colon_headers_array = {
type = "array",
default = {},
required = true,
referenceable = true,
elements = { type = "string", match = "^[^:]+:.*$", custom_validator = validate_colon_headers },
}

Expand Down
4 changes: 4 additions & 0 deletions kong/plugins/response-transformer/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ local string_array = {
type = "array",
default = {},
required = true,
referenceable = true,
elements = { type = "string" },
}

Expand All @@ -33,6 +34,7 @@ local colon_string_array = {
type = "array",
default = {},
required = true,
referenceable = true,
elements = { type = "string", match = "^[^:]+:.*$" },
}

Expand All @@ -53,6 +55,7 @@ local colon_string_record = {
{ json_types = { description = "List of JSON type names. Specify the types of the JSON values returned when appending\nJSON properties. Each string element can be one of: boolean, number, or string.", type = "array",
default = {},
required = true,
referenceable = true,
elements = {
type = "string",
one_of = { "boolean", "number", "string" }
Expand All @@ -66,6 +69,7 @@ local colon_headers_array = {
type = "array",
default = {},
required = true,
referenceable = true,
elements = { type = "string", match = "^[^:]+:.*$", custom_validator = validate_colon_headers },
}

Expand Down
201 changes: 201 additions & 0 deletions spec/03-plugins/10-basic-auth/06-vault_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
local helpers = require("spec.helpers")
local conf_loader = require("kong.conf_loader")

describe("basic-auth: (vault integration)", function()
local get

before_each(function()
local conf = assert(conf_loader(nil, {
vaults = "bundled",
}))

local kong_global = require("kong.global")
_G.kong = kong_global.new()
kong_global.init_pdk(kong, conf)

get = _G.kong.vault.get
end)

describe("vault reference resolution", function()
it("should handle all variations of variable name", function()
local env_name = "MY_VAR_NAME"
local env_value = "complex_value_789"

finally(function()
helpers.unsetenv(env_name)
end)

helpers.setenv(env_name, env_value)

assert.equal(env_value, get("{vault://env/MY_VAR_NAME}"))
assert.equal(env_value, get("{vault://env/MY-VAR-NAME}"))
assert.equal(env_value, get("{vault://env/my_var_name}"))
assert.equal(env_value, get("{vault://env/my-var-name}"))
assert.equal(env_value, get("{vault://env/My_Var_Name}"))
assert.equal(env_value, get("{vault://env/My-Var-Name}"))
end)

it("should handle vault reference with different environment variable name", function()
local env_name = "BASIC_AUTH_SECRET"
local env_value = "another_secret_456"

finally(function()
helpers.unsetenv(env_name)
end)

helpers.setenv(env_name, env_value)

local res, err = get("{vault://env/basic_auth_secret}")
assert.is_nil(err)
assert.equal(env_value, res)
end)

it("should handle vault reference with JSON secret", function()
local env_name = "TEST_JSON_SECRETS"
local env_value = '{"username": "json_user", "password": "db_secret_789"}'

finally(function()
helpers.unsetenv(env_name)
end)

helpers.setenv(env_name, env_value)

local res, err = get("{vault://env/test_json_secrets/password}")
assert.is_nil(err)
assert.equal("db_secret_789", res)
end)

it("should fail gracefully when environment variable does not exist", function()
helpers.unsetenv("NON_EXISTENT_VAR")

local res, err = get("{vault://env/non_existent_var}")
assert.matches("could not get value from external vault", err)
assert.is_nil(res)
end)

it("should handle vault reference with prefix", function()
local env_name = "TEST_PASSWORD"
local env_value = "prefixed_secret"

finally(function()
helpers.unsetenv(env_name)
end)

helpers.setenv(env_name, env_value)

local res, err = get("{vault://env/password?prefix=test_}")
assert.is_nil(err)
assert.equal(env_value, res)
end)

it("should work with empty environment variable value", function()
local env_name = "EMPTY_PASSWORD"

finally(function()
helpers.unsetenv(env_name)
end)

helpers.setenv(env_name, "")

local res, err = get("{vault://env/empty_password}")
assert.is_nil(err)
assert.equal("", res)
end)
end)

describe("username field vault references", function()
it("should handle both username and password as vault references", function()
finally(function()
helpers.unsetenv("AUTH_USERNAME")
helpers.unsetenv("AUTH_PASSWORD")
end)

helpers.setenv("AUTH_USERNAME", "vault_user_both")
helpers.setenv("AUTH_PASSWORD", "vault_pass_both")

local username_res, username_err = get("{vault://env/auth_username}")
local password_res, password_err = get("{vault://env/auth_password}")

assert.is_nil(username_err)
assert.is_nil(password_err)
assert.equal("vault_user_both", username_res)
assert.equal("vault_pass_both", password_res)
end)
end)

describe("edge cases and validation", function()
it("should handle malformed vault references gracefully", function()
-- Test various malformed vault references
local malformed_refs = {
"{vault://invalid/format",
"vault://env/missing_braces}",
"{vault://env/}",
"{vault://env}",
"{vault://}",
"{vault://env/valid_name?invalid_query=",
}

for _, ref in ipairs(malformed_refs) do
local res, err = get(ref)
-- Should either return nil with error, or return the original string unchanged
if res then
assert.equal(ref, res, "Malformed reference should be returned unchanged: " .. ref)
else
assert.is_string(err, "Should have error message for malformed reference: " .. ref)
end
end
end)

it("should preserve non-vault values unchanged", function()
local regular_value = "regular_password"

local res, err = get(regular_value)
if res then
assert.equal(regular_value, res)
else
assert.is_nil(err)
end
end)

it("should work with special characters in environment variable names", function()
local env_name = "SPECIAL_CHARS_(1337@)"
local env_value = "special_value"

finally(function()
helpers.unsetenv(env_name)
end)

helpers.setenv(env_name, env_value)

assert.equal(env_value, get("{vault://env/SPECIAL_CHARS_(1337@)}"))
assert.equal(env_value, get("{vault://env/SPECIAL-CHARS_(1337@)}"))
assert.equal(env_value, get("{vault://env/special-chars_(1337@)}"))
assert.equal(env_value, get("{vault://env/special_chars_(1337@)}"))
end)
end)

describe("integration with basic-auth plugin", function()
it("should demonstrate vault usage in basic-auth context", function()
local password_env = "BASIC_AUTH_PASSWORD"
local username_env = "BASIC_AUTH_USERNAME"

finally(function()
helpers.unsetenv(password_env)
helpers.unsetenv(username_env)
end)

helpers.setenv(password_env, "secure_password_123")
helpers.setenv(username_env, "secure_username")

-- Simulate how basic-auth would resolve vault references
local resolved_password, pass_err = get("{vault://env/basic_auth_password}")
local resolved_username, user_err = get("{vault://env/basic_auth_username}")

assert.is_nil(pass_err)
assert.is_nil(user_err)
assert.equal("secure_password_123", resolved_password)
assert.equal("secure_username", resolved_username)
end)
end)
end)

Loading