Skip to content

fix(env): resolve os.getenv prefix collision for env directives#13595

Merged
AlinsRan merged 3 commits into
apache:masterfrom
AlinsRan:fix/13055-env-prefix
Jun 29, 2026
Merged

fix(env): resolve os.getenv prefix collision for env directives#13595
AlinsRan merged 3 commits into
apache:masterfrom
AlinsRan:fix/13055-env-prefix

Conversation

@AlinsRan

@AlinsRan AlinsRan commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Fixes #13055

Problem

When two env NAME=VALUE; directives share a common prefix (e.g. KUBERNETES_CLIENT_TOKEN and KUBERNETES_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-core replaces os.getenv with a shim that delegates to the C function ngx_http_lua_ffi_get_conf_env. That function matches the queried name against each configured env entry with:

if (var[i].data[var[i].len] == '='
    && ngx_strncmp(name, var[i].data, var[i].len) == 0)

It checks that the configured entry ends at var[i].len, but it does not require the queried name to end there too. So os.getenv("KUBERNETES_CLIENT_TOKEN_FILE") matches the KUBERNETES_CLIENT_TOKEN entry on its first 23 bytes and returns the wrong value. This is an upstream lua-nginx-module bug, but it can be fully sidestepped on the APISIX side.

PR #13147 cannot fix it, because during init_by_lua the environ snapshot does not yet contain env NAME=VALUE; directive vars (nginx applies them only in ngx_set_environment at worker start), so its lookup misses and falls back to the same shim.

Fix (self-contained, no OpenResty change)

  • Resolve env variables from environ directly with exact keys (core.env.init() already builds such a table) instead of going through the buggy shim.
  • nginx assigns the directive vars to the global environ in ngx_set_environment at worker start (before init_worker_by_lua), so core.env.init() is also invoked in the worker init phase to capture directive-assigned values.
  • Add core.env.get(name) for exact-match lookups; route $ENV:// resolution and the kubernetes discovery read_env through it.

Tests

t/core/env.t:

  • TEST 10/11: prefix-colliding directives resolve correctly via $ENV://, regardless of declaration order.
  • TEST 12: core.env.get returns exact values for prefix-colliding directives.
  • TEST 13: values captured in the init_worker phase for prefix-colliding directives are correct.

Compatibility

core.env.get falls back to os.getenv for variables set dynamically after startup (e.g. via core.os.setenv), so existing behavior is preserved. The new worker-phase core.env.init() is idempotent.

AlinsRan added 3 commits June 23, 2026 04:44
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.
@AlinsRan AlinsRan marked this pull request as ready for review June 24, 2026 05:40
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Jun 24, 2026
@AlinsRan AlinsRan merged commit 43be2b9 into apache:master Jun 29, 2026
20 checks passed
@AlinsRan AlinsRan deleted the fix/13055-env-prefix branch June 29, 2026 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Environment variable with common prefix returns incorrect value in init_by_lua

4 participants