Phase 43
SSH Server
An SSH server runs inside the OS, providing encrypted remote shell access. Users can connect from any standard SSH client (`ssh user@host`) with password or public key authentication. This replaces the telnet server for untrusted networks and is a major milestone: the OS is now a secure, networked, multi-user system.
Learning goal
Understand the SSH protocol: transport layer (encryption), user authentication layer, and connection layer (channels, sessions).
Category
infrastructure
Components touched
Builds on
Unlocked by this phase
Why this phase matters
An SSH server runs inside the OS, providing encrypted remote shell access. Users can connect from any standard SSH client (ssh user@host) with password or public key authentication. This replaces the telnet server for untrusted networks and is a major milestone: the OS is now a secure, networked, multi-user system.
How it works in m3OS
The roadmap entry defines the learning scope for this phase; a deeper implementation doc has not been matched yet.
How it builds on earlier phases
This phase builds on Async Executor and turns that foundation into SSH Server.
Implementation outline
- Evaluate sunset crate: add dependency, verify it compiles for x86_64-m3os target.
- Create
userspace/sshd/crate with basic TCP accept loop. - Generate Ed25519 host key on first boot, store at
/etc/ssh/ssh_host_ed25519_key. - Implement sunset adapter: feed TCP bytes into sunset, write output back to socket.
- Implement password authentication callback against
/etc/shadow. - Implement session channel handling: PTY allocation, shell exec, data relay.
- Implement public key authentication with
~/.ssh/authorized_keys. - (Deferred) Window-change and signal forwarding.
- Add sshd to init’s startup sequence and initrd.
- Test from host with
ssh -p 2222 user@localhost. - Test multiple simultaneous SSH sessions.
- Verify encryption by inspecting traffic.
Task progress snapshot
97 of 113 tracked tasks are complete.
Documentation targets
- Documentation deliverables have not been listed yet.
Deferred follow-on work
- SFTP / SCP file transfer
- Port forwarding and tunneling
- SSH client (for connecting from the OS to other machines)
- Key agent
- Certificate authentication
- Connection rate limiting and brute-force protection
- Privilege separation (sshd runs as a single process per session)
- Multiple host key types (Ed25519 only)
- Key re-exchange during long sessions
Source materials
Important code
Code spotlight cards
Code spotlight
Create `userspace/sshd/` crate
pattern as telnetd (Phase 30). Separating the server from a library keeps the binary. Why it matters: A dedicated binary crate for the SSH server follows the same. Focus on `sshd` and `no_std` in `userspace/sshd/Cargo.toml` while you trace the snippet.
[package]
name = "sshd"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "sshd"
path = "src/main.rs"
[dependencies]
syscall-lib = { path = "../syscall-lib", features = ["alloc"] } Code spotlight
Verify sunset IO-less API compiles and runs in m3OS
tested inside m3OS. This task confirms that creating a sunset server instance,. Why it matters: sunset is designed as an IO-less library, but it has not been. Focus on `sunset::Runner` and `Runner` in `userspace/sshd/src/main.rs` while you trace the snippet.
Code spotlight
Generate Ed25519 host key on first boot
first boot there is no key, so sshd must generate one using crypto-lib's Ed25519. Why it matters: The host key proves the server's identity during key exchange. On. Focus on `generate_host_key` and `crypto_lib::asymmetric::ed25519_keygen` in `userspace/sshd/src/host_key.rs` while you trace the snippet.
/// B.2: Generate a new Ed25519 host key and save to disk.
fn generate_host_key() -> Result<HostKey, ()> {
let key = SignKey::generate(KeyType::Ed25519, None).map_err(|_| ())?;
// Extract the 32-byte seed from the key.
let seed = match &key {
SignKey::Ed25519(k) => k.to_bytes(),
_ => return Err(()),
};
// Write private key seed (mode 0600).
let fd = open(HOST_KEY_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0o600);
if fd < 0 {
write_str(STDOUT_FILENO, "sshd: cannot write host key\n");
return Err(());
}
let n = syscall_lib::write(fd as i32, &seed);
close(fd as i32);
if n != 32 {
write_str(STDOUT_FILENO, "sshd: short write on host key\n");
syscall_lib::unlink(HOST_KEY_PATH);
return Err(());
} Key files
- userspace/sshd/ — Create userspace/sshd/ crate with basic TCP accept loop.
How real OSes differ
- Production SSH servers (OpenSSH) support SFTP, SCP, port forwarding, tunneling, X11 forwarding, agent forwarding, ProxyJump, certificate auth, and GSSAPI.
- OpenSSH uses privilege separation (unprivileged child handles the network, privileged parent handles authentication) to limit the impact of vulnerabilities.
- Real systems support multiple host key types (RSA, ECDSA, Ed25519) and algorithm negotiation across dozens of ciphers and MACs.
- Dropbear intentionally omits many features for simplicity. Our deployment is further simplified: single-server, no forwarding, no SFTP (initially).
- Production systems implement connection rate limiting, fail2ban integration, and audit logging for brute-force protection.
Success criteria
- `sshd` starts at boot and listens on port 22.
- `ssh user@host` from a standard OpenSSH client connects successfully.
- Password authentication works.
- Public key authentication works with `authorized_keys`.
- The remote shell is fully functional (commands, pipes, editor, compiler).
- Multiple simultaneous SSH sessions work.
- Host key fingerprint is stable across reboots.
- Connection is encrypted (verified by inspecting traffic with tcpdump/wireshark).
- Closing the SSH client cleanly terminates the remote session.
- Invalid credentials are rejected.
Related phases
Keep exploring the roadmap
Phase 42
CompleteAsync Executor
Learn how Async Executor fits into m3OS.
Phase 43
CompleteCrash Diagnostics
Every kernel crash is self-diagnosing. When the kernel panics or takes a fatal fault, the serial output includes full CPU register state, current task metadata, and per-core scheduler context. `debug_assert!` invariants at every scheduler, fork, IPC, and stack boundary catch state corruption at the point of origin rather than surfacing later as an opaque `RIP=0x4` fault.