feat(vault): Added an option to use {vault://} in specific fields of plugins#14775
feat(vault): Added an option to use {vault://} in specific fields of plugins#14775lordgreg wants to merge 30 commits intoKong:masterfrom
Conversation
…t and hash_secret fields
|
Will it be possible to access certificates? @lordgreg |
|
Hi @mschonmeier , can you give me an example of configuration or what your use case with certificates is. |
|
I've fix the linting error being thrown by CICD. The other 2 errors arent being thrown because of my code changes. |
There was a problem hiding this comment.
Pull request overview
This PR adds vault reference support ({vault://}) to six authentication and transformation plugins, enabling secure credential and configuration management through Kong's vault system. The implementation marks sensitive fields as referenceable = true in plugin schemas/DAOs and includes comprehensive test coverage for vault integration.
- Added vault reference support to authentication plugins (jwt, basic-auth, hmac-auth, oauth2) for credential fields
- Added vault reference support to transformation plugins (request-transformer, response-transformer) for configuration arrays
- Included comprehensive test suites verifying vault reference resolution for each plugin
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| kong/plugins/basic-auth/daos.lua | Marked username and password fields as referenceable for vault support |
| kong/plugins/hmac-auth/daos.lua | Marked username and secret fields as referenceable for vault support |
| kong/plugins/jwt/daos.lua | Marked secret field as referenceable for vault support |
| kong/plugins/oauth2/daos.lua | Marked client_id, client_secret, and hash_secret fields as referenceable for vault support |
| kong/plugins/request-transformer/schema.lua | Marked string arrays, header arrays, and colon string arrays as referenceable for vault support |
| kong/plugins/response-transformer/schema.lua | Marked string arrays, colon string arrays, json_types, and header arrays as referenceable for vault support |
| spec/03-plugins/10-basic-auth/06-vault_spec.lua | Added comprehensive vault integration tests for basic-auth plugin |
| spec/03-plugins/16-jwt/06-vault_spec.lua | Added comprehensive vault integration tests for jwt plugin |
| spec/03-plugins/19-hmac-auth/06-vault_spec.lua | Added comprehensive vault integration tests for hmac-auth plugin |
| spec/03-plugins/25-oauth2/06-vault_spec.lua | Added comprehensive vault integration tests for oauth2 plugin |
| spec/03-plugins/36-request-transformer/06-vault_spec.lua | Added comprehensive vault integration tests for request-transformer plugin |
| spec/03-plugins/15-response-transformer/06-vault_spec.lua | Added comprehensive vault integration tests for response-transformer plugin |
| changelog/unreleased/kong/feat-add-vault-template-support-in-different-plugins.yml | Added changelog entry documenting the feature addition |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| 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) |
There was a problem hiding this comment.
Inconsistent test structure: This test "should work with special characters in environment variable names" is unique to basic-auth and doesn't appear in the edge cases sections of other plugin vault spec files. For consistency, either this test should be removed or the test approach should be harmonized across all plugin vault spec files.
| 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) |
| { 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 }, }, |
There was a problem hiding this comment.
Marking the HMAC credential secret field as referenceable means this value will be resolved via kong.vault.get on the data plane. If the vault reference cannot be resolved (for example, a missing or mis-typed environment variable), resolve_reference in kong.db.schema.init replaces it with an empty string, so hmac-auth will happily verify signatures using a known empty key, allowing an attacker who guesses the username to forge valid HMAC signatures. This field should fail closed on vault resolution errors (e.g., reject the credential or authentication) instead of silently falling back to an empty secret.
| { secret = { type = "string", auto = true, referenceable = true }, }, | |
| { secret = { type = "string", auto = true }, }, |
| { 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 }, }, |
There was a problem hiding this comment.
Making the JWT credential secret field referenceable causes it to be dereferenced via kong.vault.get at select time, and on failure resolve_reference replaces the value with an empty string. Because the JWT plugin treats any non-nil jwt_secret.secret as a valid key and passes it directly into jwt:verify_signature, an unresolved vault reference would downgrade the shared secret to an empty string, enabling trivial forgery of JWTs for that key if the vault reference is misconfigured. Vault resolution failures for this field should be treated as fatal (e.g., deny authentication or disable the credential) rather than defaulting to an empty secret.
| { secret = { type = "string", auto = true, referenceable = true }, }, | |
| { secret = { type = "string", auto = true }, }, |
There was a problem hiding this comment.
I don't see this as a valid point. The fields from consumer should be referenceable using the vault semantics. If its not referenceable (empty), then the user hasn't follow the guidelines. This is exactly the same as if user would add EMPTY secret directly into consumer part. Please advise @raoxiaoyan
| { 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 |
There was a problem hiding this comment.
By marking oauth2_credentials.client_secret as referenceable, the plugin will resolve secrets from vault at runtime, but if the vault reference cannot be resolved resolve_reference in kong.db.schema.init substitutes an empty string. In the non-hashed branch (hash_secret = false), the OAuth2 plugin then authenticates confidential clients by simple equality client.client_secret == client_secret, so a misconfigured or missing vault secret would reduce the client secret to an empty string and allow any caller presenting an empty secret to be accepted. For this field, vault resolution errors should cause authentication to fail (or the credential/plugin to be rejected) instead of silently falling back to an empty secret.
| { 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 | |
| { client_secret = { type = "string", required = false, auto = true, encrypted = true }, }, -- encrypted = true is a Kong Enterprise Exclusive feature. It does nothing in Kong CE |
There was a problem hiding this comment.
I dont see this as an error, almost same as with jwt. Please advise @raoxiaoyan .
raoxiaoyan
left a comment
There was a problem hiding this comment.
For testing all plugins with the vault, please refer here and update all of the test cases.
|
Thank you for your review @raoxiaoyan . I will complete the requested changes next week. |
|
@lordgreg Could you add the vault for key-credentials? |
|
@raoxiaoyan I will add the option too, sure. Which fields you want to be referencable? |
I have asked our PM for this feature. They don't agree to add this option. |
|
the code was updated, the comments were added from my side where ChatGPT gave its opinion. Please let me know if there's anything you require my update on. |
|
Hi @lordgreg. Thank you for your effort, and appreciate you. kong/spec/03-plugins/03-http-log/03-schem-vault_spec.lua Lines 26 to 44 in 1228042 |
Thank you for your comment @raoxiaoyan . I've updated all 4 unit tests that are now compliant/same as kong/spec/03-plugins/03-http-log/03-schem-vault_spec.lua. |
| local entity = plugins_schema:process_auto_fields({ | ||
| name = PLUGIN_NAME, | ||
| config = { | ||
| hide_credentials = "{vault://env/oauth2-hide-credentials}" |
There was a problem hiding this comment.
You have marked these fields client_id and client_secret as referenceable.
Right?
|
|
||
| it("should dereference vault value", function() | ||
| local env_name = "OAUTH2_HIDE_CREDENTIALS" | ||
| local env_value = "true" |
There was a problem hiding this comment.
Please use a proper value for testing.
| local entity = plugins_schema:process_auto_fields({ | ||
| name = PLUGIN_NAME, | ||
| config = { | ||
| hide_credentials = "{vault://env/basic-auth-hide-credentials}" |
There was a problem hiding this comment.
And also, you have marked username and password as referenceable.
Please check it again.
|
@lordgreg Thank you for updating quickly. I have commented on your newly tested cases. Please update it again for all test cases. |
Thank you for your reply @raoxiaoyan. You are right - my problem currently is, that all referenceable fields are written in it("handles vault reference for password with correct hashing", function()
local env_password = "my_secret_password"
helpers.setenv("BASIC_AUTH_PASSWORD", env_password)
finally(function()
helpers.unsetenv("BASIC_AUTH_PASSWORD")
end)
local consumer = bp.consumers:insert({ username = "testuser" })
local credential, err = db.basicauth_credentials:insert({
consumer = { id = consumer.id },
username = "vaultuser",
password = "{vault://env/basic-auth-password}",
})
assert.is_nil(err)
-- Verify $refs contains the vault reference
assert.equal("{vault://env/basic-auth-password}", credential["$refs"].password)
-- Use the same transformation to hash our env value
local crypto = require "kong.plugins.basic-auth.crypto"
local expected_hash = crypto.hash(consumer.id, env_password)
-- Verify it matches what's in the database
assert.equal(expected_hash, credential.password)
end)the same goes for oauth2_credentials, jwt_secrets and hmacauth_credentials (not encrypted). is this the correct approach? please advise. |


Summary
As found in #14772 , we found out, that several plugins, which are using secrets, password or any other secure string, do not support the
{vault://env/*}templating. This PR adds the support for this feature to the next integrated plugins:Checklist
changelog/unreleased/kongorskip-changeloglabel added on PR if changelog is unnecessary. README.mdIssue reference
Fix #14772
Additional info for Kong development team
The unit tests are almost the same for the plugins mentioned above with the difference of which fields we actually test.
I encountered difficulties running the tests in a Dockerized setup (using a custom Dockerfile with all required tools and a docker-compose configuration including Postgres, as well as mounted caches for Cargo, Lua, etc., to avoid reinstalling dependencies on each run). The main issue was related to LuaJIT and running on an ARM-based Mac, which caused frequent test failures such as lj_mem_realloc: allocated memory address 0xffff9588e010 outside required range. This seems to be a LuaJIT-specific problem.
To improve the developer experience, I’d suggest adding a Dockerfile, docker-compose.yaml, and an updated DEVELOPER.md with instructions for running everything inside a Dockerized environment. Please let me know if this would be considered a useful addition — I’d be happy to open a follow-up PR for it.
Please let me know if additional changes are required to the PR.