This is an automated email from the ASF dual-hosted git repository.
AlinsRan 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 b87b54d5a fix(stream): initialize env and secret for TLS cert
resolution (#12935)
b87b54d5a is described below
commit b87b54d5a7811b0d0db1b236d916f40dd6da8e4f
Author: Surya Parua <[email protected]>
AuthorDate: Wed Aug 5 04:04:14 2026 +0530
fix(stream): initialize env and secret for TLS cert resolution (#12935)
---
apisix/constants.lua | 1 +
apisix/init.lua | 2 +
docs/en/latest/terminology/secret.md | 2 +-
t/stream-node/tls.t | 183 +++++++++++++++++++++++++++++++++++
4 files changed, 187 insertions(+), 1 deletion(-)
diff --git a/apisix/constants.lua b/apisix/constants.lua
index 0b3ec160b..0124c5ee5 100644
--- a/apisix/constants.lua
+++ b/apisix/constants.lua
@@ -42,5 +42,6 @@ return {
["/ssls"] = true,
["/stream_routes"] = true,
["/plugin_metadata"] = true,
+ ["/secrets"] = true,
},
}
diff --git a/apisix/init.lua b/apisix/init.lua
index 801944c36..fe1f2f688 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -1332,6 +1332,7 @@ function _M.stream_init(args)
core.log.info("enter stream_init")
core.resolver.init_resolver(args)
+ core.env.init()
if core.config.init then
local ok, err = core.config.init()
@@ -1373,6 +1374,7 @@ function _M.stream_init_worker()
plugin.init_worker()
xrpc.init_worker()
+ apisix_secret.init_worker()
router.stream_init_worker()
require("apisix.http.service").init_worker()
apisix_upstream.init_worker()
diff --git a/docs/en/latest/terminology/secret.md
b/docs/en/latest/terminology/secret.md
index 304856fb0..347bcf7cb 100644
--- a/docs/en/latest/terminology/secret.md
+++ b/docs/en/latest/terminology/secret.md
@@ -49,7 +49,7 @@ You can use APISIX Secret functions by specifying format
variables in the consum
Secret references (`$secret://...`, `$env://...`, `$ENV://...`) can be used in
the following contexts:
- **Plugin configurations**: Any string field in any plugin configuration.
Secret references are automatically resolved at runtime in `plugin.filter()`
before the plugin executes.
-- **SSL certificates**: The `cert`, `key`, `certs`, and `keys` fields in SSL
resources. Secret references are resolved during TLS handshake.
+- **SSL certificates**: The `cert`, `key`, `certs`, and `keys` fields in SSL
resources. Secret references are resolved during TLS handshake, including
stream (L4) TLS mode.
- **Consumer auth configurations**: Any string field in consumer
authentication plugin configurations (e.g., `key-auth`, `jwt-auth`). Secret
references are resolved when consumer configuration is loaded.
:::tip
diff --git a/t/stream-node/tls.t b/t/stream-node/tls.t
index 13bdcba0f..90a932290 100644
--- a/t/stream-node/tls.t
+++ b/t/stream-node/tls.t
@@ -14,6 +14,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+BEGIN {
+ sub set_env_from_file {
+ my ($env_name, $file_path) = @_;
+
+ open my $fh, '<', $file_path or die $!;
+ my $content = do { local $/; <$fh> };
+ close $fh;
+
+ $ENV{$env_name} = $content;
+ }
+ set_env_from_file('APISIX_STREAM_ENV_CERT', 't/certs/apisix.crt');
+ set_env_from_file('APISIX_STREAM_ENV_KEY', 't/certs/apisix.key');
+}
+
use t::APISIX 'no_plan';
log_level('info');
@@ -133,3 +147,172 @@ fetch table plugins
release table ctx_var
release table plugins
release table api_ctx
+
+
+
+=== TEST 6: stream tls supports $ENV certificate reference
+--- config
+ location /t {
+ content_by_lua_block {
+ local core = require("apisix.core")
+ local t = require("lib.test_admin")
+
+ local data = {
+ cert = "$ENV://APISIX_STREAM_ENV_CERT",
+ key = "$ENV://APISIX_STREAM_ENV_KEY",
+ sni = "test.com",
+ }
+
+ local code, body = t.test('/apisix/admin/ssls/1',
+ ngx.HTTP_PUT,
+ core.json.encode(data)
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+
+ local code, body = t.test('/apisix/admin/stream_routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1995": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 7: hit stream route with $ENV cert reference
+--- stream_tls_request
+mmm
+--- stream_sni: test.com
+--- response_body
+hello world
+
+
+
+=== TEST 8: store cert and key in vault for stream tls
+--- exec
+VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault kv put kv/apisix/ssl
\
+ test.com.crt=@t/certs/apisix.crt \
+ test.com.key=@t/certs/apisix.key
+--- response_body
+Success! Data written to: kv/apisix/ssl
+
+
+
+=== TEST 9: set secret provider (vault) for stream tls
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+
+ local code, body = t('/apisix/admin/secrets/vault/stream-test',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "http://0.0.0.0:8200",
+ "prefix": "kv/apisix",
+ "token": "root"
+ }]],
+ [[{
+ "key": "/apisix/secrets/vault/stream-test",
+ "value": {
+ "uri": "http://0.0.0.0:8200",
+ "prefix": "kv/apisix",
+ "token": "root"
+ }
+ }]]
+ )
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 10: stream tls supports $secret certificate reference
+--- config
+ location /t {
+ content_by_lua_block {
+ local core = require("apisix.core")
+ local t = require("lib.test_admin")
+
+ local data = {
+ cert = "$secret://vault/stream-test/ssl/test.com.crt",
+ key = "$secret://vault/stream-test/ssl/test.com.key",
+ sni = "secret.test.com",
+ }
+
+ local code, body = t.test('/apisix/admin/ssls/3',
+ ngx.HTTP_PUT,
+ core.json.encode(data)
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+
+ local code, body = t.test('/apisix/admin/stream_routes/3',
+ ngx.HTTP_PUT,
+ [[{
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1995": 1
+ },
+ "type": "roundrobin"
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 11: hit stream route with secret cert
+--- stream_tls_request
+hello
+--- stream_sni: secret.test.com
+--- response_body
+hello world