Хабрахабр

Релиз неофициального MTProto прокси на Python, особенности протокола

image

Недавно разработчики Telegram выложили исходные тексты прокси-сервера, работающего по протоколу MTProto. На хабре вышли статьи об особенностях его сборки и перепаковке докер-контейнера с ним. Официальный прокси сервер, написанный на С, удивляет объемом кода — примерно 23 тысячи строк. Одновременно с этим, а иногда и чуть раньше, вышло несколько альтернативных реализаций, но ни одна из них не поддерживала возможность рекламы своего канала.

В данной статье хотелось бы, во-первых, рассказать о малоизвестных особенностях протокола общения прокси-сервера с внешними серверами и, во-вторых, рассказать о собственной разработке — реализации прокси-сервера на Python, которая только что достигла релиза и доступна всем желающим под свободной лицензией MIT.

Особенности взаимодействия прокси сервера с внешними серверами

  1. Официальный прокси сервер не взаимодействует с серверами телеграма напрямую, а использует для этого ещё, как минимум, один слой прокси-серверов. Мы будем называть их middle-proxy, их список доступен по ссылкам core.telegram.org/getProxyConfig и core.telegram.org/getProxyConfigV6. Соединение по IPv6 пока не поддерживается официальным прокси-сервером.
  2. Для шифрования данных между прокси-сервером и middle-proxy используется ключ, получаемый из ip адресов обеих узлов. Поэтому, прокси-сервер для соединения с middle-proxy должен знать свой внешний ip-адрес, иначе ключи шифрования на одной и на другой стороне будут разными. Помимо этого, в формировании ключа участвуют номера портов обеих узлов и общий секрет, доступный по адресу core.telegram.org/getProxySecret. Разработчики Телеграма рекомендуют обновлять этот секрет раз в сутки.
  3. При подключении прокси-сервера к middle-proxy, первый из них передаёт своё время. Если время отличается больше чем на несколько минут, вторая сторона закрывает соединение.
  4. При посылке сообщения от клиента к middle proxy, сообщение оборачивается в RPC-вызов протокола MTProto. В каждый такой RPC-вызов прокси добавляет несколько аргументов: ip и порт обеих узлов, случайный идентификатор соединения, а также тег прокси сервера, используемый для показа рекламного канала в приложении. Эти дополнительные аргументы занимают примерно 96 байт. Из-за этой особенности не получится показывать рекламные каналы при работе напрямую, не через middle proxy.
  5. Серверы Телеграма «верят» информации об ip клиента, получаемой от прокси-сервера. Эти адреса можно увидеть в информации о сессиях (прямоугольник дорисован):
    image
  6. По одному TCP-соединению между прокси-сервером и middle-proxy передаются сообщения разных пользователей. В запросах и ответах есть аргумент «случайный идентификатор соединения», который нужен для того, чтобы данные попали к нужному клиенту.
  7. Прокси сервер не может расшифровать данные клиента, но может отличить обычные сообщения от передаваемых файлов. Так же, ему известен размер каждого сообщения.

Фуф, надеюсь не утомил техническими деталями. Теперь должно быть понятно, почему во многих альтернативных прокси нет поддержки рекламы — они передают сообщения напрямую серверам телеграма, минуя middle-proxy. Получается значительно проще. Во второй части статьи описывается первая неофициальная реализация прокси сервера, которая работает через middle-proxy. В данный момент в свободном доступе можно найти три таких реализации: официальную, на Erlang и эту.

Реализация прокси сервера на Python

Изначально прокси-сервер писался для того, чтобы понять особенности протокола и был развитием другого проекта — асинхронного сокс-прокси, написанного, в свою очередь, чтобы «потрогать» async/await в Питоне.

После доработок проект вошёл в стадию бета-тестирования и стабилизации, которая длилась примерно неделю и задействовала пять серверов разных конфигураций. Постепенно у проекта появились пользователи, которые завалили вопросами, баг-репортами и фич-реквестами.

Перед тем как рассказать о фичах, которых пока нет у официального прокси сервера, но есть у альтернативного (и умолчать о функциях, которые есть у официального и нет у альтернативного), расскажу о вещи, которая у многих первой приходит в голову при упоминании слова Python.

Производительность

Для тестирования производительности использовалась виртуальная машина в облаке минимальной конфигурации: 1 CPU, 1024MB RAM.

При использовании альтернативной реализации event-loop'а на С, которая называется uvloop, а также при использовании интерпретатора PyPy данные производительности получаются иные (все измерения — в секунду):
image На синтетических тестах прокси сервер оказался способен передавать порядка 240мегабит/сек или 3000 сообщений/сек.

