A gateway service that accepts SSH connections from local clients and tunnels them to a remote SSH server over MOSH, providing resilience against packet loss and high latency on WAN links.
Local Client ──SSH──► [Gateway] ──MOSH/UDP──► Remote SSH Server
MOSH uses UDP with its State Synchronization Protocol (SSP), handling roaming, packet loss, and high-latency paths far better than a plain SSH-over-SSH tunnel.
- The remote target (
REMOTE_HOST) is fixed at startup — clients cannot redirect to arbitrary hosts. - SSH usernames are validated to
[a-zA-Z0-9._-]{1,64}and rejected immediately on mismatch. - Authentication to the remote is passed through via the client's forwarded SSH agent — the gateway holds no credentials of its own.
- Optionally pre-screen clients with a local
authorized_keysfile; if omitted, the remote server's ownauthorized_keysis the effective auth gate.
# docker-compose.yml
environment:
REMOTE_HOST: "my-server.example.com"docker compose up -dConnect from your local machine:
ssh -p 2222 alice@gateway-hostpip install -r requirements.txt
REMOTE_HOST=my-server.example.com python main.pyAll settings can be provided as environment variables, a YAML file (--config config.yaml), or CLI flags. See config.yaml.example for a fully annotated example.
| Variable | Default | Description |
|---|---|---|
REMOTE_HOST |
— | Required. The only host clients can reach through this gateway. |
REMOTE_PORT |
22 |
Remote SSH port. |
REMOTE_KNOWN_HOSTS |
system | Path to a known_hosts file to verify the remote host key. |
REMOTE_IGNORE_HOST_KEY |
false |
Skip remote host-key verification (insecure). |
By default the gateway fetches ~/.ssh/authorized_keys from the remote server for each connecting user and validates against those — no separate key management needed on the gateway.
| Variable | Default | Description |
|---|---|---|
GATEWAY_AUTHORIZED_KEYS_PATH |
— | Local authorized_keys override (takes precedence over remote key store). |
GATEWAY_AUTHORIZED_KEYS_CONTENT |
— | Inline authorized_keys text (alternative to the path). |
GATEWAY_PASSWORD_AUTH |
false |
Also accept password authentication. |
GATEWAY_PASSWORDS |
— | Comma-separated user:password pairs, e.g. alice:s3cr3t,bob:pass. |
GATEWAY_ACCEPT_ANY_KEY |
false |
Accept any client key without validation (dev / trusted LAN only). |
| Variable | Default | Description |
|---|---|---|
GATEWAY_SSH_KEY_PATH |
— | Path to the private key used to authenticate to the remote server. |
GATEWAY_SSH_KEY_CONTENT |
— | Base64-encoded private key (alternative to the file path). |
| Variable | Default | Description |
|---|---|---|
GATEWAY_HOST |
0.0.0.0 |
Bind address. |
GATEWAY_PORT |
2222 |
SSH listen port. |
GATEWAY_HOST_KEY_PATH |
/etc/gateway/host_key |
Server host key (auto-generated as ed25519 if missing). |
1. Client: ssh -p 2222 alice@gateway
│
│ SSH public-key auth
▼
2. Gateway: Fetches ~/.ssh/authorized_keys from remote for "alice"
(via gateway key, cached 60s) and validates the presented key
— or uses local GATEWAY_AUTHORIZED_KEYS_PATH if configured
│
│ Gateway uses its own key (GATEWAY_SSH_KEY_PATH)
▼
3. Gateway: asyncssh connects to REMOTE_HOST as user "alice"
using the gateway's private key
│
│ mosh-server started on remote via SSH
▼
4. Gateway: mosh-client subprocess connects to remote over MOSH/UDP
│
│ PTY I/O bridged back over SSH to local client
▼
5. Client: Interactive session, MOSH-resilient over the WAN leg
The gateway holds one key for the remote; clients need no special flags. The SSH login username becomes the remote username.
Gateway host: Python 3.11+, mosh-client, openssh-client
Remote server: mosh-server installed and reachable via SSH
Client: Any standard SSH client
Images are published to ghcr.io/siwatinc/ssh-mosh-wan-optimizer via GitHub Actions on every push to main and on semver tags. Multi-arch: linux/amd64 and linux/arm64.
docker pull ghcr.io/siwatinc/ssh-mosh-wan-optimizer:latestMount your key material into /keys/:
keys/
id_ed25519 ← gateway's private key (must be in authorized_keys on remote)
authorized_keys ← (optional) public keys of clients allowed to connect to the gateway
known_hosts ← (optional) pin the remote server's host key