docs(hermes): persistent SSH-tunnel ingress design for Hermex (tunnel user + restricted key options)
Server-side implementation landed on Dankclaw: dedicated 'tunnel' account (nologin shell, password-locked, key-only auth), sshd drop-in 99-tunnel.conf scoping AllowTcpForwarding to that user with PermitTTY off, and permitopen="localhost:8787" key-option guidance so a leaked tunnel key can only forward to the webui port. WebUI stays loopback-only published.
This commit is contained in:
1 parent
9d678122d7
commit
c527f35bd4
1 file changed
+28
-15
@@ -54,27 +54,40 @@ gateway's `0600` state files.
|
|||||||
- The healthcheck `start_period` is 600 s because the very first boot spends
|
- The healthcheck `start_period` is 600 s because the very first boot spends
|
||||||
minutes pip-installing into a fresh venv.
|
minutes pip-installing into a fresh venv.
|
||||||
|
|
||||||
## Remote access (Hermex on iOS, from anywhere)
|
## Remote access (Hermex on iOS — persistent SSH tunnel)
|
||||||
|
|
||||||
Only port `22` is forwarded to Dankclaw at the router, so the webui is
|
Only port `22` is forwarded to Dankclaw at the router, so the webui is
|
||||||
reached through an SSH local forward — no extra router ports, no third-party
|
reached through an SSH **local forward** — no extra router ports, no
|
||||||
relay (no Tailscale, no Cloudflare):
|
third-party relay (no Tailscale, no Cloudflare). Implemented 2026-08-31:
|
||||||
|
|
||||||
1. On the iPhone, open **Termius** (or any SSH client with local port
|
- Dedicated `tunnel` account on Dankclaw (`/usr/sbin/nologin` shell, password
|
||||||
forwarding) and connect to `<dankclaw-public>:22` with the usual key.
|
locked). Authentication is key-only; the account can do nothing except
|
||||||
2. On that connection, add a **local forward**: `127.0.0.1:8787` →
|
authenticate — `ForceCommand /usr/sbin/nologin` kills any session channel
|
||||||
`127.0.0.1:8787`. The forward runs inside the encrypted SSH session;
|
while the transport stays up for port forwarding.
|
||||||
nothing else on the phone routes through it.
|
- sshd drop-in `/etc/ssh/sshd_config.d/99-tunnel.conf` scopes
|
||||||
3. In Hermex, set the server URL to `http://localhost:8787` and sign in with
|
`AllowTcpForwarding yes` to that user only (plus `PermitTTY no`,
|
||||||
the webui password.
|
`X11Forwarding no`).
|
||||||
|
- Key options on the authorized key pin the blast radius:
|
||||||
The tunnel lives as long as the SSH session on the phone — iOS does not allow
|
`command="/usr/sbin/nologin",no-pty,restrict,port-forwarding,permitopen="localhost:8787"`
|
||||||
a true background daemon, so connect before you use Hermex.
|
— a leaked tunnel key can only forward to `localhost:8787`, never act as a
|
||||||
|
general SOCKS/SSH proxy.
|
||||||
|
- On the phone (client side): create a key on-device (private key never
|
||||||
|
leaves the device), hand the **public** key to the admin who appends it to
|
||||||
|
`/home/tunnel/.ssh/authorized_keys`, then connect from the SSH client:
|
||||||
|
- In-network target `192.168.0.143:22`, off-network target the public
|
||||||
|
hostname (router forwards only port 22 → Dankclaw) — same forward config
|
||||||
|
otherwise.
|
||||||
|
- Local forward `127.0.0.1:8787 → localhost:8787`; Hermex server URL is then
|
||||||
|
`http://localhost:8787` in both locations.
|
||||||
|
- For a *persistent* tunnel on iOS, Secure ShellFish's personal-VPN mode
|
||||||
|
(Network Extension keeps the tunnel up and routes `127.0.0.0/8` through
|
||||||
|
it); Termius works but only while its session is connected — iOS allows no
|
||||||
|
background SSH daemon.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph LR
|
graph LR
|
||||||
A[Hermex app] -->|http://localhost:8787| B[SSH local forward on iPhone]
|
A[Hermex app] -->|http://localhost:8787| B[SSH local forward on iPhone<br/>persistent via personal VPN]
|
||||||
B ==>|encrypted SSH to public IP:22| C[Dankclaw sshd]
|
B ==>|encrypted SSH to port 22| C[Dankclaw sshd<br/>tunnel user, forced nologin]
|
||||||
C --> D["hermes-webui 127.0.0.1:8787"]
|
C --> D["hermes-webui 127.0.0.1:8787"]
|
||||||
D --> E["/home/dank/hermes-data (shared agent state)"]
|
D --> E["/home/dank/hermes-data (shared agent state)"]
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in new issue
Block a user