How It Works
This page explains the architecture and internals of SciTeX SSH.
Warning
Before setting up reverse tunnels, please check your organization’s acceptable use policy and network terms of service. Reverse tunnels may bypass institutional firewalls or network policies. The authors accept no responsibility for any consequences arising from the use of this software.
The Problem: NAT (Network Address Translation) Traversal
Machines behind NAT or institutional firewalls cannot receive incoming connections. A standard SSH connection from a remote client to a lab workstation fails because:
The workstation has a private IP (e.g.,
192.168.x.x) that is not routable from the internet.The router/firewall blocks unsolicited incoming connections.
Port forwarding on institutional networks is typically not available to researchers.
The Solution: Reverse SSH Tunnels
A reverse tunnel inverts the connection direction. Instead of the client connecting to the workstation, the workstation connects outward to a publicly reachable bastion server and opens a listening port there. When a client connects to that port on the bastion, the traffic is forwarded back through the existing tunnel to the workstation.
┌─────────────────────────────────────────┐
│ Lab Workstation (behind NAT/firewall) │
│ │
│ ┌──────────────────────────────────┐ │
│ │ systemd service │ │
│ │ autossh-tunnel-{port}.service │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ autossh │ │ │
│ │ │ (auto-reconnect daemon) │ │ │
│ │ └────────────┬─────────────┘ │ │
│ └────────────────┼─────────────────┘ │
│ │ │
│ localhost:22 ◄───┘ │
│ (SSH server) │
└───────────────────┼─────────────────────┘
│
│ reverse tunnel
│ -R {port}:localhost:22
▼
┌──────────────────────────────────────────┐
│ Bastion Server (public IP) │
│ │
│ sshd listening on port {port} │
│ forwards to tunnel → workstation:22 │
└──────────────────────┬───────────────────┘
│
│ ssh -p {port} bastion-server
│
┌──────────────────────┴───────────────────┐
│ Remote Client (researcher laptop) │
│ │
│ ssh -p {port} user@bastion-server │
│ → routed through tunnel to workstation │
└──────────────────────────────────────────┘
Persistence via systemd and autossh
A plain ssh -R tunnel breaks when the network drops or the machine reboots.
SciTeX SSH solves this with two layers of persistence:
Layer 1: autossh (connection-level persistence)
autossh is a program that monitors an SSH
connection and automatically re-establishes it when it detects failure. It uses
-M 0 (rely on SSH’s built-in ServerAliveInterval/ServerAliveCountMax)
and runs as a daemon.
Layer 2: systemd (process-level persistence)
Each tunnel is registered as a systemd service unit. This provides:
Boot persistence:
WantedBy=multi-user.targetensures the tunnel starts on boot.Crash recovery:
Restart=alwayswithRestartSec=3restarts autossh if it crashes.Standard management:
systemctl start/stop/statusfor the tunnel service.
The Service File
When you run scitex-ssh setup -p 2222 -b user@bastion -s ~/.ssh/id_rsa,
the following systemd unit file is created:
File: /etc/systemd/system/autossh-tunnel-2222.service
[Unit]
Description=AutoSSH tunnel service
After=network-online.target
Wants=network-online.target
[Service]
User=<your-username>
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -N \
-o "PubkeyAuthentication=yes" \
-o "PasswordAuthentication=no" \
-i /home/<user>/.ssh/id_rsa \
-R 2222:localhost:22 user@bastion
RestartSec=3
Restart=always
[Install]
WantedBy=multi-user.target
After writing this file, the setup script runs:
systemctl daemon-reload
systemctl enable autossh-tunnel-2222.service
systemctl restart autossh-tunnel-2222.service
The Three Operations
Operation |
What it does |
Under the hood |
|---|---|---|
setup |
Creates and starts a persistent reverse tunnel |
Writes systemd unit file → |
status |
Reports whether the tunnel is active |
Runs |
remove |
Stops and removes the tunnel permanently |
|
Each operation is available through all three interfaces:
Python:
scitex_ssh.setup(),scitex_ssh.status(),scitex_ssh.remove()CLI:
scitex-ssh setup,scitex-ssh status,scitex-ssh removeMCP:
tunnel_setup,tunnel_status,tunnel_removetools
Environment Variables
To avoid repeating the bastion server and key path on every command, set these environment variables:
Variable |
Description |
Example |
|---|---|---|
|
Default bastion server address |
|
|
Default SSH private key path |
|
|
Enable verbose logging ( |
|
CLI flags always take precedence over environment variables.
Prerequisites
autossh: Must be installed on the host (
sudo apt install autossh).SSH key pair: Public key must be authorized on the bastion server (
~/.ssh/authorized_keys).Bastion server: Any machine with a public IP and an SSH server (
sshd). The bastion must haveGatewayPorts yesorGatewayPorts clientspecifiedin/etc/ssh/sshd_configif you want the forwarded port to be accessible from machines other than the bastion itself.sudo access: Required on the lab workstation to write systemd unit files in
/etc/systemd/system/and runsystemctl.
Alternative: No-sudo Setup via ~/.bashrc
If you do not have sudo access (e.g., shared HPC nodes, university servers), you
can run autossh directly from your shell profile without systemd. Add to ~/.bashrc:
# Persistent reverse tunnel without sudo — starts on every login
# Checks if tunnel is already running before starting
if ! pgrep -f "autossh.*-R 2222:localhost:22" > /dev/null 2>&1; then
autossh -M 0 -f -N \
-o "PubkeyAuthentication=yes" \
-o "PasswordAuthentication=no" \
-o "ServerAliveInterval=30" \
-o "ServerAliveCountMax=3" \
-i ~/.ssh/id_rsa \
-R 2222:localhost:22 user@bastion.example.com
fi
Trade-offs vs. systemd approach:
Aspect |
systemd (with sudo) |
~/.bashrc (no sudo) |
|---|---|---|
Root access |
Required |
Not required |
Starts on |
Boot (no login needed) |
User login only |
Crash recovery |
systemd restarts automatically |
autossh reconnects SSH; no restart if autossh itself crashes |
Management |
|
|
Alternative: Persistent Session (screen, tmux, nohup)
For long-running sessions where you want the tunnel to survive logout without sudo or systemd:
# Option 1: screen (detaches from terminal)
screen -dmS tunnel autossh -M 0 -N \
-o "ServerAliveInterval=30" -o "ServerAliveCountMax=3" \
-i ~/.ssh/id_rsa -R 2222:localhost:22 user@bastion.example.com
# Reattach: screen -r tunnel
# Kill: screen -S tunnel -X quit
# Option 2: tmux
tmux new-session -d -s tunnel "autossh -M 0 -N \
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
-i ~/.ssh/id_rsa -R 2222:localhost:22 user@bastion.example.com"
# Reattach: tmux attach -t tunnel
# Kill: tmux kill-session -t tunnel
# Option 3: nohup (simplest)
nohup autossh -M 0 -N \
-o "ServerAliveInterval=30" -o "ServerAliveCountMax=3" \
-i ~/.ssh/id_rsa -R 2222:localhost:22 user@bastion.example.com \
> /dev/null 2>&1 &
# Kill: pkill -f "autossh.*-R 2222:localhost:22"
Tip
To survive reboots without sudo, add the command to a cron @reboot job:
crontab -e
# Add this line:
@reboot autossh -M 0 -f -N -o "ServerAliveInterval=30" -o "ServerAliveCountMax=3" -i ~/.ssh/id_rsa -R 2222:localhost:22 user@bastion.example.com
Approach |
Sudo |
Survives logout |
Survives reboot |
Management |
|---|---|---|---|---|
systemd (default) |
Yes |
Yes |
Yes |
|
~/.bashrc |
No |
No (restarts on login) |
No |
|
screen / tmux / nohup |
No |
Yes |
No (unless cron @reboot) |
|
cron @reboot + nohup |
No |
Yes |
Yes |
|
Alternative: Direct Shell Scripts
If you have sudo access but prefer not to install Python, use the shell scripts directly:
# Download the scripts (one-time)
curl -o ~/.local/bin/setup-autossh-service.sh \
https://raw.githubusercontent.com/ywatanabe1989/scitex-ssh/main/src/scitex_ssh/scripts/setup-autossh-service.sh
curl -o ~/.local/bin/remove-autossh-service.sh \
https://raw.githubusercontent.com/ywatanabe1989/scitex-ssh/main/src/scitex_ssh/scripts/remove-autossh-service.sh
chmod +x ~/.local/bin/setup-autossh-service.sh ~/.local/bin/remove-autossh-service.sh
# Usage (requires sudo for systemd operations)
setup-autossh-service.sh -p 2222 -b user@bastion.example.com -s ~/.ssh/id_rsa
remove-autossh-service.sh -p 2222