A terminal user interface (TUI) application to manage multiple SSH tunnels from a single configuration file.
- Multiple tunnel types: Local (-L), Remote (-R), and Dynamic SOCKS5 (-D) port forwarding
- Jump host support: Connect through bastion/jump hosts with chained jumps
- Auto-reconnection: Automatic reconnection with exponential backoff
- SSH keep-alive: Prevent connections from timing out
- Tunnel groups: Organize tunnels into groups for batch operations
- Real-time stats: Monitor connections, bytes transferred, and uptime
- Interactive TUI: Navigate and control tunnels with keyboard shortcuts
go install github.com/labbs/sshtunnel/cmd/sshtunnel@latestgit clone https://github.com/labbs/sshtunnel.git
cd sshtunnel
go build -o sshtunnel ./cmd/sshtunnel- Create a default configuration file:
sshtunnel --initThis creates ~/.config/sshtunnel/config.yaml with example configuration.
-
Edit the configuration file with your hosts and tunnels.
-
Run the application:
sshtunnelOr specify a custom config path:
sshtunnel --config /path/to/config.yamlglobal:
log_level: info # debug, info, warn, error
log_file: "" # Leave empty to disable file logging
reconnect:
enabled: true
max_attempts: 5 # 0 = unlimited
initial_delay: 1s
max_delay: 60s
backoff_multiplier: 2.0
keep_alive:
enabled: true
interval: 30s
timeout: 15sDefine reusable SSH host configurations:
hosts:
bastion:
hostname: bastion.example.com
port: 22
user: deploy
identity_file: ~/.ssh/id_ed25519
agent_forwarding: false
internal-server:
hostname: internal.private
port: 22
user: admin
identity_file: ~/.ssh/id_ed25519
jump_host: bastion # Connect through bastion firstForward a remote service to a local port:
tunnels:
- name: postgres-prod
description: "PostgreSQL production database"
host: bastion
type: local
local:
address: 127.0.0.1
port: 5432
remote:
address: db.internal
port: 5432
auto_start: trueExpose a local service on the remote server:
tunnels:
- name: expose-metrics
description: "Expose local Prometheus"
host: bastion
type: remote
local:
address: 127.0.0.1
port: 9090
remote:
address: 0.0.0.0
port: 9090Create a SOCKS5 proxy through the SSH connection:
tunnels:
- name: socks-proxy
description: "SOCKS5 proxy"
host: bastion
type: dynamic
local:
address: 127.0.0.1
port: 1080Organize tunnels into groups:
groups:
- name: production
description: "All production tunnels"
tunnels:
- postgres-prod
- redis-prod
auto_start: false| Key | Action |
|---|---|
↑/k |
Move up |
↓/j |
Move down |
Enter/Space |
Toggle selected tunnel |
a |
Start all tunnels |
s |
Stop all tunnels |
g |
View groups |
r |
Reload configuration |
? |
Show help |
q |
Quit |
| Key | Action |
|---|---|
↑/k |
Move up |
↓/j |
Move down |
Enter/s |
Start group |
x |
Stop group |
Esc |
Back to main view |
q |
Quit |
sshtunnel/
├── cmd/sshtunnel/ # Application entry point
├── internal/
│ ├── config/ # Configuration loading and validation
│ ├── format/ # Formatting utilities
│ ├── ssh/ # SSH client with jump host support
│ ├── tunnel/ # Tunnel implementations (local, remote, dynamic)
│ └── ui/ # Terminal UI (BubbleTea)
│ ├── components/ # Reusable UI components
│ ├── messages/ # Message types for UI events
│ ├── styles/ # Visual styling (lipgloss)
│ └── views/ # Screen implementations
└── config.example.yaml # Example configuration
- BubbleTea - Terminal UI framework
- Lipgloss - Styling
- Viper - Configuration management
- x/crypto/ssh - SSH client
MIT