Add docs/THREAT_MODEL.md deriving a publishable threat model from NATIVE_V1_SPEC.md sections 4-6: assets, in/out-of-scope attackers, security properties vs SSH (including where Dosh aims to exceed it), cryptographic building blocks as implemented, and an honest accepted-residual-risks / known-gaps section. Refresh docs/PUBLIC_READINESS.md: feature matrix now reflects native auth, host-key trust, authorized_keys policy, doctor, and -L/-R/-D forwarding; add a per-item "Native v1 verification checklist status" table mapping spec section 16 to done/in-progress/pending from code. Update README.md status with a native v1 section and honest claim posture pointing to THREAT_MODEL.md. Annotate NATIVE_V1_SPEC.md with a v1 status block summarizing milestone progress. Point SPEC.md security model and header at native auth and the threat model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
187 lines
11 KiB
Markdown
187 lines
11 KiB
Markdown
# Dosh Public Readiness
|
|
|
|
Dosh's defensible public claim is fast terminal attach and reconnect. It should not
|
|
claim full Mosh replacement status until the feature matrix below is green and the
|
|
comparison benchmark is reproducible outside the author's homelab.
|
|
|
|
The plan for replacing the day-to-day SSH workflow with native Dosh authentication
|
|
and forwarding is specified in `docs/NATIVE_V1_SPEC.md`, and the published threat
|
|
model is in `docs/THREAT_MODEL.md`. Native v1 is now substantially implemented:
|
|
native key-exchange + user auth, host-key pinning/trust, `-L`/`-R`/`-D` forwarding,
|
|
and `dosh doctor` all exist (see the feature matrix and the verification-checklist
|
|
status table below). It is **not yet fully verified**: per-IP token-bucket rate
|
|
limiting, protocol-version negotiation hardening, fuzzing in CI, and external review
|
|
are still open. Until the verification checklist (`NATIVE_V1_SPEC.md` section 16) is
|
|
green and that review is done, Dosh's defensible public security claim remains fast
|
|
encrypted native attach/reconnect with SSH-equivalent transport security and an
|
|
explicit SSH bootstrap fallback — not a fully verified, externally reviewed SSH
|
|
replacement.
|
|
|
|
## Objective Benchmarks
|
|
|
|
Run the same-host SSH comparison:
|
|
|
|
```bash
|
|
make bench-docker-ssh
|
|
```
|
|
|
|
Run the Dosh, SSH, and Mosh comparison:
|
|
|
|
```bash
|
|
make bench-docker-mosh
|
|
```
|
|
|
|
The Mosh-inclusive target builds one Docker image with OpenSSH, Mosh, `dosh-server`,
|
|
and `dosh-auth`. It uses the same generated key, localhost network path, SSH server,
|
|
and container for all samples.
|
|
|
|
Reported metrics:
|
|
|
|
| Metric | Meaning |
|
|
| --- | --- |
|
|
| `ssh_true_ms` | Time to run `ssh host true` with the same key/options. |
|
|
| `dosh_attach_ms` | Time for `dosh-client --attach-only` to render the first frame and detach on the configured path. With `--no-cache`, this is cold SSH bootstrap plus UDP attach. |
|
|
| `dosh_cached_attach_ms` | Time for a warmed Dosh client to attach using cached UDP credentials/tickets, render the first frame, and detach. This is the fast-path claim and should be approximately network RTT plus local process/render overhead. |
|
|
| `mosh_start_true_ms` | Time for `mosh host -- true` to bootstrap, run `true`, and exit. |
|
|
|
|
These are not identical workloads. They are still useful because they measure the
|
|
startup path each tool must pay before useful remote work begins. Public numbers
|
|
must include the command, machine, OS, CPU, network path, and sample count.
|
|
|
|
Do not cite cold `dosh_attach_ms` as the core speed claim. Cold Dosh still pays SSH
|
|
startup/authentication. Cite `dosh_cached_attach_ms` for repeat attach/reconnect
|
|
speed, and cite cold `dosh_attach_ms` only to show that fallback remains competitive
|
|
with ordinary SSH.
|
|
|
|
## Feature Matrix
|
|
|
|
| Feature | Mosh | Dosh now | Public status |
|
|
| --- | --- | --- | --- |
|
|
| SSH-based first authentication | yes | yes | ready |
|
|
| Native UDP key auth (no SSH per attach) | no | yes, Ed25519 via ssh-agent or encrypted key | implemented; pending full verification |
|
|
| Dosh host-key pinning and trust | no | yes, `known_hosts` + `dosh trust` + mismatch hard-fail | implemented |
|
|
| `authorized_keys` policy enforcement | no | yes, `from=`/`no-port-forwarding`/`permitopen=`, unsupported fail closed | implemented |
|
|
| `dosh doctor` diagnostics | no | yes, config/auth/UDP/forwarding-policy check | implemented |
|
|
| Encrypted UDP terminal data | yes | yes | ready |
|
|
| Roaming by client address change | yes | yes | needs more hostile-network tests |
|
|
| Survive sleep or network loss | yes | yes | needs long-running soak tests |
|
|
| Fast repeat attach without SSH | no | yes, via attach tickets | core differentiator |
|
|
| Resident server daemon | no | yes | core differentiator |
|
|
| One UDP port for all sessions | port range by default | yes | ready |
|
|
| Fresh session by default | yes | yes | ready |
|
|
| Named persistent sessions | no built-in shared session model | yes | ready |
|
|
| Multiple clients on one session | no | yes | needs conflict-policy docs |
|
|
| View-only clients | no | yes | ready |
|
|
| Full-screen TUI correctness | yes | improving | must stay green before public push |
|
|
| Predictive local echo | mature | guarded printable-only opt-in | not parity |
|
|
| Non-destructive disconnect UI | yes | not currently | needed |
|
|
| Unicode edge-case handling | strong | basic terminal emulator dependent | not parity |
|
|
| X11 forwarding | no | no | non-goal unless tunneled separately |
|
|
| SSH agent forwarding | no | no | planned as forwarding channel |
|
|
| Local TCP forwarding, `-L` | no | yes, native encrypted stream mux | implemented; needs hostile-network tests |
|
|
| Remote TCP forwarding, `-R` | no | yes, loopback bind by default | implemented; needs hostile-network tests |
|
|
| Dynamic SOCKS forwarding, `-D` | no | yes, SOCKS5 over native streams | implemented; needs hostile-network tests |
|
|
| Forward-only / background forwarding, `-N` / `-f` | no | yes, `-f` requires `-N` | implemented |
|
|
| Per-stream flow control / terminal priority | no | yes, windowed credit per stream | implemented; needs load tests |
|
|
|
|
## SSH Config Inheritance
|
|
|
|
Dosh intentionally delegates SSH parsing and authentication to OpenSSH. Bootstrap
|
|
uses the user's normal `ssh` command, so `~/.ssh/config` options such as `Host`,
|
|
`HostName`, `User`, `Port`, `IdentityFile`, `ProxyJump`, and `UserKnownHostsFile`
|
|
continue to apply.
|
|
|
|
Dosh also calls `ssh -G <alias>` to infer the UDP target host when no `dosh_host` is
|
|
configured. To write explicit Dosh host entries from SSH aliases:
|
|
|
|
```bash
|
|
dosh import-ssh palav homelab
|
|
```
|
|
|
|
This appends entries to `~/.config/dosh/hosts.toml` without trying to become an
|
|
OpenSSH config parser.
|
|
|
|
## Forwarding (Implemented)
|
|
|
|
SSH forwarding cannot be copied by keeping the bootstrap SSH connection open,
|
|
because that would remove Dosh's fast reconnect advantage and would break after
|
|
roaming. Dosh forwarding therefore runs as native encrypted stream channels over the
|
|
Dosh transport, and is now implemented.
|
|
|
|
| CLI | Meaning |
|
|
| --- | --- |
|
|
| `dosh -L 8080:127.0.0.1:80 host` | Local listener on the client; server connects to target. |
|
|
| `dosh -R 8080:127.0.0.1:80 host` | Remote listener on the server (loopback by default); client connects to target. |
|
|
| `dosh -D 1080 host` | SOCKS5 listener on the client; server connects to whatever the SOCKS request names. |
|
|
|
|
Implemented:
|
|
|
|
- `StreamOpen`/`StreamOpenOk`/`StreamOpenReject`/`StreamData`/`StreamWindowAdjust`/
|
|
`StreamEof`/`StreamClose` packet types.
|
|
- Per-stream windowed flow control (initial 1 MiB credit) separate from terminal
|
|
frames, so bulk forwarding does not block PTY input/output.
|
|
- Forwarding bound to the native-authenticated user; forwarding refuses to run under
|
|
`--local-auth` and requires the native auth path.
|
|
- Server-side policy enforcement: `allow_tcp_forwarding`, `allow_remote_forwarding`,
|
|
loopback-only remote bind unless `allow_remote_non_loopback_bind`, plus per-key
|
|
`no-port-forwarding` and `permitopen=`.
|
|
- `-N` forward-only (no PTY) and `-f` background (requires `-N`, backgrounds only
|
|
after listeners are ready).
|
|
|
|
Still open before claiming forwarding parity:
|
|
|
|
- A dedicated dropped/reordered/replayed-UDP forwarding test suite.
|
|
- Load tests proving large forwarded transfers add no visible terminal input lag.
|
|
|
|
## Native v1 Verification Checklist Status
|
|
|
|
This maps each item in `NATIVE_V1_SPEC.md` section 16 to its status based on what the
|
|
code in `src/` actually does. Done = implemented and exercised by tests or obvious
|
|
from code; in progress = partially implemented; pending = not yet implemented.
|
|
|
|
| Spec section 16 item | Status | Evidence / note |
|
|
| --- | --- | --- |
|
|
| Unknown host key fails unless TOFU explicitly enabled | done | Client refuses `KnownHostStatus::Unknown` unless `trust_on_first_use`. |
|
|
| Known host-key mismatch hard fails | done | `KnownHostStatus::Mismatch` aborts; `trust_host` refuses overwrite without `--replace`. |
|
|
| Native Ed25519 auth via ssh-agent | done | `src/ssh_agent.rs` signs the user-auth transcript. |
|
|
| Native Ed25519 auth via encrypted key prompt | done | `load_ed25519_identity_with_passphrase` decrypts OpenSSH keys. |
|
|
| Removed authorized key can no longer authenticate | done | Covered by `native_user_auth_accepts_authorized_key_and_rejects_removed_key`. |
|
|
| Unsupported authorized-key options fail closed | done | `ensure_native_allowed` bails on any unsupported option. |
|
|
| Replayed handshake packets rejected | done | Handshake is transcript-bound and signature-verified; pending entries TTL-evicted. |
|
|
| Replayed transport packets rejected | done | `ReplayWindow` (128-wide) over per-direction counter. |
|
|
| Stale encrypted packets after reconnect ignored, not fatal | done | `session_key_id` mismatch drops the packet instead of erroring fatally. |
|
|
| Client IP/port change preserves session | done | Server matches by `ClientId`/session key id, updates endpoint. |
|
|
| Native cold auth beats cold `ssh host true` | pending | Benchmark gate not yet run for native cold path (Track C / `BENCHMARKS.md`). |
|
|
| Cached attach near network RTT | pending | Same benchmark dependency. |
|
|
| `-L` works without delaying terminal input | in progress | Implemented with per-stream window; load proof pending. |
|
|
| `-R` enforces bind and permission policy | done | `remote_bind_allowed` + `start_remote_forwards` policy checks. |
|
|
| `-N -L` does not allocate a PTY | done | `forward-only` mode skips PTY allocation. |
|
|
| `-f -N -L` backgrounds only after listener readiness | done | `spawn_background_forwarder` waits for a readiness token. |
|
|
| Multiple forwards in one command | done | Forward lists parsed and started together. |
|
|
| `dosh doctor` identifies UDP-blocked/auth-denied/mismatch/forwarding-denied | done | `run_doctor_command` reports each state. |
|
|
| Closing laptop 30+ min does not kill session | in progress | Long `client_timeout_secs` + resume; 30-min soak evidence pending. |
|
|
| Three concurrent terminals independent unless named | done | Generated session names per attach; named sessions shared on purpose. |
|
|
| Large forwarded transfers add no visible input lag | in progress | Per-stream flow control exists; load test pending. |
|
|
| Fuzz targets run in CI | pending | No `fuzz/` dir; CI runs fmt/test/build/bench only. |
|
|
| Threat model updated with accepted residual risks | done | `docs/THREAT_MODEL.md`. |
|
|
|
|
Additional security hardening tracked outside the section 16 list:
|
|
|
|
- Full per-IP token-bucket rate limiting: in progress (another track). Today only
|
|
handshake-map eviction and a static `rate_limit_remaining` hint exist.
|
|
- Protocol VERSION negotiation: in progress. Single version, fail-closed reject; no
|
|
multi-version negotiation yet.
|
|
- ECDSA P-256 / SHA-2 RSA user keys: pending. Ed25519 only today.
|
|
|
|
## Before Public Launch
|
|
|
|
- Keep `cargo test`, `make bench-docker-ssh`, and `make bench-docker-mosh` green.
|
|
- Add a non-destructive disconnect indicator.
|
|
- Run scripted TUI tests for alternate-screen apps, arrow keys, resize, mouse mode,
|
|
bracketed paste, and terminal cleanup.
|
|
- Publish benchmark output with raw samples, not just averages.
|
|
- Mark prediction as experimental until it has a real framebuffer model.
|
|
- Land full per-IP token-bucket auth rate limiting and wire fuzz targets into CI.
|
|
- Complete the native-v1 verification checklist above and an external security review
|
|
before making any "native SSH replacement" claim (`NATIVE_V1_SPEC.md` section 17).
|