Skip to content

chore(docker-compose): tighten host port exposure (LAN-safe defaults)#644

Merged
neoneye merged 3 commits intomainfrom
chore/restrict-default-bind-host
Apr 28, 2026
Merged

chore(docker-compose): tighten host port exposure (LAN-safe defaults)#644
neoneye merged 3 commits intomainfrom
chore/restrict-default-bind-host

Conversation

@neoneye
Copy link
Copy Markdown
Member

@neoneye neoneye commented Apr 28, 2026

Summary

The compose stack used Docker's short-form ports: everywhere, which binds the host side to 0.0.0.0. Combined with several permissive defaults (admin/admin on the frontend, PLANEXE_MCP_REQUIRE_AUTH=false with no API key on mcp_cloud, default planexe/planexe postgres credentials, no auth at all on worker_plan), the whole stack was reachable from the LAN out of the box. This PR closes that and also drops host port mappings for the two services that nothing outside the docker network should ever call.

Changes (docker-compose.yml)

  • database_postgresports: removed entirely. Reachable as database_postgres:5432 inside the docker network. Side benefit: no more host-side collision with a developer's local Postgres on 5432; the prior PLANEXE_POSTGRES_PORT=5433 workaround is no longer needed.
  • worker_planports: removed entirely. frontend_multi_user reaches it as worker_plan:8000 inside the docker network.
  • frontend_multi_user (5001) and mcp_cloud (8001) — still published, now bound to ${PLANEXE_BIND_HOST:-127.0.0.1}.
  • New "Published ports / bind host" comment block at the top of the file documenting the convention.

Docs (docker-compose.md)

  • TL;DR rewritten.
  • Old "Port conflict with local Postgres" section replaced by the new "Published ports / bind host" section, including a worked docker-compose.override.yml example for ad-hoc host access (DBeaver, psql, curl to worker_plan) during debugging.
  • Per-service notes and the example docker compose ps output updated to match.

Opting back into LAN access

For the two published services (testing on phone, Claude Desktop on another machine):

export PLANEXE_BIND_HOST=0.0.0.0
docker compose up

…after also setting strong PLANEXE_FRONTEND_MULTIUSER_ADMIN_PASSWORD, PLANEXE_MCP_API_KEY, and PLANEXE_MCP_REQUIRE_AUTH=true.

To poke at the unpublished services from the host, either docker compose exec or drop a docker-compose.override.yml (template in the docs).

Test plan

  • docker compose config shows only frontend_multi_user and mcp_cloud published, both with host_ip: 127.0.0.1
  • database_postgres / worker_plan no longer have a published: entry in docker compose config
  • Stack comes up clean via ./rebuild.sh even on a host with a local Postgres listening on 5432 (the conflict that motivated this change)
  • http://127.0.0.1:5001 still serves the frontend
  • PLANEXE_BIND_HOST=0.0.0.0 docker compose up makes frontend reachable from another LAN device

neoneye and others added 3 commits April 28, 2026 23:11
The stack ships with several permissive defaults that are fine for
localhost-only development but become a foot-gun on a shared LAN:
- frontend_multi_user defaults to admin / admin
- mcp_cloud defaults to PLANEXE_MCP_REQUIRE_AUTH=false with empty API key
- database_postgres defaults to user/password planexe / planexe
- worker_plan has no auth at all

Previously every published port bound to 0.0.0.0 (Docker's default for
the short-form ports: syntax), so anyone on the same network could hit
them.

Now:
- database_postgres and worker_plan are pinned to 127.0.0.1; nothing
  outside the host should call them directly.
- frontend_multi_user and mcp_cloud honor PLANEXE_BIND_HOST (default
  127.0.0.1). Set PLANEXE_BIND_HOST=0.0.0.0 to expose them to the LAN
  after also setting strong credentials / API keys.

Documented at the top of docker-compose.yml next to the existing port-
conflict note.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_HOST

Mirror the docker-compose.yml change in docker-compose.md:
- TL;DR notes the new default and links to the new section.
- New "Bind host (LAN exposure)" section spells out which services
  ship with permissive defaults, what is now bound to 127.0.0.1, and
  how to opt back into LAN access via PLANEXE_BIND_HOST=0.0.0.0
  (with the credentials checklist that should accompany it).
- Per-service notes updated to reflect bind-host behavior.
- Example "docker compose ps" output shows 127.0.0.1 mappings instead
  of 0.0.0.0 / [::].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
database_postgres and worker_plan are only ever called by other
containers in the stack, so there is no reason to publish them to the
host at all. Remove their `ports:` directives.

Side benefits:
- Eliminates the long-standing collision with a local Postgres on
  5432 that previously required PLANEXE_POSTGRES_PORT=5433 to work
  around. The conflict cannot happen if the port is never published.
- Reduces attack surface further: even on 127.0.0.1 the postgres
  default-password and unauthenticated worker_plan are no longer
  reachable from any host process.

frontend_multi_user (5001) and mcp_cloud (8001) keep host mappings
since the user's browser / external MCP clients need them; they
remain bound to PLANEXE_BIND_HOST (default 127.0.0.1).

Documentation in docker-compose.md updated to match, with a worked
example showing how to add a docker-compose.override.yml for ad-hoc
host access during debugging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@neoneye neoneye changed the title chore(docker-compose): bind published ports to 127.0.0.1 by default chore(docker-compose): tighten host port exposure (LAN-safe defaults) Apr 28, 2026
@neoneye neoneye merged commit aed1acd into main Apr 28, 2026
3 checks passed
@neoneye neoneye deleted the chore/restrict-default-bind-host branch April 28, 2026 22:18
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