Spec native Dosh auth and forwarding v1
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
**Default language:** Rust, unless benchmarks prove the stack is the bottleneck
|
||||
**Binaries:** `dosh-server`, `dosh-client`, `dosh-auth`, `dosh-bench`
|
||||
**Helper mode:** `dosh-server auth` or `~/.local/bin/dosh-auth`, invoked by SSH with `-T`
|
||||
**Native v1 plan:** `docs/NATIVE_V1_SPEC.md`
|
||||
|
||||
---
|
||||
|
||||
@@ -17,7 +18,9 @@ as quickly as possible.
|
||||
The daemon is resident. Sessions are named. A session owns one PTY and one
|
||||
authoritative terminal screen. Clients attach to that session over encrypted UDP.
|
||||
SSH is used for first trust establishment and as fallback when cached credentials are
|
||||
missing, expired, or rejected.
|
||||
missing, expired, or rejected. Native Dosh auth is specified separately in
|
||||
`docs/NATIVE_V1_SPEC.md` as the path toward replacing the day-to-day SSH workflow on
|
||||
Dosh-installed servers.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,464 @@
|
||||
# Dosh Native v1 Spec
|
||||
|
||||
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
|
||||
keeping SSH as a compatibility and recovery fallback.
|
||||
|
||||
Native Dosh is not an RFC-compatible SSH implementation. It deliberately avoids the
|
||||
full SSH transport/channel protocol and implements the smaller set of behavior Dosh
|
||||
needs: authenticated login, encrypted terminal transport, reconnect/roaming, and TCP
|
||||
forwarding.
|
||||
|
||||
## 1. Product Contract
|
||||
|
||||
User-facing commands:
|
||||
|
||||
```bash
|
||||
dosh host
|
||||
dosh host command...
|
||||
dosh -L 8080:127.0.0.1:80 host
|
||||
dosh -R 9000:127.0.0.1:9000 host
|
||||
dosh --session work host
|
||||
dosh --view-only --session work host
|
||||
```
|
||||
|
||||
Compatibility expectations:
|
||||
|
||||
- Existing SSH keys and `ssh-agent` are reused.
|
||||
- Server-side authorization uses `~/.ssh/authorized_keys`.
|
||||
- Host trust is pinned in a Dosh known-hosts file and can be bootstrapped by SSH.
|
||||
- `~/.ssh/config` host aliases, `HostName`, `User`, `Port`, `IdentityFile`,
|
||||
`ProxyJump`, and `UserKnownHostsFile` are honored where practical.
|
||||
- SSH bootstrap remains available with `--auth=ssh` and is used automatically when
|
||||
native auth is disabled or cannot complete.
|
||||
|
||||
Non-goals for v1:
|
||||
|
||||
- RFC-compatible SSH server/client behavior.
|
||||
- Arbitrary SSH subsystems.
|
||||
- X11 forwarding.
|
||||
- SFTP/SCP compatibility.
|
||||
- Multi-user daemon mode with privileged account switching.
|
||||
- Replacing OpenSSH on hosts that do not run `dosh-server`.
|
||||
|
||||
## 2. Security Contract
|
||||
|
||||
Native Dosh must match the security properties users rely on from SSH for this use
|
||||
case:
|
||||
|
||||
- Server authentication before terminal data is trusted.
|
||||
- User authentication by possession of an authorized private key or agent key.
|
||||
- Forward secrecy for terminal and forwarding traffic.
|
||||
- AEAD encryption and authentication for every post-handshake packet.
|
||||
- Replay protection for handshake and transport packets.
|
||||
- Host-key pinning with explicit first-use behavior.
|
||||
- No plaintext terminal bytes after handshake begins.
|
||||
- No custom cryptographic primitives.
|
||||
- Clear downgrade behavior: native auth failure must not silently fall back to an
|
||||
unauthenticated mode.
|
||||
|
||||
Native Dosh does not claim SSH's full protocol security surface. It claims equivalent
|
||||
security for Dosh terminal and forwarding sessions on Dosh-installed servers.
|
||||
|
||||
## 3. Threat Model
|
||||
|
||||
In scope:
|
||||
|
||||
- Passive network observer.
|
||||
- Active network attacker that can spoof, drop, replay, reorder, or modify packets.
|
||||
- NAT rebinding and client IP/port changes.
|
||||
- Stolen attach-ticket cache without the user's private key.
|
||||
- Server restart and key rotation.
|
||||
- Malicious unauthenticated client flooding auth attempts.
|
||||
- Compromised low-privilege local user trying to read Dosh caches on a shared client.
|
||||
|
||||
Out of scope:
|
||||
|
||||
- Compromised client machine.
|
||||
- Compromised server account.
|
||||
- Malicious kernel, terminal emulator, or PTY implementation.
|
||||
- Protecting against a server that is already authorized and then becomes malicious.
|
||||
|
||||
## 4. Cryptographic Building Blocks
|
||||
|
||||
Allowed primitives:
|
||||
|
||||
- Handshake pattern: Noise `NK` or `XX` through a maintained Rust Noise framework, or
|
||||
a small audited construction over `x25519-dalek` plus transcript binding.
|
||||
- KEX: X25519.
|
||||
- Signatures for user auth: Ed25519 and ECDSA P-256 via SSH-agent and OpenSSH key
|
||||
formats. RSA may be accepted only for compatibility and must use SHA-2 signatures.
|
||||
- AEAD: ChaCha20-Poly1305 by default; AES-GCM optional when hardware support is known.
|
||||
- KDF: HKDF-SHA256.
|
||||
- Hash/transcript: SHA-256.
|
||||
- Randomness: OS CSPRNG only.
|
||||
|
||||
Disallowed:
|
||||
|
||||
- Homegrown ciphers, MACs, padding, or key derivation.
|
||||
- Reusing a nonce/key pair.
|
||||
- Unauthenticated encryption.
|
||||
- MD5/SHA-1 signatures for user auth.
|
||||
|
||||
## 5. Identity And Trust
|
||||
|
||||
### Server Identity
|
||||
|
||||
Each `dosh-server` has a persistent host key:
|
||||
|
||||
```text
|
||||
~/.config/dosh/host_key
|
||||
~/.config/dosh/host_key.pub
|
||||
```
|
||||
|
||||
Default host-key algorithm: Ed25519.
|
||||
|
||||
Client pins host keys in:
|
||||
|
||||
```text
|
||||
~/.config/dosh/known_hosts
|
||||
```
|
||||
|
||||
Entry format:
|
||||
|
||||
```text
|
||||
host-pattern key-type base64-public-key first-seen=unix-seconds source=tofu|ssh|manual
|
||||
```
|
||||
|
||||
First-use policy:
|
||||
|
||||
- Default for public internet: refuse unknown native host key and suggest
|
||||
`dosh trust host` or SSH bootstrap.
|
||||
- Default for local/private hosts may be TOFU only when `trust_on_first_use = true`.
|
||||
- `dosh trust host` may verify the Dosh host key over the existing SSH bootstrap path.
|
||||
|
||||
Host-key mismatch:
|
||||
|
||||
- Hard fail.
|
||||
- Print old fingerprint, new fingerprint, and known-hosts file path.
|
||||
- Never auto-replace.
|
||||
|
||||
### User Identity
|
||||
|
||||
Native auth user identity is the login user resolved from:
|
||||
|
||||
1. Explicit CLI user: `user@host`.
|
||||
2. Dosh host config.
|
||||
3. `ssh -G host` `user`.
|
||||
4. Local username.
|
||||
|
||||
Server verifies user keys against:
|
||||
|
||||
```text
|
||||
~/.ssh/authorized_keys
|
||||
~/.config/dosh/authorized_keys
|
||||
```
|
||||
|
||||
`~/.config/dosh/authorized_keys` is optional and may restrict Dosh access without
|
||||
changing SSH access.
|
||||
|
||||
Authorized-key options required in v1:
|
||||
|
||||
- `from=`
|
||||
- `command=` must reject native Dosh terminal login unless explicitly supported later.
|
||||
- `restrict`
|
||||
- `no-port-forwarding`
|
||||
- `permitopen=`
|
||||
|
||||
Unsupported restrictive options must fail closed.
|
||||
|
||||
## 6. Native Auth Handshake
|
||||
|
||||
Native auth runs over UDP on the same Dosh port. It establishes a short-lived
|
||||
authenticated control channel and returns the same terminal attach material that SSH
|
||||
bootstrap returns today.
|
||||
|
||||
Target path:
|
||||
|
||||
```text
|
||||
client -> server: ClientHello
|
||||
server -> client: ServerHello
|
||||
client -> server: UserAuth
|
||||
server -> client: AuthOk + first terminal snapshot
|
||||
```
|
||||
|
||||
The server may combine `AuthOk` and `AttachOk` to get terminal-ready in the final
|
||||
handshake flight.
|
||||
|
||||
### ClientHello
|
||||
|
||||
Fields:
|
||||
|
||||
- protocol version
|
||||
- client random
|
||||
- client ephemeral X25519 public key
|
||||
- requested host alias
|
||||
- requested user
|
||||
- requested session
|
||||
- requested mode
|
||||
- terminal size
|
||||
- supported AEAD algorithms
|
||||
- supported user key algorithms
|
||||
- optional cached host-key id
|
||||
- optional attach ticket envelope
|
||||
|
||||
### ServerHello
|
||||
|
||||
Fields:
|
||||
|
||||
- protocol version
|
||||
- server random
|
||||
- server ephemeral X25519 public key
|
||||
- server host public key
|
||||
- server host-key signature over transcript
|
||||
- chosen AEAD
|
||||
- server key epoch
|
||||
- auth challenge
|
||||
- rate-limit metadata when applicable
|
||||
|
||||
The client must verify the host key before sending user authentication.
|
||||
|
||||
### UserAuth
|
||||
|
||||
Fields:
|
||||
|
||||
- selected public key
|
||||
- key algorithm
|
||||
- signature over transcript and auth challenge
|
||||
- optional agent identity metadata
|
||||
- optional requested forwarding declarations
|
||||
|
||||
The signature must bind:
|
||||
|
||||
- both ephemeral keys
|
||||
- both randoms
|
||||
- server host key
|
||||
- requested user/session/mode/terminal size
|
||||
- selected algorithms
|
||||
- protocol version
|
||||
|
||||
### AuthOk
|
||||
|
||||
Fields:
|
||||
|
||||
- assigned `ClientId`
|
||||
- session name
|
||||
- mode
|
||||
- session key id
|
||||
- encrypted session key material or derived key confirmation
|
||||
- attach ticket
|
||||
- attach ticket PSK encrypted to the handshake key
|
||||
- initial output sequence
|
||||
- first snapshot
|
||||
- server policy flags
|
||||
|
||||
`AuthOk` is AEAD-encrypted under the handshake traffic key.
|
||||
|
||||
## 7. Key Schedule
|
||||
|
||||
Handshake transcript:
|
||||
|
||||
```text
|
||||
H = SHA256(protocol_label || ClientHello || ServerHello || UserAuth)
|
||||
```
|
||||
|
||||
Shared secret:
|
||||
|
||||
```text
|
||||
dh = X25519(client_ephemeral, server_ephemeral)
|
||||
```
|
||||
|
||||
Handshake key:
|
||||
|
||||
```text
|
||||
handshake_key = HKDF-SHA256(dh, H, "dosh/native/handshake/v1")
|
||||
```
|
||||
|
||||
Session traffic keys:
|
||||
|
||||
```text
|
||||
c2s_key = HKDF-SHA256(handshake_key, H, "dosh/native/c2s/v1")
|
||||
s2c_key = HKDF-SHA256(handshake_key, H, "dosh/native/s2c/v1")
|
||||
```
|
||||
|
||||
Attach ticket PSKs and rotated session keys must be derived independently from server
|
||||
secret material and fresh randomness. They must not reuse handshake traffic keys.
|
||||
|
||||
## 8. Attach Tickets And Cache
|
||||
|
||||
Native attach tickets replace most cold auth after first login.
|
||||
|
||||
Ticket properties:
|
||||
|
||||
- Server-sealed AEAD blob.
|
||||
- Scoped to server host key, user, session, mode, client key fingerprint, server key
|
||||
epoch, and policy flags.
|
||||
- Paired with a client-held random PSK.
|
||||
- Default TTL: 24 hours for trusted personal machines, configurable down to zero.
|
||||
- Stored mode `0600`.
|
||||
- Revoked by server host-key rotation, server secret rotation, or user key removal.
|
||||
|
||||
Client cache path:
|
||||
|
||||
```text
|
||||
~/.local/share/dosh/credentials/
|
||||
```
|
||||
|
||||
Cache entries must include:
|
||||
|
||||
- host identity fingerprint
|
||||
- user
|
||||
- session
|
||||
- mode
|
||||
- ticket expiry
|
||||
- last rendered sequence
|
||||
- client id
|
||||
- session key id
|
||||
- attach ticket
|
||||
- attach ticket PSK
|
||||
|
||||
## 9. Transport
|
||||
|
||||
Post-auth terminal traffic continues to use the current Dosh UDP packet model:
|
||||
|
||||
- fixed binary header
|
||||
- AEAD body
|
||||
- monotonic packet sequence
|
||||
- ack field
|
||||
- replay window
|
||||
- server snapshots for recovery
|
||||
|
||||
Required v1 changes:
|
||||
|
||||
- Separate packet namespaces for terminal frames, control messages, and forwarding
|
||||
streams.
|
||||
- Explicit `key_epoch` or `session_key_id` in packet metadata so stale packets can be
|
||||
ignored without fatal decrypt errors.
|
||||
- Rekey command after configurable packet count or wall-clock interval.
|
||||
- Connection migration must be accepted after any valid encrypted packet from a new
|
||||
source address.
|
||||
|
||||
## 10. Forwarding
|
||||
|
||||
Native forwarding is a Dosh stream multiplexer over the encrypted transport.
|
||||
|
||||
CLI:
|
||||
|
||||
```bash
|
||||
dosh -L [bind_host:]listen_port:target_host:target_port host
|
||||
dosh -R [bind_host:]listen_port:target_host:target_port host
|
||||
dosh -D [bind_host:]listen_port host
|
||||
```
|
||||
|
||||
Stream packet types:
|
||||
|
||||
- `StreamOpen`
|
||||
- `StreamOpenOk`
|
||||
- `StreamOpenReject`
|
||||
- `StreamData`
|
||||
- `StreamWindowAdjust`
|
||||
- `StreamEof`
|
||||
- `StreamClose`
|
||||
|
||||
Forwarding rules:
|
||||
|
||||
- Terminal traffic has priority over stream bulk data.
|
||||
- Each stream has independent flow control.
|
||||
- Backpressure must not block PTY input or output.
|
||||
- Server enforces `no-port-forwarding` and `permitopen=`.
|
||||
- Remote listeners bind to loopback by default.
|
||||
- Non-loopback remote bind requires explicit config.
|
||||
|
||||
## 11. Config
|
||||
|
||||
Client:
|
||||
|
||||
```toml
|
||||
auth_preference = "native,ssh"
|
||||
trust_on_first_use = false
|
||||
native_auth_timeout_ms = 700
|
||||
known_hosts = "~/.config/dosh/known_hosts"
|
||||
credential_cache = "~/.local/share/dosh/credentials"
|
||||
identity_files = ["~/.ssh/id_ed25519"]
|
||||
use_ssh_agent = true
|
||||
```
|
||||
|
||||
Server:
|
||||
|
||||
```toml
|
||||
native_auth = true
|
||||
host_key = "~/.config/dosh/host_key"
|
||||
authorized_keys = ["~/.ssh/authorized_keys", "~/.config/dosh/authorized_keys"]
|
||||
native_auth_rate_limit_per_minute = 30
|
||||
attach_ticket_ttl_secs = 86400
|
||||
allow_tcp_forwarding = true
|
||||
allow_remote_forwarding = false
|
||||
```
|
||||
|
||||
## 12. Migration Plan
|
||||
|
||||
Milestone 1: host identity and trust
|
||||
|
||||
- Generate Dosh host key on server install.
|
||||
- Add `dosh trust host`.
|
||||
- Add known-hosts file and mismatch handling.
|
||||
- Keep SSH bootstrap as the only auth path.
|
||||
|
||||
Milestone 2: native user auth
|
||||
|
||||
- Implement `ClientHello`/`ServerHello`/`UserAuth`/`AuthOk`.
|
||||
- Support ssh-agent Ed25519 first.
|
||||
- Verify against `authorized_keys`.
|
||||
- Add `--auth=native|ssh|auto`.
|
||||
|
||||
Milestone 3: default native auth
|
||||
|
||||
- Make `auth_preference = "native,ssh"` default.
|
||||
- Keep SSH fallback explicit and visible.
|
||||
- Add benchmark gates for native cold auth.
|
||||
|
||||
Milestone 4: forwarding
|
||||
|
||||
- Add stream mux and `-L`.
|
||||
- Add `-R`.
|
||||
- Add `-D` only after flow control is proven.
|
||||
|
||||
Milestone 5: hardening
|
||||
|
||||
- Fuzz packet parsing, authorized-key parsing, known-host parsing, and handshake state.
|
||||
- Add hostile-network integration tests.
|
||||
- Add external review checklist before public security claims.
|
||||
|
||||
## 13. Verification
|
||||
|
||||
Native v1 is not complete until all are true:
|
||||
|
||||
- Unknown host key fails by default unless TOFU is explicitly enabled.
|
||||
- Known host key mismatch hard fails.
|
||||
- Native Ed25519 auth succeeds via ssh-agent.
|
||||
- Native Ed25519 auth succeeds via encrypted private key prompt.
|
||||
- Removed authorized key can no longer authenticate.
|
||||
- Restrictive unsupported authorized-key options fail closed.
|
||||
- Replayed handshake packets are rejected.
|
||||
- Replayed transport packets are rejected.
|
||||
- Stale encrypted packets after reconnect are ignored, not fatal.
|
||||
- Client IP/port change preserves the session.
|
||||
- Native cold auth benchmark beats cold `ssh host true` on the same host.
|
||||
- Cached attach remains near network RTT plus local render overhead.
|
||||
- `-L` forwarding works without delaying terminal input.
|
||||
- `-R` forwarding enforces bind and permission policy.
|
||||
- Fuzz targets run in CI.
|
||||
- Threat model is updated with any accepted residual risks.
|
||||
|
||||
## 14. Public Claim Gate
|
||||
|
||||
Dosh may claim "native SSH replacement for Dosh-installed servers" only after:
|
||||
|
||||
- Native auth is default on at least one real host.
|
||||
- SSH fallback remains available.
|
||||
- The verification checklist is green.
|
||||
- The threat model is published.
|
||||
- Benchmarks include raw samples for SSH cold, Dosh native cold, Dosh cached attach,
|
||||
and Mosh startup.
|
||||
|
||||
Dosh must not claim generic SSH compatibility unless it implements the SSH protocol.
|
||||
@@ -4,6 +4,11 @@ Dosh's defensible public claim is fast terminal attach and reconnect. It should
|
||||
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`. Until that spec is
|
||||
implemented and verified, Dosh's public security claim remains SSH-bootstrap plus
|
||||
encrypted Dosh transport.
|
||||
|
||||
## Objective Benchmarks
|
||||
|
||||
Run the same-host SSH comparison:
|
||||
|
||||
Reference in New Issue
Block a user