Skip to content

feat(vault): Added an option to use {vault://} in specific fields of plugins#14775

Open
lordgreg wants to merge 30 commits intoKong:masterfrom
lordgreg:master
Open

feat(vault): Added an option to use {vault://} in specific fields of plugins#14775
lordgreg wants to merge 30 commits intoKong:masterfrom
lordgreg:master

Conversation

@lordgreg
Copy link
Copy Markdown

@lordgreg lordgreg commented Oct 16, 2025

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:

  • jwt
  • basic-auth
  • hmac-auth
  • oauth2
  • response-transformer
  • request-transformer

Checklist

  • The Pull Request has tests
  • A changelog file has been created under changelog/unreleased/kong or skip-changelog label added on PR if changelog is unnecessary. README.md
  • There is a user-facing docs PR against https://github.com/Kong/developer.konghq.com - PUT DOCS PR HERE

Issue 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.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Oct 16, 2025

CLA assistant check
All committers have signed the CLA.

@team-eng-enablement team-eng-enablement added the author/community PRs from the open-source community (not Kong Inc) label Oct 16, 2025
@mschonmeier
Copy link
Copy Markdown

Will it be possible to access certificates? @lordgreg

@lordgreg
Copy link
Copy Markdown
Author

Hi @mschonmeier ,

can you give me an example of configuration or what your use case with certificates is.

@lordgreg
Copy link
Copy Markdown
Author

lordgreg commented Nov 4, 2025

I've fix the linting error being thrown by CICD.

The other 2 errors arent being thrown because of my code changes.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +159 to +174

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)
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
{ 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 }, },
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
{ secret = { type = "string", auto = true, referenceable = true }, },
{ secret = { type = "string", auto = true }, },

Copilot uses AI. Check for mistakes.
{ 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 }, },
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
{ secret = { type = "string", auto = true, referenceable = true }, },
{ secret = { type = "string", auto = true }, },

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
{ 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

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see this as an error, almost same as with jwt. Please advise @raoxiaoyan .

Copy link
Copy Markdown
Contributor

@raoxiaoyan raoxiaoyan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For testing all plugins with the vault, please refer here and update all of the test cases.

@lordgreg
Copy link
Copy Markdown
Author

lordgreg commented Jan 6, 2026

Thank you for your review @raoxiaoyan . I will complete the requested changes next week.

@raoxiaoyan
Copy link
Copy Markdown
Contributor

raoxiaoyan commented Jan 8, 2026

@lordgreg Could you add the vault for key-credentials?
https://github.com/Kong/kong/blob/master/kong/plugins/key-auth/daos.lua

@lordgreg
Copy link
Copy Markdown
Author

lordgreg commented Jan 8, 2026

@raoxiaoyan I will add the option too, sure. Which fields you want to be referencable?

@raoxiaoyan
Copy link
Copy Markdown
Contributor

raoxiaoyan commented Jan 8, 2026

@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.
Please skip this one. Thank you.

@lordgreg
Copy link
Copy Markdown
Author

Hi @raoxiaoyan, @mschonmeier

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.

@lordgreg lordgreg requested a review from raoxiaoyan February 3, 2026 05:49
@raoxiaoyan
Copy link
Copy Markdown
Contributor

Hi @lordgreg. Thank you for your effort, and appreciate you.
Honestly, the following diagram's test cases do not meet our requirements.
image
Here is an example. Please refers.

it("should dereference vault value", function()
local env_name = "HTTP_LOG_HTTP_ENDPOINT"
local env_value = "http://example.com"
finally(function()
helpers.unsetenv(env_name)
end)
helpers.setenv(env_name, env_value)
local entity = plugins_schema:process_auto_fields({
name = PLUGIN_NAME,
config = {
http_endpoint = "{vault://env/http-log-http-endpoint}"
},
}, "select")
assert.equal(env_value, entity.config.http_endpoint)
end)

@pull-request-size pull-request-size bot added size/L and removed size/XL labels Feb 3, 2026
@lordgreg
Copy link
Copy Markdown
Author

lordgreg commented Feb 3, 2026

Hi @lordgreg. Thank you for your effort, and appreciate you. Honestly, the following diagram's test cases do not meet our requirements. image Here is an example. Please refers.

it("should dereference vault value", function()
local env_name = "HTTP_LOG_HTTP_ENDPOINT"
local env_value = "http://example.com"
finally(function()
helpers.unsetenv(env_name)
end)
helpers.setenv(env_name, env_value)
local entity = plugins_schema:process_auto_fields({
name = PLUGIN_NAME,
config = {
http_endpoint = "{vault://env/http-log-http-endpoint}"
},
}, "select")
assert.equal(env_value, entity.config.http_endpoint)
end)

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}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also, you have marked username and password as referenceable.
Please check it again.

@raoxiaoyan
Copy link
Copy Markdown
Contributor

@lordgreg Thank you for updating quickly. I have commented on your newly tested cases. Please update it again for all test cases.

@lordgreg
Copy link
Copy Markdown
Author

lordgreg commented Feb 3, 2026

http_endpoint

Thank you for your reply @raoxiaoyan. You are right - my problem currently is, that all referenceable fields are written in daos.lua, not schema for those plugins, thus the tests need to be updated. I have to add the entries in the db first, then check if values are equal.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author/community PRs from the open-source community (not Kong Inc) size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kong dbless basic_auth credentials not working with envionment variable

6 participants