Мигрируя в этом году свою инфраструктуру в новый датацентр, поймал себя на мысли о том, что возраст моей виртуальной частной сети (VPN) для доступа к серверам и устройствам перевалил за 10 лет.

Мой старый товарищ OpenVPN ни разу не подвел меня.

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

Под катом вы найдете детали того, что получилось. Кратко, результат следующий: композиция из двух docker-контейнеров, которые превращаются в telegram-бота для управления VPN сервисом. И для этого нужно выполнить всего две команды в консоли.

Механизм управления VPN сервисом теперь не требует каких-либо навыков системного администрирования или доступа к Linux-консоли при выполнении рутинных операций. Например, чтобы сделать сертификаты новому пользователю вам достаточно иметь телефон с telegram-клиентом.

Для тех же, кому интересны технические детали реализации (помимо готового кода), в статье есть описание подходов, которые упрощают сопряжение систем в нескольких контейнерах. Раньше я "ходил более сложными тропами"...

Почему все это и для чего

OpenVPN очень удобное решение для объединения сетей, устройств и пользователей общей частной сетью. Продукт имеет клиенты под все основные ОС (iOS, android, windows, Linux, macOS*). Cерверная версия поставляется как в community так и в enterprise вариантах.

Типовая ситуация: нужно дать заболевшему сотруднику/временному работнику/клиенту(на посмотреть промежуточный результат) доступ к набору сервисов компании/рабочей группы. Ответ на все эти ситуации, сертификат OpenVPN.

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

Где все это применялось

За последние 10 лет моей жизни OpenVPN был героем самых разных историй...

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

В моем запасе личных впечатлений набралось несколько десятков счастливых улыбок пользователей из серии: «...я увидел свою папочку на файловом сервере офиса, сидя дома за кухонным столом. Спасибо тебе! Ты мой личный Дед Мороз)».

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

А для чего ты нас всех пригласил это читать?

А весь этот рассказ из-за того, что "в оригинале" OpenVPN сервер требует квалификации в управлении и настройке.

Нужно уметь:

  1. Настроить сервер;

  2. Настроить фабрику сертификатов;

  3. Настроить механизм создания пользователей;

  4. Настроить механизм управления видимостью хостов.

И вот, когда вы уже справились с пп.1-4, каждый раз когда вам нужно завести нового пользователя, вы идете в консоль, ну и... дальше вы все знаете админскую работу.

Последнее время появились контейнеры, которые решают задачи пп.1-3 (я вот этот использовал).

Есть веб-интерфейсы для управления сертификатами (например, вот этот). Я и сам написал что-то похожее для проекта с роботами и футбольными полями. И хотя мой заказчик доволен и пользуется написанным, но... как-то не очень удобно мне это всегда казалось.

Хотелось, чтобы вопрос с пользователями могла решить условная секретарь ресепшен. Назову ее Маша. Длинноногая, белокурая/рыжая/брюнетка и не знающая таких слов как bash, iptables и пр. заклинаний.

Возможные Маши, подходящие для бота
Возможные Маши, подходящие для бота

И вот время пришло! Маши еще не перевелись, а я уже написал систему управления пользователями OpenVPN при помощи telegram бота и хочу поделиться написанным с вами.

Для тех, кому не терпится попробовать, вот ссылка на репозиторий. Там есть документация.

Для тех, кто не прочел в репозитории

Установка (делает администратор на VPS)

  1. Клонировать репозиторий и зайти в папку;

  2. Переименовать .env.example to .env;

  3. Минимально заполнить переменные .env:

# Telegram settings:
BOT_API_KEY='<tg-bot-token>'
BOT_USERNAME='<tg-bot-name>'
HOOK_URL=https://<bot-hook>

# Bot Admins Settings:
ADMINS=mashauserid1,mashauserid2,...

# OpenVPN Server Settings:
VPN_DOMAIN=<vpn_host_name_or_address>
VPN_PORT=<vpn_port_number>
VPN_NET=192.168.0.0/24
API_PORT=<api_port_number>
  1. "Поднять" контейнеры: docker-compose up --build -d;

  2. Выполнить инициализирующий скрипт : ./init.sh (потребует ответить на один вопрос. любая строка подходит. Например вот эта: <vpn_host_name_or_address>);

  3. Создать виртуальный хост и перенаправить запросы на пост контейнера (https://<bot-hook>) на адрес127.0.0.1:<api_port_number>(пример конфигурации nginx найдете в репозитории);

  4. Опционально: открыть <vpn_port_number> в вашем firewall.

Поведение любого нового пользователя бота (происходит telegram-боте):

  1. Кнопка СТАРТ или команда /start;

  2. Команда /newuser.

Я рассчитываю на то, что

  1. У вас на VPS установлен web-сервер и вы справитесь с перенаправлением веб-запросов на порт контейнера;

  2. У вас на VPS установлен docker и docker-compose;

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

А в чем, собственно, новация?

На этапе развертывания VPN-сервиса вам потребуется изменить 8 строчек конфигурации и выполнить две консольных команды. И ваш vpn-сервис, управляемый telegram-ботом, готов. Не забудьте в админы Машу добавить).

