From 04ddc6bef8bc585f2fdcdb42c502200a4d6c1782 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Mon, 17 Aug 2026 19:28:09 +0200 Subject: [PATCH] chore: update cors for pg routes (#49136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Bug fix - config hardening ## What is the current behavior? CORS is applied at the global level in a permissive mode ## What is the new behavior? Self-hosted envoy config should apply CORS to the `/pg` routes. These should only be called from the studio dashboard (when called via a browser). uses `SUPABASE_PUBLIC_URL`, which should mean this isn't a breaking change. ## Summary by CodeRabbit * **Security & Access** * Added stricter CORS controls for the `/pg/` route. * Requests are limited to the configured public URL and localhost origins. * Standard HTTP methods and headers are supported, with preflight responses cached for one hour. * **Documentation** * Updated self-hosting guidance to describe the `/pg/` route’s CORS policy. --- .../content/guides/self-hosting/self-hosted-envoy.mdx | 2 ++ docker/docker-compose.yml | 1 + docker/volumes/api/envoy/docker-entrypoint.sh | 1 + docker/volumes/api/envoy/lds.template.yaml | 11 +++++++++++ 4 files changed, 15 insertions(+) diff --git a/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx b/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx index 150cb4a7c54..f4369c1fd3d 100644 --- a/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx +++ b/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx @@ -199,6 +199,8 @@ The gateway applies a permissive CORS policy at the virtual-host level: This matches both the current Supabase platform behavior and the previous Kong-based gateway. The auth boundary for Supabase APIs is the `apikey` header rather than the request origin. +The `/pg/` route (direct Postgres introspection via `meta`, gated by the service role key) is the exception: it carries its own stricter per-route CORS override, restricted to `SUPABASE_PUBLIC_URL` and `localhost`/`127.0.0.1` origins, since there's no legitimate reason for this route to be called from an arbitrary third-party origin. + If you customize the `cors:` block in `lds.template.yaml` to enable `allow_credentials: true`, you must restrict `allow_origin_string_match` to specific origins - browsers (and Envoy) reject the combination of credentials with a wildcard origin. diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 33972e9b136..d05807667aa 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -101,6 +101,7 @@ services: SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-} ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-} SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-} + SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} DASHBOARD_USERNAME: ${DASHBOARD_USERNAME} DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD} entrypoint: ["/bin/sh", "/docker-entrypoint.sh"] diff --git a/docker/volumes/api/envoy/docker-entrypoint.sh b/docker/volumes/api/envoy/docker-entrypoint.sh index 7836038590f..2c67584a30e 100755 --- a/docker/volumes/api/envoy/docker-entrypoint.sh +++ b/docker/volumes/api/envoy/docker-entrypoint.sh @@ -15,6 +15,7 @@ sed -e "s|\${ANON_KEY}|${ANON_KEY}|g" \ -e "s|\${SERVICE_ROLE_KEY_ASYMMETRIC}|${SERVICE_ROLE_KEY_ASYMMETRIC}|g" \ -e "s|\${SUPABASE_PUBLISHABLE_KEY}|${SUPABASE_PUBLISHABLE_KEY}|g" \ -e "s|\${SUPABASE_SECRET_KEY}|${SUPABASE_SECRET_KEY}|g" \ + -e "s|\${SUPABASE_PUBLIC_URL}|${SUPABASE_PUBLIC_URL}|g" \ -e "s|\${DASHBOARD_BASIC_AUTH}|${DASHBOARD_BASIC_AUTH}|g" \ /etc/envoy/lds.template.yaml > /etc/envoy/lds.yaml diff --git a/docker/volumes/api/envoy/lds.template.yaml b/docker/volumes/api/envoy/lds.template.yaml index d6f5f314a9b..1a3c3451886 100644 --- a/docker/volumes/api/envoy/lds.template.yaml +++ b/docker/volumes/api/envoy/lds.template.yaml @@ -523,6 +523,17 @@ resources: '@type': >- type.googleapis.com/envoy.config.route.v3.FilterConfig disabled: true + envoy.filters.http.cors: + '@type': >- + type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy + allow_origin_string_match: + - exact: '${SUPABASE_PUBLIC_URL}' + - safe_regex: + regex: 'https?://(localhost|127\.0\.0\.1)(:[0-9]+)?' + allow_methods: "GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD" + allow_headers: "*" + expose_headers: "*" + max_age: "3600" - match: prefix: /api/mcp