Комментарии 86
После прочтения этой строки как-то сразу появилось желание попробовать повторить реализацию на Go…
Можно попробовать запилить.
Но мне сама идея MTProxy не очень нравится, легкость обнаружения и блокировки. Имхо нужен протокол, который легче прятать.
Авторы протокола предприняли много усилий, чтобы заблокировать было сложно. В протоколе клиент телеграм<->прокси нет сигнатуры, трафик выглядит как случайный поток байт, в котором четыре определённых байта — проверочные, но проверить можно только если знать секрет прокси сервера. Можно блокировать пакеты, которые выглядят как случайный поток байт или определённой длины, но при таких блокировках пострадают другие сервисы, работающие на непопулярных протоколах.
Недавно была статья, где по пунктам разобраны слабые места протокола, упрощающие обнаружение.
Можете подробнее рассказать, как именно протокол усложняет блокировку?
Могу. В самом начале клиент генерирует случайные 56 байт и посылает их серверу. На основе этих байт и общего секрета, обе стороны генерируют по два ключа шифрования: для шифрования данных от сервера к клиенту и в обратную сторону. После этого и клиент и сервер перед посылкой данных шифруют их соответствующими ключами.
После случайных 56 байт клиент посылает зашифрованные магические четыре байта и номер датацентра с которым его должен соединить прокси. По этим магическим четырем байтам сервер понимает, что с ним действительно общаются по нужному протоколу, но, для внешнего наблюдателя эти данные выглядят как случайные т.к. он не знает секрета, на основе которого сформированы ключи шифрования.
Erlang версия умеет по ядрам расползаться. Другой вопрос, что из за того как активно прокси блокируют, удобнее иметь 10 одноядерных виртуалок с 10 IP адресами, чем одну на 12 ядер с одним адресом. (Если есть возможность иметь одну с 10 IP адресами то это, конечно, приобретает смысл.)
Плюс к этому в статье умпоянуто, что обычно узкое место это не CPU, а память.
Вопрос возник, при запуске оно ругается что нет pycryptodome или pycrypto, что из этого лучше выбрать в плане наименьшего потребления ресурсов? Или можно на slow AES implementation оставаться?
В плане потребления ресурсов они pycryptodome и pycrypto работают примерно одинаково. Мне больше нравится первый, он активнее развивается и под windows легче устанавливается. Slow implementation медленный, но если скорости хватает, то можно оставаться на нём.
А какой из crypto/cryptodome он выберет, если стоят оба, и как на это повлиять?
Одновременное использование нескольких ядер CPU не реализовано (привет, GIL).
А думали над тем, что бы просто запускать несколько инстансов и через что-то их роутить? Как делает gunicorn например. Из самых простых способов, это nginx + streams.
Нужно как-то передавать по всей цепочке IP адрес клиента (если мы не хотим врать телеграму про IP адреса пользователей). При простой TCP балансировке это невозможно, нужно какой-то протокол изобретать.
Ну так и первый способ с proxy_bind это какое-то совсем шаманство, а второе proxy_protocol, как я и сказал, потребует для mtproto proxy дописывать ещё поддержку PROXY protocol.
поддержка PROXY protocol добавлена
мы точно об одном и том же proxy protocol?
я про https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
Есть идея попробовать опцию SO_REUSEPORT, которая позволяет заслушать один и тот же порт несколькими процессами. Думаю, это должно помочь.
Призываю Vespertilio. Он как раз интересовался подробностями.
Тем временем предпосылки к очередной головной боли.
Неужели только я хотел бы для узкого круга приближенных дать просто прокси, а всем остальным навязать свой канал и все это без необходимости запуска двух инстансов прокси?
Действительно. Дополнил статью ссылкой на репозиторий https://github.com/mtProtoProxy, в котором собраны разные реализации MTProto-прокси.
Хорошая идея, попробую реализовать в ближайшее время
На самом деле потребление памяти получается примерно одинаковым, там почти вся память — разнобразные буферы, которые занимают примерно одинаково на си и на питоне.
С pypy3 и alpine возникла проблема — такого пакета по умолчанию нет. Проблема находится в процессе решения.
По IP с которого пришёл пакет.
Прокси должен его знать. Официальному прокси нужно указывать 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 и MTProto proxy. Их не то чтоб нет, но они маловероятны.
- Официальный прокси мультиплексирует несколько пользователей в одно исходящее соединение. Т.е. исходящих соединений сравнительно немного.
- Выбор исходящего порта внутри контейнера зависит от
port_offset
, который зависит от IP клиента. У каждого контейнера свой внутренний IP, поэтому при небольшом числе контейнеров и небольшом числе соединений конфликтов по исходящим TCP портам не случается. - SNAT и MASQUERADE, через которые трафик из контейнера уходит в интернет, не меняют порт при отсутствии конфликтов: where possible, no port alteration will occur.
Чтобы конфигурационный файл был в yaml и указывался через опцию можно написать примерно так: https://pastebin.com/Amzh5jJe
Для других форматов это будет выглядеть аналогично.
Не, не порождает
Он деманд
А что под потоками подразумевается? Трелы ОС в 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/mtprotoproxy/issues/20 тут это обсуждается
Отличный вопрос. У меня есть проект, который работает ровно так как описано: https://github.com/alexbers/tgsocksproxy. Правда, работает по протоколу socks т.к. протокол MTProto не поддерживает испльзование логина/пароля.
Да, такой протокол. С этим, к сожалению, ничего не поделать
Впилить можно, но тогда нужно будет пропатчить всех клиентов, чтобы они не передавали в открытом виде пароль.
В MTProto-версии поддерживается произвольное число секретов, но сильно много не рекомендуется.
Когда клиент подключается к mtproto серверу, то он присылает 64байта — данные сессии, зашифрованные этим вот секретом. Если расшифровать эти 64 байта правильным секретом, то там в расшифрованном пакете определённой позиции будет проверочная сумма. Если она не совпала, то нужно пробовать расшифровать следующим ключом. Т.е. в худшем случае придётся много раз расшифровывать, перебирая ключ.
Отслеживание мертвых записей, удаление отключившихся, вот это всё.
Не понял вопрос.
Если вы про python mtprotoproxy, то чем больше вы секретов для одного порта назначите, тем больше будет нагрузка на CPU на установление подключения. Максимальная нагрузка будет если кто-то подключится и отправит 64 байта данных, к которым не один ключ не подойдёт, потому что сервер будет вынужден перепробовать их все.
А чего вы опасаетесь? Если боитесь, что перехватят пароль от вашего сервиса прослушивая трафик SOCKS, генерируйте вашим пользователям отдельные пароли для SOCKS, отличные от паролей от сервиса.
Телеграм сейчас сконцентрирован на количестве и качестве публичных прокскй, поэтому и монетизацию прикрутили в виде промоченный каналов. А приватные прокси в данный момент времени телеграму особо не нужны.
Официальная версия тоже может работать без докера, достаточно собрать из исходников и запустить. Все это делается в течении двух минут.
Ключевое слово "простого". Когда я собрал официальный прокси, я дочитал readme до слов "Simple MT-Proto proxy" и ошибочно подумал что запуск будет простым. Примерно таким ./proxy <port> <secret>. Я запустил его с --help и увидел следующее: https://alexbers.com/proxy.png. Затем было несколько часов безуспешных попыток его поднять, закончившиеся гуглением.
Перечислю несколько проблем, которые усложняют запуск без докера:
- Не указан минимальный набор опций, которые необходимо передать, чтобы прокси заработал.
- В параметрах сказано, что прокси может быть запущен с конфигурационным файлом, однако, нет информации о формате, в котором он должен быть или примера такого файла
- Базовые и экспертные опции идут вперемешку. Понять, что делают некоторые опции можно только читая исходный текст
- Для запуска нужно совершить действия, которые, кажутся "магическими": загрузить "секрет" из адреса в интернете, передать свой внутренний и внешний ip-адрес, загрузить некий список узлов
- В выводе --help надпись "Simple MT-Proto proxy" вводит в заблуждение. Вместо "simple" можно было бы написать что без чтения readme даже не пытайтесь его поднять.
Релиз неофициального MTProto прокси на Python, особенности протокола