Более лучшей рекламы для прекрасного мессенджера и придумать невозможно — “несгибаемый Дуров держит оборону и стоит за конфиденциальность своих клиентов, как за свою жизнь”. Но суть данного поста не в маркетинговой составляющей блокировки Telegram на территории России. Если ваш Telegram бот перестал работать и вам нужно эту работу срочно восстановить, добро пожаловать под кат.


Вчера вечером мы столкнулись с проблемой, с которой было ожидаемо столкновение. API сервер одного нашего клиента располагался в Heroku. Вчера недовольный клиент мне пишет (в Telegram, разумеется), что на сайте информация рандомно то появляется, то нет. Сам сайт (приложение на NodeJS) уже был заранее перенесен на Московский сервер для уменьшения пинга.


Спустя полчаса изучения проблемы был получен простой вывод: Роскомнадзор заблокировал часть адресов heroku. Ротация серверов heroku происходит на уровне DNS и DNS иногда отдавал рабочий IP, иногда — нет. К слову, выяснять причину проблемы было довольно таки непросто — когда тестировали мы с разработчиками — проблемы не было. Когда тестировал клиент — были. Уже появлялись мысли ответить клиенту великую фразу разработчика “у меня на компе все работает”.


Решение было довольно простым — мы перенесли API сервер приложения на Питерский сервер клиента и там его спокойно развернули. Все заработало отлично, кроме, конечно же, интеграции с Telegram. Поскольку отказаться от этой интеграции не является возможным, т.к. нет достойной альтернативы, мы начали искать решение данной проблемы. С родным клиентом Telegram все просто — в него уже встроено взаимодействие с proxy сервером и его настройка отнимает несколько секунд. С Bot API все немного по другому. Приложение взаимодействует с https://api.telegram.org/ для каждого действия Telegram бота, а этот адрес, разумеется, заблокирован РКН.


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


В результате пришла в голову простая и очевидная мысль — поднять самостоятельно простейший прокси сервер для соединения в телеграм. Ниже предоставлен конфиг nginx который сейчас уже отлично справляется со своей задачей


Nginx config

server {
listen 80;
server_name my-telegram-proxy.server;
location / {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://api.telegram.org/;
client_max_body_size 100M;
}
}


Далее в приложении я подменил url BOT API для взаимодействия с telegram — вместо
https://api.telegram.org/bot
написал
http://my-telegram-proxy.server/bot
и интеграция с Bot Api успешно заработала


Для большего удобства создал docker контейнер с подробной инструкцией по его использованию. Это позволит вам поднять свою telegram bot proxy одной командой за считанные секунды
https://hub.docker.com/r/zvinger/docker-proxy-rkn/builds/
Пример команды:
docker run -d -p 8012:80 zvinger/docker-proxy-rkn
и указываете http://адрес.сервера:8012/ в конфигурации приложения. Порт можете выбрать любой при вводе команды

Комментарии (32)


  1. time2rfc
    26.09.2018 01:42

    Спасибо за проделаннуб работу, возникло 2 вопроса


    • с помощью синей изоленты нет возможности скрестить бота с официальной mtproxy
    • не могу понять отчего http проксирование работает быстрее VPN решения которое умеет сжимать трафик


    1. Nikobraz
      26.09.2018 02:13

      VPN требует ресурсов на установление канала связи, ну и латентность чуть больше.


    1. constb
      26.09.2018 12:16

      все-таки бот и клиент телеграма – это разные вещи, я сомневаюсь что клиент работает http-запросами на api, скорее всего у него свой бинарный протокол и другие сервера. так что боту socks/mtproxy не нужны, достаточно обычного http reverse proxy, что автор и проделал…


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


      1. zvinger Автор
        26.09.2018 12:17

        Так вебхуки уже другая история. Или РКН блокирует и входящий траффик от Telegram тоже?


        1. inkvizitor68sl
          26.09.2018 14:42

          Если телеграм придет с заблокированного адреса — то TCP-сессия не установится, в сторону таких адресов дропаются любые пакеты.


    1. im_stD
      26.09.2018 13:09

      del


  1. mickvav
    26.09.2018 07:43

    Ну я бы ещё прикрыл фаерволом проксёвый порт от всего остального мира.


  1. Taraflex
    26.09.2018 09:42

    Если речь о nodejs, то можно использовать github.com/Bannerets/tdl#login-as-a-bot на основе tdlib, который в теории должен пробиваться через блокировку как и обычный телеграм клиент.
    Правда лично я его не тестил при подключении в качестве бота — только как обычного пользователя.


  1. Merlyel
    26.09.2018 10:20
    +1

    proxy_pass https://api.telegram.org/;
    А резолвинг-то, наверное, все еще идет только при старте или релоаде nginx'а? Т.е. если вдруг поменяется IP, то все встанет?


    1. aol-nnov
      26.09.2018 11:40

      кажется, не должно быть проблем или надо добавить resolver
      nginx.org/ru/docs/http/ngx_http_core_module.html#resolver


    1. JetMaster
      26.09.2018 11:54

      вот так можно попробовать

      location / {

      resolver 8.8.8.8;
      set $url «api.telegram.org»;
      proxy_pass https://$url;

      }


      1. zvinger Автор
        26.09.2018 12:16

        не совсем понял в чем профит от этого варианта


        1. Merlyel
          26.09.2018 13:06

          Я думаю, корни тут идут отсюда:

          When you use a variable to specify the domain name in the proxy_pass directive, NGINX re?resolves the domain name when its TTL expires.

          Хотя если смотреть прямо в доках, то вроде как завязываться на переменную уже не обязательно.


  1. reci
    26.09.2018 11:09

    <grammar_nazi>
    Более лучше
    Выберите уж что-то одно. И… спасибо за конфиг :)


    1. aol-nnov
      26.09.2018 11:38

      сударь грамматический нацист просто не в курсе мема про «более лучше (одеваться)» :)


    1. zvinger Автор
      26.09.2018 12:15

      Спасибо большое поправил. Фейл:) Причем перечитал прежде чем публиковать раз 10)


  1. SLASH_CyberPunk
    26.09.2018 12:11

    Простите, я правильно понимаю, что вы решились не шифровать запросы до своего сервера в сети, по сути, которая является «прослушиваемой»?


    1. zvinger Автор
      26.09.2018 12:15

      нет конечно) обязательно https, но для быстрого старта, чтобы убедиться, что «работает» можно на http сделать — решил что так проще писать для инструкции)


      1. SLASH_CyberPunk
        26.09.2018 13:31

        При https есть проблема, раньше апи бота работало без валидации сертификата, а сейчас вроде как нет, поэтому вариант проксирования через nginx у вас просто не будет работать…


  1. onlinehead
    26.09.2018 12:17

    Есть способ еще проще через streams (и правильнее, так как шифрование сохранится) с тем же Nginx, и даже ничего в коде скрипта менять не придется.
    На стороне приложения пишем в /etc/hosts:

    ip.of.my.proxy api.telegram.org

    Для Nginx на проксе пишем:

    stream {
    # Конфигурация апстрима
    upstream tgapi {
    server api.telegram.org:443;
    }
    # Вот этот блок ради того, чтобы можно было один сервер использовать как прокси для нескольких имен
    map $ssl_preread_server_name $upstream {
    hostnames;
    default tgapi;
    api.telegram.org tgapi;
    }
    server {
    listen 443;
    ssl_preread on;
    proxy_pass $upstream;
    }
    }


    Кстати, в большинстве дистрибутивов nginx собран без stream, но оно есть в nginx-full в официальной репе самого nginx.


    1. zvinger Автор
      26.09.2018 12:56

      Спасибо! Как будет время выпущу попробую, дополню эту статью и соберу еще один контейнер


  1. Green2
    26.09.2018 12:27

    Не опасно ли бизнесу работать через телеграмм, на который наезжает государственная машина, и по закону? На него может спокойно наезжать роскомнадзор и он может внезапно найти на него управу, и отключить.
    Тогда бизнес может оказаться без канала связи.
    Я думаю, надо найти другие боты для связи с клиентами, для надежности.


    1. zvinger Автор
      26.09.2018 12:29

      Да, совершенно согласен. Поэтому используем телеграм только в качестве дублирующего канала для отправки уведомлений о новых заявках или проблемах. Основа идет через почту


  1. loderunner84
    26.09.2018 12:30

    правильно понимаю? трафик от бота до прокси не защищен?


    1. zvinger Автор
      26.09.2018 12:30

      В примере в статье да. В продакшне https


      1. onlinehead
        26.09.2018 12:43

        Простите за нудность, но — а зачем вы вообще используете именно nginx и именно так?
        В целом, у вас задача стоит «просто отфорвардить запросы через разблокированное место». Есть вариант с nginx + stream, который я выше написал, есть haproxy, который может просто запроксить не трогая содержимое. Наконец, есть всякие другие подобные штуки которые делают то же самое. Все они позволяют 2 основных вещи:
        1. Сохранить оригинальное шифрование.
        2. Обеспечить доступ через разблоченный узел.
        Схема «свой домен + сертификат + апстрим» конечно имеет право на жизнь, но она сложнее, требует выписки (и поддержки) своего сертификата, требует изменения в конфигурации приложения (имя домена) при том, что совершенно не дает профита.


        1. zvinger Автор
          26.09.2018 12:46

          А можно подробней описать более простой способ с сохранением шифрования и тд? Я бы с удовольствием на него перешел в работе) Я понимал, что мой вариант не идеален, но когда нужно что-то сделать срочно, то я делаю тем способом, который проверен)


          1. onlinehead
            26.09.2018 12:49

            Вот — comment_19159361.


          1. onlinehead
            26.09.2018 12:57
            +1

            Вот так будет выглядеть конфигурация для haproxy, которая будет делать то же самое (но только для одного домена, что не всегда удобно):

            resolvers default
                nameserver default 4.2.2.2:53
            
            frontend localhost
                bind *:443
                option tcplog
                mode tcp
                default_backend nodes
             
            backend nodes
                mode tcp
                balance roundrobin
                option ssl-hello-chk
                server api api.telegram.org:443 check resolvers default
            


          1. Bonio
            27.09.2018 10:43

            Можно еще socat, там все вообще к одной маленькой команде сводится:
            socat TCP4-LISTEN:443,reuseaddr,fork,bind=serverip TCP4:api.telegram.org:443
            После чего все запросы на 443 порт вашего сервера будут проксироваться на api.telegram.org.


  1. punkkk
    26.09.2018 16:56

    А зачем nginx если бот на nodejs поддерживает проксирование:

    const TelegramBot = require('node-telegram-bot-api')
    
    if (config.proxy) {
       options.request = {
          proxy: config.proxy
       }
    }
    
    const bot = new TelegramBot(config.token, options)
    


    Или я что то не так понял?


    1. constb
      27.09.2018 06:07

      можно и так, но – сквида с авторизацией поднимать дольше и сложнее чем реверс на nginx сделать…


      лично я бы просто поднял tcp-тоннель с помощью socat, а на сервере прописал адрес тоннеля в /etc/hosts для api.telegram.org, тогда и с сертификатами не пришлось бы ничего городить. а уж научить systemd поднимать socat на автозапуске – вообще ничего не стоит…