Skip to content

fix(standalone): resolve env var references in config pushed via Admin API - #13540

Open
AlinsRan wants to merge 4 commits into
apache:masterfrom
AlinsRan:fix/standalone-admin-env-var
Open

AlinsRan wants to merge 4 commits into
apache:masterfrom
AlinsRan:fix/standalone-admin-env-var

Conversation

@AlinsRan

@AlinsRan AlinsRan commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

In standalone mode, ${{VAR}} references in config pushed via the Admin API are stored literally (never resolved), so a reference in a pattern-constrained field (e.g. proxy-rewrite.uri) is even rejected with 400.

Cause

Standalone has two config sources; only the file path resolves ${{VAR}}:

  • File (apisix.yaml): resolved in core/config_yaml.lua parse(), before schema validation.
  • Admin API (admin/standalone.lua update()): validates and stores the request body without ever calling resolve_conf_var.

The divergence was introduced by #13078, which moved resolution into the file-parse path only.

Fix

update() now resolves ${{VAR}} while parsing the body, the same way the file path does for each format:

  • application/yaml: file.resolve_conf_var_in_text() on the raw text before yaml.load, so YAML quoting decides the type, as in apisix.yaml (retries: ${{N}} is an integer, desc: "${{N}}" stays a string).
  • JSON: file.resolve_conf_var() after decoding, which coerces resolved scalars to their native type (e.g. "2" -> 2), as for apisix.json.

Both the schema check and the stored config see resolved values, and an unresolvable reference (no env var, no :=default) fails with 400 instead of being stored literally.

Scope: this resolves the ${{VAR}} syntax only. $ENV:// / $secret:// references are a separate, lazily-resolved mechanism and are unaffected.

Test

t/admin/standalone.spec.ts, Variable resolution:

  • ${{VAR:=default}} in proxy-rewrite.uri resolves and the route returns the hello body (a literal value would not match);
  • ${{VAR:=2}} into the integer upstream.retries returns 202 (native-type coercion; a literal "2" would fail integer validation);
  • a YAML body with an unquoted retries: ${{VAR:=2}} and a quoted desc: "${{VAR:=123}}" returns 202, and the dump shows retries as 2 and desc as "123";
  • an unresolvable ${{VAR}} returns 400 with failed to resolve variables in config.

All four fail against master's apisix/admin/standalone.lua and pass with this change.

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change (N/A)
  • I have verified that the changes pass the existing tests

@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jun 12, 2026
@AlinsRan
AlinsRan force-pushed the fix/standalone-admin-env-var branch from 3fa7450 to dd45c6a Compare June 12, 2026 06:39
…n API

In standalone mode, the file-based config path resolves ${{VAR}} / $ENV://
references in config_yaml.parse(), but config pushed through the Admin API
(`PUT /apisix/admin/configs`) is JSON-decoded straight into _update_config
and bypassed that step, so the references were kept literal.

Resolve them in the Admin API update path before applying the config, so
both standalone config sources behave consistently. Adds a test that pushes
a route whose proxy-rewrite uri uses a ${{VAR:=default}} reference and
verifies the gateway resolves it.

Signed-off-by: AlinsRan <alinsran@apache.org>
@AlinsRan
AlinsRan force-pushed the fix/standalone-admin-env-var branch from dd45c6a to 83204eb Compare June 12, 2026 08:45
@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 4 weeks if no further activity occurs. If you think that's incorrect or this pull request should instead be reviewed, please simply write any comment. Even if closed, you can still revive the PR at any time or discuss it on the dev@apisix.apache.org list. Thank you for your contributions.

@github-actions github-actions Bot added the stale label Sep 14, 2026
@github-actions

Copy link
Copy Markdown

This pull request/issue has been closed due to lack of activity. If you think that is incorrect, or the pull request requires review, you can revive the PR at any time.

@github-actions github-actions Bot closed this Oct 27, 2026
… file

A YAML body is now substituted before it is parsed, so that quoting decides
the type of a resolved value as it does in apisix.yaml; a JSON body is still
resolved after decoding. The resolution moves to the parse step, which is
where the file path does it.
@AlinsRan AlinsRan reopened this Oct 29, 2026
@AlinsRan AlinsRan removed the stale label Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant