Skip to content

0.6.1 - fix tcp-bridge

Choose a tag to compare

@LEADBERG-studio LEADBERG-studio released this 13 Aug 17:11
· 7 commits to main since this release

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.exe
  • tunnellio-windows-x64-v0.6.1.zip
  • tunnellio-source-v0.6.1.zip

Как проверялось

  • Тесты: 91 в клиенте, 154 на сервере.
  • Живая проверка насквозь, на настоящих сокетах, а не на заглушках: настоящий
    управляющий порт моста, настоящий публичный порт, настоящий локальный сервис и
    запрос снаружи. Настроенный домен по токену открыл сессию без ключа, запрос
    дошёл до локального сервиса и вернулся, удар сердца сессию не убил, а чужой
    ключ адреса отвергнут с invalid_bridge_key. Проверка лежит в репозитории
    сервера как tests/live_bridge_check.py — именно потому, что модульные тесты
    обеих половин были зелёными, пока половины не работали вместе.

Порядок обновления

  • Сначала деплой сервера: клиент присылает ключ адреса, и что с ним делать,
    знает только исправленный сервер.
  • Когда новый клиент разойдётся, поставить на сервере
    TCP_BRIDGE_OWNER_KEY_REQUIRED=1. До этого старые клиенты продолжают
    подключаться и видны в журнале как bridge.legacy — по этой строке и понятно,
    кого осталось обновить.
  • Конфигурации, команды и флаги не менялись.