Skip to content

Commit c22058c

Browse files
authored
feat: extend secret references to all plugins with central resolution (#13312)
1 parent f1018cc commit c22058c

21 files changed

Lines changed: 599 additions & 71 deletions

apisix-master-0.rockspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ dependencies = {
5252
"lua-resty-openidc = 1.8.0-1",
5353
"luafilesystem = 1.8.0-1",
5454
"nginx-lua-prometheus-api7 = 0.20250302-1",
55-
"jsonschema = 0.9.9-0",
55+
"jsonschema = 0.9.13-0",
5656
"lua-resty-ipmatcher = 0.6.1-0",
5757
"lua-resty-kafka = 0.23-0",
5858
"lua-resty-logger-socket = 2.0.3-0",

apisix/core/schema.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ local lrucache = require("apisix.core.lrucache")
2424
local schema_def = require("apisix.schema_def")
2525
local cached_validator = lrucache.new({count = 1000, ttl = 0})
2626
local pcall = pcall
27+
local require = require
2728
local error = error
2829

2930
local _M = {
@@ -39,7 +40,10 @@ local function create_validator(schema)
3940
-- local file2=io.output("/tmp/2.txt")
4041
-- file2:write(code)
4142
-- file2:close()
42-
local ok, res = pcall(jsonschema.generate_validator, schema)
43+
local secret = require("apisix.secret")
44+
local ok, res = pcall(jsonschema.generate_validator, schema, {
45+
skip_validation = secret.is_secret_ref,
46+
})
4347
if ok then
4448
return res
4549
end
@@ -58,6 +62,7 @@ local function get_validator(schema)
5862
return validator, nil
5963
end
6064

65+
6166
function _M.check(schema, json)
6267
local validator, err = get_validator(schema)
6368

apisix/plugin.lua

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ local enable_debug = require("apisix.debug").enable_debug
2121
local wasm = require("apisix.wasm")
2222
local expr = require("resty.expr.v1")
2323
local apisix_ssl = require("apisix.ssl")
24+
local secret = require("apisix.secret")
2425

2526
local ngx = ngx
2627
local ngx_ok = ngx.OK
@@ -65,6 +66,56 @@ local merge_global_rule_lrucache = core.lrucache.new({
6566
ttl = 300, count = 512
6667
})
6768

69+
-- Cache for resolved plugin confs: original_conf -> {resolved, secret_vals}
70+
-- Weak keys ensure entries are GC'd when original conf is replaced (config reload)
71+
-- Weak-keyed cache: original_conf -> {resolved, secret_vals}.
72+
-- Avoids deepcopy on every request when secret values haven't changed,
73+
-- which preserves plugins' internal caches that use conf table identity
74+
-- as cache key (e.g. ai-rate-limiting's limit_conf_cache).
75+
local _resolved_cache = setmetatable({}, {__mode = "k"})
76+
local _no_secret_ref = setmetatable({}, {__mode = "k"})
77+
78+
local function vals_equal(a, b)
79+
if a == b then
80+
return true
81+
end
82+
if not a or not b then
83+
return false
84+
end
85+
for k, v in pairs(a) do
86+
if b[k] ~= v then
87+
return false
88+
end
89+
end
90+
for k in pairs(b) do
91+
if a[k] == nil then
92+
return false
93+
end
94+
end
95+
return true
96+
end
97+
98+
local function resolve_plugin_conf(conf)
99+
if _no_secret_ref[conf] then
100+
return conf
101+
end
102+
if not secret.has_secret_ref(conf) then
103+
_no_secret_ref[conf] = true
104+
return conf
105+
end
106+
107+
local current_vals = secret.collect_secret_values(conf, true)
108+
109+
local cached = _resolved_cache[conf]
110+
if cached and vals_equal(cached.secret_vals, current_vals) then
111+
return cached.resolved
112+
end
113+
114+
local resolved = secret.fetch_secrets(conf, true)
115+
_resolved_cache[conf] = {resolved = resolved, secret_vals = current_vals}
116+
return resolved
117+
end
118+
68119
local local_conf
69120
local check_plugin_metadata
70121

@@ -575,6 +626,14 @@ function _M.filter(ctx, conf, plugins, route_conf, phase)
575626
core.tablepool.release("tmp_plugin_confs", tmp_plugin_confs)
576627
end
577628

629+
-- resolve $secret:// and $env:// references in plugin confs
630+
for i = 2, #plugins, 2 do
631+
local resolved = resolve_plugin_conf(plugins[i])
632+
if resolved ~= plugins[i] then
633+
plugins[i] = resolved
634+
end
635+
end
636+
578637
return plugins
579638
end
580639

@@ -600,6 +659,14 @@ function _M.stream_filter(user_route, plugins)
600659

601660
trace_plugins_info_for_debug(nil, plugins)
602661

662+
-- resolve $secret:// and $env:// references in stream plugin confs
663+
for i = 2, #plugins, 2 do
664+
local resolved = resolve_plugin_conf(plugins[i])
665+
if resolved ~= plugins[i] then
666+
plugins[i] = resolved
667+
end
668+
end
669+
603670
return plugins
604671
end
605672

@@ -902,6 +969,7 @@ function _M.conf_version(conf)
902969
end
903970

904971

972+
905973
local function check_single_plugin_schema(name, plugin_conf, schema_type, skip_disabled_plugin)
906974
if type(plugin_conf) ~= "table" then
907975
return false, "invalid plugin conf " ..

apisix/plugins/ai-aws-content-moderation.lua

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ local aws = require("resty.aws")
2121
local aws_instance
2222

2323
local http = require("resty.http")
24-
local fetch_secrets = require("apisix.secret").fetch_secrets
2524

2625
local pairs = pairs
2726
local unpack = unpack
@@ -88,11 +87,6 @@ end
8887

8988

9089
function _M.rewrite(conf, ctx)
91-
conf = fetch_secrets(conf, true)
92-
if not conf then
93-
return HTTP_INTERNAL_SERVER_ERROR, "failed to retrieve secrets from conf"
94-
end
95-
9690
local body, err = core.request.get_body()
9791
if not body then
9892
return HTTP_BAD_REQUEST, err

apisix/plugins/ai-proxy-multi.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
--
1717

1818
local core = require("apisix.core")
19+
local secret = require("apisix.secret")
1920
local schema = require("apisix.plugins.ai-proxy.schema")
2021
local base = require("apisix.plugins.ai-proxy.base")
2122
local plugin = require("apisix.plugin")
@@ -110,7 +111,7 @@ function _M.check_schema(conf)
110111
return false, "ai provider: " .. instance.provider .. " is not supported."
111112
end
112113
local sa_json = core.table.try_read_attr(instance, "auth", "gcp", "service_account_json")
113-
if sa_json then
114+
if sa_json and not secret.is_secret_ref(sa_json) then
114115
local _, err = core.json.decode(sa_json)
115116
if err then
116117
return false, "invalid gcp service_account_json: " .. err

apisix/plugins/ai-proxy.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
-- limitations under the License.
1616
--
1717
local core = require("apisix.core")
18+
local secret = require("apisix.secret")
1819
local schema = require("apisix.plugins.ai-proxy.schema")
1920
local base = require("apisix.plugins.ai-proxy.base")
2021
local exporter = require("apisix.plugins.prometheus.exporter")
@@ -42,7 +43,7 @@ function _M.check_schema(conf)
4243
return false, "ai provider: " .. conf.provider .. " is not supported."
4344
end
4445
local sa_json = core.table.try_read_attr(conf, "auth", "gcp", "service_account_json")
45-
if sa_json then
46+
if sa_json and not secret.is_secret_ref(sa_json) then
4647
local _, err = core.json.decode(sa_json)
4748
if err then
4849
return false, "invalid gcp service_account_json: " .. err

apisix/plugins/authz-keycloak.lua

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ local sub_str = string.sub
2020
local type = type
2121
local ngx = ngx
2222
local plugin_name = "authz-keycloak"
23-
local fetch_secrets = require("apisix.secret").fetch_secrets
2423

2524
local log = core.log
2625
local pairs = pairs
@@ -763,8 +762,6 @@ local function generate_token_using_password_grant(conf,ctx)
763762
end
764763

765764
function _M.access(conf, ctx)
766-
-- resolve secrets
767-
conf = fetch_secrets(conf, true)
768765
local headers = core.request.headers(ctx)
769766
local need_grant_token = conf.password_grant_token_generation_incoming_uri and
770767
ctx.var.request_uri == conf.password_grant_token_generation_incoming_uri and

apisix/plugins/clickhouse-logger.lua

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
-- limitations under the License.
1616
--
1717

18-
local fetch_secrets = require("apisix.secret").fetch_secrets
1918
local bp_manager_mod = require("apisix.utils.batch-processor-manager")
2019
local log_util = require("apisix.utils.log-util")
2120
local plugin = require("apisix.plugin")
@@ -107,8 +106,6 @@ end
107106

108107

109108
local function send_http_data(conf, log_message)
110-
conf = fetch_secrets(conf, true)
111-
112109
local err_msg
113110
local res = true
114111
local selected_endpoint_addr

apisix/plugins/jwt-auth.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ local consumer_mod = require("apisix.consumer")
1919
local new_tab = require ("table.new")
2020
local auth_utils = require("apisix.utils.auth")
2121

22+
local secret = require("apisix.secret")
2223
local ngx_decode_base64 = ngx.decode_base64
2324
local ngx = ngx
2425
local sub_str = string.sub
@@ -182,7 +183,7 @@ function _M.check_schema(conf, schema_type)
182183
return false, "property \"secret\" is required when using HS based algorithms"
183184
end
184185

185-
if conf.base64_secret then
186+
if conf.base64_secret and not secret.is_secret_ref(conf.secret) then
186187
if ngx_decode_base64(conf.secret) == nil then
187188
return false, "base64_secret required but the secret is not in base64 format"
188189
end

apisix/plugins/limit-count.lua

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
-- See the License for the specific language governing permissions and
1515
-- limitations under the License.
1616
--
17-
local fetch_secrets = require("apisix.secret").fetch_secrets
1817
local limit_count = require("apisix.plugins.limit-count.init")
1918
local workflow = require("apisix.plugins.workflow")
2019

@@ -34,7 +33,6 @@ end
3433

3534

3635
function _M.access(conf, ctx)
37-
conf = fetch_secrets(conf, true)
3836
return limit_count.rate_limit(conf, ctx, plugin_name, 1)
3937
end
4038

0 commit comments

Comments
 (0)