Как стать автором
Обновить

Комментарии 86

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

После прочтения этой строки как-то сразу появилось желание попробовать повторить реализацию на Go…

Можно попробовать запилить.
Но мне сама идея MTProxy не очень нравится, легкость обнаружения и блокировки. Имхо нужен протокол, который легче прятать.

Авторы протокола предприняли много усилий, чтобы заблокировать было сложно. В протоколе клиент телеграм<->прокси нет сигнатуры, трафик выглядит как случайный поток байт, в котором четыре определённых байта — проверочные, но проверить можно только если знать секрет прокси сервера. Можно блокировать пакеты, которые выглядят как случайный поток байт или определённой длины, но при таких блокировках пострадают другие сервисы, работающие на непопулярных протоколах.

Вот тут проблема, что там всем наплевать что пострадает :(

Недавно была статья, где по пунктам разобраны слабые места протокола, упрощающие обнаружение.
Можете подробнее рассказать, как именно протокол усложняет блокировку?

Могу. В самом начале клиент генерирует случайные 56 байт и посылает их серверу. На основе этих байт и общего секрета, обе стороны генерируют по два ключа шифрования: для шифрования данных от сервера к клиенту и в обратную сторону. После этого и клиент и сервер перед посылкой данных шифруют их соответствующими ключами.
После случайных 56 байт клиент посылает зашифрованные магические четыре байта и номер датацентра с которым его должен соединить прокси. По этим магическим четырем байтам сервер понимает, что с ним действительно общаются по нужному протоколу, но, для внешнего наблюдателя эти данные выглядят как случайные т.к. он не знает секрета, на основе которого сформированы ключи шифрования.

Erlang версия умеет по ядрам расползаться. Другой вопрос, что из за того как активно прокси блокируют, удобнее иметь 10 одноядерных виртуалок с 10 IP адресами, чем одну на 12 ядер с одним адресом. (Если есть возможность иметь одну с 10 IP адресами то это, конечно, приобретает смысл.)


Плюс к этому в статье умпоянуто, что обычно узкое место это не CPU, а память.

Ну так питон по памяти тоже немного прожорливее Go/C/Erlang.
Эти 10 адресов будут наверняка в одной небольшой подсети)
Спасибо, в отличие от официального mtproxy, эта реализация работает без проблем.

Вопрос возник, при запуске оно ругается что нет pycryptodome или pycrypto, что из этого лучше выбрать в плане наименьшего потребления ресурсов? Или можно на slow AES implementation оставаться?

В плане потребления ресурсов они pycryptodome и pycrypto работают примерно одинаково. Мне больше нравится первый, он активнее развивается и под windows легче устанавливается. Slow implementation медленный, но если скорости хватает, то можно оставаться на нём.

А какой из crypto/cryptodome он выберет, если стоят оба, и как на это повлиять?

Насколько я знаю оба установить не получится т.к. у них модули называются одинаково. Есть pycryptodomex у которого называются по-другому модули. Ещё, кстати, поддержал pycryptography, он должен быть очень быстрый т.к. использует openssl

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

А думали над тем, что бы просто запускать несколько инстансов и через что-то их роутить? Как делает gunicorn например. Из самых простых способов, это nginx + streams.

Нужно как-то передавать по всей цепочке IP адрес клиента (если мы не хотим врать телеграму про IP адреса пользователей). При простой TCP балансировке это невозможно, нужно какой-то протокол изобретать.

Ну, nginx может такие штуки делать.

Ну так и первый способ с proxy_bind это какое-то совсем шаманство, а второе proxy_protocol, как я и сказал, потребует для mtproto proxy дописывать ещё поддержку PROXY protocol.

поддержка PROXY protocol добавлена

Куда?
И что? proxy protocol он не умеет
А опс. Посыпал голову пеплом

Есть идея попробовать опцию SO_REUSEPORT, которая позволяет заслушать один и тот же порт несколькими процессами. Думаю, это должно помочь.

Призываю Vespertilio. Он как раз интересовался подробностями.
Тем временем предпосылки к очередной головной боли.

Я вот одного не могу понять, почему возможность использовать несколько SECRET есть, а несколько AD_TAG — нет?
Неужели только я хотел бы для узкого круга приближенных дать просто прокси, а всем остальным навязать свой канал и все это без необходимости запуска двух инстансов прокси?
Вот, но это слегка «грязноватая» реализация.
Отличная содержательная статья. Одна из лучших. А почему обойдены вниманием реализации на rust и go? На go даже вполне рабочая (хоть и простая).

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

