Bump version to 1.0.0; expand README with usage details
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -220,7 +220,7 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "porthole"
|
name = "porthole"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "porthole"
|
name = "porthole"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Create and manage named SSH port forwards."
|
description = "Create and manage named SSH port forwards."
|
||||||
license = "AGPL-3+"
|
license = "AGPL-3+"
|
||||||
|
|||||||
165
README.md
165
README.md
@@ -1,3 +1,166 @@
|
|||||||
# porthole
|
# porthole
|
||||||
|
|
||||||
Create and manage named SSH port fowards easily.
|
Create and manage named SSH port forwards easily.
|
||||||
|
|
||||||
|
Porthole wraps `ssh` to turn tunnel commands into named profiles: define a
|
||||||
|
forward once, then open, close, and inspect it by name. Each open forward
|
||||||
|
runs under a small supervisor process that keeps it alive and reconnects
|
||||||
|
automatically if the connection drops.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Named profiles** for local, remote, and dynamic/SOCKS forwards, saved
|
||||||
|
to disk instead of retyped each time.
|
||||||
|
- **Multi-hop jump chains**, built on `ssh -J`.
|
||||||
|
- **Auto-reconnect** with exponential backoff, unless a failure looks
|
||||||
|
permanent (bad auth, host key mismatch, port already bound).
|
||||||
|
- **Status and listing** with live state, uptime, and reconnect counts, as
|
||||||
|
text or JSON.
|
||||||
|
- **Import/export** of profiles as a single TOML file, for moving a setup
|
||||||
|
to another machine.
|
||||||
|
- **Shell completions** for bash, zsh, fish, and others.
|
||||||
|
- **Hardened by default**: every spawned `ssh` call ignores the caller's
|
||||||
|
own config, runs in batch mode with no interactive prompts, and applies
|
||||||
|
the same hardening to every hop in a jump chain, not just the final
|
||||||
|
target.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Requires a Rust toolchain and an `ssh` binary on `PATH`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
The binary is written to `target/release/porthole`. Place it on `PATH`,
|
||||||
|
e.g.:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
install -Dm755 target/release/porthole ~/.local/bin/porthole
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Aliases | Description |
|
||||||
|
|---------------|----------------|-----------------------------------------------------------|
|
||||||
|
| `add` | `mk`, `create` | Save a new forward profile. |
|
||||||
|
| `open` | `start` | Start a saved forward as a supervised background process. |
|
||||||
|
| `close` | `stop` | Stop a running forward. |
|
||||||
|
| `edit` | `change` | Update a saved profile. |
|
||||||
|
| `status` | | Show detailed status for one forward. |
|
||||||
|
| `list` | `ls` | List all saved profiles with live status. |
|
||||||
|
| `remove` | `rm`, `delete` | Delete a saved profile. |
|
||||||
|
| `wipe` | `reset` | Close and delete every forward, tracked or not. |
|
||||||
|
| `transfer` | `data` | Export saved profiles to a file, or import them from one. |
|
||||||
|
| `completions` | | Generate a shell completion script. |
|
||||||
|
|
||||||
|
Run `porthole` or `porthole --help` for the full flag reference.
|
||||||
|
|
||||||
|
### `add` / `edit` flags
|
||||||
|
|
||||||
|
One of `-l/--local`, `-r/--remote`, or `-d/--dynamic` selects the forward
|
||||||
|
kind (required for `add`, optional for `edit`):
|
||||||
|
|
||||||
|
| Flag | Value | Meaning | Default |
|
||||||
|
|--------------------|-------------------------|-------------------------------------------------------------|:-------:|
|
||||||
|
| `-l, --local` | `[BIND:]PORT:HOST:PORT` | Local forward (this machine -> remote machine). | - |
|
||||||
|
| `-r, --remote` | `[BIND:]PORT:HOST:PORT` | Remote forward (remote machine -> this machine). | - |
|
||||||
|
| `-d, --dynamic` | `[BIND:]PORT` | Dynamic forward (SOCKS proxy). | - |
|
||||||
|
| `--via` | `[USER@]HOST[:PORT]` | Jump-host chain, ending at the connection target. Required. | - |
|
||||||
|
| `-u, --user` | `USER` | Default user for the target and any hop without one. | - |
|
||||||
|
| `-i, --identity` | `PATH` | Identity file override. | - |
|
||||||
|
| `-p, --port` | `PORT` | SSH port of the final target. | `22` |
|
||||||
|
| `--reconnect` | `BOOL` | Auto-reconnect on connection drop. | `true` |
|
||||||
|
| `--retry-interval` | `SECONDS` | Base delay between reconnection attempts. | `5` |
|
||||||
|
| `--backoff-max` | `SECONDS` | Cap on the doubling reconnection delay. | `60` |
|
||||||
|
| `--keepalive` | `SECONDS` | SSH `ServerAliveInterval`. | `15` |
|
||||||
|
|
||||||
|
`edit` only touches the fields given on the command line; everything else
|
||||||
|
stays as-is.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Remote forward, exposing this machine's port 3000 to the remote host.
|
||||||
|
porthole add expose-app -r 3000:localhost:3000 --via ops@server.example.com
|
||||||
|
|
||||||
|
# Dynamic SOCKS proxy.
|
||||||
|
porthole add socks -d 1080 --via user@gateway
|
||||||
|
|
||||||
|
# Two-hop jump chain: bastion1, then bastion2, ending at db-host.
|
||||||
|
porthole add mydb -l 5432:internal-db:5432 --via bastion1 --via bastion2 -u ops
|
||||||
|
|
||||||
|
# Open every profile with reconnect enabled that isn't already running.
|
||||||
|
porthole open --all
|
||||||
|
|
||||||
|
# Run attached in the current shell instead of detaching.
|
||||||
|
porthole open mydb --foreground
|
||||||
|
|
||||||
|
# Force-kill instead of a graceful SIGTERM-then-wait.
|
||||||
|
porthole close mydb --force
|
||||||
|
|
||||||
|
# Machine-readable status/listing.
|
||||||
|
porthole status mydb --json
|
||||||
|
porthole list --json
|
||||||
|
|
||||||
|
# Back up all profiles, then restore them elsewhere.
|
||||||
|
porthole transfer --export backup.toml
|
||||||
|
porthole transfer --import backup.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
> **ⓘ** Note:<br>
|
||||||
|
> `transfer --export` does not include identity file contents, only
|
||||||
|
> their configured paths; key files need to be copied to the target machine
|
||||||
|
> separately.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How a forward stays open
|
||||||
|
|
||||||
|
`open` spawns a detached copy of the `porthole` binary running an internal
|
||||||
|
supervisor loop for that one profile. The supervisor:
|
||||||
|
|
||||||
|
- Builds and runs the `ssh` command for the profile (`-N -T` plus the
|
||||||
|
right `-L`/`-R`/`-D` flag), holding an advisory file lock for its whole
|
||||||
|
lifetime so `status`/`list` can reliably tell if it's still alive.
|
||||||
|
- Watches the connection; once it survives a short grace period it's
|
||||||
|
reported as `up`.
|
||||||
|
- On a dropped or failed connection, classifies the failure:
|
||||||
|
- **Fatal** (bad auth, host key mismatch, port already in use): gives up
|
||||||
|
immediately, state becomes `error`.
|
||||||
|
- **Known transient** (connection refused, DNS failure, timeout):
|
||||||
|
reconnects with exponential backoff.
|
||||||
|
- **Unrecognized**: also retries, but gives up after too many
|
||||||
|
consecutive unrecognized failures in a row.
|
||||||
|
- Resets the backoff delay once a connection has stayed up long enough to
|
||||||
|
be considered stable again.
|
||||||
|
- Logs `ssh` output to a per-profile log file, rotating it once it grows
|
||||||
|
past 10 MB.
|
||||||
|
- Exits cleanly and removes its own state on `close` (SIGTERM) or Ctrl-C
|
||||||
|
in foreground mode (SIGINT).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## State on disk
|
||||||
|
|
||||||
|
- Profiles: `$XDG_CONFIG_HOME/porthole/profiles/<name>.toml`
|
||||||
|
- Runtime state, lock, and log for each open forward:
|
||||||
|
`$XDG_STATE_HOME/porthole/<name>.{json,lock,log}`.
|
||||||
|
|
||||||
|
State locations can be overridden with the `PORTHOLE_STATE_DIR_OVERRIDE` environment variable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Shell completions
|
||||||
|
|
||||||
|
```sh
|
||||||
|
porthole completions bash > /etc/bash_completion.d/porthole
|
||||||
|
porthole completions zsh > "${fpath[1]}/_porthole"
|
||||||
|
porthole completions fish > ~/.config/fish/completions/porthole.fish
|
||||||
|
```
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub struct Cli {
|
|||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
/// Save a new forward profile.
|
/// Save a new forward profile.
|
||||||
#[command(visible_alias = "create", visible_alias = "new")]
|
#[command(visible_alias = "create", visible_alias = "mk")]
|
||||||
Add(AddArgs),
|
Add(AddArgs),
|
||||||
|
|
||||||
/// Start a saved forward as a supervised background process.
|
/// Start a saved forward as a supervised background process.
|
||||||
@@ -42,6 +42,7 @@ pub enum Commands {
|
|||||||
Wipe(WipeArgs),
|
Wipe(WipeArgs),
|
||||||
|
|
||||||
/// Export saved profiles to a file, or import them from one.
|
/// Export saved profiles to a file, or import them from one.
|
||||||
|
#[command(visible_alias = "data")]
|
||||||
Transfer(TransferArgs),
|
Transfer(TransferArgs),
|
||||||
|
|
||||||
/// Generate a shell completion script.
|
/// Generate a shell completion script.
|
||||||
|
|||||||
Reference in New Issue
Block a user