feat(studio): bundle studio-expose bridge + socat (opt-in STUDIO_EXPOSE)
pi-studio binds the container's 127.0.0.1, which a published Docker port can't reach. Add a robust, portable bridge rather than a doc-only one-liner: - Dockerfile.base: add socat (~1 MB, generally useful TCP relay). - rootfs/usr/local/bin/studio-expose: socat TCP relay listening on the container's egress IPv4 (not 0.0.0.0 — that would EADDRINUSE against Studio's loopback listener) forwarding to 127.0.0.1:PORT on the SAME port, so Studio's printed token URL works verbatim. Robust egress-IP detection (hostname -I, loopback-filtered; ip route get fallback), --help, port validation, foreground. - entrypoint-user.sh: opt-in STUDIO_EXPOSE=1 auto-starts the bridge in the background (studio variant only). Default OFF — Studio stays loopback-only (its secure default) unless explicitly opted in. - README: 'Using pi-studio' now documents host-networking (A) and the studio-expose/STUDIO_EXPOSE bridge (B) with a security note; ssh -L for remote, mosh caveat retained. - smoke-test: assert socat + studio-expose present (base-level). - CHANGELOG/AGENTS updated. No tag — stopping for review.
This commit is contained in:
@@ -164,7 +164,7 @@ There is no `--host`/bind flag. This matters for a container: a plain
|
||||
interface, **not** its loopback, so it will not reach Studio. Two paths
|
||||
work:
|
||||
|
||||
**A. Host networking (simplest — recommended on OrbStack / single-host).**
|
||||
**A. Host networking (simplest — OrbStack / single-host, no bridge).**
|
||||
Run the container with host networking so the container's loopback is the
|
||||
host's loopback:
|
||||
|
||||
@@ -175,28 +175,37 @@ services:
|
||||
```
|
||||
|
||||
Then `http://127.0.0.1:8765/?token=…` works in a browser on the Docker
|
||||
host. (Note: host networking changes `host.docker.internal` semantics, so
|
||||
weigh it against the LAN-jump SSH feature if you use that.)
|
||||
host. This is the most secure option (Studio never leaves loopback). Note:
|
||||
host networking changes `host.docker.internal` semantics, so weigh it
|
||||
against the LAN-jump SSH feature if you use that.
|
||||
|
||||
**B. Loopback bridge (portable — bridge networking).** Publish a port and
|
||||
bridge the container's loopback to its external interface with a one-liner
|
||||
(uses the bundled `node`; binds the eth0 IP only, so it never clashes with
|
||||
Studio's own `127.0.0.1:8765` listener):
|
||||
**B. `studio-expose` bridge (portable — any networking mode).** Publish a
|
||||
port and run the bundled `studio-expose` helper, which uses `socat` to
|
||||
bridge the container's loopback to its external interface (binding the
|
||||
egress IP on the same port, so the token URL Studio printed works
|
||||
verbatim):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
devbox:
|
||||
ports:
|
||||
- "127.0.0.1:8765:8765" # host-localhost only
|
||||
environment:
|
||||
- STUDIO_EXPOSE=1 # auto-start the bridge on container boot
|
||||
```
|
||||
|
||||
With `STUDIO_EXPOSE=1`, the entrypoint starts the bridge for you; just run
|
||||
`/studio --port 8765` in your pi session. To bridge manually instead
|
||||
(leave `STUDIO_EXPOSE` unset), run `studio-expose` in a container shell:
|
||||
|
||||
```bash
|
||||
# inside the container, after /studio --port 8765:
|
||||
EXT=$(hostname -i)
|
||||
node -e 'const net=require("net"),p=process.env.STUDIO_PORT||8765,h=process.argv[1];\
|
||||
net.createServer(c=>{const u=net.connect(p,"127.0.0.1");c.pipe(u);u.pipe(c);u.on("error",()=>c.destroy());c.on("error",()=>u.destroy());}).listen(p,h,()=>console.log("bridge "+h+":"+p+" -> 127.0.0.1:"+p));' "$EXT"
|
||||
studio-expose # bridges $STUDIO_PORT (default 8765); --help for details
|
||||
```
|
||||
|
||||
> **Security:** the bridge intentionally exposes Studio beyond loopback;
|
||||
> its tokenized URL is the only auth. Keep the host-side publish on
|
||||
> `127.0.0.1:` and use `ssh -L` for remote access. Default is **off**.
|
||||
|
||||
### Remote host (SSH / mosh)
|
||||
|
||||
When the Docker host is remote, keep Studio on localhost and forward the
|
||||
|
||||
Reference in New Issue
Block a user