Skip to content

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

Open
AlinsRan wants to merge 3 commits into
apache:masterfrom
AlinsRan:fix/13055-env-prefix
Open

fix(env): resolve os.getenv prefix collision for env directives#13595
AlinsRan wants to merge 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
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

2 participants