Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

SSH Tunnel Manager

License: MIT Bash OpenSSH

SSH Tunnel Manager is a lightweight, secure, and fully configurable CLI tool for managing SSH port‑forwarding tunnels (local forwarding) across multiple environments. It uses SSH ControlMaster sockets for lifecycle management — no kill or taskkill hacks, no hard‑coded configuration.

Perfect for:

  • Developers who need persistent tunnels to staging/dev databases, caches, or message queues.
  • DevOps engineers integrating tunnel setup into CI/CD pipelines.
  • Teams sharing a common jump‑host without clashing credentials.

✨ Features

  • No hard‑coded parameters – all configuration is injected via environment variables.
  • Socket‑based lifecycle – each tunnel is controlled via its own ControlPath socket (ssh -O check/exit).
  • Environment‑aware – define as many environments as you want (dev, stage, prod, …) with a single variable per environment.
  • Zero process‑killing – graceful shutdown via SSH control commands; no risk of interfering with other SSH sessions.
  • Cross‑platform – works on Linux, macOS, Windows (Git Bash, WSL, Cygwin).
  • Minimal dependencies – requires only bash (4.3+) and OpenSSH client.
  • CI/CD friendly – can be sourced or run non‑interactively with ssh-agent support.

🚀 Quick Start

1. Clone the repository

git clone https://github.com/rayyee/ssh-tunnel-manager.git
cd ssh-tunnel-manager
chmod +x tunnel-manager.sh

2. Set up your environment variables (example)

# Connection settings (use either alias OR host+user)
export BASTION_ALIAS="jump-prod"          # ~/.ssh/config Host
# OR
export BASTION_HOST="jump.example.com"
export BASTION_USER="admin"
export BASTION_PORT="22"                  # optional, default 22

# Optional private key
export SSH_KEY="$HOME/.ssh/id_rsa"

# Tunnel rules for each environment (comma‑separated)
export BASTION_PROFILE_dev="3306:10.0.2.10:3306,6379:10.0.2.20:6379"
export BASTION_PROFILE_stage="8848:10.0.3.10:8848,9092:10.0.3.20:9092"

3. Run

./tunnel-manager.sh start dev
./tunnel-manager.sh status stage
./tunnel-manager.sh stop dev
./tunnel-manager.sh restart stage

📋 Configuration via Environment Variables

All configuration is passed through environment variables – no editing of the script.

Variable Required Description
BASTION_ALIAS Conditional¹ Host alias defined in ~/.ssh/config. If set, BASTION_HOST/USER are ignored.
BASTION_HOST Conditional¹ Jump‑host IP or domain.
BASTION_USER Conditional¹ SSH username for the jump‑host.
BASTION_PORT No SSH port (default: 22).
SSH_KEY No Path to private key. If not set, defaults to ~/.ssh/id_rsa or ssh-agent provided keys.
BASTION_PROFILE_<ENV> Yes Comma‑separated list of tunnel rules: local_port:remote_host:remote_port. Example: "3306:db.internal:3306,6379:cache.internal:6379"

¹ Either BASTION_ALIAS or both BASTION_HOST and BASTION_USER must be set.


🧩 Usage

./tunnel-manager.sh {start|stop|status|restart} <environment>

Commands

Command Action
start <env> Start all tunnels defined in BASTION_PROFILE_<env>.
stop <env> Gracefully shut down all tunnels in that environment.
status <env> Show the running status of each tunnel (based on socket health).
restart <env> Stop, then start the environment (with a 1‑second pause).

Examples

# Start the 'dev' environment
export BASTION_PROFILE_dev="3306:10.0.2.10:3306,6379:10.0.2.20:6379"
./tunnel-manager.sh start dev

# Check status
./tunnel-manager.sh status dev
# Output:
# ========== 环境 [dev] 状态 ==========
#   ● 3306 -> 10.0.2.10:3306 (活跃, socket: /home/user/.ssh/sock.d/jump-prod_3306.sock)
#   ● 6379 -> 10.0.2.20:6379 (活跃, socket: /home/user/.ssh/sock.d/jump-prod_6379.sock)
# =======================================

# Stop them
./tunnel-manager.sh stop dev

🛠 How It Works

  1. Socket per tunnel – Each tunnel gets its own ControlPath socket under ~/.ssh/sock.d/ (named <target>_<local_port>.sock).
  2. Start – The script runs ssh -f -N -L ... with ControlMaster=auto and ControlPersist=yes, creating a socket.
  3. Stop – It calls ssh -S <socket> -O exit to gracefully shut down that specific tunnel.
  4. Status – It uses ssh -S <socket> -O check to verify the socket is alive.

No process scanning, no kill, no taskkillpure SSH control.


🔒 Security & Best Practices

  • No hard‑coded credentials – all secrets are externalised.
  • SSH agent integration – if SSH_KEY is specified, the script will attempt to add it to the agent (with a 1‑hour timeout).
  • Socket directory~/.ssh/sock.d/ is created with 0700 permissions (inherited from mkdir).
  • Avoids port conflicts – checks socket existence before starting a new tunnel.

🌍 Cross‑Platform Compatibility

The script is designed to run on:

  • Linux (any distribution)
  • macOS
  • Windows via Git Bash, WSL, or Cygwin (requires OpenSSH client in PATH).

The ssh -S option is fully supported by OpenSSH for Windows (since version 8.1).


📦 Integration with CI/CD

You can source the script and use it in your pipelines. For example, in a GitHub Actions workflow:

- name: Set up tunnels
  run: |
    export BASTION_ALIAS="ci-jump"
    export BASTION_PROFILE_test="5432:postgres.internal:5432"
    ./tunnel-manager.sh start test
    # run your tests here
    ./tunnel-manager.sh stop test

🤝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository.
  2. Create a feature branch (git checkout -b feature/amazing-feature).
  3. Commit your changes (git commit -m 'Add some amazing feature').
  4. Push to the branch (git push origin feature/amazing-feature).
  5. Open a Pull Request.

Please ensure your code is bash‑compatible (4.3+) and passes basic testing on Linux/macOS/Windows (Git Bash). Add comments for clarity.


📄 License

Distributed under the MIT License. See LICENSE for more information.


❓ FAQ

Q: Can I use this with multiple jump‑hosts?
A: Yes – each environment can point to a different jump‑host by setting BASTION_ALIAS/BASTION_HOST accordingly before running the command. You can even wrap the script in a shell function that sets different variables per call.

Q: What if I need to use -J ProxyJump?
A: The script uses plain -L forwarding. If you need a proxy chain, you can set BASTION_ALIAS to a Host that already defines ProxyJump in ~/.ssh/config.

Q: How do I list available environments?
A: The script does not know them in advance – you define them via BASTION_PROFILE_*. To see which ones you have set, use printenv | grep BASTION_PROFILE_.

Q: Why not use autossh?
A: autossh is great for persistence, but this tool focuses on management (start/stop/status) without external monitoring. For production, you can combine it with systemd or cron.

Q: Does this work with MFA or OTP?
A: Yes, as long as you can authenticate non‑interactively (e.g., via ssh-agent with a certificate or using ControlMaster from a previously authenticated session). You may need to pre‑authenticate once manually.


🙏 Acknowledgements

  • Inspired by the need for a clean, process‑free SSH tunnel manager.
  • Built on OpenSSH's robust ControlMaster feature.

Happy Tunneling! 🚇

About

SSH Tunnel Manager is a lightweight, secure, and fully configurable CLI tool for managing SSH port‑forwarding tunnels (local forwarding) across multiple environments. It uses SSH ControlMaster sockets for lifecycle management.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages