Fix --via/-J grammar and --force process-group kill in spec
Found both while translating the spec into code: - `ssh -J a,b,c` alone isn't valid - ssh -J only carries jump hosts before the final hop; it still needs a positional destination. --via's last entry has to double as that target, which also makes --via mandatory (there's no other field naming "the host ssh connects to"), reversing the earlier "no" in the required column. - A plain single-pid SIGKILL on `close --force` would leave the `ssh` child orphaned, since a killed process can't forward anything to it. Documents sending SIGKILL to the whole process group instead, relying on the supervisor's own setsid() call making it the group leader.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
||||
|
||||
target/
|
||||
*.lock
|
||||
!Cargo.lock
|
||||
|
||||
@@ -105,10 +105,18 @@ re-exec of its own binary:
|
||||
supervisor loop inline, attached to the current session.
|
||||
4. The supervisor's loop: spawn `ssh` with the flags in §3.1, wait on it,
|
||||
classify the exit per §4, sleep/backoff or give up accordingly, and
|
||||
rewrite the instance file after every state change. `close` sends
|
||||
SIGTERM to the *supervisor* pid (not raw `ssh`), which forwards it to
|
||||
its `ssh` child, waits briefly, then exits and removes its lock and
|
||||
instance file. `--force` skips the wait and SIGKILLs both immediately.
|
||||
rewrite the instance file after every state change. A signal handler
|
||||
installed on the supervisor (not exit-code inference on `ssh` itself,
|
||||
which is unreliable) is what distinguishes an intentional `close` from
|
||||
a dropped connection: `close` sends SIGTERM to the *supervisor* pid,
|
||||
whose handler kills its `ssh` child, waits briefly, deletes its lock and
|
||||
instance file, and exits — any *other* way the `ssh` child ends (any
|
||||
exit status) is treated as a failure to reconnect from, per §4. Because
|
||||
`step 2`'s `setsid()` makes the supervisor its own process group leader
|
||||
and `ssh` inherits that group, `--force` sends SIGKILL to the whole
|
||||
group (`kill(-pid, SIGKILL)`) rather than just the supervisor pid — a
|
||||
plain single-pid SIGKILL would leave `ssh` running, orphaned and
|
||||
untracked, since a killed process can't forward anything to its child.
|
||||
A supervisor that exits on its own due to a fatal failure (§4.2) leaves
|
||||
the instance file in place with `state: error` rather than deleting it,
|
||||
so the failure stays visible to `status`/`list` until the user acts.
|
||||
@@ -124,11 +132,24 @@ regardless of the user's own `~/.ssh/config`:
|
||||
| `-o ExitOnForwardFailure=yes` | Makes `ssh` exit non-zero immediately if the requested forward can't be bound, instead of staying up as a plain (forward-less) session that *looks* healthy. |
|
||||
| `-o ConnectTimeout=10` | Bounds how long one connection attempt can hang before porthole's own backoff logic (§4.1) gets a turn. |
|
||||
| `-N` | No remote command — porthole only ever wants the forward, never a shell. |
|
||||
| `-J <via>` | Only when `--via` is set. The comma-joined hop list is passed to `ssh -J` verbatim — porthole does not re-implement jump-host chaining itself (see §5.1). |
|
||||
| `-J <hops>` + positional target | See below — `--via`'s *last* hop is the actual connection target, not another jump. |
|
||||
|
||||
`ServerAliveInterval` comes from the profile's `keepalive` field (not
|
||||
hardcoded), so it stays user-tunable.
|
||||
|
||||
**`--via` → `ssh` argument translation:** `ssh -J a,b,c` is not itself a
|
||||
valid invocation — `-J` only ever carries jump hosts *before* the final
|
||||
hop; `ssh` still needs a positional `destination` to actually connect (and
|
||||
run the forward from). So porthole splits `--via`'s comma list at the last
|
||||
entry: everything before it becomes `-J`'s value (omitted entirely if
|
||||
`--via` has only one hop), and the last entry becomes `ssh`'s positional
|
||||
target argument. `--via jumpbox` → `ssh ... jumpbox` (no `-J`). `--via
|
||||
bastion1,bastion2` → `ssh -J bastion1 ... bastion2` (connect through
|
||||
bastion1, forward runs from bastion2). This is also why `--via` is
|
||||
**required**, not optional (see §5.1) — there is no other field
|
||||
representing "the host `ssh` actually connects to"; `--via`'s last hop
|
||||
*is* that field.
|
||||
|
||||
---
|
||||
|
||||
## 4. Reconnect & failure handling
|
||||
@@ -183,7 +204,7 @@ Saves a new profile. Does **not** open it.
|
||||
| `-l, --local` | `[bind:]port:host:hostport` | one of `-l/-r/-d` | — | Local forward: your machine → remote |
|
||||
| `-r, --remote` | `[bind:]port:host:hostport` | one of `-l/-r/-d` | — | Remote forward: remote → your machine |
|
||||
| `-d, --dynamic` | `[bind:]port` | one of `-l/-r/-d` | — | Dynamic forward (SOCKS proxy) |
|
||||
| `--via` | `[user@]host[:port][,...]` | no | — | SSH jump-host chain, comma-separated; passed to `ssh -J` verbatim (§3.1) |
|
||||
| `--via` | `[user@]host[:port][,...]` | **yes** | — | Comma-separated hop chain; the *last* hop is the actual `ssh` connection target, any before it are `-J` jumps (§3.1) |
|
||||
| `-u, --user` | `user` | no | current user / ssh_config | Default user for the final target and any `--via` hop that doesn't specify its own |
|
||||
| `-i, --identity` | `path` | no | ssh_config default | |
|
||||
| `-p, --port` | `port` | no | `22` | SSH port on the final target only — a `--via` hop needs its own inline `:port` if it isn't 22 |
|
||||
@@ -202,8 +223,9 @@ error.
|
||||
stored verbatim as whichever `-l/-r/-d` payload was given (without the
|
||||
flag itself) — `kind` records which one it was, so re-deriving the right
|
||||
`-L`/`-R`/`-D` flag at `open` time is a lookup, not a re-parse.
|
||||
- `--via` hosts resolved/checked against `~/.ssh/config` if present, but
|
||||
not required to exist there.
|
||||
- `--via` requires at least one hop (its last entry is the mandatory
|
||||
connection target, see §3.1); hosts are resolved/checked against
|
||||
`~/.ssh/config` if present, but not required to exist there.
|
||||
|
||||
**Examples:**
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user