fix(hosting): bind Postgres and the dashboard to loopback by default - #5308
Conversation
…default A self-hoster on a public IP no longer exposes their database (default creds username/password) or an unauthenticated dashboard to the internet. Set POSTGRES_PORT=0.0.0.0:5432 to opt back in. Claude-Session: https://claude.ai/code/session_01XhENr63WL9npkKrJGnzDc1
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The exposure
Every docker-compose file publishes Postgres as
- "${POSTGRES_PORT:-5432}:5432". That default binds 0.0.0.0:5432, so on a host with a public IP the database is reachable from the internet. The shipped env examples ship default credentialsusername/password, so a self-hoster who follows the quickstart hands anyone on the internet a login to their database.Separately, several compose files run the Traefik dashboard and publish it as
- "${TRAEFIK_UI_PORT:-8080}:8080", also bound to 0.0.0.0. Four of them enable the dashboard without authentication (--api.insecure=true, orinsecure: truein the mountedssl/traefik.yml), exposing an unauthenticated admin dashboard to the internet. The other three publish 8080 with no listener today, a dead publish that silently becomes a hole the moment someone adds the flag.The fix
Bind both datastore/admin ports to loopback by default. The public web entrypoint (
TRAEFIK_PORT/:80) is deliberately left untouched.- "${POSTGRES_PORT:-5432}:5432"->- "${POSTGRES_PORT:-127.0.0.1:5432}:5432". The default now binds loopback; the value stays env-overridable.- "${TRAEFIK_UI_PORT:-8080}:8080"->- "127.0.0.1:${TRAEFIK_UI_PORT:-8080}:8080". Hard loopback prefix, since the shipped.env.oss.ghsetsTRAEFIK_UI_PORT=8080explicitly and a:-defaultwould not protect it.Files:
oss/andee/×docker-compose.{gh,gh.ssl,gh.local,dev}.yml(7 total — ee has nogh.ssl).Nothing else changes: no in-network URIs (those hardcode
@postgres:5432), no healthchecks, no.env*example values, no redis/seaweedfs/supertokens.How to opt back in
A self-hoster who genuinely wants remote Postgres access sets it deliberately:
The dashboard is loopback-only by design (it is unauthenticated); reach it over an SSH tunnel, or edit the compose file if you must publish it.
Why this is safe
A
127.0.0.1bind still serves localhost, so local dev, tests, and anything on the host are unaffected. Verified againstPOSTGRES_PORThandling:api/oss/src/utils/env.pyexplicitly documents thatPOSTGRES_PORTonly remaps the host-published port and must not feed connection URIs; in-network URIs hardcode@postgres:5432and healthchecks pass no-p. Re-grepped the current tree to confirm nothing readsPOSTGRES_PORTas a bare integer.docker compose configrenders confirm the change on both editions:POSTGRES_PORT): Postgres publish rendershost_ip: 127.0.0.1; dashboard rendershost_ip: 127.0.0.1; the:80web entrypoint renders with nohost_ip(stays public).POSTGRES_PORT=5434: renderspublished: "5434"with nohost_ip(0.0.0.0) — the remote-access override still works.POSTGRES_PORT=(as shipped in.env.<lic>.gh)::-treats empty as unset, so the loopback default applies.Follow-up (not in this PR)
The opt-in OTel overlay (
hosting/docker-compose/docker-compose.otel.yml) publishes hardcoded4317/4318on 0.0.0.0. It is opt-in and lower priority; left as a noted follow-up.https://claude.ai/code/session_01XhENr63WL9npkKrJGnzDc1