При управлении пользователями, сертификатами и вашей приватной сетью бот позволит Маше делать следующее:

  • нанимать и увольнять сотрудников (команды: /hire и /fire);

  • создавать для сотрудников сертификаты (принцип: 1 сертификат - 1 хост - 1 telegram аккаунт. Команда: /newuser);

  • создавать виртуальные хосты (без привязки к telegram аккаунту. Команда:/newvirt);

  • отзывать, приостанавливать и возобновлять действия сертификатов (команды: /revoke, /disable, /enable);

  • удалять виртуальные хосты (команда: /rmvirt);

  • настраивать видимость между двумя хостами (команда: /connect);

  • публиковать хосты (делать так, чтобы хост был доступен любому участнику частной сети. Команда: /public);

  • скрывать публичный хост от некоторых хостов сети (команда: /obscure);

  • обнулять настройки видимости хоста (по умолчанию хост не виден другим хостам. Команда: /hide);

  • получать информацию о настройках видимости хостов, о сертификатах и их статусе, о хостах и их адресах, о пользователях и их статусе (команды: /net, /users, /staff).

Помимо этого, бот извещает всех админов обо всех значимых происходящих событиях (найм, сертификаты и пр.), позволяет переписываться админам и пользователям, предоставляет механизм оповещения пользователей бота через внешнее REST API (от оповещений вы можете отписаться или подписаться на них. Команды: /subs и /unsubs), позволяет осуществлять регистрацию информации о каждом предоставлении сертификата во внешнем api.

Стандартный путь для пользователя вашего нового бота:

Пользователь: Попадает в telegram-бота;

Пользователь: Нажимает кнопку "Старт"/вводит команду /start(Маша получит запрос на найм);

Маша:"Нанимает" пользователя (Маше будет предложен шаблон команды /hire для найма);

Пользователь:Отправляет запрос на получение сертификата (команда: /newuser);

Маша: Делает и отправляет сертификат пользователю (Маше будет предложен шаблон команды /newuser для создания/получения и отправки нового/не нового сертификата);

Маша: Настраивает видимость для хоста пользователя (команды:/connect, /public, /obscure).

Как все это устроено

Все построение выполнено на базе готового контейнера с OpenVPN сервером и фабрикой сертификатов.

Ему добавлен iptables (для управления видимостью хостов), внесены изменения в оригинальные shell-скрипты и добавлены новые.

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

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

Учитывая, что контейнер OpenVPN управляется shell-скриптами, был очень большой соблазн использовать какой-то легкий пакет (типа webhook) для придания shell-скриптам возможности быть вызванными как http-api. Однако при ближайшем рассмотрении подтвердилось, что многие функции (например, файлообмен) без дополнительных затрат (только средствами подобного пакета) реализовать не получится.

И тут было найдено решение, которое позволило решить эту задачу с наименьшими затратами...

Суть решения

Между двумя контейнерами делается общее файловое пространство (обыкновенный volume).

Контейнер, в котором реализован бот, создает в этом пространстве трубу, организованную по принципу fifo:

mkfifo /path/to/pipe

В эту трубу наш бот будет писать команду по принципу:

fwrite(file, “command uid arg1 arg2 ...”);

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

Если возникла ошибка, пишет вывод в файл <uid>.err, в другом случае вывод попадает в <uid>. Выходные файлы складываются по заранее известному пути. Удаляются через несколько десятков секунд после записи.

Пример скрипта, ниже:

# ./www/tgbot/vendor/loandbeholdru/pipe/example/serve.sh 

...  

while true; do

     read OPTS < "$PIPE"

     UNIC=$(echo "$OPTS" | awk '{print $1}')

     echo "$UNIC"    

    # Контроль ошибки невыполнения скрипта

     (watcher "$PTH/$UNIC" &)

     # Выполнение скрипта, соответсвующего команде

     executer $OPTS

     # Почистить за собой выходные файлы

     (clean "$PTH/$UNIC" "" ".err" &)

done  

...

Файл в контейнере-источнике-команды ждет появления файлов для своего uid и реагирует в зависимости от содержимого.

Решение получилось простым легким и минималистичным по коду. Его я вынес в отдельный пакет. Там же есть каталог с полным примером shell-скрипта и тремя PHP классами. Простыми и функциональными.

Что дает нам такой подход?

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

Для данной конкретной ситуации наш бот является заказчиком результатов работы других систем (в этом конкретном случае: системы управления сертификатами, iptables), используя MOM подход.

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

В конкретном случае, это nginx и мой небольшой, но очень функциональный, пакет.

Сделан он поверх SlimPHP. Предназначен специально для быстрого создания минималистичных REST API. Хотя и для больших затей, годится.

Все это много времени мне сэкономило.

Отказы и отговорки

Дорогой читатель, обращаю ваше внимание, что представленный код "не идеален". При его написании я не ставил себе целей "полировки". Мне нужен был удобный инструмент для управления сертификатами своей инфраструктуры. Я его написал. И буду очень рад, если он пригодится вам. Все нарекания, просьбы об исправлениях и улучшениях я принимаю через GitHub. Предпочтительный язык, английский.

Для тех, кто интересуется вопросами предоставления VPN-сервиса с перенаправлением всего объема трафика подключенного устройства через VPN при помощи описанного кода (например, чтобы исходящий IP адрес устройства принадлежал другой стране), хочу сказать, что представленный код и инструкции не предполагают такой конфигурации.

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

Обращаю ваше внимание на то, что при определенных обстоятельствах на территории ряда стран подобное (доделование и дописывание) может оцениваться как действие "выходящее за рамки закона". И может повлечь за собой ответственность.

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

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

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

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

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


  1. robotxt
    30.12.2022 10:59
    +1

    Реально годно. У нас в 3 зданиях по городу (Организациях установлено 3 впн WireGuard клиента/сервера. Займемся этим вопросом. Управление через телеграмм это что то новое.


    1. mopkob Автор
      30.12.2022 11:05
      +2

      После беглого просмотра сайта WireGuard, кажется, что продукт легко сопрягается с роботом. Заменяете один контейнер и все получится)

      Удачи вам справится с этим в новом году. С наступающим!