SSH Reference
SSH is the encrypted access method — the modern counterpart to telnet for reaching the gateway, and the way the gateway dials out to remote shells.
Where telnet is a cleartext NVT
stream the gateway implements byte-for-byte, SSH is a full encrypted
transport. The gateway does not hand-roll the SSH wire
protocol — it builds on the
russh crate (pure-Rust
SSH) and supplies the policy around it: host keys, authentication, the
bridge into a terminal session, and trust decisions when dialing out.
Everything here lives in src/ssh.rs (the inbound server and
key management) and the gateway driver in src/telnet.rs
(the outbound proxy).
The gateway speaks SSH in both directions — keep the two straight, because they use different keys.
| Role | Direction | What it is |
|---|---|---|
| SSH Server | Inbound — a client connects to the gateway | An encrypted front door onto the same menus telnet offers. The gateway proves its identity with its host key; the client authenticates with the unified username / password. Off by default. |
| SSH Gateway | Outbound — the gateway dials out to a remote SSH server | A proxy: from the main menu you open an interactive shell on some other machine. The gateway authenticates with either a password you type or its own client key, and verifies the remote's host key trust-on-first-use. |
An encrypted way in, on port 2222 by default (disabled until you enable it).
| Property | Value |
|---|---|
| Default state | ssh_enabled = false — opt-in. |
| Default port | 2222 (ssh_port). |
| Implementation | russh server (src/ssh.rs). |
| Host key | Ed25519, auto-generated on first run, persisted to ethernet_ssh_host_key (OpenSSH PEM, 0o600 on Unix). |
| Authentication | Password only, against the unified username / password (shared with telnet and the web UI). Constant-time comparison. |
| Brute-force lockout | Shared per-IP map with telnet: 3 failures → 5-minute ban. auth_rejection_time is 1 s. |
| Capacity | Bounded by max_sessions; connections over the cap are rejected at auth. |
SSH clients expect a shell, not a raw socket, so the server bridges the SSH channel into the gateway's ordinary terminal session:
~/.ssh/known_hosts).auth_password runs the per-IP lockout check, then a constant-time compare of the username and password, and — only on a successful match — claims a session slot, rejecting if the server is already at max_sessions. Claiming the slot at successful login (rather than at connect) means an unauthenticated peer that opens many connections and stalls can't exhaust the session cap. The credentials are snapshotted at connect time, so saving a new password mid-session never invalidates an already-authenticated connection.shell_request the server opens one shell per connection and builds a duplex bridge to a TelnetSession (started in ANSI mode). SSH input is forwarded into the bridge; the session's output is read back out and sent to the client as channel data.SSH touches three on-disk files in the gateway's working directory. They are easy to confuse — here is exactly what each one is for.
| File | Used when | What it holds |
|---|---|---|
ethernet_ssh_host_key | A client connects to our SSH server | The gateway's own SSH server host key (Ed25519 private key). This is the identity remote clients pin. |
ethernet_gateway_ssh_key | We dial out to a remote SSH server in key mode | The gateway's outgoing client keypair (Ed25519). Put its public half into the remote's authorized_keys. |
gateway_hosts | We dial out and verify the remote | Trusted remote-server fingerprints — the gateway's equivalent of OpenSSH's known_hosts. One host:port algorithm base64 entry per line. |
All three are written atomically and chmod 0o600 on Unix.
The private keys carry no passphrase — the gateway process must use
them without interaction, so the file mode is the at-rest protection.
gateway_hosts is locked down too: the stored public keys are
not secret, but the file also reveals the gateway's dial history
(which hosts the operator has connected to), which other local users
shouldn't see.
Proxy out to a remote shell. Press S from the main menu.
authorized_keys), or any other key to continue.gateway_hosts (see Host-Key Verification).The outbound client uses a 600-second inactivity timeout and a bounded connect timeout, so a dead or unreachable host fails cleanly rather than hanging the session.
Before authenticating, the gateway checks the remote's host key against gateway_hosts — trust-on-first-use, just like OpenSSH.
On connect, the gateway captures the remote's public host key and looks
up host:port in gateway_hosts. One of three
things happens:
| Status | Meaning | What the gateway does |
|---|---|---|
| Known | Stored key matches the presented key. | Proceeds silently to authentication. |
| Unknown | No entry for this host:port. | Shows the key type and SHA-256 fingerprint and asks Trust this host? (Y/N). On Y it saves the key (a TOFU accept, logged) and continues; on N it disconnects. |
| Changed | Stored key for this host does not match. | Prints WARNING: HOST KEY HAS CHANGED! with the new fingerprint and makes you review before accepting — a possible man-in-the-middle. |
# gateway_hosts — one entry per host:port
example.com:22 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
192.168.1.10:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
# blank lines and # comments are ignored
gateway_hosts; it does not read
or write any ~/.ssh/known_hosts. Every trust decision
(first-time accept, change, rejection) is written to the server log for
auditability.
Inbound and outbound authenticate differently — and the outbound gateway uses exactly one method, chosen by config.
Password only, against the unified username /
password shared across telnet, SSH, and the web UI, compared
in constant time, behind the shared per-IP lockout.
The ssh_gateway_auth key selects one method
— there is no silent fallback, so the remote sees
exactly one auth attempt and a failure is unambiguous:
ssh_gateway_auth | Method | Behavior |
|---|---|---|
password (default) | Keyboard password | The gateway prompts you for the remote password each dial and authenticates with it. Nothing is stored. |
key | Gateway client key | The gateway authenticates with its own Ed25519 key (ethernet_gateway_ssh_key, auto-generated on first use). No password is requested and there is no fallback — the remote must already trust the key. |
ssh_gateway_auth = key in config (telnet, web, or GUI).ssh-ed25519 AAAA…, with no stray comment field).~/.ssh/authorized_keys.key
mode the gateway never prompts for a password, and in
password mode it never offers the key. If a
key-mode dial fails, fix the remote's
authorized_keys (or switch the mode back to
password) — the gateway will not quietly try the other
method.
A remote shell assumes a VT-style terminal. A Commodore 64 isn't one.
When you reach the SSH gateway from a PETSCII or ASCII terminal, the gateway strips ANSI escape sequences from the remote's output — CSI, OSC, DCS, PM, APC, and SOS sequences — so raw colour and cursor-control codes don't splatter across a screen that can't interpret them. The escape-state machine is carried across reads so sequences split over packet boundaries are still caught. ANSI terminals get the stream through unchanged.
SSH here is built for a trusted-user / LAN deployment, and the design reflects that.
username / password covers telnet, SSH, and the web UI — fewer secrets to manage. Compared in constant time to avoid timing leaks.0o600, written atomically so they are never briefly world-readable.Editable from the telnet Configuration menu, the GUI, the web UI, or egateway.conf directly.
| Key | Default | Meaning |
|---|---|---|
ssh_enabled | false | Enable the inbound SSH server. |
ssh_port | 2222 | Port the SSH server listens on. |
ssh_gateway_auth | password | Outbound auth method: password (prompt each dial) or key (gateway client key, no fallback). |
username / password | admin / changeme | Unified credentials for telnet, SSH, and the web UI. Change the password before exposing the gateway. |
max_sessions | 50 | Concurrent session cap, applied independently to telnet and SSH (each protocol allows up to this many; only the per-IP lockout map is shared between them). |
ssh_* config keys control
behavior; the actual keys live in the ethernet_ssh_host_key,
ethernet_gateway_ssh_key, and gateway_hosts
files described under Key Files. The legacy
separate ssh_username / ssh_password keys were
removed — SSH now shares the one credential pair.
Deep-dive reference pages for every protocol and interface, plus the character-set tables and ANSI escape-sequence reference.
128-byte blocks; CRC-16 / checksum negotiation.
Block-0 metadata, batch, exact size truncation.
Streaming; ZDLE, CRC-32, autostart, resume.
Send-Init negotiation; F / A / D / Z / B transfer.
C1 dual checksum, two-phase, GOO/BAD/ACK.
Hayes command set, S-registers, +++ escape.
IAC negotiation, every option, the NVT data phase.
Server, gateway, host keys, TOFU, auth modes.
Hex tables for every encoding: ASCII, ANSI, PETSCII, ATASCII, Baudot/ITA2, ZX Spectrum, TRS-80.
Cursor, colour/SGR, erase, and screen-mode escape codes, with the raw hex bytes.