101 lines
4.2 KiB
Markdown
101 lines
4.2 KiB
Markdown
to-do: rewrite & convert to html for docs pages
|
|
|
|
# SSH
|
|
|
|
MontaukOS ships an SSH-2 server, `sshd`, that gives a remote client a shell on
|
|
the machine. It speaks enough of the protocol to interoperate with stock
|
|
OpenSSH clients:
|
|
|
|
```
|
|
ssh dan@montauk-box interactive shell
|
|
ssh dan@montauk-box 'ls 0:/' run one command
|
|
```
|
|
|
|
Remote access is **off by default**. Turn it on from Settings > SSH Server,
|
|
which flips `services.ssh.enabled` in `0:/config/init.toml`; `init` starts the
|
|
daemon on the next boot.
|
|
|
|
Two gates stand in front of a login: the account must exist in the MontaukOS
|
|
user database with a valid password, and it must be listed in the `[allow]`
|
|
table of `0:/config/ssh.toml`. A user absent from that table is refused even
|
|
with the right password. The Settings applet maintains the table; `sshd` never
|
|
writes it.
|
|
|
|
## Layout
|
|
|
|
```
|
|
programs/src/sshd/
|
|
main.cpp transport framing, key exchange, auth, session/channel loop
|
|
crypto.hpp host key storage, DH group 14 arithmetic, cipher/MAC wrappers
|
|
programs/src/sshserver/
|
|
main.cpp Settings applet: enable the service, edit the allow table
|
|
programs/include/montauk/ssh.h
|
|
shared policy helpers used by both
|
|
```
|
|
|
|
The crypto primitives (SHA-256, HMAC, AES-CTR, RSA signing, the DRBG) come from
|
|
BearSSL. The modular exponentiation for Diffie-Hellman is in `crypto.hpp`
|
|
because BearSSL exposes no public bignum API.
|
|
|
|
## What it implements
|
|
|
|
| | |
|
|
|---|---|
|
|
| Key exchange | `diffie-hellman-group14-sha256` |
|
|
| Host key | `rsa-sha2-256`, 2048-bit, generated on first start |
|
|
| Cipher | `aes128-ctr` |
|
|
| MAC | `hmac-sha2-256` |
|
|
| Compression | none |
|
|
| Auth | password only |
|
|
|
|
Client-initiated rekeying works, so long sessions survive OpenSSH's 1 GiB /
|
|
one-hour rekey threshold. Channel windows are tracked in both directions, so
|
|
sessions that move more than a window's worth of data keep flowing.
|
|
|
|
## What it does not
|
|
|
|
No public key authentication, no port or X11 forwarding, no SFTP or SCP. One
|
|
session channel per connection, and connections are served one at a time --
|
|
there is no `fork`, so a second client waits for the first to finish. A
|
|
pre-authentication timeout keeps an idle peer from holding the daemon shut.
|
|
|
|
`exec` requests are run by typing the command into an interactive shell rather
|
|
than executing it directly, so the prompt and the echoed command come back
|
|
mixed into the output. This is why `scp` and friends will not work as-is.
|
|
|
|
## The host key
|
|
|
|
Generated on first start and written to `0:/config/ssh_hostkey.toml`, separate
|
|
from the policy file so the unprivileged Settings applet -- which rewrites
|
|
`ssh.toml` whenever the allow table changes -- never round-trips private key
|
|
material.
|
|
|
|
That separation is hygiene, not protection: **the filesystem has no permission
|
|
model**, so any local process can read the host key. Closing that hole needs
|
|
FS-level access control, at which point the key file should be restricted to
|
|
the account `sshd` runs as. A key found in an older `ssh.toml` is migrated to
|
|
the new file on first start so the host key does not change under existing
|
|
clients.
|
|
|
|
## Debugging
|
|
|
|
`sshd` calls `montauk::print`, which reaches the boot console only. **It has no
|
|
usable diagnostics once the desktop is up**, pending the unified syslog.
|
|
|
|
Worth understanding, because it catches out anything written as a daemon:
|
|
`montauk::print` is `SYS_PRINT`, which writes the kernel *terminal*, not the
|
|
kernel *log*. Only in-kernel `KernelLogStream` writes raise `g_kernelLogDepth`,
|
|
and only those append to the ring buffer `SYS_LOG` reads -- so daemon output
|
|
never shows up in `klog`. On top of that, `Sys_Print` returns early once
|
|
`g_suppressKernelLog` is set, which the desktop does at startup, so the output is
|
|
discarded outright from then on. `init` spawns services with `spawn` rather than
|
|
`spawn_redir`, so there is no stream to capture either.
|
|
|
|
Until there is somewhere for it to go, debugging a handshake means watching the
|
|
boot console, or temporarily pointing the print calls at a file.
|
|
|
|
Failures print a numeric code -- `key exchange failed (N)`, `authentication
|
|
failed (N)` -- where N identifies the step; see the `kex_error` assignments in
|
|
`main.cpp`. Running the client with `ssh -vvv` and lining its trace up against
|
|
those codes is usually the fastest way to find where a handshake diverged.
|