- Go 98.1%
- Dockerfile 1.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
The bare patterns 'console-gw'/'console' also matched the cmd/console-gw and cmd/console directories, silently excluding both main packages from the initial commit. Anchor binary ignores to the repo root. |
||
| cmd | ||
| internal | ||
| .dockerignore | ||
| .gitignore | ||
| console-gw.example.yaml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
console-gw
SSH gateway for home-lab serial consoles. The Cisco 2611XM router lab-r1
(lab-r1.net.mcda.dev / 10.20.1.70) acts as a console server, exposing its
async serial lines via unauthenticated reverse-TELNET on TCP ports 2033+.
console-gw puts a modern, authenticated SSH front-end in front of those
cleartext ports, and will later also serve direct local serial ports.
Current lab-r1 telnet port mapping (fronted by this gateway):
| Port | Device | Console name |
|---|---|---|
| 2033 | C892FSP | c892fsp |
| 2034 | SRX320 | srx320 |
Components
console-gw— the daemon. SSH server (public-key auth only); the SSH username selects the console (ssh srx320@gw -p 2222). Sessions are bridged to the backend and logged raw tolog_dir.console— thin CLI client (console list,console attach srx320).
Build
go build ./cmd/...
Configuration
Copy console-gw.example.yaml to console-gw.yaml (default path; override
with -config):
listen: ":2222"
host_key: "./host_key" # ed25519; generated if missing
authorized_keys: "./authorized_keys"
log_dir: "./sessions"
consoles:
- name: srx320
description: "Juniper SRX320 (core-1) console"
transport: telnet
address: "lab-r1.net.mcda.dev:2034"
- name: c892fsp
description: "Cisco C892FSP console"
transport: telnet
address: "lab-r1.net.mcda.dev:2033"
authorized_keys is a standard OpenSSH authorized_keys file; each key's
comment is recorded and used in session log filenames.
Docker
Multi-stage build: static binary on gcr.io/distroless/static-debian12:nonroot
(runs as uid 65532). The image's entrypoint reads
/etc/console-gw/console-gw.yaml; writable state (host key, session logs)
lives under /data.
docker build -t console-gw .
docker run --rm -p 2222:2222 \
-v "$PWD/console-gw.yaml:/etc/console-gw/console-gw.yaml:ro" \
-v "$PWD/authorized_keys:/etc/console-gw/authorized_keys:ro" \
-v console-gw-data:/data \
console-gw
The mounted config should point at the container paths:
listen: ":2222"
host_key: "/data/host_key"
authorized_keys: "/etc/console-gw/authorized_keys"
log_dir: "/data/sessions"
The daemon creates missing state directories (/data/sessions, the host
key's parent) itself as the nonroot user.
Usage
# plain ssh, username selects the console
ssh srx320@gw -p 2222
ssh list@gw -p 2222 # table of consoles
# CLI client
console list
console attach srx320
console -addr gw.example.com:2222 -i ~/.ssh/id_ed25519 attach c892fsp
The CLI defaults its gateway address from ~/.config/console/config.yaml:
gateway: gw.example.com:2222
Behavior notes:
- PTY is required to attach; a bare session (or username
list) prints the console table and closes. - One active session per console; a concurrent attach gets a "console busy" error. Read-only shadow (follow) sessions are a planned future feature.
- Window resizes are accepted but ignored by the telnet backend.
Security
- SSH-only access, public-key authentication only (no passwords); anything
not in
authorized_keysis rejected and logged. - The telnet hop between the gateway and lab-r1 remains cleartext — keep it on the isolated lab network.
- Every session is logged raw to
<log_dir>/<console>-<timestamp>-<key>.log(backend output only). - The
consoleCLI verifies the gateway against~/.ssh/known_hostswhen that file exists (add the gateway's host key there); otherwise it warns and skips verification. - Deployment: runs behind Tailscale, reachable only on the tailnet. A later Ansible role will automate deployment.
Roadmap: replacing the 2611XM
- Add direct local serial consoles (
transport: serial, e.g. a Linux box with USB-serial hubs) — config schema and backend interface already support it. - Migrate consoles off lab-r1 reverse-telnet port by port; config change only, clients unchanged.
- Retire the 2611XM as console server once all lines are direct-attached.