• Список серверов описывается массивом 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 "$@"

[file-name 000350_2026-02-26_00-13-41.txt]