Skip to content

Security: VITAZGIO/ssh_tunel

Security

docs/SECURITY.md

Модель угроз: что видно со стороны, а что нет

Разбор для тех, кто хочет понимать, что на самом деле даёт SSH-туннель, а что только кажется, что даёт. Отдельно — почему в логе программы то IP-адрес, то имя сайта, и что это значит.

Что видно наблюдателю в сети

Под наблюдателем здесь понимается любой, кто видит твой трафик по дороге: Wi-Fi в кафе, локальная сеть офиса, оборудование на пути. Всё, что уходит с компьютера в туннель, зашифровано внутри SSH, поэтому снаружи видно только это:

Что Видно? Пояснение
Факт соединения с твоим сервером Да IP сервера, порт 22, время, объём трафика
Что это именно SSH Да SSH не маскируется, и это нормально
Какие адреса ты открываешь Нет Имена и адреса передаются внутри шифрования
Содержимое страниц и данных Нет Двойное шифрование: TLS внутри SSH
SNI (имя хоста в начале TLS-рукопожатия) Нет Оно тоже внутри туннеля
DNS-запросы Зависит См. следующий раздел — это единственная реальная утечка

Ключевое, что стоит вынести из таблицы: скрывается содержимое, а не сам факт. Соединение с твоим сервером видно, объём трафика виден, и по времени и объёму о характере активности можно судить и без расшифровки.

Чем это отличается от VPN

Полезное сравнение, чтобы понимать границы применимости — устроены они принципиально по-разному.

VPN (WireGuard, OpenVPN) SSH-туннель
Уровень работы сетевой интерфейс: перехватывает пакеты прикладной: приложение само идёт в прокси
Что заворачивает весь трафик машины, включая UDP и ICMP только TCP и только то, что умеет ходить через прокси
Нужен ли софт на сервере да, демон и настройка нет, хватает штатного sshd
Права на клиенте обычно администратор (виртуальный адаптер) не нужны
Приложения без поддержки прокси покрываются не покрываются

Отсюда честный вывод: SSH-туннель — это не замена VPN, а другой инструмент. Он проще (ничего не ставить на сервер, никаких прав администратора), но покрывает меньше: UDP не проходит вовсе, а программа со своим сетевым стеком просто пойдёт мимо.

Строчки в логе: почему то имя, то адрес

В старом логе было так:

[127.0.0.1:63675] -> www.youtube.com:443    ← имя
[127.0.0.1:58406] -> 160.79.104.10:443      ← адрес

Разница не случайная, и она важна. Она показывает, кто разрешал имя в адрес — твой компьютер или сервер на том конце туннеля.

Строка с именем (www.youtube.com:443) — приложение попросило прокси самому найти адрес. Имя ушло в туннель зашифрованным, DNS-запрос сделал сервер. Наружу с твоей машины не ушло ничего, кроме SSH.

Строка с адресом (160.79.104.10:443) — приложение сначала спросило у DNS-сервера «какой адрес у youtube.com», получило ответ и пришло к прокси уже с готовым адресом. Этот DNS-запрос ушёл в обход туннеля, открытым текстом, с твоей машины. То есть имя было видно наблюдателю в сети — пусть само соединение дальше и пошло зашифрованным.

Это и есть утечка DNS. В интерфейсе такие соединения помечаются DNS мимо туннеля — теперь видно сразу, какое приложение так делает.

Важная оговорка: сам лог никуда не уходит, он живёт только в окне программы. Строка -> www.youtube.com:443 не означает, что кто-то снаружи это видел — скорее наоборот, именно эта форма записи означает, что не видел никто.

Что с этим сделано

Системный прокси теперь прописывается как HTTP-прокси, а не только SOCKS. Разница принципиальная: в HTTP-прокси браузер шлёт CONNECT www.youtube.com:443, то есть имя, и разрешает его уже сервер. При SOCKS многие приложения Windows резолвят имя сами, заранее.

Что осталось за пределами: приложения, которые лезут в DNS напрямую, минуя любые настройки прокси (часть игр, некоторые desktop-клиенты). Их DNS-запросы утекут в любом случае — как и весь их трафик, потому что системный прокси они тоже игнорируют.

Как убрать остатки утечки

  • Firefox: about:config → network.proxy.socks_remote_dns = true (для SOCKS) — впрочем, при системном HTTP-прокси это уже не нужно.
  • Chrome/Edge: при HTTP-прокси разрешают имена через прокси сами.
  • DNS-over-HTTPS в браузере: включённый DoH дополнительно прячет остаточные запросы, но и он не спасает приложения со своим сетевым стеком.

Проверка ключа сервера

Раньше стояло InsecureIgnoreHostKey() — клиент подключался к серверу, не проверяя, тот ли это сервер. Это давало возможность подмены: кто-то в сети между тобой и сервером мог встать посередине, представиться твоим сервером и получить весь трафик в открытом виде. SSH-шифрование от этого не защищает — оно защищает канал, а не выбор собеседника.

Сейчас работает обычная схема, та же что у ssh в терминале: при первом подключении ключ сервера запоминается в %APPDATA%\ssh_tunnel\known_hosts, дальше сверяется при каждом подключении. Если ключ изменился, программа откажется подключаться и скажет об этом прямо.

Если ты сам переустановил сервер — удали строку с его адресом из этого файла. Если не переустанавливал — подключаться нельзя.

Что не покрывает туннель

  • Приложения со своим сетевым стеком, игнорирующие и системный прокси, и переменные среды (часть игр, некоторые мессенджеры).
  • UDP целиком. SSH пробрасывает только TCP — значит, QUIC/HTTP3 и голосовые звонки через UDP пойдут напрямую или не пойдут вовсе. Браузеры при недоступности QUIC откатываются на TCP сами.
  • Трафик до включения и после выключения программы.
  • Локальную сеть — и это намеренно. Адреса вида 192.168.x.x, 100.64.x.x (mesh-VPN вроде NetBird и Tailscale), имена без точек и суффиксы .local/.lan/.home/.internal идут напрямую, иначе домашние сервисы (роутер, NAS, Home Assistant) переставали бы открываться при включённом туннеле. Переключается настройкой «Локальную сеть тоже вести через сервер»; чужие сети добавляются в список «Всегда напрямую».

Локальные порты

Прокси слушают только 127.0.0.1 — из внешней сети к ним не подключиться. Окно программы (веб-интерфейс) тоже слушает только петлевой адрес и вдобавок требует токен: без него любая открытая в браузере страница могла бы слать запросы на 127.0.0.1 и, например, выключить туннель или прочитать настройки.

Флаг -web-lan в версии для Linux меняет здесь ровно одно: панель слушает все адреса машины, и токен не спрашивается у тех, кто пришёл из локальной сети или mesh-VPN. Публичные адреса без токена не проходят по-прежнему, а проверка Origin остаётся в силе в обоих режимах — именно она, а не токен, закрывает главную дыру: чужую страницу, открытую в соседней вкладке. Цена включения честная: управлять туннелем сможет любой, кто дотянулся до этой сети. Для домашней сети это осознанный размен на удобство, для чужой — нет.

Пароль sudo в галочке автозапуска

Галочка «Запускать при старте системы» делает две вещи, и вторая требует прав администратора: разрешить службе работать без входа пользователя в систему (loginctl enable-linger), а если попросили — включить автозапуск Docker.

Сначала программа пробует выполнить это без пароля: на многих системах такое разрешено политикой или правилом NOPASSWD. Только если система пароль действительно требует, панель показывает поле ввода.

Что с этим паролем происходит: он передаётся команде sudo на стандартный ввод, живёт ровно на время одной команды и после неё нигде не остаётся — ни в настройках, ни в журнале, ни в ответе обратно на страницу.

Чего это не отменяет: до программы пароль идёт по обычному HTTP. На 127.0.0.1 это неважно — трафик не покидает машину. А вот с -web-lan он пересекает локальную сеть открытым текстом, и любой, кто эту сеть слушает, увидит его целиком. Поэтому:

  • в домашней сети, где кроме тебя никого, — приемлемо;

  • в общей, офисной или гостевой сети — не вводи его туда вовсе. Служба к этому моменту уже включена, и всё, чего не хватает, делается одной командой в терминале:

    sudo loginctl enable-linger $USER

There aren't any published security advisories