open-webui / open-webui/terminals

issue: Docker backend publishes every terminal's port 8000 on all host interfaces even when TERMINALS_NETWORK is set

Open Beginner friendly
#57 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
108
Forks
23
Avg merge
12h 8m
Merged PRs (30d)
1

Description

Summary

With TERMINALS_NETWORK set, every terminal container the Docker backend starts still has its port 8000 published on the Docker host, on every interface, at a random high port. The orchestrator does not use that port in this mode, because it reaches the container by name on the named network. Every user's terminal is therefore reachable from any network the Docker host sits on. The terminal's own API key still gates commands and files. The health and config endpoints and the OpenAPI document answer without a key.

Expected Behavior

When TERMINALS_NETWORK names a network, terminal containers are reachable only on that network, by name. The README and the docs describe the setting that way. Containers communicate by name when it is set, and TERMINALS_DOCKER_HOST is described as relevant only without it. No host port is published in that mode. Without a network, the published port is bound to the address the orchestrator uses to reach it, which defaults to 127.0.0.1, rather than to every interface.

Actual Behavior

Running docker port on a terminal container shows port 8000 bound to 0.0.0.0 and to [::] on the host at a random port. The container sits on the named network, and the orchestrator talks to it by name. From a container on a different Docker network, and through the host's LAN address, /health, /api/config, and the OpenAPI document endpoint return 200 without any key. /files/cwd and /execute return 401. The published port has no consumer, since the orchestrator's routing uses the container name whenever a network is configured.

Steps to Reproduce
  1. Run the orchestrator with TERMINALS_BACKEND=docker and TERMINALS_NETWORK set to a user-defined network. The Compose file in the docs does this with open-webui-network.
  2. Connect it to Open WebUI and open a terminal as any user, so one terminal container is provisioned.
  3. On the Docker host, run docker port on that container.
  4. From a container on a different Docker network, or from another machine on the host's LAN, request /health at the host's address and the published port.

Observed: step 3 shows port 8000 bound to 0.0.0.0 and [::], and step 4 returns a status of ok. /api/config and the OpenAPI document endpoint answer the same way, and /files/cwd and /execute return 401.

Logs, Screenshots, and Config
  • Terminals 0.2.3 (ghcr.io/open-webui/terminals:latest, digest 5d2fd44366a4, built 2026-08-31), Docker backend, TERMINALS_NETWORK=owui-t8-net
  • Docker Engine 29.8.0 on Linux
  • Open WebUI 0.11.3

The orchestrator container itself was published on 127.0.0.1 only. The code on main at e15903af is the same as in the image.

$ docker port terminals-21f7d802ec1b-analysts-4360f4a92e62
8000/tcp -> 0.0.0.0:32825
8000/tcp -> [::]:32825
$ docker inspect terminals-21f7d802ec1b-analysts-4360f4a92e62 --format 'PublishAllPorts={{.HostConfig.PublishAllPorts}} NetworkMode={{.HostConfig.NetworkMode}}'
PublishAllPorts=true NetworkMode=owui-t8-net

From a container on the default bridge, which is not the terminals' network, through the Docker host's gateway address (the host's LAN address gave the same results):

$ for p in /health /api/config /openapi.json /files/cwd /execute; do docker run --rm --network bridge curlimages/curl -s -o /dev/null -w "$p %{http_code}\n" http://172.17.0.1:32825$p; done
/health 200
/api/config 200
/openapi.json 200
/files/cwd 401
/execute 401
$ docker run --rm --network bridge curlimages/curl -s http://172.17.0.1:32825/health
{"status":"ok"}
Additional Information

Anchor commit e15903af532d61b8264f354128edbca3e3ebd418 on main. The Docker backend sets PublishAllPorts for every container regardless of the network setting (docker.py#L102) and sets NetworkMode only when a network is named (#L139). The routing code reads the published port only when no network is configured (#L253-L268), so with a network configured the publication is never used. Terminal API keys come from secrets.token_urlsafe(24), so the protected endpoints are not open to guessing. The unauthenticated ones reveal a status line and three feature flags, plus the OpenAPI document.

A minimal fix publishes only when no network is named:

         host_config: dict = {
             "Binds": [f"{host_data_dir}:/home/user"],
-            "PublishAllPorts": True,
         }
+        if not network_name:
+            host_config["PublishAllPorts"] = True

When no network is named, the binding could also be narrowed to the address the orchestrator uses. An explicit PortBindings entry with HostIp set to TERMINALS_DOCKER_HOST (default 127.0.0.1) would do that. I left it out of the diff because that setting may hold a hostname rather than an address.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in terminals/backends/docker.py, especially the host_config setup around line 102, the network configuration around line 139, and routing around lines 253-268. Reproduce with TERMINALS_NETWORK set and inspect docker port, then verify that named-network containers have no published host port while containers without a network retain their expected routing.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, python
Domain
backend, infrastructure, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.