Я бы ещё сделал автоматический образ (чтобы сам подтягивал изменения на github) docker на DockerHub. На той же alpine. И перебивку параметров config.py через командную строку.
Причем на alpine pypy3

Хорошая идея, попробую реализовать в ближайшее время

А можете сделать 2 образа, один с pypy, другой с cpython (либо один образ, но с опциональным переключением)? Если память является узким местом, то PyPy — не лучший выбор. Помимо скорости, он еще ощутимо более прожорливый в смысле оперативки.

На самом деле потребление памяти получается примерно одинаковым, там почти вся память — разнобразные буферы, которые занимают примерно одинаково на си и на питоне.


С pypy3 и alpine возникла проблема — такого пакета по умолчанию нет. Проблема находится в процессе решения.

Гуглится докер для сборки. Там достаточно простой патч и OpenSSL, да.
Кстати, а как определяется IP которым идет подпись?

По IP с которого пришёл пакет.

Не совсем понял, там же мой proxy добавляет свой IP при коннекте к middle-proxy…

Прокси должен его знать. Официальному прокси нужно указывать ip при запуске с помощью ключа --nat-info, а то он использует ip какого-нибудь интерфейса. Официальный прокси в докер-образе ходит на https://digitalresistance.dog/myIp и узнаёт его. Питоновский ходит на https://v4.ifconfig.co/ip и https://v6.ifconfig.co/ip.


Зачем они так сделали и зачем нужен секрет https://core.telegram.org/getProxySecret, который все и так знают является для меня большой загадкой. Возможно, у них есть внутренние middle-proxy, секрет которых знают не все.

в формировании ключа участвуют номера портов обеих узлов

А проверяет ли middleProxy то, что по переданному IP:port слушает именно этот сервис?

Почитал. "Проверяет". Но используется не ip:port "сервиса", а ip:port исходящего соединения mtproto proxy в сторону middle proxy.

А вот берет он откуда его…

Код берёт его из getsockname(). Но из этого следует, что MTProxy адекватно работать не должна за почти любым NAT, в том числе и за докерным. А она вроде как работает.
Надо проверить что ли :-)

Родной лезет на сайт и там берет — посмотри в скрипт запуска

Это IP. С IP всё сравнительно просто в "обычных" случаях. Мне интересно поведение приложения, если случится на NAT изменение source port, которое в случае с docker носит вероятностный характер.

В таком случае у двух сторон получатся разные ключи шифрования и они "не поймут" друг друга.

Да-да. Так и должно быть. Но если это так, это означает, что инструкция имени schors по запуску telegrammessenger/proxy намного лучше, чем официальная. Что довольно забавно :-)

Ага, понятно почему на NAT VPS ничего не работало.

Я разобрался, почему никто не говорит про проблемы с докерным NAT и MTProto proxy. Их не то чтоб нет, но они маловероятны.


  • Официальный прокси мультиплексирует несколько пользователей в одно исходящее соединение. Т.е. исходящих соединений сравнительно немного.
  • Выбор исходящего порта внутри контейнера зависит от port_offset, который зависит от IP клиента. У каждого контейнера свой внутренний IP, поэтому при небольшом числе контейнеров и небольшом числе соединений конфликтов по исходящим TCP портам не случается.
  • SNAT и MASQUERADE, через которые трафик из контейнера уходит в интернет, не меняют порт при отсутствии конфликтов: where possible, no port alteration will occur.
Хотелось бы конфигурационный файл отдельной сущностью с возможностью указывать через опцию при запуске и в YAML или TOML формате.
Да там параметров-то кот наплакал
Это не много

Чтобы конфигурационный файл был в yaml и указывался через опцию можно написать примерно так: https://pastebin.com/Amzh5jJe


Для других форматов это будет выглядеть аналогично.

На мой взгляд конфиг было бы удобнее сделать полностью отдельной сущностью, которую можно положить в любое место и без правки питоновских исходников.
Эта реализация не порождает 50 потоков сразу после запуска как официальная?

Не, не порождает

Это уже радует. Запустил на погонять вместо официального. По аппетитам сабжевая реализация потребляет CPU/RAM заметно меньше.
А сколько порождает? Или он деманд?

