This is an automated email from the ASF dual-hosted git repository.
shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 473a18976 fix(security): encrypt missing secret-like plugin fields at
rest (#13389)
473a18976 is described below
commit 473a18976a25e20ae346e3aacc4a5ab78ef023aa
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Tue Jun 2 17:03:41 2026 +0800
fix(security): encrypt missing secret-like plugin fields at rest (#13389)
---
apisix/plugins/ai-aws-content-moderation.lua | 1 +
apisix/plugins/azure-functions.lua | 9 ++-
apisix/plugins/error-log-logger.lua | 2 +-
apisix/plugins/http-logger.lua | 1 +
apisix/plugins/kafka-logger.lua | 3 +-
apisix/plugins/loggly.lua | 1 +
apisix/plugins/openfunction.lua | 6 +-
apisix/plugins/openid-connect.lua | 3 +-
apisix/plugins/splunk-hec-logging.lua | 1 +
t/plugin/error-log-logger-kafka.t | 69 +++++++++++++++++++++++
t/plugin/kafka-logger3.t | 76 +++++++++++++++++++++++++
t/plugin/loggly.t | 65 ++++++++++++++++++++++
t/plugin/openid-connect2.t | 83 ++++++++++++++++++++++++++++
t/plugin/splunk-hec-logging.t | 70 +++++++++++++++++++++++
14 files changed, 384 insertions(+), 6 deletions(-)
diff --git a/apisix/plugins/ai-aws-content-moderation.lua
b/apisix/plugins/ai-aws-content-moderation.lua
index bd3cbf109..0fe62e231 100644
--- a/apisix/plugins/ai-aws-content-moderation.lua
+++ b/apisix/plugins/ai-aws-content-moderation.lua
@@ -69,6 +69,7 @@ local schema = {
default = 0.5
}
},
+ encrypt_fields = { "comprehend.secret_access_key" },
required = { "comprehend" },
}
diff --git a/apisix/plugins/azure-functions.lua
b/apisix/plugins/azure-functions.lua
index 0b0e64d4f..9d723306d 100644
--- a/apisix/plugins/azure-functions.lua
+++ b/apisix/plugins/azure-functions.lua
@@ -30,7 +30,8 @@ local metadata_schema = {
properties = {
master_apikey = {type = "string", default = ""},
master_clientid = {type = "string", default = ""}
- }
+ },
+ encrypt_fields = {"master_apikey"}
}
local function request_processor(conf, ctx, params)
@@ -57,5 +58,9 @@ local function request_processor(conf, ctx, params)
end
-return require("apisix.plugins.serverless.generic-upstream")(plugin_name,
+local plugin =
require("apisix.plugins.serverless.generic-upstream")(plugin_name,
plugin_version, priority, request_processor, azure_authz_schema,
metadata_schema)
+
+plugin.schema.encrypt_fields = {"authorization.apikey"}
+
+return plugin
diff --git a/apisix/plugins/error-log-logger.lua
b/apisix/plugins/error-log-logger.lua
index 88eca65ca..ee530e14a 100644
--- a/apisix/plugins/error-log-logger.lua
+++ b/apisix/plugins/error-log-logger.lua
@@ -146,7 +146,7 @@ local metadata_schema = {
-- for compatible with old schema
{required = {"host", "port"}}
},
- encrypt_fields = {"clickhouse.password"},
+ encrypt_fields = {"clickhouse.password",
"kafka.brokers.sasl_config.password"},
}
diff --git a/apisix/plugins/http-logger.lua b/apisix/plugins/http-logger.lua
index 3ee5f238a..cc8481344 100644
--- a/apisix/plugins/http-logger.lua
+++ b/apisix/plugins/http-logger.lua
@@ -56,6 +56,7 @@ local schema = {
enum = {"json", "new_line"}},
ssl_verify = {type = "boolean", default = false},
},
+ encrypt_fields = {"auth_header"},
required = {"uri"}
}
diff --git a/apisix/plugins/kafka-logger.lua b/apisix/plugins/kafka-logger.lua
index b93a60531..0a9dcdf8c 100644
--- a/apisix/plugins/kafka-logger.lua
+++ b/apisix/plugins/kafka-logger.lua
@@ -132,7 +132,8 @@ local schema = {
oneOf = {
{ required = {"broker_list", "kafka_topic"},},
{ required = {"brokers", "kafka_topic"},},
- }
+ },
+ encrypt_fields = {"brokers.sasl_config.password"},
}
local metadata_schema = {
diff --git a/apisix/plugins/loggly.lua b/apisix/plugins/loggly.lua
index 18cdc2297..d7d792764 100644
--- a/apisix/plugins/loggly.lua
+++ b/apisix/plugins/loggly.lua
@@ -108,6 +108,7 @@ local schema = {
additionalProperties = false
}
},
+ encrypt_fields = {"customer_token"},
required = {"customer_token"}
}
diff --git a/apisix/plugins/openfunction.lua b/apisix/plugins/openfunction.lua
index 935d6ebbc..e7ef88390 100644
--- a/apisix/plugins/openfunction.lua
+++ b/apisix/plugins/openfunction.lua
@@ -31,5 +31,9 @@ local function request_processor(conf, ctx, params)
params.headers = headers
end
-return require("apisix.plugins.serverless.generic-upstream")(plugin_name,
+local plugin =
require("apisix.plugins.serverless.generic-upstream")(plugin_name,
plugin_version, priority, request_processor, openfunction_authz_schema)
+
+plugin.schema.encrypt_fields = {"authorization.service_token"}
+
+return plugin
diff --git a/apisix/plugins/openid-connect.lua
b/apisix/plugins/openid-connect.lua
index 3ecf9d246..4a7bf53d4 100644
--- a/apisix/plugins/openid-connect.lua
+++ b/apisix/plugins/openid-connect.lua
@@ -401,7 +401,8 @@ local schema = {
default = nil,
}
},
- encrypt_fields = {"client_secret", "client_rsa_private_key"},
+ encrypt_fields = {"client_secret", "client_rsa_private_key",
+ "session.secret", "session.redis.password"},
required = {"client_id", "client_secret", "discovery"}
}
diff --git a/apisix/plugins/splunk-hec-logging.lua
b/apisix/plugins/splunk-hec-logging.lua
index 2024cc9fe..a7b9b2df0 100644
--- a/apisix/plugins/splunk-hec-logging.lua
+++ b/apisix/plugins/splunk-hec-logging.lua
@@ -68,6 +68,7 @@ local schema = {
},
log_format = {type = "object"},
},
+ encrypt_fields = {"endpoint.token"},
required = { "endpoint" },
}
diff --git a/t/plugin/error-log-logger-kafka.t
b/t/plugin/error-log-logger-kafka.t
index afae2a558..0ae6a52ac 100644
--- a/t/plugin/error-log-logger-kafka.t
+++ b/t/plugin/error-log-logger-kafka.t
@@ -201,3 +201,72 @@ qr/send data to kafka: .*test5/]
}
--- response_body
passed
+
+
+
+=== TEST 7: data encryption for kafka.brokers[].sasl_config.password (metadata)
+--- yaml_config
+apisix:
+ data_encryption:
+ enable_encrypt_fields: true
+ keyring:
+ - edd1c9f0985e76a2
+--- config
+ location /t {
+ content_by_lua_block {
+ local json = require("toolkit.json")
+ local t = require("lib.test_admin").test
+ local kafka_password = "super-secret-kafka-password"
+ local code, body =
t('/apisix/admin/plugin_metadata/error-log-logger',
+ ngx.HTTP_PUT,
+ json.encode({
+ kafka = {
+ brokers = {{
+ host = "127.0.0.1",
+ port = 9092,
+ sasl_config = {
+ mechanism = "PLAIN",
+ user = "admin",
+ password = kafka_password,
+ }
+ }},
+ kafka_topic = "test",
+ },
+ level = "ERROR",
+ inactive_timeout = 1,
+ })
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.sleep(0.1)
+
+ -- get plugin metadata from admin api, password is decrypted
+ local code, message, res =
t('/apisix/admin/plugin_metadata/error-log-logger',
+ ngx.HTTP_GET
+ )
+ res = json.decode(res)
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(message)
+ return
+ end
+
+ local plain = res.value.kafka.brokers[1].sasl_config.password
+ ngx.say(plain == kafka_password)
+
+ -- get plugin metadata from etcd, password must be encrypted
+ local etcd = require("apisix.core.etcd")
+ local etcd_res =
assert(etcd.get('/plugin_metadata/error-log-logger'))
+ local stored =
etcd_res.body.node.value.kafka.brokers[1].sasl_config.password
+ ngx.say(type(stored) == "string" and stored ~= "" and stored ~=
kafka_password)
+ }
+ }
+--- response_body
+true
+true
+--- no_error_log
+[alert]
diff --git a/t/plugin/kafka-logger3.t b/t/plugin/kafka-logger3.t
index 82c8248c6..18d02baba 100644
--- a/t/plugin/kafka-logger3.t
+++ b/t/plugin/kafka-logger3.t
@@ -118,3 +118,79 @@ location /t {
--- error_log
max pending entries limit exceeded. discarding entry
--- timeout: 5
+
+
+
+=== TEST 2: data encryption for brokers[].sasl_config.password
+--- yaml_config
+apisix:
+ data_encryption:
+ enable_encrypt_fields: true
+ keyring:
+ - edd1c9f0985e76a2
+--- config
+ location /t {
+ content_by_lua_block {
+ local json = require("toolkit.json")
+ local t = require("lib.test_admin").test
+ local kafka_password = "super-secret-kafka-password"
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ json.encode({
+ plugins = {
+ ["kafka-logger"] = {
+ brokers = {{
+ host = "127.0.0.1",
+ port = 9092,
+ sasl_config = {
+ mechanism = "PLAIN",
+ user = "admin",
+ password = kafka_password,
+ }
+ }},
+ kafka_topic = "test",
+ batch_max_size = 1,
+ }
+ },
+ upstream = {
+ nodes = {["127.0.0.1:1980"] = 1},
+ type = "roundrobin"
+ },
+ uri = "/hello"
+ })
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.sleep(0.1)
+
+ -- get plugin conf from admin api, password is decrypted
+ local code, message, res = t('/apisix/admin/routes/1',
+ ngx.HTTP_GET
+ )
+ res = json.decode(res)
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(message)
+ return
+ end
+
+ local plain =
res.value.plugins["kafka-logger"].brokers[1].sasl_config.password
+ ngx.say(plain == kafka_password)
+
+ -- get plugin conf from etcd, password must be encrypted
+ local etcd = require("apisix.core.etcd")
+ local etcd_res = assert(etcd.get('/routes/1'))
+ local stored = etcd_res.body.node.value
+
.plugins["kafka-logger"].brokers[1].sasl_config.password
+ ngx.say(type(stored) == "string" and stored ~= "" and stored ~=
kafka_password)
+ }
+ }
+--- response_body
+true
+true
+--- no_error_log
+[alert]
diff --git a/t/plugin/loggly.t b/t/plugin/loggly.t
index 7c092d792..d19186445 100644
--- a/t/plugin/loggly.t
+++ b/t/plugin/loggly.t
@@ -843,3 +843,68 @@ opentracing
qr/message received: [ -~]+/
--- grep_error_log_out eval
qr/message received: <14>1 [\d\-T:.]+Z \w+ apisix [\d]+ - \[tok\@41058
tag="apisix"] \{"client":"[\d.]+","host":"\w+","route_id":"1"\}/
+
+
+
+=== TEST 21: data encryption for customer_token
+--- yaml_config
+apisix:
+ data_encryption:
+ enable_encrypt_fields: true
+ keyring:
+ - edd1c9f0985e76a2
+--- config
+ location /t {
+ content_by_lua_block {
+ local json = require("toolkit.json")
+ local t = require("lib.test_admin").test
+ local customer_token = "super-secret-loggly-token"
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ json.encode({
+ plugins = {
+ loggly = {
+ customer_token = customer_token,
+ }
+ },
+ upstream = {
+ nodes = {["127.0.0.1:1980"] = 1},
+ type = "roundrobin"
+ },
+ uri = "/hello"
+ })
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.sleep(0.1)
+
+ -- get plugin conf from admin api, customer_token is decrypted
+ local code, message, res = t('/apisix/admin/routes/1',
+ ngx.HTTP_GET
+ )
+ res = json.decode(res)
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(message)
+ return
+ end
+
+ local plain = res.value.plugins.loggly.customer_token
+ ngx.say(plain == customer_token)
+
+ -- get plugin conf from etcd, customer_token must be encrypted
+ local etcd = require("apisix.core.etcd")
+ local etcd_res = assert(etcd.get('/routes/1'))
+ local stored =
etcd_res.body.node.value.plugins.loggly.customer_token
+ ngx.say(type(stored) == "string" and stored ~= "" and stored ~=
customer_token)
+ }
+ }
+--- response_body
+true
+true
+--- no_error_log
+[alert]
diff --git a/t/plugin/openid-connect2.t b/t/plugin/openid-connect2.t
index e4760a11d..b7877ac33 100644
--- a/t/plugin/openid-connect2.t
+++ b/t/plugin/openid-connect2.t
@@ -1081,3 +1081,86 @@ true
true
--- no_error_log
[alert]
+
+
+
+=== TEST 21: data encryption for session.redis.password
+--- yaml_config
+apisix:
+ data_encryption:
+ enable_encrypt_fields: true
+ keyring:
+ - edd1c9f0985e76a2
+--- config
+ location /t {
+ content_by_lua_block {
+ local json = require("toolkit.json")
+ local t = require("lib.test_admin").test
+ local redis_password = "super-secret-redis-password"
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ json.encode({
+ plugins = {
+ ["openid-connect"] = {
+ client_id = "kbyuFDidLLm280LIwVFiazOqjO3ty8KH",
+ client_secret =
"60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa",
+ discovery =
"http://127.0.0.1:1980/.well-known/openid-configuration",
+ redirect_uri = "https://iresty.com",
+ ssl_verify = false,
+ timeout = 10,
+ scope = "apisix",
+ use_pkce = false,
+ session = {
+ secret = "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK",
+ storage = "redis",
+ redis = {
+ host = "127.0.0.1",
+ port = 6379,
+ password = redis_password,
+ }
+ }
+ }
+ },
+ upstream = {
+ nodes = {
+ ["127.0.0.1:1980"] = 1
+ },
+ type = "roundrobin"
+ },
+ uri = "/hello"
+ })
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.sleep(0.1)
+
+ -- get plugin conf from admin api, password is decrypted
+ local code, message, res = t('/apisix/admin/routes/1',
+ ngx.HTTP_GET
+ )
+ res = json.decode(res)
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(message)
+ return
+ end
+
+ local plain_password =
res.value.plugins["openid-connect"].session.redis.password
+ ngx.say(plain_password == redis_password)
+
+ -- get plugin conf from etcd, password must be encrypted (not
plaintext)
+ local etcd = require("apisix.core.etcd")
+ local etcd_res = assert(etcd.get('/routes/1'))
+ local stored =
etcd_res.body.node.value.plugins["openid-connect"].session.redis.password
+ ngx.say(type(stored) == "string" and stored ~= "" and stored ~=
redis_password)
+ }
+ }
+--- response_body
+true
+true
+--- no_error_log
+[alert]
diff --git a/t/plugin/splunk-hec-logging.t b/t/plugin/splunk-hec-logging.t
index 3d6b108f2..4610784af 100644
--- a/t/plugin/splunk-hec-logging.t
+++ b/t/plugin/splunk-hec-logging.t
@@ -463,3 +463,73 @@ qr/.*test batched data.*/
}
--- response_body
passed
+
+
+
+=== TEST 14: data encryption for endpoint.token
+--- yaml_config
+apisix:
+ data_encryption:
+ enable_encrypt_fields: true
+ keyring:
+ - edd1c9f0985e76a2
+--- config
+ location /t {
+ content_by_lua_block {
+ local json = require("toolkit.json")
+ local t = require("lib.test_admin").test
+ local token = "BD274822-96AA-4DA6-90EC-18940FB2414C"
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ json.encode({
+ plugins = {
+ ["splunk-hec-logging"] = {
+ endpoint = {
+ uri =
"http://127.0.0.1:18088/services/collector",
+ token = token,
+ },
+ batch_max_size = 1,
+ inactive_timeout = 1
+ }
+ },
+ upstream = {
+ nodes = {["127.0.0.1:1980"] = 1},
+ type = "roundrobin"
+ },
+ uri = "/hello"
+ })
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.sleep(0.1)
+
+ -- get plugin conf from admin api, token is decrypted
+ local code, message, res = t('/apisix/admin/routes/1',
+ ngx.HTTP_GET
+ )
+ res = json.decode(res)
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(message)
+ return
+ end
+
+ local plain =
res.value.plugins["splunk-hec-logging"].endpoint.token
+ ngx.say(plain == token)
+
+ -- get plugin conf from etcd, token must be encrypted
+ local etcd = require("apisix.core.etcd")
+ local etcd_res = assert(etcd.get('/routes/1'))
+ local stored =
etcd_res.body.node.value.plugins["splunk-hec-logging"].endpoint.token
+ ngx.say(type(stored) == "string" and stored ~= "" and stored ~=
token)
+ }
+ }
+--- response_body
+true
+true
+--- no_error_log
+[alert]