fix(env): resolve os.getenv prefix collision for env directives#13595
Open
AlinsRan wants to merge 3 commits into
Open
fix(env): resolve os.getenv prefix collision for env directives#13595AlinsRan wants to merge 3 commits into
AlinsRan wants to merge 3 commits into
Conversation
When two `env NAME=VALUE;` directives share a common prefix (e.g. KUBERNETES_CLIENT_TOKEN and KUBERNETES_CLIENT_TOKEN_FILE), looking up the longer-named one in the init/init_worker phase returns the shorter one's value. The root cause is lua-resty-core's os.getenv shim, which delegates to ngx_http_lua_ffi_get_conf_env; that function matches with a prefix-only ngx_strncmp and does not require the queried name to end at the configured entry's length. Resolve env variables from `environ` directly with exact keys instead of the shim. nginx applies env directives to `environ` in ngx_set_environment at worker start, so core.env.init() is also invoked in the worker init phase to capture directive-assigned values. The kubernetes discovery and $ENV:// resolution now go through core.env.get for an exact-match lookup. Fixes apache#13055
TEST 13 supplied its own init_worker_by_lua_block via --- http_config,
which collides with the one the test framework always emits inside the
http{} block, causing nginx to fail with 'init_worker_by_lua_block is
duplicate'. Use the framework's --- extra_init_worker_by_lua section,
which injects into the existing block (after http_init_worker, where the
env snapshot is already rebuilt for the worker phase).
The stream subsystem runs in a separate Lua VM, so the snapshot built in http_init_worker is absent there. kubernetes discovery's read_env runs in stream_init_worker -> discovery.init_worker() during the init_worker phase (get_request()==nil), so without re-init core.env.get falls back to the buggy os.getenv shim and the apache#13055 prefix collision is not fixed under the stream subsystem.
membphis
approved these changes
Jun 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #13055
Problem
When two
env NAME=VALUE;directives share a common prefix (e.g.KUBERNETES_CLIENT_TOKENandKUBERNETES_CLIENT_TOKEN_FILE), resolving the longer-named variable during the init / init_worker phase returns the shorter variable's value, depending on declaration order.Root cause
Before any request is served,
lua-resty-corereplacesos.getenvwith a shim that delegates to the C functionngx_http_lua_ffi_get_conf_env. That function matches the queried name against each configuredenventry with:It checks that the configured entry ends at
var[i].len, but it does not require the queriednameto end there too. Soos.getenv("KUBERNETES_CLIENT_TOKEN_FILE")matches theKUBERNETES_CLIENT_TOKENentry on its first 23 bytes and returns the wrong value. This is an upstreamlua-nginx-modulebug, but it can be fully sidestepped on the APISIX side.PR #13147 cannot fix it, because during
init_by_luatheenvironsnapshot does not yet containenv NAME=VALUE;directive vars (nginx applies them only inngx_set_environmentat worker start), so its lookup misses and falls back to the same shim.Fix (self-contained, no OpenResty change)
environdirectly with exact keys (core.env.init()already builds such a table) instead of going through the buggy shim.environinngx_set_environmentat worker start (beforeinit_worker_by_lua), socore.env.init()is also invoked in the worker init phase to capture directive-assigned values.core.env.get(name)for exact-match lookups; route$ENV://resolution and the kubernetes discoveryread_envthrough it.Tests
t/core/env.t:$ENV://, regardless of declaration order.core.env.getreturns exact values for prefix-colliding directives.init_workerphase for prefix-colliding directives are correct.Compatibility
core.env.getfalls back toos.getenvfor variables set dynamically after startup (e.g. viacore.os.setenv), so existing behavior is preserved. The new worker-phasecore.env.init()is idempotent.