Merge track D: threat model + readiness docs
This commit is contained in:
@@ -267,3 +267,33 @@ resident PTY server, encrypted UDP bootstrap attach, UDP resume, sealed UDP atta
|
|||||||
tickets, client ACKs, server retransmit bookkeeping, sliding replay protection,
|
tickets, client ACKs, server retransmit bookkeeping, sliding replay protection,
|
||||||
server-side `vt100` screen snapshots/diffs, a hardened user systemd unit, an install
|
server-side `vt100` screen snapshots/diffs, a hardened user systemd unit, an install
|
||||||
script, Docker SSH benchmark gates, CI, and protocol/integration tests.
|
script, Docker SSH benchmark gates, CI, and protocol/integration tests.
|
||||||
|
|
||||||
|
### Native v1
|
||||||
|
|
||||||
|
Beyond the SSH-bootstrap core, native v1 (`docs/NATIVE_V1_SPEC.md`) is substantially
|
||||||
|
implemented and aims to replace the day-to-day `ssh host` workflow on Dosh-installed
|
||||||
|
servers:
|
||||||
|
|
||||||
|
- **Native UDP auth** with X25519 key exchange, transcript-bound Ed25519 user auth
|
||||||
|
via ssh-agent or an encrypted OpenSSH key, ChaCha20-Poly1305 transport, and
|
||||||
|
`authorized_keys` policy enforcement (`from=`, `no-port-forwarding`, `permitopen=`;
|
||||||
|
unsupported options fail closed).
|
||||||
|
- **Dosh host-key trust**: pinned `known_hosts`, `dosh trust [--remove|--replace]`,
|
||||||
|
TOFU only when explicitly enabled, and hard-fail on host-key mismatch.
|
||||||
|
- **TCP forwarding**: local `-L`, remote `-R` (loopback bind by default), dynamic
|
||||||
|
SOCKS5 `-D`, forward-only `-N`, and background `-f`, with per-stream flow control so
|
||||||
|
bulk transfers do not lag the terminal.
|
||||||
|
- **Diagnostics**: `dosh doctor host` for config/auth/UDP/forwarding-policy checks.
|
||||||
|
|
||||||
|
Native auth is **opt-in alongside SSH fallback** (`auth_preference = "native,ssh"`):
|
||||||
|
the native authenticated path is tried first and falls back to SSH bootstrap
|
||||||
|
explicitly when native auth is disabled, unavailable, or rejected. It never silently
|
||||||
|
degrades to an unauthenticated mode.
|
||||||
|
|
||||||
|
Native v1 is **not yet fully verified**. Per-IP token-bucket rate limiting, protocol
|
||||||
|
VERSION negotiation hardening, fuzzing in CI, ECDSA/RSA user keys, and an external
|
||||||
|
security review are still open. See `docs/THREAT_MODEL.md` for the published threat
|
||||||
|
model and accepted residual risks, and the "Native v1 verification checklist status"
|
||||||
|
table in `docs/PUBLIC_READINESS.md` for the item-by-item state. Dosh does not yet
|
||||||
|
claim a fully verified SSH replacement; its defensible claim remains fast encrypted
|
||||||
|
native attach/reconnect with SSH-equivalent transport security and SSH fallback.
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
**Default language:** Rust, unless benchmarks prove the stack is the bottleneck
|
**Default language:** Rust, unless benchmarks prove the stack is the bottleneck
|
||||||
**Binaries:** `dosh-server`, `dosh-client`, `dosh-auth`, `dosh-bench`
|
**Binaries:** `dosh-server`, `dosh-client`, `dosh-auth`, `dosh-bench`
|
||||||
**Helper mode:** `dosh-server auth` or `~/.local/bin/dosh-auth`, invoked by SSH with `-T`
|
**Helper mode:** `dosh-server auth` or `~/.local/bin/dosh-auth`, invoked by SSH with `-T`
|
||||||
**Native v1 plan:** `docs/NATIVE_V1_SPEC.md`
|
**Native v1 plan:** `docs/NATIVE_V1_SPEC.md` (substantially implemented; see its v1 status block)
|
||||||
|
**Threat model:** `docs/THREAT_MODEL.md`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -157,8 +158,11 @@ owner per session is preferred. Cross-thread designs are allowed only if benchma
|
|||||||
|
|
||||||
## 7. Security Model
|
## 7. Security Model
|
||||||
|
|
||||||
SSH is the first trust root. dosh does not implement a competing public-key login
|
SSH is the first trust root and the recovery/bootstrap fallback. The v0 core relies
|
||||||
system in v0.
|
on SSH for first authentication. Native v1 (`docs/NATIVE_V1_SPEC.md`) adds an opt-in
|
||||||
|
native public-key login over the Dosh UDP transport that is tried before SSH, with
|
||||||
|
its assets, attackers, and residual risks documented in `docs/THREAT_MODEL.md`. SSH
|
||||||
|
remains the explicit fallback and the host-key trust bootstrap path.
|
||||||
|
|
||||||
The UDP channel uses AEAD. Recommended default: `ChaCha20-Poly1305` for portable
|
The UDP channel uses AEAD. Recommended default: `ChaCha20-Poly1305` for portable
|
||||||
speed, with `AES-GCM` allowed when hardware acceleration is known to be available.
|
speed, with `AES-GCM` allowed when hardware acceleration is known to be available.
|
||||||
|
|||||||
@@ -1,5 +1,30 @@
|
|||||||
# Dosh Native v1 Spec
|
# Dosh Native v1 Spec
|
||||||
|
|
||||||
|
> **v1 status (annotation, not part of the spec text below).** Native v1 is
|
||||||
|
> substantially implemented and being stabilized; it is not yet fully verified.
|
||||||
|
> Milestone progress against section 15:
|
||||||
|
>
|
||||||
|
> - Milestone 1 — host identity and trust: **done.** Host key generation, `dosh trust`,
|
||||||
|
> `known_hosts`, and mismatch hard-fail are implemented.
|
||||||
|
> - Milestone 2 — native user auth: **done.** `ClientHello`/`ServerHello`/`UserAuth`/
|
||||||
|
> `AuthOk`, ssh-agent and encrypted-key Ed25519, and `authorized_keys` verification
|
||||||
|
> exist. ECDSA/RSA user keys are still pending (Ed25519 only today).
|
||||||
|
> - Milestone 3 — default native auth: **done.** `auth_preference = "native,ssh"` is
|
||||||
|
> the default with explicit, visible SSH fallback. Cold-auth benchmark gates are
|
||||||
|
> pending (Track C / `BENCHMARKS.md`).
|
||||||
|
> - Milestone 4 — forwarding: **done.** Stream mux, `-L`, `-R`, `-D`, `-N`, `-f`, and
|
||||||
|
> per-stream flow control are implemented; hostile-network and load tests pending.
|
||||||
|
> - Milestone 5 — hardening: **in progress.** Full per-IP token-bucket rate limiting,
|
||||||
|
> protocol VERSION negotiation hardening, fuzzing in CI, and external review are not
|
||||||
|
> yet complete. The threat model is published (`docs/THREAT_MODEL.md`).
|
||||||
|
> - Milestone 6 — workflow parity: **mostly done.** `dosh doctor`, host-trust
|
||||||
|
> management, and the encrypted-key prompt flow exist; cross-OS daily-driver soak is
|
||||||
|
> ongoing.
|
||||||
|
>
|
||||||
|
> The section 16 verification checklist is **not yet fully green** — see the
|
||||||
|
> item-by-item status table in `docs/PUBLIC_READINESS.md` and the residual-risk list in
|
||||||
|
> `docs/THREAT_MODEL.md`. The section 17 public-claim gate is therefore **not met**.
|
||||||
|
|
||||||
Native Dosh is a remote-login protocol for Dosh-installed servers. It is intended to
|
Native Dosh is a remote-login protocol for Dosh-installed servers. It is intended to
|
||||||
replace the user's day-to-day `ssh host` workflow for terminals and forwarding while
|
replace the user's day-to-day `ssh host` workflow for terminals and forwarding while
|
||||||
keeping SSH as a compatibility and recovery fallback.
|
keeping SSH as a compatibility and recovery fallback.
|
||||||
|
|||||||
+84
-19
@@ -5,9 +5,17 @@ claim full Mosh replacement status until the feature matrix below is green and t
|
|||||||
comparison benchmark is reproducible outside the author's homelab.
|
comparison benchmark is reproducible outside the author's homelab.
|
||||||
|
|
||||||
The plan for replacing the day-to-day SSH workflow with native Dosh authentication
|
The plan for replacing the day-to-day SSH workflow with native Dosh authentication
|
||||||
and forwarding is specified in `docs/NATIVE_V1_SPEC.md`. Until that spec is
|
and forwarding is specified in `docs/NATIVE_V1_SPEC.md`, and the published threat
|
||||||
implemented and verified, Dosh's public security claim remains SSH-bootstrap plus
|
model is in `docs/THREAT_MODEL.md`. Native v1 is now substantially implemented:
|
||||||
encrypted Dosh transport.
|
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
|
## Objective Benchmarks
|
||||||
|
|
||||||
@@ -50,6 +58,10 @@ with ordinary SSH.
|
|||||||
| Feature | Mosh | Dosh now | Public status |
|
| Feature | Mosh | Dosh now | Public status |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| SSH-based first authentication | yes | yes | ready |
|
| 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 |
|
| Encrypted UDP terminal data | yes | yes | ready |
|
||||||
| Roaming by client address change | yes | yes | needs more hostile-network tests |
|
| Roaming by client address change | yes | yes | needs more hostile-network tests |
|
||||||
| Survive sleep or network loss | yes | yes | needs long-running soak tests |
|
| Survive sleep or network loss | yes | yes | needs long-running soak tests |
|
||||||
@@ -66,9 +78,11 @@ with ordinary SSH.
|
|||||||
| Unicode edge-case handling | strong | basic terminal emulator dependent | not parity |
|
| Unicode edge-case handling | strong | basic terminal emulator dependent | not parity |
|
||||||
| X11 forwarding | no | no | non-goal unless tunneled separately |
|
| X11 forwarding | no | no | non-goal unless tunneled separately |
|
||||||
| SSH agent forwarding | no | no | planned as forwarding channel |
|
| SSH agent forwarding | no | no | planned as forwarding channel |
|
||||||
| Local TCP forwarding, `-L` | no | not implemented | planned |
|
| Local TCP forwarding, `-L` | no | yes, native encrypted stream mux | implemented; needs hostile-network tests |
|
||||||
| Remote TCP forwarding, `-R` | no | not implemented | planned |
|
| Remote TCP forwarding, `-R` | no | yes, loopback bind by default | implemented; needs hostile-network tests |
|
||||||
| Dynamic SOCKS forwarding, `-D` | no | not implemented | later |
|
| 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
|
## SSH Config Inheritance
|
||||||
|
|
||||||
@@ -87,29 +101,77 @@ dosh import-ssh palav homelab
|
|||||||
This appends entries to `~/.config/dosh/hosts.toml` without trying to become an
|
This appends entries to `~/.config/dosh/hosts.toml` without trying to become an
|
||||||
OpenSSH config parser.
|
OpenSSH config parser.
|
||||||
|
|
||||||
## Forwarding Plan
|
## Forwarding (Implemented)
|
||||||
|
|
||||||
SSH forwarding cannot be copied by keeping the bootstrap SSH connection open,
|
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
|
because that would remove Dosh's fast reconnect advantage and would break after
|
||||||
roaming. Dosh forwarding needs native encrypted channels over the Dosh transport.
|
roaming. Dosh forwarding therefore runs as native encrypted stream channels over the
|
||||||
|
Dosh transport, and is now implemented.
|
||||||
Minimum viable forwarding design:
|
|
||||||
|
|
||||||
| CLI | Meaning |
|
| CLI | Meaning |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `dosh -L 8080:127.0.0.1:80 host` | Local listener on the client; server connects to target. |
|
| `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; client 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. |
|
||||||
|
|
||||||
Protocol work required:
|
Implemented:
|
||||||
|
|
||||||
- Add stream-open, stream-data, stream-ack, and stream-close packet types.
|
- `StreamOpen`/`StreamOpenOk`/`StreamOpenReject`/`StreamData`/`StreamWindowAdjust`/
|
||||||
- Use per-stream flow control separate from terminal frame ordering.
|
`StreamEof`/`StreamClose` packet types.
|
||||||
- Never let bulk forwarding traffic delay terminal input/output packets.
|
- Per-stream windowed flow control (initial 1 MiB credit) separate from terminal
|
||||||
- Bind forwarding permissions to the same SSH-authenticated user as the terminal.
|
frames, so bulk forwarding does not block PTY input/output.
|
||||||
- Add tests with dropped/reordered UDP packets.
|
- 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).
|
||||||
|
|
||||||
This is a publishable differentiator once implemented, but it should not be claimed
|
Still open before claiming forwarding parity:
|
||||||
until it exists.
|
|
||||||
|
- 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
|
## Before Public Launch
|
||||||
|
|
||||||
@@ -119,3 +181,6 @@ until it exists.
|
|||||||
bracketed paste, and terminal cleanup.
|
bracketed paste, and terminal cleanup.
|
||||||
- Publish benchmark output with raw samples, not just averages.
|
- Publish benchmark output with raw samples, not just averages.
|
||||||
- Mark prediction as experimental until it has a real framebuffer model.
|
- 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).
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
# Dosh Threat Model
|
||||||
|
|
||||||
|
This is the published threat model for Dosh native v1, derived from
|
||||||
|
`docs/NATIVE_V1_SPEC.md` sections 4-6. It states the assets Dosh protects, the
|
||||||
|
attackers it does and does not defend against, the security properties Dosh claims
|
||||||
|
relative to SSH, the cryptographic building blocks actually in use, and an honest
|
||||||
|
list of accepted residual risks and known gaps.
|
||||||
|
|
||||||
|
Dosh is a remote-login transport for Dosh-installed servers. It is intended to
|
||||||
|
replace the day-to-day `ssh host` workflow for terminals and TCP forwarding while
|
||||||
|
keeping OpenSSH as a recovery and bootstrap fallback. Dosh is **not** an
|
||||||
|
RFC-compatible SSH implementation and does not claim SSH's entire protocol security
|
||||||
|
surface. It claims security that is **equivalent to, and in some respects stronger
|
||||||
|
than, SSH for the Dosh terminal and forwarding use case** on hosts running
|
||||||
|
`dosh-server`.
|
||||||
|
|
||||||
|
## 1. Assets
|
||||||
|
|
||||||
|
Dosh protects:
|
||||||
|
|
||||||
|
- **Terminal session contents.** Keystrokes, command output, and the authoritative
|
||||||
|
screen state for every named or generated session.
|
||||||
|
- **Forwarded TCP streams.** Bytes carried over `-L`, `-R`, and `-D` channels.
|
||||||
|
- **User authentication credentials.** The user's SSH/Dosh private keys and any
|
||||||
|
ssh-agent identities. Dosh never sees private key material in plaintext on the
|
||||||
|
wire; signatures are produced locally or by the agent.
|
||||||
|
- **Server-issued credentials.** Session keys, `ClientId` association state,
|
||||||
|
server-sealed attach tickets, and the client-held attach-ticket PSK.
|
||||||
|
- **Server identity.** The persistent Dosh host key (`~/.config/dosh/host_key`) and
|
||||||
|
the server secret used to seal attach tickets and derive bootstrap material.
|
||||||
|
- **Host-trust state.** The client's pinned known-hosts file
|
||||||
|
(`~/.config/dosh/known_hosts`).
|
||||||
|
- **Authorization policy.** `~/.ssh/authorized_keys` /
|
||||||
|
`~/.config/dosh/authorized_keys` and the forwarding policy they encode.
|
||||||
|
|
||||||
|
## 2. Attackers
|
||||||
|
|
||||||
|
### In scope (Dosh must defend against these)
|
||||||
|
|
||||||
|
- **Passive network observer.** May record all UDP traffic between client and
|
||||||
|
server.
|
||||||
|
- **Active network attacker.** May spoof, drop, replay, reorder, or modify any
|
||||||
|
packet, and may attempt to inject forged packets in either direction.
|
||||||
|
- **NAT rebinding / roaming.** Client source IP and port may change mid-session,
|
||||||
|
including across sleep, network switch, and NAT timeout.
|
||||||
|
- **Stolen attach-ticket cache without the user's private key.** An attacker who
|
||||||
|
reads a client cache copies a server-sealed ticket plus its PSK but does not have
|
||||||
|
the user's SSH private key.
|
||||||
|
- **Server restart and key rotation.** Stale session keys, tickets, and replay
|
||||||
|
state must not be usable after the server rotates its secret or host key.
|
||||||
|
- **Malicious unauthenticated client flooding auth attempts.** A peer that has no
|
||||||
|
authorized key tries to exhaust server resources or guess credentials.
|
||||||
|
- **Compromised low-privilege local user on a shared client.** A different local
|
||||||
|
user attempts to read Dosh credential caches on the same machine.
|
||||||
|
|
||||||
|
### Out of scope (explicitly not defended against)
|
||||||
|
|
||||||
|
- **Compromised client machine.** If the endpoint running `dosh-client` is owned by
|
||||||
|
the attacker, the attacker has the user's keys and terminal.
|
||||||
|
- **Compromised server account.** If the login account on the server is owned, the
|
||||||
|
attacker already has the shell Dosh would have given them.
|
||||||
|
- **Malicious kernel, terminal emulator, or PTY implementation** on either side.
|
||||||
|
- **A server that was legitimately authorized and later turns malicious.** Host-key
|
||||||
|
pinning detects a *substituted* server, not a trusted server that decides to
|
||||||
|
misbehave.
|
||||||
|
|
||||||
|
These exclusions match SSH's own boundaries: SSH likewise cannot protect a
|
||||||
|
compromised endpoint or a malicious authorized peer.
|
||||||
|
|
||||||
|
## 3. Security Properties Claimed vs SSH
|
||||||
|
|
||||||
|
| Property | SSH | Dosh native v1 | Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Server authentication before trusting session data | yes | yes | Host key signs the handshake transcript; client verifies before sending user auth or accepting terminal bytes. |
|
||||||
|
| User authentication by private-key possession | yes | yes | Ed25519 via ssh-agent or encrypted OpenSSH key; signature binds the full transcript. |
|
||||||
|
| Forward secrecy | yes | yes | Ephemeral X25519 per connection; long-term host/user keys never derive the traffic key. |
|
||||||
|
| AEAD on every post-handshake packet | yes | yes | ChaCha20-Poly1305 with per-direction, per-sequence nonces. |
|
||||||
|
| Replay protection | yes | yes | Sliding replay window over the AEAD packet counter, plus transcript-bound handshake. |
|
||||||
|
| Host-key pinning with explicit first use | TOFU, weakly tied to transport | yes, with explicit policy | Default refuses unknown host keys; TOFU only when `trust_on_first_use` is set; mismatch hard-fails and never auto-replaces. |
|
||||||
|
| No plaintext terminal bytes after handshake | yes | yes | All `Frame`/`Input`/stream packets are AEAD-sealed. |
|
||||||
|
| No custom cryptographic primitives | yes | yes | Standard X25519/HKDF-SHA256/ChaCha20-Poly1305/Ed25519 crates only. |
|
||||||
|
| Fail-closed downgrade behavior | yes | yes | Native auth failure surfaces an explicit error and SSH fallback is explicit; it never silently drops to an unauthenticated mode. |
|
||||||
|
| Fast resumption without re-auth | ControlMaster only | yes, native | Cached session/ticket attach skips a fresh round of public-key proof; this is a deliberate speed/security trade discussed in section 6. |
|
||||||
|
|
||||||
|
### Where Dosh aims to *exceed* SSH for this use case
|
||||||
|
|
||||||
|
- **Tighter transcript binding.** A user-auth signature binds both ephemeral keys,
|
||||||
|
both randoms, the server host key, requested user/session/mode/terminal size,
|
||||||
|
selected algorithms, and protocol version into one transcript. This forecloses
|
||||||
|
cross-protocol and partial-replay confusion classes for the narrow Dosh surface.
|
||||||
|
- **Smaller attack surface.** Dosh deliberately omits the full SSH transport/channel
|
||||||
|
machinery, arbitrary subsystems, X11, SFTP/SCP, and forced-command subsystems.
|
||||||
|
Fewer features means fewer parsers and fewer reachable states.
|
||||||
|
- **Explicit, file-pinned host trust.** Host trust is a first-class, inspectable
|
||||||
|
known-hosts entry with source provenance (`tofu`/`ssh`/`manual`) and a hard-fail
|
||||||
|
mismatch path, rather than the looser default TOFU behavior most SSH clients ship.
|
||||||
|
- **Modern primitives only.** Ed25519 and X25519 by default; no DSA, no SHA-1
|
||||||
|
signatures, no CBC-and-MAC constructions.
|
||||||
|
|
||||||
|
Dosh does **not** claim generic SSH compatibility and must not be described as an
|
||||||
|
SSH-protocol implementation.
|
||||||
|
|
||||||
|
## 4. Cryptographic Building Blocks (as implemented)
|
||||||
|
|
||||||
|
These reflect the code in `src/crypto.rs`, `src/native.rs`, `src/auth.rs`, and
|
||||||
|
`src/protocol.rs`, not just the spec.
|
||||||
|
|
||||||
|
- **Key exchange:** X25519 ephemeral-ephemeral (`x25519-dalek`). The shared secret
|
||||||
|
is checked for contributory behaviour; a non-contributory result is rejected.
|
||||||
|
- **Handshake/transport KDF:** HKDF-SHA256 (`hkdf`), salted with the SHA-256 of the
|
||||||
|
serialized `ClientHello` and `ServerHello`, binding traffic keys to the transcript.
|
||||||
|
- **AEAD:** ChaCha20-Poly1305 (`chacha20poly1305`) for every encrypted packet and
|
||||||
|
for sealed attach tickets. Nonces are derived as `direction || sequence`, giving a
|
||||||
|
unique nonce per `(key, direction, sequence)`. AES-GCM is reserved for later and is
|
||||||
|
not selectable today.
|
||||||
|
- **Host-key signatures:** Ed25519 (`ed25519-dalek`) over the handshake transcript.
|
||||||
|
- **User-auth signatures:** Ed25519, produced either by ssh-agent over a Unix socket
|
||||||
|
(`src/ssh_agent.rs`) or from an encrypted/plaintext OpenSSH private key
|
||||||
|
(`ssh-key`). The signature covers the user-auth transcript described above.
|
||||||
|
- **Bootstrap auth (SSH fallback path):** HMAC-SHA256 attach tokens and HKDF-SHA256
|
||||||
|
derived session keys, with attach tickets sealed under an HKDF-derived
|
||||||
|
ticket key. Token comparison is constant-time.
|
||||||
|
- **Hashing/transcript:** SHA-256 (`sha2`).
|
||||||
|
- **Randomness:** OS CSPRNG via `rand::thread_rng()` / `OsRng`.
|
||||||
|
|
||||||
|
No homegrown ciphers, MACs, padding schemes, or key derivation are used. No nonce or
|
||||||
|
key pair is reused within a direction.
|
||||||
|
|
||||||
|
## 5. How the Properties Are Enforced (handshake and transport)
|
||||||
|
|
||||||
|
- **Server authentication.** `ServerHello` carries the host public key and an Ed25519
|
||||||
|
signature over `dosh/native/server-hello/v1 || ClientHello || ServerHello(unsigned)`.
|
||||||
|
The client verifies this signature *and* checks the host key against its pinned
|
||||||
|
known-hosts entry before sending user auth or accepting terminal bytes. Unknown
|
||||||
|
keys are refused unless TOFU is enabled; mismatches hard-fail with both
|
||||||
|
fingerprints and the file path.
|
||||||
|
- **User authentication.** `UserAuth` carries an Ed25519 signature over
|
||||||
|
`dosh/native/user-auth/v1 || ClientHello || ServerHello || UserAuth(unsigned)`. The
|
||||||
|
server looks the public key up in `authorized_keys`, enforces authorized-key
|
||||||
|
options, then verifies the signature. A removed key can no longer authenticate.
|
||||||
|
- **Authorization options.** `from=` (with CIDR, glob, and negation),
|
||||||
|
`no-port-forwarding`, and `permitopen=` are enforced. `command=` is rejected for
|
||||||
|
native terminal login. Any unrecognized restrictive option fails closed rather than
|
||||||
|
being silently ignored.
|
||||||
|
- **Forwarding policy.** The server enforces `allow_tcp_forwarding`,
|
||||||
|
`allow_remote_forwarding`, and a loopback-only default for remote binds
|
||||||
|
(`allow_remote_non_loopback_bind`), in addition to per-key `permitopen=` /
|
||||||
|
`no-port-forwarding`.
|
||||||
|
- **Replay protection.** A 128-wide sliding window over the per-direction packet
|
||||||
|
counter rejects duplicates and stale sequences. Sequence 0 is never accepted.
|
||||||
|
- **Stale-key tolerance.** Each encrypted packet carries a `session_key_id`; packets
|
||||||
|
under an old key are dropped as "stale or wrong session key id" rather than treated
|
||||||
|
as fatal decrypt failures, so reconnect after rotation is non-destructive.
|
||||||
|
- **Roaming.** The server keys clients by `ClientId` and session key id, not by
|
||||||
|
source address, and updates the endpoint after any valid encrypted packet from a
|
||||||
|
new address — without weakening authentication, because the packet must still
|
||||||
|
decrypt and verify.
|
||||||
|
|
||||||
|
## 6. Accepted Residual Risks and Known Gaps
|
||||||
|
|
||||||
|
These are stated openly so the public claim gate (`NATIVE_V1_SPEC.md` section 17) can
|
||||||
|
be evaluated honestly. Items here are *not* yet "green".
|
||||||
|
|
||||||
|
### Accepted residual risks (by design)
|
||||||
|
|
||||||
|
- **Attach tickets prove recent server-issued possession, not fresh private-key
|
||||||
|
possession.** A stolen client cache containing both the sealed ticket and its PSK
|
||||||
|
can attach until the ticket expires (default TTL 24h, configurable down to zero) or
|
||||||
|
until the server rotates its secret/host key or the user key is removed. This is the
|
||||||
|
deliberate speed trade. It is bounded by TTL, scoped to host/user/session/mode, and
|
||||||
|
can be disabled. SSH ControlMaster has an analogous live-socket exposure.
|
||||||
|
- **Local cache confidentiality relies on filesystem permissions.** Host keys,
|
||||||
|
server secret, known-hosts, and credential caches are written `0600`. A
|
||||||
|
same-machine attacker who can already read another user's `0600` files (e.g. via
|
||||||
|
root) is out of scope, as with SSH's `~/.ssh`.
|
||||||
|
- **A trusted server that turns malicious is not detected.** Host-key pinning
|
||||||
|
detects substitution, not betrayal by an already-authorized server. This matches
|
||||||
|
SSH.
|
||||||
|
|
||||||
|
### Known gaps / work in progress (must close before the public claim)
|
||||||
|
|
||||||
|
- **Per-IP rate limiting is partial.** The server evicts half-finished native
|
||||||
|
handshakes on a TTL so a flood of `ClientHello` packets cannot grow the pending map
|
||||||
|
without bound, and it reports a static `rate_limit_remaining` hint in `ServerHello`.
|
||||||
|
A full per-source token-bucket limiter is **in progress on another track** and is
|
||||||
|
not yet enforced. Until it lands, sustained auth flooding is mitigated only by the
|
||||||
|
handshake-eviction TTL and OS-level limits.
|
||||||
|
- **Protocol VERSION negotiation is being hardened.** The wire format pins a single
|
||||||
|
protocol version: the packet header rejects any non-matching `VERSION` byte and the
|
||||||
|
native handshake rejects any non-matching `protocol_version`. There is no
|
||||||
|
multi-version negotiation yet, so cross-version interop and downgrade-resistance for
|
||||||
|
future versions are still being designed. Today's behavior is fail-closed (reject),
|
||||||
|
not silent downgrade.
|
||||||
|
- **Fuzzing is not yet wired into CI.** CI currently runs format, tests, release
|
||||||
|
build, and the Docker SSH benchmark gate. Fuzz targets for packet parsing,
|
||||||
|
authorized-key parsing, known-host parsing, and handshake state (spec milestone 5)
|
||||||
|
are **being wired in** and are not yet running in CI.
|
||||||
|
- **User-key algorithm coverage is Ed25519-only today.** The spec permits ECDSA
|
||||||
|
P-256 and (compatibility-only, SHA-2) RSA, but native auth currently accepts and
|
||||||
|
produces `ssh-ed25519` only. ECDSA/RSA support is pending. This is a parity gap, not
|
||||||
|
a weakening of what *is* supported.
|
||||||
|
- **Hostile-network and long-soak integration tests are partial.** Roaming,
|
||||||
|
retransmit, resize, and multi-client tests exist; a dedicated adversarial
|
||||||
|
drop/reorder/replay suite and 30-minute-sleep soak (spec section 16) are still being
|
||||||
|
expanded.
|
||||||
|
- **No external security review yet.** The spec's milestone 5 requires an external
|
||||||
|
review checklist before public security claims. That review has not happened.
|
||||||
|
|
||||||
|
### Auth posture
|
||||||
|
|
||||||
|
Native auth is **opt-in alongside SSH fallback**. `auth_preference` defaults to
|
||||||
|
`native,ssh`: Dosh tries the native authenticated path first and falls back to SSH
|
||||||
|
bootstrap explicitly and visibly when native auth is disabled, unavailable, or
|
||||||
|
rejected. Native auth failure never silently degrades to an unauthenticated mode.
|
||||||
|
Forwarding (`-L`/`-R`/`-D`) requires the native authenticated path and refuses to run
|
||||||
|
under `--local-auth`.
|
||||||
|
|
||||||
|
## 7. Verification and Public-Claim Status
|
||||||
|
|
||||||
|
Dosh may claim "native SSH replacement for Dosh-installed servers" only after the
|
||||||
|
conditions in `NATIVE_V1_SPEC.md` section 17 are met: native auth default on a real
|
||||||
|
host, SSH fallback available, the section 16 checklist green, this threat model
|
||||||
|
published, and benchmarks with raw samples for SSH cold, Dosh native cold, Dosh
|
||||||
|
cached attach, and Mosh startup.
|
||||||
|
|
||||||
|
Current status: this threat model is published (this document). The verification
|
||||||
|
checklist is **not yet fully green** — see the item-by-item status table in
|
||||||
|
`docs/PUBLIC_READINESS.md` ("Native v1 verification checklist status") and the known
|
||||||
|
gaps in section 6 above. Until the gaps close and an external review is complete,
|
||||||
|
Dosh's defensible public claim remains **fast, encrypted native attach/reconnect with
|
||||||
|
SSH-equivalent transport security and SSH bootstrap fallback** — not a fully verified,
|
||||||
|
externally reviewed SSH replacement.
|
||||||
Reference in New Issue
Block a user