fix(cli): quote nginx_config.envs entries so values with spaces work#13713
Open
AlinsRan wants to merge 2 commits into
Open
fix(cli): quote nginx_config.envs entries so values with spaces work#13713AlinsRan wants to merge 2 commits into
AlinsRan wants to merge 2 commits into
Conversation
The `env` directive parameters were rendered verbatim, so an entry like `TEST=a b` produced `env TEST=a b;` which nginx tokenizes into two arguments and rejects with `invalid number of arguments in "env" directive`. Render each entry as a double-quoted parameter and escape embedded `\` and `"` where all envs sources converge. Reject control characters at the CLI schema level. Fixes apache#11467
AlinsRan
force-pushed
the
fix/nginx-config-envs-with-spaces
branch
from
July 20, 2026 05:28
e00d1e4 to
9c11a6e
Compare
The config schema validates nginx_config.envs, but kubernetes discovery
appends entries afterwards by copying whatever sits between ` ${` and `}`
in service.host/port and client.token/token_file. Those never see the
schema pattern, and the token_file pattern in particular accepts control
characters, so a newline could reach nginx.conf and produce an error
pointing at a line the user never wrote.
Check the final list instead of trusting the schema alone.
nic-6443
approved these changes
Jul 21, 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.
What this PR does
Makes
nginx_config.envsentries whose value contains a space (or;,{,#, tab) work instead of producing an nginx.conf that fails to parse.Fixes #11467
Root cause
apisix/cli/ngx_tpl.luarenders each entry verbatim into theenvdirective, without quoting:So
nginx_config.envs: [TEST=a b]generatesenv TEST=a b;. nginx's tokenizer splits that into two parameters, andenvaccepts exactly one:This writing has been unchanged since 952450b (2020-10). Only
nginx_config.envsentries written asNAME=valueare affected; entries exported via${{VAR}}render as a bareenv NAME;and were never broken.This is not an nginx limitation
The issue thread concludes "This is also a limitation of nginx." That is wrong — nginx handles env values with spaces fine, the value just has to arrive as a single token.
Verified against nginx 1.29.2
src/core/ngx_conf_file.c: ngx_conf_read_token(): inside a double-quoted token, space /;/{/#/ tab / newline are all taken literally and only"ends the token; the copy stage turns\"into"and\\into\. Confirmed empirically too — hand-editing the generated conf toenv "TEST=a b";makesnginx -tpass, and a worker-sideos.getenv("TEST")returnsa b.So this is purely a template rendering bug on the APISIX side.
Changes
ngx_tpl.lua— render asenv "{*name*}";.ops.lua— escape\and"in each entry, placed right before-- fix up lua path, which is where all three envs sources (explicitnginx_config.envs,${{VAR}}exported names, kubernetes discovery injected names) have converged. Escaping is a no-op for bareNAMEentries, so no branching is needed.schema.lua—pattern = [[\A[^\x00-\x1f]*\z]]on the array items.Quoting is unconditional rather than "only when the value looks special". Getting that character set wrong by one character reproduces the original bug, and the tokenizer guarantees quoting is lossless for any non-control value, so the simple version is also the correct one.
Why
$is not escapedenvis a core directive and does not compile its argument as a script, and nginx's conf parser does no interpolation at tokenize time —ngx_conf_read_token()only uses avariableflag so that${...}doesn't break the token, then stores the bytes literally. A value likeTEST=$FOOreaches the environment as the literal$FOO, both before and after this change.Why the control-character rejection
NUL cannot be carried through the C environ at all (it would silently truncate), and other control characters have no sane config source. nginx would technically accept a literal newline inside a quoted token, but a multi-line
envdirective makes the generated conf unreadable and ungreppable, so\x00-\x1fis rejected wholesale.Note
\A...\zrather than^...$: cli/schema.lua patterns are PCRE, where$also matches before a trailing newline, so^[^\x00-\x1f]*$would let a value ending in\nthrough.Test changes
t/clihas ~20 assertions grepping the generated conf forenv NAME;. Those patterns no longer match once the directive is quoted, so they are updated toenv "NAME";intest_main.sh,test_standalone.shandtest_kubernetes.sh. Without this they would all fail on the fixed code — the assertions are checking the rendered form, and the rendered form is exactly what this PR changes.One assertion is deliberately left alone:
test_main.shenv APISIX_DEPLOYMENT_ETCD_HOST;is a hardcoded line in the template, not part of theenvsloop.New cases added to
test_main.shafter "change default env": value with spaces renders quoted and survivesnginx -t; embedded"/\are escaped and survivenginx -t; a value with a control character is rejected atapisix initwithfailed to validate config.Compatibility
Configs that work today are unaffected — for a value with no
\,"or control character, the token nginx parses out of the quoted form is byte-identical to the unquoted one. Configs that were guaranteed to fail at startup now work. The one new hard failure is control characters, which move from "generates an invalid nginx.conf and dies with an obscure nginx syntax error" to a clear schema error at init time.External tooling that greps the generated
conf/nginx.confforenv NAME;will stop matching. That file declares itself read-only and generated, and its format is not a stability promise.Not covered
The template renders several other values unquoted (
user,error_log,access_log,lua_ssl_trusted_certificate,ssl_certificate, ...), which have the same theoretical problem for paths containing spaces. No issue reports on those and the fix surface is independent, so this PR deliberately stays scoped toenvs.Test status
Lua syntax checks and luacheck pass. The
t/clisuite has not been run locally; relying on CI for that.