Skip to content

feat: share this machine's network with a host from its session - #175

Merged
Petyok merged 2 commits into
developmentfrom
feature/174-share-net
Oct 27, 2026
Merged

Petyok merged 2 commits into
developmentfrom
feature/174-share-net

Conversation

@Petyok

@Petyok Petyok commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Closes #174.

What changed

  • feat: share this machine's network with a host from its session.
    • Key: Alt+Shift+N in an ssh session (rebindable) starts a second ssh connection to the same host, the "sidecar" (src/session/share_net.rs).
    • What the host gets: the sidecar carries only a reverse dynamic forward (-R 127.0.0.1:0), so the host gets a SOCKS5 proxy on its loopback whose traffic leaves from this machine.
    • Session untouched: the session itself is never reconnected, so a root shell stays.
    • Stopping: the same key stops it, and so do closing the tab, the session ending and quitting.
    • Footer: shows Alt+Shift+N share net, ⇄ net ..., ⇄ net :PORT · Alt+Shift+N stop or ⇄ net failed · Alt+Shift+N retry. It stays visible while scrolled back.
    • ssh log: the host's log gets one line with ready-to-paste apt and curl commands.
  • Limits:
    • PermitRemoteOpen allows ports 80 and 443 only. The first entry is passed twice because OpenSSH 8.8 to 9.1 skip it (bz3515); checked on real 8.9 and 9.6 clients.
    • ControlMaster=no and ControlPath=none, because a mux master applies no limit.
    • StrictHostKeyChecking=yes, and ExitOnForwardFailure=no so the user's own ssh_config forwards cannot stop the share.
    • BatchMode when no secret is stored.
  • Login: the stored password or key of the saved host, looked up again because the session drops its copy once authenticated. Jump-host secrets go through the tunnels' askpass staging.
  • Lifecycle:
    • On Linux, PR_SET_PDEATHSIG ends the sidecar with sshub however sshub ends.
    • The sidecar is not in its own process group, so a terminal hangup reaches it.
    • Elsewhere only a SIGKILL of sshub can leave it running.
  • Port: read from the sidecar's own -E log in a private 0700 dir; stderr, where a server banner lands, is never parsed.
  • Refused cases: mosh, local shell and key-push tabs show a notice; the key does nothing until the session is connected.
  • CLI: sshub connect <host> --share-net adds the same options inline.
  • Docs: CHANGELOG (### Added), README feature and session key table, help, man page (--share-net), openwiki sessions-sftp.md and cli.md. All state the security trade-off: while sharing, any user on the host can reach what this machine reaches on ports 80/443.

How tested

  • cargo fmt --check, cargo clippy --all-targets -D warnings, and just test (1740 + 127 + 127) are green.
  • Oracle tests against the tempdir sshd with real ssh and curl, each confirmed to fail with its fix reverted:
    • the allowed ports pass and a non-allowed port is refused;
    • toggle off and session drop end the proxy and the ssh process;
    • a sidecar dies with a SIGKILLed parent;
    • AllowTcpForwarding no shows Failed and the session goes on;
    • a user ControlMaster is not used;
    • the user's own LocalForward does not stop the share;
    • a server banner cannot set the port or fail the share;
    • the startup command never runs in the sidecar;
    • a password host without a stored secret fails fast with the reason;
    • the stored password is looked up again after the session dropped its copy.
  • Real binary in a PTY against a local sshd:
    • https://example.com returned 200 through the proxy, and a non-web port was refused.
    • The sidecar and the proxy port were gone after toggle off, Ctrl+W, closing the terminal, and kill -9 of sshub.
  • The maintainer tried it on a real password host, which found the stored-password bug fixed in this PR.
  • Adversarial review by 5 critics over two rounds. Verified findings fixed:
    • ControlMaster bypass;
    • footer hint evicting dashboard hints;
    • orphaned sidecar;
    • user forwards killing the sidecar;
    • banner forgery;
    • retry needing two presses;
    • starting before connect;
    • host-key reason wording;
    • scrolled footer.

Not changed

  • A host whose password is not stored in SSHub, or that asks for a one-time code, cannot share: the background connection cannot show a prompt, so it fails with the reason. Routing its prompts through SSHub's login modal is follow-up work.
  • On macOS a SIGKILL of sshub can leave the sidecar running. A sshub that dies abruptly leaves a small private log dir in /tmp.
  • At 80 columns the Alt+Shift+N share net hint takes the place of Shift+drag in mouse apps.
  • If the session reconnects after a host-key repair, it comes back unshared.

Written by Claude Opus 5.5 (Oh My Pi) on behalf of the maintainer.

petruha added 2 commits October 27, 2026 23:32
A host without DNS or a way out could not install anything. Alt+Shift+N
in a session starts a second ssh connection to the host that carries
only a reverse dynamic forward (`-R 127.0.0.1:0`): the host gets a
SOCKS5 proxy on its loopback whose traffic leaves from this machine, so
`apt -o Acquire::http::Proxy=socks5h://127.0.0.1:PORT` works there. The
session itself is never touched; the key stops it again, and so do
closing the tab, the session ending and quitting. On Linux the
connection also dies with sshub however sshub ends. It logs in with the
saved host's stored password or key, looked up again because the
session drops its copy once authenticated.

The proxy may open ports 80 and 443 only (PermitRemoteOpen, its first
entry passed twice because OpenSSH 8.8 to 9.1 skip it, bz3515), it never
rides a ControlMaster, and the allocated port is read from the second
connection's own -E log, where a server banner cannot reach. The session
footer shows the state and the host's ssh log the ready-to-paste apt and
curl lines. `sshub connect <host> --share-net` does the same for the
CLI.
The test's ControlPath was the sshd tempdir plus `cm-%C`. Under macOS's
long per-user TMPDIR the 40-character hash and the suffix ssh adds while
binding passed the ~104 byte limit of a Unix socket path, so the master
never started and the test failed on CI. `cm-%p` names its one master.
@Petyok
Petyok merged commit b44c730 into development Oct 27, 2026
6 checks passed
@Petyok
Petyok deleted the feature/174-share-net branch October 27, 2026 19:45
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