#!/bin/bash # ~/.ssh/start-my-tunnel.sh # Persistent multi-port SSH tunnel using autossh + systemd # Survives suspend, reboot, network changes # ──────────────────────────────── # EDIT THIS SECTION ONLY # ──────────────────────────────── USER="root" # Remote SSH user HOST="oscara2.hopto.org" # Remote SSH host SSH_PORT="4922" # Usually 22, change if needed # Define your port forwarding map: # Format: "local_port:remote_ip:remote_port" declare -a FORWARDS=( "3000:192.168.1.38:3000" # ryzen7 Ollama "8188:192.168.1.38:8188" # ryzen7 ComfyUI "3822:192.168.1.38:22" # ryzen7 ssh "11922:192.168.1.119:22" # yazoo ssh "9084:192.168.1.119:9084" # yazoo transmission-gm ) # custom identity file (recommended) IDENTITY_FILE="$HOME/.ssh/id_ed25519" # 1. You're on your home Wi-Fi SSID HOME_SSID="W739A" # leave empty to disable SSID check # 2. Your laptop has a specific local IP range (skip this test as it will fail on Franklin router which assigns the same ip range 192.168.1.0 like that at home 39A) HOME_IP_PREFIX="192.168.1." # e.g. 192.168.1.0/24 → your home LAN # HOME_IP_PREFIX="10.0.0." # uncomment if you use 10.x at home # 3. You already have the corporate/openconnect/anyconnect VPN up VPN_INTERFACE="wg0" # WireGuard example # 4. Test if you can reach one of the remote destinations directly (ultimate test) DIRECT_REACH_TEST="192.168.6.1:22" # IP:port that only works when on VPN or home # ──────────────────────────────── # DO NOT EDIT BELOW THIS LINE # ──────────────────────────────── log() { echo "$(date '+%Y-%m-%d %H:%M:%S') | $*"; } # 1. Check if already running → exit cleanly if pgrep -f "autossh.*$USER@$HOST" > /dev/null; then log "Tunnel already running – nothing to do." exit 0 fi # 2. Are we at home (local IP match)? #if [[ -n "$HOME_IP_PREFIX" ]]; then # CURRENT_IP=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7}' | head -n1) # if [[ "$CURRENT_IP" == "$HOME_IP_PREFIX"* ]]; then # log "On home network ($CURRENT_IP) – no tunnel needed." # exit 0 # fi #fi # 3. Are we on home Wi-Fi SSID? if [[ -n "$HOME_SSID" && -x /usr/sbin/iwgetid || -x /sbin/iwgetid ]]; then CURRENT_SSID=$(iwgetid -r 2>/dev/null || true) if [[ "$CURRENT_SSID" == "$HOME_SSID" ]]; then log "Connected to home SSID '$HOME_SSID' – skipping tunnel." exit 0 fi fi # 5. Can we reach a target directly? (most definitive test) if [[ -n "$DIRECT_REACH_TEST" ]]; then HOST_PORT=(${DIRECT_REACH_TEST/:/ }) if timeout 3 bash -c "cat < /dev/null > /dev/tcp/${HOST_PORT[0]}/${HOST_PORT[1]}" 2>/dev/null; then log "Direct connection to $DIRECT_REACH_TEST works – no tunnel needed." exit 0 fi fi # If we get here → we really need the tunnel log "Outside trusted network/VPN – starting tunnel to $USER@$HOST" # Build the -L arguments for all forwards FORWARD_ARGS="" for mapping in "${FORWARDS[@]}"; do # Skip empty lines or comments [[ -z "$mapping" || "$mapping" =~ ^[[:space:]]*# ]] && continue # Validate format if ! [[ $mapping =~ ^([0-9]+):([^:]+):([0-9]+)$ ]]; then echo "ERROR: Invalid forwarding syntax: $mapping" >&2 echo " Expected: local_port:remote_ip:remote_port" >&2 exit 1 fi LOCAL="${BASH_REMATCH[1]}" REMOTE_IP="${BASH_REMATCH[2]}" REMOTE_PORT="${BASH_REMATCH[3]}" FORWARD_ARGS="$FORWARD_ARGS -L $LOCAL:$REMOTE_IP:$REMOTE_PORT" done if [[ -z "$FORWARD_ARGS" ]]; then echo "No valid port forwards defined. Exiting." exit 1 fi # Build the final command CMD="autossh -M 0 \ -o ServerAliveInterval=30 \ -o ServerAliveCountMax=3 \ -o ExitOnForwardFailure=yes \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -f -N \ $FORWARD_ARGS \ ${IDENTITY_FILE:+-i \"$IDENTITY_FILE\"} \ $USER@$HOST -p $SSH_PORT" echo "$(date): Starting/restarting tunnel to $USER@$HOST ($SSH_PORT)" echo "Forwards: ${FORWARDS[*]}" # Execute eval $CMD # Log for debugging echo "$(date): autossh command executed (PID: $!)" >> ~/autossh-tunnel.log