Доступ к linux серверу с помощью Telegram-бота на Python +12



Довольно часто происходят ситуации, когда доступ к серверу нужен здесь и сейчас. Однако, не всегда подключение по SSH является наиболее удобным способом, потому что под рукой может не оказаться SSH клиента, адреса сервера или связки «пользователь/пароль». Конечно, есть Webmin, который упрощает администрирование, но он также не даёт моментальный доступ.

Поэтому я решил реализовать простое, но любопытное решение. А именно — написать Telegram-бота, который, запускаясь на самом сервере, будет выполнять присылаемые ему команды и возвращать результат. Изучив несколько статей на эту тему, я понял, что подобных реализаций ещё никто не описывал.

Данный проект я реализовывал на Ubuntu 16.04, но для беспроблемного запуска на других дистрибутивах я постарался сделать всё в общем виде.

Регистрация бота


Регистрируем нового бота у @BotFather. Отправляем ему /newbot и далее по тексту. Нам понадобятся токен нового бота и ваш id (получить его можно, например, у @userinfobot).

Подготовка питона


Для запуска бота будем использовать библиотеку telebot (pip install pytelegrambotapi). С помощью библиотеки subprocess будем выполнять команды на сервере.

Запуск бота


На сервере создаем файл bot.py:
nano bot.py

И вставляем в него код:

from subprocess import check_output
import telebot
import time

bot = telebot.TeleBot("XXXXXXXXX:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")#токен бота
user_id = 0 #id вашего аккаунта
@bot.message_handler(content_types=["text"])
def main(message):
   if (user_id == message.chat.id): #проверяем, что пишет именно владелец
      comand = message.text  #текст сообщения
      try: #если команда невыполняемая - check_output выдаст exception
         bot.send_message(message.chat.id, check_output(comand, shell = True))
      except:
         bot.send_message(message.chat.id, "Invalid input") #если команда некорректна
if __name__ == '__main__':
    while True:
        try:#добавляем try для бесперебойной работы
            bot.polling(none_stop=True)#запуск бота
        except:
            time.sleep(10)#в случае падения

Заменяем в нём токен бота на тот, который выдал @BotFather, и user_id — на значение id вашего аккаунта. Проверка id юзера нужна для того, чтобы бот предоставлял доступ к вашему серверу только вам. Функция check_output() выполняет переданную команду и возвращает результат.

Осталось только запустить бота. Для запуска процессов на сервере я предпочитаю использовать screen (sudo apt-get install screen):

screen -dmS ServerBot python3 bot.py
(где «ServerBot» — идентификатор процесса)

Процесс автоматически запустится в фоном режиме. Перейдем в диалог с ботом и проверим, что всё работает, как надо:



Сongratulations! Бот выполняет присылаемые ему команды. Теперь, чтобы получить доступ к серверу, вам достаточно открыть диалог с ботом.

Повторение команд


Зачастую, для мониторинга состояния сервера приходится выполнять одни и те же команды. Поэтому реализация повтора команд без их повторного отправления будет очень к месту.

Реализовывать будем при помощи inline кнопок под сообщениями:

from subprocess import check_output
import telebot
from telebot import types #Добавляем импорт кнопок
import time

bot = telebot.TeleBot("XXXXXXXXX:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")#Токен бота
user_id = 0 #id вашего аккаунта
@bot.message_handler(content_types=["text"])
def main(message):
   if (user_id == message.chat.id): #проверяем, что пишет именно владелец
      comand = message.text  #текст сообщения
      markup = types.InlineKeyboardMarkup() #создаем клавиатуру
      button = types.InlineKeyboardButton(text="Повторить", callback_data=comand) #создаем кнопку
      markup.add(button) #добавляем кнопку в клавиатуру
      try: #если команда невыполняемая - check_output выдаст exception
         bot.send_message(user_id, check_output(comand, shell = True,  reply_markup = markup)) #вызываем команду и отправляем сообщение с результатом
      except:
         bot.send_message(user_id, "Invalid input") #если команда некорректна

@bot.callback_query_handler(func=lambda call: True)
def callback(call):
  comand = call.data #считываем команду из поля кнопки data
  try:#если команда не выполняемая - check_output выдаст exception
     markup = types.InlineKeyboardMarkup() #создаем клавиатуру
     button = types.InlineKeyboardButton(text="Повторить", callback_data=comand) #создаем кнопку и в data передаём команду
     markup.add(button) #добавляем кнопку в клавиатуру
     bot.send_message(user_id, check_output(comand, shell = True), reply_markup = markup) #вызываем команду и отправляем сообщение с результатом
  except:
     bot.send_message(user_id, "Invalid input") #если команда некорректна

if __name__ == '__main__':
    while True:
        try:#добавляем try для бесперебойной работы
            bot.polling(none_stop=True)#запуск бота
        except:
            time.sleep(10)#в случае падения

Перезапускаем бота:

killall python3
screen -dmS ServerBot python3 bot.py

Снова проверим, что всё работает корректно:



По нажатию кнопки под сообщением бот должен повторять команду, от которой было прислано данное сообщение.

Вместо заключения


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

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (20):

  1. denaspireone
    /#19889268

    del

  2. bvn13
    /#19889426

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

    • Duss
      /#19891552 / +1

      Только в случае, если сервера расположены в РФ, ну или может в Иране.

  3. Ktulhy
    /#19889442

    Крайне желательно сделать проверку id пользователя, иначе — это огромная дыра в безопасности

    • MonkAlex
      /#19889488 / +3

      Зато хоть что-то будет в безопасности! (С)

      На деле — очень странная идея, изобретать такой вот дикий костыль при куче готовых и вполне испытанных решений по доступу ~_~

      • ZloyKishechnik
        /#19891558 / -2

        а какие еще есть доступы?
        знаю webmin, vnc ну и стандартный ссш.
        а еще? когда оторвало ссш или просто нет возможности к нему приконнектиться? можете для общего развития рассказать о тех, которые известны вам?

  4. pavelpromin
    /#19889554

    Хмм. Это в давние времена, когда ssh-клиентов не было на Symbian (Nokia E5-00), а был лишь асечный клиент jimm,- делал такой на php через аську.
    Но сегодня нет примерно не одной ситуации когда можно поставить телеграм, но нельзя ssh-клент. Причем для ssh (ну когда вдруг остался без своего телефона) можно использовать закрытый ключ с паролем и хранить его в любом месте интернета (почта, гуглдрайв, на сайте, ...)

    • Blaine_Mono
      /#19889690

      Вообще есть ситуация когда openssh сервер бесполезен. Например домашний компьютер за NAT

      • gecube
        /#19892380

        Почему? Можно вывернуться и сделать обратный ssh туннель :-) вариантов масса — от dyndns до плясок с промежуточным узлом с внешним ip

    • jrthwk
      /#19890042

      Для ссш-клиента надо белый IP, с чем как бы совсем не так хорошо как хотелось бы…

      зы А вообще для такого полуинтерактивного удаленного рулежа я бы вообще email использовал…

      • pavelpromin
        /#19890094

        Если есть доступ к редактированию dns (dyndns-ы или api регистратора), то не нужен.

        Просто, несмотря на кажущиеся плюсы, у предложенного(через телеграм) подхода есть также куча минусов. Например, что с интерактивным вводом?! или как послать ^C для прерывания команды?

        • jrthwk
          /#19890372

          >Если есть доступ к редактированию dns (dyndns-ы или api регистратора), то не нужен.

          Тащемта провайдеры стараются запихать всех за нат, чем тут редактирование днс поможет? Или я чего-то пропустил в мире сетей?

  5. skymal4ik
    /#19889592

    Решение интересное, и может быть полезным для быстрого мониторинга/проверки.
    Но я бы всё равно ограничил набор доступных команд через sudo и самим скриптом. И емейл бы посылал при подключении через бота. Ато вдруг утечёт доступ, а вы и не узнаете.

  6. msa
    /#19889796

    Аналогичное решение, но без программирования, shell-команды задаются через командную строку — shell2telegram

  7. rSedoy
    /#19890526 / +3

    killall python3
    Серьезно?

    • Anshi85
      /#19895292

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

  8. tmnhy
    /#19891728

    Безусловно, такой метод не претендует стать заменой классическим способам подключения, однако, он позволяет ...

    Однако он аналогичен такому методу развлечения, как «зажать яйца между дверью и коробкой, самому давить со всей силы на дверь и самому же кричать, как же больно».

  9. tuxi
    /#19893362

    Если нам нужно иметь доступ к ограниченному списку операций, то задача сильно упрощается, телеграмм тут становится лишним звеном, достаточно простого браузера и доступ по урл содержащий хэш md5, который меняется (например) каждые сутки. Онлайн сервисов генерации MD5 в интернете много. Если MD5 указан верно, получаем доступ к нами написанной панельке с перечислением доступных команд и тп, данный hash привязывается к айпи, с другого айпи на него уже не зайти, даже если браузер сольет ссылку поисковой машине. Чем не решение?

  10. immaculate
    /#19893396

    Для Android есть приложение JuiceSSH (есть еще несколько ssh клиентов, просто к этому уже привык). Генерируем или добавляем ключ, копируем его на все сервера сразу же. В JuiceSSH создаем записи для всех серверов (чтобы потом не вспоминать и не искать лихорадочно, какие же у них ip-адреса, и какое имя пользователя/ключ надо использовать).


    Еще для Android есть приложение termux.


    Для iOS есть свои аналоги, если не ошибаюсь.


    Это во-первых, намного удобнее, чем костыль с телеграмом (как через телеграм конфиг редактировать? через sed?), во-вторых, надежнее и безопаснее.


    Ну и код, даже несмотря на минимальный размер, имеет массу недостатков, начиная с банального несоблюдения PEP-8 и массы совершенно ненужных комментариев а-ля:


    command = message.text #текст сообщения

    Серьезно найдется человек, которому этот комментарий поможет разобраться в коде???


    В общем, очередной велосипед с квадратными колесами (TM).

  11. marazm76
    /#19893570

    Мне вот этот фреймворк для разработки телеграмм ботов нравится больше. В нем и прокси можно легко настроить и сертификаты