<===

ProNotes

2026-02-26 00:13:41
- Написан bash‑скрипт, который по меню переключает SSH‑туннели для работы через SOCKS5‑прокси. [digitalocean](https://www.digitalocean.com/community/tutorials/ssh-port-forwarding)
- Список серверов описывается массивом `ip:ssh_port:user:passwd`; при выборе пункта скрипт:
  - глушит старый туннель (если есть);
  - поднимает новый через `sshpass + autossh` с динамическим форвардингом `-D 8080`. [mankier](https://www.mankier.com/1/autossh)
- На локальной машине постоянно доступен SOCKS5‑прокси `127.0.0.1:8080`, который идёт по SSH на выбранный сервер; срок жизни — пока живы сеть и sshd, autossh сам перезапускает сессию при обрывах. [redhat](https://www.redhat.com/en/blog/ssh-dynamic-port-forwarding)
- Работоспособность проверена:
  - через `ss -nltp` (виден `ssh` слушающий `127.0.0.1:8080`);
  - через `curl --proxy socks5h://127.0.0.1:8080 http://ur4uqu.com/f/` — трафик успешно ходит через туннель. [systutorials](https://www.systutorials.com/port-forwarding-using-ssh-tunnel/)
- В браузере (SeaMonkey) всё работает, когда выставлен SOCKS5 `127.0.0.1:8080`; проблема была только в привычке указать 1080. [kb.mozillazine](https://kb.mozillazine.org/Issues_with_SeaMonkey)

########################################
#!/usr/bin/env bash

DEBUG="${DEBUG:-0}"    # DEBUG=1 sockssw ...  включит подробный вывод

LOCAL_PORT=8080

# список серверов: ip:ssh_port:user:password
servers=(
  "111.222.222.111:1234:user:passwd"
  "111.222.222.112:1234:user:passwd"
)

if [[ "$DEBUG" == "1" ]]; then
    set -x
fi

kill_tunnel() {
    [[ "$DEBUG" == "1" ]] && echo "[DEBUG] kill_tunnel: LOCAL_PORT=${LOCAL_PORT}"

    local pid
    pid=$(lsof -t -iTCP:${LOCAL_PORT} -sTCP:LISTEN 2>/dev/null || true)
    if [[ -n "$pid" ]]; then
        echo "Убиваю старый туннель (pid $pid)..."
        kill "$pid" 2>/dev/null || true
    fi

    pkill -f "autossh .* -D ${LOCAL_PORT}" 2>/dev/null || true
}

start_tunnel() {
    local ip="$1"
    local ssh_port="$2"
    local user="$3"
    local pass="$4"

    [[ "$DEBUG" == "1" ]] && echo "[DEBUG] start_tunnel: ip=${ip} ssh_port=${ssh_port} user=${user}"

    kill_tunnel

    echo "Запускаю туннель через ${user}@${ip}:${ssh_port} (SOCKS5 на 127.0.0.1:${LOCAL_PORT})..."

    # если DEBUG=1 — не глушим вывод autossh/ssh, чтобы видеть ошибки
    if [[ "$DEBUG" == "1" ]]; then
        AUTOSSH_DEBUG=1 AUTOSSH_LOGLEVEL=7 \
        sshpass -p "${pass}" \
          autossh -M 0 \
            -o "StrictHostKeyChecking=no" \
            -o "ServerAliveInterval=5" \
            -o "ServerAliveCountMax=5" \
            -o "ExitOnForwardFailure=yes" \
            -D "${LOCAL_PORT}" -N -T \
            "${user}@${ip}" -p "${ssh_port}" &
    else
        sshpass -p "${pass}" \
          autossh -M 0 \
            -o "StrictHostKeyChecking=no" \
            -o "ServerAliveInterval=5" \
            -o "ServerAliveCountMax=5" \
            -o "ExitOnForwardFailure=yes" \
            -D "${LOCAL_PORT}" -N -T \
            "${user}@${ip}" -p "${ssh_port}" \
            >/dev/null 2>&1 &
    fi
}

menu() {
    while true; do
        echo
        echo "Выбери сервер (SOCKS5 будет на 127.0.0.1:${LOCAL_PORT}):"
        echo "0) отключить (убить туннель)"
        local i=1
        for srv in "${servers[@]}"; do
            IFS=":" read -r ip ssh_port user pass <<<"$srv"
            echo "${i}) ${user}@${ip}:${ssh_port}"
            ((i++))
        done
        echo -n "> "
        read -r choice

        if [[ "$choice" == "0" ]]; then
            kill_tunnel
            echo "Туннель отключен."
            continue
        fi

        if ! [[ "$choice" =~ ^[0-9]+$ ]]; then
            echo "Нужно число."
            continue
        fi

        local idx=$((choice-1))
        if (( idx < 0 || idx >= ${#servers[@]} )); then
            echo "Нет такого пункта."
            continue
        fi

        IFS=":" read -r ip ssh_port user pass <<<"${servers[$idx]}"
        start_tunnel "$ip" "$ssh_port" "$user" "$pass"
    done
}

main() {
    local sel="$1"

    if [[ -n "$sel" ]]; then
        if [[ "$sel" == "0" ]]; then
            kill_tunnel
            exit 0
        fi
        if ! [[ "$sel" =~ ^[0-9]+$ ]]; then
            echo "Нужно число."
            exit 1
        fi
        local idx=$((sel-1))
        if (( idx < 0 || idx >= ${#servers[@]} )); then
            echo "Нет такого пункта."
            exit 1
        fi
        IFS=":" read -r ip ssh_port user pass <<<"${servers[$idx]}"
        start_tunnel "$ip" "$ssh_port" "$user" "$pass"
        exit 0
    fi

    menu
}

main "$@"   
← Previous Next →
Back to list