А что под потоками подразумевается? Трелы ОС в python версии не запускаются вообще, всё однопоточное (asyncio).
Важное различие между официальным прокси и всеми остальными, что официальный мультиплексирует много коннектов клиент-прокси в небольшое количество коннектов прокси-сервер телеграм.
Другие реализации всегда создают пару сокетов.

Сабжевый прокси
$ sudo systemctl status mtproxy-python.service
● mtproxy-python.service - Telegram proxy Python
...
Tasks: 1 (limit: 4915)

Официальная реализация
$ sudo systemctl status mtproxy.service
● mtproxy.service - Telegram proxy
...
Tasks: 50 (limit: 4915)

Так это были реально потоки ОС? Я когда увидел 50 посчитал что речь идёт о TCP-потоках.


Кстати, у прокси есть недокументированная особенность. Если хочется больше одного таска, например, у вас многоядерный CPU и не хватает производительности, то можно просто запустить несколько экземпляров программы. Они автоматически поделят между собой нагрузку.

Молодец, очень радует что развиваешь и что на питоне.
НЛО прилетело и опубликовало эту надпись здесь

Отличный вопрос. У меня есть проект, который работает ровно так как описано: https://github.com/alexbers/tgsocksproxy. Правда, работает по протоколу socks т.к. протокол MTProto не поддерживает испльзование логина/пароля.

НЛО прилетело и опубликовало эту надпись здесь

Да, такой протокол. С этим, к сожалению, ничего не поделать

НЛО прилетело и опубликовало эту надпись здесь

Впилить можно, но тогда нужно будет пропатчить всех клиентов, чтобы они не передавали в открытом виде пароль.


В MTProto-версии поддерживается произвольное число секретов, но сильно много не рекомендуется.

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь
А список «клиент-ключ» и его обслуживание сильно повлияет на потребление памяти/CPU?
Отслеживание мертвых записей, удаление отключившихся, вот это всё.

Не понял вопрос.
Если вы про python mtprotoproxy, то чем больше вы секретов для одного порта назначите, тем больше будет нагрузка на CPU на установление подключения. Максимальная нагрузка будет если кто-то подключится и отправит 64 байта данных, к которым не один ключ не подойдёт, потому что сервер будет вынужден перепробовать их все.

А чего вы опасаетесь? Если боитесь, что перехватят пароль от вашего сервиса прослушивая трафик SOCKS, генерируйте вашим пользователям отдельные пароли для SOCKS, отличные от паролей от сервиса.

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Телеграм сейчас сконцентрирован на количестве и качестве публичных прокскй, поэтому и монетизацию прикрутили в виде промоченный каналов. А приватные прокси в данный момент времени телеграму особо не нужны.

НЛО прилетело и опубликовало эту надпись здесь
Все верно, именно так и живет все, что касается, например, ShadowSocks: быстро разворачиваем, пользуемся, пока не прибили. Сами прокси передаются либо сарафанным радио (во всех клиентах даже есть возможность считывания QR-кода + его генерация для каких-нибудь существующих), либо через сайты вроде free-ss.site Какие уж спонсорские штуки, да множественные секреты.
Какая производительность офицального прокси на том же железе?
>Возможность простого запуска без докера
Официальная версия тоже может работать без докера, достаточно собрать из исходников и запустить. Все это делается в течении двух минут.

Ключевое слово "простого". Когда я собрал официальный прокси, я дочитал readme до слов "Simple MT-Proto proxy" и ошибочно подумал что запуск будет простым. Примерно таким ./proxy <port> <secret>. Я запустил его с --help и увидел следующее: https://alexbers.com/proxy.png. Затем было несколько часов безуспешных попыток его поднять, закончившиеся гуглением.


Перечислю несколько проблем, которые усложняют запуск без докера:


  1. Не указан минимальный набор опций, которые необходимо передать, чтобы прокси заработал.
  2. В параметрах сказано, что прокси может быть запущен с конфигурационным файлом, однако, нет информации о формате, в котором он должен быть или примера такого файла
  3. Базовые и экспертные опции идут вперемешку. Понять, что делают некоторые опции можно только читая исходный текст
  4. Для запуска нужно совершить действия, которые, кажутся "магическими": загрузить "секрет" из адреса в интернете, передать свой внутренний и внешний ip-адрес, загрузить некий список узлов
  5. В выводе --help надпись "Simple MT-Proto proxy" вводит в заблуждение. Вместо "simple" можно было бы написать что без чтения readme даже не пытайтесь его поднять.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории