A script made for managing my SSH hosts and keys
  • Rust 99.3%
  • Shell 0.7%
Find a file
Arctic 5ffe9a238e
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Merge pull request 'proxy-jump' (#4) from proxy-jump into main
Reviewed-on: #4
2026-04-23 05:45:20 +00:00
assets v0.1.15: managed SSH install, config migration, and remote fixes 2026-04-23 00:27:51 -05:00
src fix(config): strict Include detection; align README inventory table 2026-04-23 00:35:43 -05:00
tests v0.1.15: managed SSH install, config migration, and remote fixes 2026-04-23 00:27:51 -05:00
.drone.yml Fix BUG_REVIEW B-01–B-19: SSH flows, validation, keys, UX, CI 2026-04-22 23:56:14 -05:00
.gitignore chore: ignore local docs folder 2026-04-21 05:07:20 -05:00
Cargo.lock v0.1.15: managed SSH install, config migration, and remote fixes 2026-04-23 00:27:51 -05:00
Cargo.toml v0.1.15: managed SSH install, config migration, and remote fixes 2026-04-23 00:27:51 -05:00
README.md fix(config): strict Include detection; align README inventory table 2026-04-23 00:35:43 -05:00

SSH Manager

Build Status

SSH Manager is a small Rust interactive CLI (crate ssh-manager-rs, binary ssh-manager) for organizing SSH hosts under ~/.ssh/conf, generating per-host keys, and updating host config files without manually editing a large ~/.ssh/config.

What It Solves

It helps when your SSH setup starts getting messy:

  • too many hosts in one config file
  • host keys scattered around ~/.ssh
  • repeated manual ssh-keygen and authorized_keys work
  • awkward key rotation and cleanup

SSH Manager keeps each host in its own directory and gives you a simple menu-driven way to manage them.

What It Does

  • lists hosts in a table sorted by resolved IP (no row numbers) with columns Host, User, Port, Via (Proxy) (ProxyJump, a trimmed ProxyCommand, or -), HostName, and IP Address; per-host files still live under ~/.ssh/conf/<label>/ (see Layout)
  • groups the inventory by network scope (private/local vs public vs unresolved) with optional separator lines between groups (see Inventory separators)
  • the main menu shows a live connectivity table (DNS, ping, TCP port, SSH) with color-coded columns above the menu; Test configurations summarizes that view; Connect to Host opens an interactive SSH session using the selected hosts config (ssh -F … <Host>)
  • adds a new host config
  • generates a per-host ed25519 key
  • installs the new public key on the remote server
  • edits an existing host: tabulated current values, aligned prompts, and a host picker with columns lined up; the edit picker omits resolved IP from each line
  • rotates a host key
  • removes a host and its local folder

Layout

Managed hosts live under ~/.ssh/conf/<host>/.

Example:

~/.ssh/conf/
  web-01/
    config
    id_ed25519
    id_ed25519.pub

If ~/.ssh/config does not exist yet, the app creates one that includes Include conf/*/config, a Host * block with connection defaults, and ControlPath ~/.ssh/s/%C (the app ensures ~/.ssh/s/ exists). See [src/config.rs](src/config.rs) for the full default template.

Run

From the repository root:

cargo run

Flags

  • --no-divider or --no-dividers — omit the horizontal rules between inventory groups in the host table (including the live connectivity table above the menu). Example: cargo run -- --no-divider

Inventory separators (local vs remote)

When more than one scope is present, the host table inserts ASCII separator lines (+---+---+…) between blocks, in this order:

  1. Private / local — RFC 1918 private IPv4, loopback, IPv4 link-local, CGNAT (100.64.0.0/10), IPv6 unique local, and IPv6 link-local.
  2. Remote / public — other routable addresses.
  3. Unresolved — no mapped IP for display (or unparseable).

Within each block, rows stay sorted by resolved IP, then host label. If only one scope has hosts, no extra separators are drawn.

To show one continuous table without those separators, start the app with --no-divider or --no-dividers (see Flags).

Main screen

Each time the menu appears, the screen is cleared and you see:

  1. SSH Host Inventory (Sorted by IP address.) — the table of hosts (or an empty table with a warning if none are configured).
  2. A blank line, then SSH Config Manager Menu (the title includes the app version from Cargo.toml, e.g. v0.1.15) — an interactive list (on a normal terminal: ↑/↓ or j/k to move, Enter to choose, number keys 1N to jump to an item, q quits to the last item). N is the number of menu entries (see MENU_OPTIONS and MENU_TITLE in the source). With 10 or more entries, type the multi-digit index (digits within about 400ms of each other); a short pause finalizes a single-digit jump.

Menu options (current order):

  1. Connect to Host
  2. Add a Host
  3. Edit a Host
  4. Regenerate Key
  5. Remove Host
  6. Test configurations
  7. Exit

The selected line is marked with **** at the left.

Example: host list

What the inventory table looks like when hosts exist (same column headers as HEADERS in the source):

SSH Host Inventory (Sorted by IP address.)
+-------------+--------+------+----------------+------------------+----------------+
| Host        | User   | Port | Via (Proxy)    | HostName         | IP Address     |
+-------------+--------+------+----------------+------------------+----------------+
| prod-web-01 | deploy | 22   | -              | 10.0.0.15        | 10.0.0.15      |
| db-primary  | root   | 2222 | bastion        | db.internal      | 10.0.0.21      |
+-------------+--------+------+----------------+------------------+----------------+
| edge-box    | admin  | 22   | -              | edge.example.com | 203.0.113.8    |
+-------------+--------+------+----------------+------------------+----------------+

If both private/local and public hosts exist, a separator row (like the line between db-primary and edge-box above) appears between the groups. Use --no-divider to print a single block without those lines.

Typical Flow

  1. Run cargo run
  2. Choose Add a Host
  3. Enter the host label, hostname, user, and port
  4. Choose whether to generate a new key
  5. Optionally copy the public key to the remote server
  6. Connect later with normal SSH, for example ssh prod-web-01

Tests

cargo test --offline

Build

cargo build --release --offline

The release binary is target/release/ssh-manager.