Skip to content

nginx: allow /.well-known/* to reach the app (RFC 8615)#9

Merged
sylvesterdamgaard merged 1 commit into
mainfrom
fix/nginx-well-known-allow
May 10, 2026
Merged

nginx: allow /.well-known/* to reach the app (RFC 8615)#9
sylvesterdamgaard merged 1 commit into
mainfrom
fix/nginx-well-known-allow

Conversation

@sylvesterdamgaard
Copy link
Copy Markdown
Contributor

The catch-all `location ~ /\.` block was 404'ing every `/.well-known/*` request before forwarding to PHP. That breaks OIDC discovery, OAuth metadata, security.txt and ACME http-01 — exactly the paths RFC 8615 reserves.

Surfaced while preparing vault bootstrap: `id.cbox.systems/.well-known/jwks.json` returned the nginx default-error page despite Laravel having `oauth.jwks` registered. Vault's OIDC backend can't be configured without id's discovery endpoint reachable.

Summary

  • Add `location ^~ /.well-known/` before the dotfile catch-all in both `default.conf` and `default-rootless.conf`. `^~` short-circuits regex evaluation when the prefix matches, so the deny-all stops winning for this path. Apps that don't register a `/.well-known/*` route still get a Laravel 404 (404-from-app), not a 403-from-nginx.
  • Three new `test-security.sh` assertions: `/.well-known/openid-configuration` must not be 403, response body must not be nginx default error, `/.env` regression guard.

Test plan

  • `docker run nginx:1.27-alpine nginx -t` against both configs — syntax clean.
  • e2e suite (`tests/e2e/scenarios/test-security.sh`) — runs in CI.

The catch-all dotfile block (`location ~ /\.`) was 404'ing every
request whose path started with `/.well-known/` before nginx ever
forwarded it to PHP. That broke OIDC discovery, OAuth authorization-
server metadata, security.txt, and ACME http-01 challenges — anything
RFC 8615 says belongs in the well-known namespace.

Symptom that surfaced this: id.cbox.systems/.well-known/jwks.json
returned `<center>nginx</center>` 404 even though Laravel had the
route registered (`oauth.jwks → JwksController`). Vault's OIDC
backend can't be configured without id's discovery endpoint
reachable, so this is a hard prerequisite for vault bootstrap.

Fix:

* Add `location ^~ /.well-known/` BEFORE the regex catch-all in
  both `default.conf` and `default-rootless.conf`. The `^~`
  modifier short-circuits regex evaluation when the prefix matches,
  so the dotfile block stops winning for this specific path. Apps
  that don't register a /.well-known/* route still get a Laravel
  404 (404-from-app), not a 403-from-nginx.

* Three new test-security.sh assertions:
  - /.well-known/openid-configuration must NOT return 403
  - response body must NOT be the nginx default-error page
  - /.env must STILL be blocked (regression guard against the
    well-known allow over-broadening into other dotfiles)

Verified via `docker run nginx:1.27-alpine nginx -t` against both
configs — syntax clean.
@sylvesterdamgaard sylvesterdamgaard merged commit 063e8a3 into main May 10, 2026
4 checks passed
@sylvesterdamgaard sylvesterdamgaard deleted the fix/nginx-well-known-allow branch May 10, 2026 17:33
sylvesterdamgaard added a commit that referenced this pull request May 10, 2026
PR #9 fixed default.conf and default-rootless.conf — the static, baked-
into-image versions. But the running container generates its config
from default.conf.template via envsubst at startup, overwriting the
baked default.conf with the templated output. The fix needs to live
on the template path or it never reaches running pods.

Verified live: a fresh id pod showed the correct `location ^~
/.well-known/` in default.conf (baked) but a regenerated
runtime default.conf without the block. /.well-known/openid-configuration
remained 404. Adding the well-known allow to the template, with
${NGINX_TRY_FILES} for the framework's index target, so the
runtime config gets it.

Build-and-push needs another workflow_dispatch since the workflow
doesn't auto-trigger on path changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant