0.6.1 - fix tcp-bridge
Tunnellio v0.6.1
Коротко
TCP-мост наконец поднимает туннель. Настроенный домен у зарегистрированного
пользователя не публиковался вовсе, а случайный только выглядел работающим,
потому что его никто ни о чём не спрашивал. Мешало шесть разных поломок
одновременно, и каждой по отдельности хватало, чтобы туннеля не было.
Главное
- Настроенный домен работает — и по токену, и просто с регистрацией.
POST /v1/sessions/openбольше не требуетkeyId. В мосту нет SSH, значит
нет и ключа, который можно назвать. Шесть источников уже отвечали
requiresSshKey: false; седьмая, проверка запроса, говорила обратное и
побеждала. - Ключ адреса. Выдаётся вместе с поддоменом, приезжает владельцу в
connectionProfile.tcpBridge.tokenи идёт в кадре приветствия. До этого любой,
кто знал чужой поддомен, забирал живой туннель себе: прежний сеанс закрывался
какreplaced, и никто ничего не спрашивал. - Рукопожатие завершается. Сервер отвечает публичным портом, и клиент
перестал считать успешный вход провалом. - Туннель стоит. Тишина в управляющем канале больше не читается как разрыв,
а клиент сам подаёт голос раз в пятнадцать секунд, чтобы домашний роутер не
забыл сопоставление за ночь. - OAuth через мост отвергается честно. Мост передаёт байты насквозь, токен
проверить негде. Раньше такой домен выглядел защищённым в консоли и был
открыт всему интернету.
Что работает и при каких условиях
| Ситуация | Адрес | Мост | Цена |
|---|---|---|---|
| Без учётной записи, без ключей и токена | случайный tmp-xxxxxxxx, 24 часа |
работает | бесплатно |
| Регистрация, домен настроен на мост | свой постоянный поддомен | работает | бесплатно |
| На домене задан пароль моста | свой поддомен, нужен пароль | работает | Pro |
| Работа через API по токену, адрес любой | настроенный или случайный | работает | Pro |
- Постоянный поддомен по-прежнему только для зарегистрированного: это имя в
нашем домене на годы, и за ним обязан стоять человек. Случайный адрес живёт
сутки и умирает сам. - На тарифе Free до 10 доменов; лишние помечаются
plan_limited. authMode: oauthиdualнесовместимы сconnectionMode: tcp_bridgeи
auto. Для OAuth — Cloud proxy, для моста — Legacy.
# Без всего, случайный адрес
.\tunnellio.exe connect --transport tcp-bridge --domain random --local-port 8787 --run
# Регистрация и настроенный домен, SSH-ключа нет нигде
.\tunnellio.exe --token <ТОКЕН> connect --domain existing:tuntun ^
--local-host 127.0.0.1 --local-port 8787 --transport tcp-bridge --run --watch --name myvpn-files
# Домен с паролем моста (Pro)
.\tunnellio.exe --token <ТОКЕН> connect --domain existing:demo ^
--tcp-bridge-password <ПАРОЛЬ> --local-port 8787 --transport tcp-bridge --runЧто изменилось
- Ключ адреса уходит в кадре приветствия и подтверждает право публиковать под
этим именем. - Ключ отдаётся всегда, когда сервер его прислал, а не только когда потребовал:
мягкий сервер тоже должен знать, с кем говорит. - Пароль моста больше не берётся из служебного токена. Это разные вещи, и
подстановка давала гарантированныйinvalid_bridge_passwordна каждом домене
с паролем. - Рукопожатие с загадкой пропускается для родного протокола. Наш мост не
присылаетChallenge, поэтому каждое соединение стояло до таймаута, а
выглядело это как «туннель поднялся и не отвечает». - Тишина в управляющем канале не принимается за разрыв. Ждали полсекунды,
сервер подаёт голос раз в тридцать: мост умирал через полсекунды после
успешного рукопожатия. - Клиент сам подаёт голос раз в пятнадцать секунд. Никто этого не требует, но
домашний роутер забывает простаивающее сопоставление молча, и туннель,
простоявший ночь, наутро уже никуда не ведёт. - Отсутствие публичного порта больше не считается провалом рукопожатия:
HTTP-мост публикует по имени, и номера порта в ответе может не быть. - Идентификатор соединения посылается под двумя именами,
idиconnectionId.
Сервер читает первое, мы посылали второе, и каждое соединение заканчивалось
connection_not_found. - Предел кадра поднят до 8192 байт, как на сервере. 256 хватало ровно до того
дня, когда в приветствии появились адрес и порт. .well-known/oauth-protected-resourceбольше не запрашивается у публичного
адреса на legacy- и бридж-доменах: там стоит чужая программа, она отвечает
404, и этот 404 попадал в лог как ошибка.Runtime name:печатается один раз; второй, пустой, убран.docs/TCP_BRIDGE.mdиdocs/ru/TCP_BRIDGE.mdприведены к тому, как протокол
работает на самом деле: ключ адреса, коды отказов, ограничение по OAuth.
Со стороны сервера, для справки (репозиторий tunnellio)
POST /v1/sessions/openпринимает запрос безkeyId, если режим домена
tcp_bridgeилиauto. Явно неверныйkeyIdвида-5по-прежнему
validation_error, а Direct SSH без ключа так и остаётся невозможным.- Сессия без ключа больше не стирает ключ, уже настроенный на домене.
heartbeat,resumeиcloseпроверены на сессии без ключа; ключа они не
требовали и не требуют.- Мост отвергает незнакомое имя (
unknown_hostname), неверный ключ владельца
(invalid_bridge_key, всегда) и недоступную базу (bridge_unavailable)
вместо того, чтобы пускать всех. Клиент вовсе без ключа — по
TCP_BRIDGE_OWNER_KEY_REQUIRED; пока0, такой клиент проходит и
записывается какbridge.legacy. - Приветствие сервера несёт публичный порт, публичный адрес и период голоса.
oauth_requires_proxyотвергает сочетание моста и OAuth и при создании
домена, и при открытии сессии.- Ответ о сессии называет свой
keyId, включая его отсутствие.
Файлы
tunnellio.exetunnellio-windows-x64-v0.6.1.ziptunnellio-source-v0.6.1.zip
Как проверялось
- Тесты: 91 в клиенте, 154 на сервере.
- Живая проверка насквозь, на настоящих сокетах, а не на заглушках: настоящий
управляющий порт моста, настоящий публичный порт, настоящий локальный сервис и
запрос снаружи. Настроенный домен по токену открыл сессию без ключа, запрос
дошёл до локального сервиса и вернулся, удар сердца сессию не убил, а чужой
ключ адреса отвергнут сinvalid_bridge_key. Проверка лежит в репозитории
сервера какtests/live_bridge_check.py— именно потому, что модульные тесты
обеих половин были зелёными, пока половины не работали вместе.
Порядок обновления
- Сначала деплой сервера: клиент присылает ключ адреса, и что с ним делать,
знает только исправленный сервер. - Когда новый клиент разойдётся, поставить на сервере
TCP_BRIDGE_OWNER_KEY_REQUIRED=1. До этого старые клиенты продолжают
подключаться и видны в журнале какbridge.legacy— по этой строке и понятно,
кого осталось обновить. - Конфигурации, команды и флаги не менялись.