191 lines
5.8 KiB
Markdown
191 lines
5.8 KiB
Markdown
# netssh
|
|
|
|
A transparent SSH wrapper that resolves hostnames via [NetBox](https://netbox.dev/) before connecting.
|
|
|
|
Instead of looking up an IP manually, you just type the hostname as it appears in NetBox:
|
|
|
|
```sh
|
|
netssh my-router-01
|
|
netssh -p 2222 admin@app-server-03 uptime
|
|
```
|
|
|
|
`netssh` looks up the host in NetBox, resolves the right IP using a configurable strategy chain, and replaces the process with the native `ssh` binary — so all your existing SSH configs, keys, and agent forwarding work without any changes.
|
|
|
|
## Features
|
|
|
|
- **Transparent proxy** — replaces itself with `ssh` via `syscall.Exec`, preserving all SSH flags and options
|
|
- **Flexible IP resolution** — configurable chain of strategies: management subnet, primary IP, or named interface
|
|
- **Interactive TUI** — fuzzy search with live NetBox queries and 300 ms debouncing (start with `netssh`, no arguments)
|
|
- **Persistent cache** — successful lookups are cached to `~/.cache/netssh/hosts.json` for instant shell completion
|
|
- **Shell completion** — tab-complete hostnames from the cache in zsh, bash, and fish
|
|
- **Default SSH user** — set a fallback username once in config instead of typing it every time
|
|
|
|
## Installation
|
|
|
|
### One-liner (Linux & macOS)
|
|
|
|
```sh
|
|
curl -fsSL https://git.zb-server.de/Sebi/ssh-netbox-wrapper/raw/branch/main/install.sh | bash
|
|
```
|
|
|
|
The script detects your OS and architecture, downloads the matching binary from the [latest release](https://git.zb-server.de/Sebi/ssh-netbox-wrapper/releases/latest), verifies the SHA-256 checksum, and installs to `/usr/local/bin/netssh` (using `sudo` only if necessary).
|
|
|
|
To install to a custom directory:
|
|
|
|
```sh
|
|
INSTALL_DIR=~/.local/bin curl -fsSL https://git.zb-server.de/Sebi/ssh-netbox-wrapper/raw/branch/main/install.sh | bash
|
|
```
|
|
|
|
### Build from source
|
|
|
|
```sh
|
|
git clone ssh://git@git.zb-server.de:30022/Sebi/ssh-netbox-wrapper.git
|
|
cd ssh-netbox-wrapper
|
|
go build -o netssh ./cmd/netssh
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Create `~/.config/netssh.yaml`:
|
|
|
|
```yaml
|
|
netbox:
|
|
url: https://netbox.example.com
|
|
token: your-api-token-here
|
|
|
|
resolver:
|
|
# Strategies are tried in order; the first to return an IP wins.
|
|
strategies:
|
|
- management_subnet
|
|
- primary_ip
|
|
# Used by the management_subnet strategy.
|
|
management_subnets:
|
|
- 10.0.0.0/8
|
|
- 172.16.0.0/12
|
|
# Used by the interface_name strategy.
|
|
interface_name: mgmt0
|
|
|
|
cache:
|
|
ttl: 3600 # seconds; 0 = always query NetBox on connect (cache still used for completion)
|
|
# path: ~/.cache/netssh/hosts.json # default
|
|
|
|
ssh:
|
|
default_user: admin # used when no user is specified on the command line
|
|
```
|
|
|
|
Any value can be overridden with environment variables (`NETSSH_NETBOX_URL`, `NETSSH_NETBOX_TOKEN`, etc.) or will be read from the config file.
|
|
|
|
## Usage
|
|
|
|
### SSH wrapper mode
|
|
|
|
Pass any SSH flags and a NetBox hostname:
|
|
|
|
```sh
|
|
netssh my-router-01
|
|
netssh -p 2222 admin@app-server-03 uptime
|
|
netssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no db-primary
|
|
```
|
|
|
|
The process is replaced by `ssh` with the resolved IP — your `~/.ssh/config`, agent, and keys all work as normal.
|
|
|
|
### Default username
|
|
|
|
Set `ssh.default_user` in the config to avoid typing a username every time:
|
|
|
|
```sh
|
|
netssh my-router # → ssh -l admin 10.0.0.1
|
|
```
|
|
|
|
The default is only applied when no user is specified on the command line. An explicit user always takes precedence:
|
|
|
|
```sh
|
|
netssh root@my-router # user@ prefix wins → ssh root@10.0.0.1
|
|
netssh -l ops my-router # -l flag wins → ssh -l ops 10.0.0.1
|
|
```
|
|
|
|
### Interactive TUI
|
|
|
|
Run without arguments to open the interactive search:
|
|
|
|
```sh
|
|
netssh
|
|
```
|
|
|
|
| Key | Action |
|
|
|-----|--------|
|
|
| type | filter hosts (300 ms debounce → NetBox query) |
|
|
| `Tab` | autocomplete top result into the search field |
|
|
| `↑` / `↓` | navigate results |
|
|
| `Enter` | connect to selected host |
|
|
| `Esc` / `Ctrl+C` | quit |
|
|
|
|
### Cache management
|
|
|
|
```sh
|
|
netssh cache list # show all cached entries
|
|
netssh cache refresh # re-fetch all hosts from NetBox
|
|
netssh cache clear # wipe the cache
|
|
```
|
|
|
|
### Search (for scripting)
|
|
|
|
```sh
|
|
netssh search app- # prints matching hostnames, one per line
|
|
```
|
|
|
|
## IP Resolution Strategies
|
|
|
|
Strategies are tried in the configured order; the first to succeed wins.
|
|
|
|
| Name | Description |
|
|
|------|-------------|
|
|
| `primary_ip` | Returns the `primary_ip4` (or `primary_ip6`) set in NetBox. No extra API call. |
|
|
| `management_subnet` | Fetches all IPs for the host and returns the first one matching a configured CIDR. |
|
|
| `interface_name` | Fetches IPs attached to a specific named interface (e.g. `mgmt0`). |
|
|
|
|
## Shell Completion
|
|
|
|
### zsh
|
|
|
|
```sh
|
|
netssh completion zsh > "${fpath[1]}/_netssh"
|
|
```
|
|
|
|
Or add to `.zshrc`:
|
|
|
|
```zsh
|
|
source <(netssh completion zsh)
|
|
```
|
|
|
|
### bash
|
|
|
|
```sh
|
|
netssh completion bash > /etc/bash_completion.d/netssh
|
|
```
|
|
|
|
### fish
|
|
|
|
```sh
|
|
netssh completion fish > ~/.config/fish/completions/netssh.fish
|
|
```
|
|
|
|
Completions are served from the local cache — no network request on every `<Tab>`.
|
|
|
|
## Development
|
|
|
|
```sh
|
|
go test ./... # run all tests
|
|
go build ./... # build all packages
|
|
```
|
|
|
|
The test suite covers the cache, NetBox client (via `httptest`), IP resolver chain, and SSH argument parser.
|
|
|
|
## How it works
|
|
|
|
1. `netssh` checks whether the first argument is a known subcommand (`search`, `cache`, `completion`). If not, it enters SSH wrapper mode.
|
|
2. It parses the SSH arguments to extract the destination hostname, handling all flags that consume an extra argument (`-p`, `-i`, `-J`, …).
|
|
3. It checks the local cache. If the entry exists and is within the TTL, it connects immediately.
|
|
4. Otherwise it queries NetBox (`/api/dcim/devices/` and `/api/virtualization/virtual-machines/` in parallel), runs the result through the resolver chain, and caches the IP.
|
|
5. It calls `syscall.Exec` to replace itself with `ssh`, substituting the hostname with the resolved IP.
|