Выглядит это так: При тестировании на реальных пользователях оказалось, что такого сервера хватает, чтобы с комфортом обслуживать 4 000 пользователей или 8 000 при использовании PyPy.Большим сюрпризом оказалось то, что как бы не рекламировался тестовый сервер в русскоязычных каналах, все равно 89% пользователей были из Ирана (возможно, для других стран количество одновременно обслуживаемых пользователей будет отличаться).

image

Возможно, это связано с тем, что на территории России телеграм хорошо работает без прокси-серверов. Я поспрашивал нескольких администраторов других серверов — у них ситуация такая же. В Иране тестовые сервера блокировались для населения спустя несколько часов после создания.

image

Чётко виден момент блокировки сервера для граждан Ирана. Нагрузка на сервер при 2 000 пользователях.

При 10 000 клиентах, скорее всего, закончится память. Таким образом производительность CPU не является узким местом на тестируемом узле.

Одновременное использование нескольких ядер CPU не реализовано (привет, GIL).

Фичи, которых пока нет у официального прокси сервера

Работа по протоколу IPv6.
Прокси-сервер без дополнительной настройки умеет использовать IPv6 для исходящих соединений. Соединения по IPv6 не блокируются на территории России (пока).

Это быстрее и надёжнее. Режим работы без middle-proxy
Если реклама канала не нужна, прокси автоматически соединяется напрямую с серверами телеграма, минуя middle-proxy.

Таким образом прокси не нужно перешифровывать сообщения — он отправляет их как есть. Так же, реализован опциональный "быстрый режим", когда сообщения от сервера Телеграма до прокси и от прокси до клиента шифруются одним и тем же ключём. в любом случае у администратора прокси-сервера нет доступа к сообщениям пользователей. На безопасность это не должно влиять т.к.

Новые соединения могут не установиться если, например, в стране заблокировали сервер. Автообновление списка middle-proxy и секрета раз в сутки.
Официальный прокси сервер для обновления списка middle-proxy рекомендует рестартовать docker-контейнер раз в сутки, что сбрасывает все соединения. Питоновская версия периодически ходит на сайт и обновляет список.

Получалось запустить его даже на iPad, правда, внешние входящие соединения блокировались устройством. Многоплатформенность
Поддерживаются любые платформы, на которых запускается Python. Хотя под Windows можно запустить и официальный клиент, если использовать технологии виртуализации или докер. Отдельно поддерживается Windows, для меня стало сюрпризом как много людей запускают прокси под данной ОС.

Нужно указать минимум два параметра в файле конфигурации: порт и секрет, также можно задать опциональный рекламный тег, затем выполнить команду: python3 mtprotoproxy.py. Возможность простого запуска без докера.
Если (вдруг) есть те, кто не любят докер, прокси может быть запущен и без него. Ещё нужно будет установить pycrypto или pycryptodome, без него будет работать, но очень медленно. Правда, в таком случае придётся думать над автозапуском в ОС, например писать unit-file для systemd.

В случае с докером контейнер можно пересобрать командой docker-compose up --build.

Фичи, запланированные на следующий релиз

Ограничение скорости скачивания больших файлов.
При скачивании больших файлов можно, на уровне TCP, «просить» middle-proxy или сервер Телеграма посылать данные медленнее. Сейчас это сделано с помощью установки маленького значения буфера приёма, что дополнительно экономит память сервера.

Размер одного сообщения может достигать 1МБ. Потоковая передача сообщений.
Сейчас, все известные прокси-серверы, работающие с middle-proxy, сначала считывают от клиента сообщение и только потом его передают. Можно передавать данные потоково. Требуется память на его хранение и немного увеличивается задержка передачи. Это усложнит код, но сократит потребление памяти в худшем случае.

Изменение длины пакетов для обхода фильтра по длине пакета.
Не успело попасть в релиз.

Установка и запуск

  1. git clone -b stable github.com/alexbers/mtprotoproxy.git; cd mtprotoproxy
  2. (опционально, рекомендуется) указать PORT, USERS и AD_TAG в config.py
  3. docker-compose up --build -d (или python3 mtprotoproxy.py, чтобы без докера)
  4. (опционально, выводит ссылку вида tg://) docker-compose logs

image

Другие реализации MTProto-прокси с поддержкой рекламы каналов:

Благодарности
seriyps — за помощь с тестированием на реальных пользователях
shifttstas — за советы по докеру
forst(github) — за идею и реализацию работы по IPv6
p1ratrulezzz(github) — за советы и за статью про проект
freekzy(github) — за патч бага с утечкой дескрипторов

UPD: репозиторий, в котором собраны разные реализации MTProto-прокси: github.com/mtProtoProxy

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть