Демон Для Удаленного Управления Компьютером По Электронной Почте

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



Введение

В своей работе я использую множество удаленных машин.

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

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

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

Со временем сложился ряд требований:

  • безопасность системы;
  • легкий доступ к системе без лишних движений (с телефона, чужого компьютера и т. д.);
  • история выполненных команд и результатов выполнения.

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

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

Список писем всегда доступен на сервере и вы можете удобно анализировать изменения состояния системы.

Среди готовых решений ничего подходящего не нашлось, поэтому я решил сделать это сам.



Выполнение

В качестве языка программирования был выбран Python; критерием выбора была не только гибкость самого языка, но и давнее желание использовать его на практике.

Алгоритм программы достаточно прост:

  1. Получение команд по электронной почте
  2. Выполнение команд
  3. Отправка результатов обратно пользователю


1. Получение команд по электронной почте.

Сначала устанавливаем соединение с сервером, есть два варианта: POP3 или IMAP4. Выбор зависит как от поддерживаемых протоколов на почтовом сервере, так и от открытости портов на целевой машине.

Подключение к серверу по протоколу POP3

  
  
  
  
   

if is_enabled(self.get_param_str("Mail", "USE_SSL")): session = poplib.POP3_SSL(self.get_param_str("Mail", "POP_SERVER"), self.get_param_int("Mail", "POP_SSL_PORT")) else: session = poplib.POP3(self.get_param_str("Mail", "POP_SERVER"), self.get_param_int("Mail", "POP_PORT")) #if if is_enabled(self.get_param_str("Debug", "NETWORK_COMM_LOGGING")): session.set_debuglevel(10) #if try: session.user(self.get_param_str("Mail", "EMAIL_USER")) session.pass_(self.get_param_str("Mail", "EMAIL_PASS")) except poplib.error_proto as e: sys.stderr.write("Got an error while connecting to POP server: '%s'\n" % (e)) return False #try

Подключение к серверу по протоколу IMAP4

if is_enabled(self.get_param_str("Mail", "USE_SSL")): session = imaplib.IMAP4_SSL(self.get_param_str("Mail", "IMAP_SERVER"), self.get_param_int("Mail", "IMAP_SSL_PORT")) else: session = imaplib.IMAP4(self.get_param_str("Mail", "IMAP_SERVER"), self.get_param_int("Mail", "IMAP_PORT")) #if if is_enabled(self.get_param_str("Debug", "NETWORK_COMM_LOGGING")): session.debug = 10 #if try: session.login(self.get_param_str("Mail", "EMAIL_USER"), self.get_param_str("Mail", "EMAIL_PASS")) except imaplib.IMAP4.error as e: sys.stderr.write("Got an error while connecting to IMAP server: '%s'\n" % (e)) return False #try

После того, как соединение установлено, нам необходимо отфильтровать команды для нашего бота из всех сообщений.

Я решил использовать трехуровневую фильтрацию:

  • фильтрация по теме сообщения;
  • фильтрация отправителей по белому и черному спискам;
  • авторизация по логину+паролю.

Алгоритм фильтрации по теме в случае POP3 следующий: получаем только заголовки сообщений, проверяем поле «Тема:», если тема верна, получаем сообщение полностью и передаем его на дальнейшую обработку.



numMessages = len(session.list()[1]) for i in range(numMessages): m_parsed = Parser().

parsestr("\n".

join(session.top(i+1, 0)[1])) if self.get_param_str('Main', 'SUBJECT_CODE_PHRASE') == m_parsed['subject']: #Looks like valid cmd for bot, continue if self._process_msg("\n".

join(session.retr(i+1)[1])): session.dele(i+1) #if #if #for

В случае с IMAP все немного проще, протокол позволяет осуществлять выборку на стороне сервера, т.е.

нам достаточно указать тему, и сервер сам выдаст нам все подходящие буквы.



session.select(self.get_param_str('Mail', 'IMAP_MAILBOX_NAME')) typ, data = session.search(None, 'SUBJECT', self.get_param_str("Main", "SUBJECT_CODE_PHRASE"))

Следующий шаг — фильтрация отправителя по белому и черному спискам (можно использовать регулярные выражения).

И последний бастион — авторизация с использованием пары логин:пароль, которая должна появиться в первой строке письма с командой.

На клиенте вместо паролей хранятся только md5 хеши.

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

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

Также был введен верхний предел количества активных процессов.

Недостатком выполнения произвольных команд является возможность зависания системы запуском интерактивной программы (mc, htop и т.п.

).

Я пока не придумал, как с этим бороться.



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

Модуль smtplib используется для отправки

self.__send_lock.acquire() if not msg is None: print "[%s] Sending response to '%s'" % (datetime.today().

strftime('%d/%m/%y %H:%M'), email_from) recipients = [email_from, self.get_param_str('Mail', 'SEND_COPY_TO')] message = "%s%s%s\n%s" % ('From: %s \n' % (self.get_param_str('Main', 'BOT_NAME')), 'To: %s \n' % (email_from), 'Subject: Report %s \n' % (datetime.today().

strftime('%d/%m/%y %H:%M')), msg) # Currently in python SMTP_SSL is broken, so always using usual version session = smtplib.SMTP(self.get_param_str("Mail", "SMTP_SERVER"), self.get_param_int("Mail", "SMTP_PORT")) if is_enabled(self.get_param_str("Debug", "NETWORK_COMM_LOGGING")): session.set_debuglevel(10) #if session.login(self.get_param_str("Mail", "EMAIL_USER"), self.get_param_str("Mail", "EMAIL_PASS")) session.sendmail(self.get_param_str("Mail", "EMAIL_USER"), recipients, message) session.quit() #if self.__send_lock.release()

Был использован для создания демона этот Сорт.

Заключение

В качестве примера отправляем боту команду:

Демон для удаленного управления компьютером по электронной почте

Через некоторое время видим ответ:

Демон для удаленного управления компьютером по электронной почте

Код проекта доступен на github Надеюсь, что эта информация будет кому-то полезна.

Спасибо за внимание, жду ваших комментариев.

UPD: исправлена ошибка, связанная с некорректной обработкой многочастных сообщений, благодаря пользователю github megres. UPD2: добавлена возможность устанавливать собственный таймаут для команды.

Для использования нужно перед командой добавить префикс ":time=x", т.е.

":time=10 make", даст 10 секунд на сборку, а потом выстрелит. Спасибо Таненн за идею.

Теги: #python #дистанционное управление #уведомления по электронной почте #python

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.