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

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages