Довольно часто происходят ситуации, когда доступ к серверу нужен здесь и сейчас. Однако, не всегда подключение по 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
    15.03.2019 13:59

    del


  1. bvn13
    15.03.2019 14:15

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


    1. Duss
      15.03.2019 20:12
      +1

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


  1. Ktulhy
    15.03.2019 14:17

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


    1. MonkAlex
      15.03.2019 14:23
      +3

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

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


      1. ZloyKishechnik
        15.03.2019 20:12
        -2

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


  1. pavelpromin
    15.03.2019 14:32

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


    1. Blaine_Mono
      15.03.2019 14:54

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


      1. gecube
        15.03.2019 23:20

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


    1. jrthwk
      15.03.2019 15:51

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

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


      1. pavelpromin
        15.03.2019 15:58

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

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


        1. jrthwk
          15.03.2019 16:33

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

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


  1. skymal4ik
    15.03.2019 14:38

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


  1. msa
    15.03.2019 15:10

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


  1. rSedoy
    15.03.2019 16:55
    +3

    killall python3
    Серьезно?


    1. Anshi85
      16.03.2019 17:49

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


  1. tmnhy
    15.03.2019 20:51

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

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


  1. tuxi
    16.03.2019 10:36

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


  1. immaculate
    16.03.2019 10:46

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


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


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


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


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


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

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


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


  1. marazm76
    16.03.2019 11:42

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