Как я написал свою RMM
Я работаю в FlashFormat — компании, которая занимается IT-аутсорсингом. Мы обслуживаем чужую технику: рабочие станции, серверы, роутеры, принтеры — всё это раскидано по десяткам клиентов и живёт своей жизнью, пока не сломается.
Работа устроена просто. Звонит человек и говорит: «не печатает». Или «сервер не открывается». Дальше инженер должен за минуту понять, кто это вообще такой, какая у него машина, как к ней подключиться и под каким паролем. А если это сервер — ещё и вспомнить, что там стоит, кто последний туда ходил и не было ли чего-то подобного в прошлом месяце.
Пока клиентов пять, всё это помещается в голове. У нас на обслуживании около 1000 рабочих станций, 190 серверов и 245 сетевых устройств. Голова заканчивается примерно на втором десятке клиентов.
Где на самом деле жили доступы
Часть — в головах инженеров. Это не фигура речи: реально существовало знание вида «на этот сервер ходит только Сергей, он его ставил». Работает безотказно ровно до того дня, когда Сергей уходит в отпуск.

Часть — в RDM, платном менеджере подключений. И вот здесь важно: RDM не плохой. Он огромный. Это комбайн для крупных предприятий — десятки типов подключений, хранилища секретов, политики доступа, интеграции со всем подряд.
Только нам, как и большинству системных администраторов, нужны были четыре кнопки:
- подключиться к пользователю и посмотреть, что у него на экране;
- RDP на сервер;
- SSH;
- Winbox — потому что сетевое оборудование у нас в 99% случаев MikroTik.
Всё. За остальное мы платили и каждый день продирались сквозь интерфейс, рассчитанный на совсем другой масштаб задач.
Но даже будь он удобным, это не закрыло бы главного. Дерево подключений — не учёт техники. RDM не знает, сколько у клиента машин и что на них стоит. Не скажет, что сервер лежит уже сорок минут. Не подскажет, что этот ноутбук мы сняли с обслуживания ещё весной. А главное — он живёт отдельной вселенной от Битрикс24, где у нас уже есть и клиенты, и договоры, и заявки. Получались две параллельные картотеки, которые надо сводить руками и которые всё равно расходятся.
Отдельная история — люди. Инженеров много, каждому нужен доступ ко всему парку, потому что заявка может прилететь любая. А когда человек уходит, нужно закрыть этот доступ везде и сразу — и вот тут выясняется, что «везде» никто перечислить не может.
Последней капли не было
Ничего не взорвалось. Просто накопилось.
Самое разрушительное в сложном инструменте — не то, что в нём неудобно работать. А то, что в него перестают вносить данные. Каждое действие в RDM требовало усилий, и сотрудники — совершенно естественно — начали забивать. Поставили новый сервер: занесём потом. Сменился пароль: да он и так у меня записан. Через полгода справочник, за который мы платили деньги, описывал реальность примерно наполовину.
А неактуальный справочник хуже, чем никакого. На отсутствующий ты хотя бы не полагаешься.
Вторая половина проблемы — RDM ничего не знал о том, где мы на самом деле работаем. Заявки, клиенты, договоры, проекты — всё это живёт в Битрикс24. Инженер подключался к машине из одной программы, а задачу по этой же работе заводил в другой: руками, перенося данные глазами из окна в окно.
Одна кнопка
С этого Fleet и начался. Не с архитектуры и не со стека — с желания, чтобы работало вот так:
выбрал клиента → выбрал компьютер → «поставить задачу» → готово.
Задача создана. Клиент привязан. Карточка компьютера привязана. Проект выбран. Ничего не нужно вводить руками и ничего не нужно вспоминать.
Когда это заработало, все выдохнули.
Звучит как мелочь. Но именно из этой мелочи вырастает всё остальное: чтобы кнопка сработала, система должна знать клиентов, знать их технику, уметь связать одно с другим и иметь право писать в Битрикс24. То есть быть той самой полноценной картотекой, которую никто не хотел заполнять руками.
Значит, заполняться она должна сама.
Битрикс24 уже знал половину ответа
Первым порывом было завести свою базу клиентов. Хорошо, что удержался.
В Битрикс24 у нас уже есть клиенты, договоры, заявки, проекты и сотрудники. Завести рядом вторую картотеку — значит гарантированно получить две реальности, которые разойдутся в первый же месяц. Ровно та болезнь, от которой мы и лечились.
Поэтому решение было такое: Битрикс24 — источник правды по клиентам и по карточкам техники. Fleet не заводит своих клиентов, он читает их с портала и туда же пишет результат.
Из этого же выросло правило, которое потом сэкономило много нервов:
Битрикс24 отвечает на вопрос «кто ты», а не «что тебе можно».
Вход — через OAuth портала. Но сама по себе учётка в корпоративном портале не открывает ничего: сотрудник должен быть отдельно заведён в системе. Новый менеджер, бухгалтер, стажёр — по умолчанию видят «доступ не предоставлен».
Это не паранойя. В системе лежат пароли от инфраструктуры всех клиентов. Пускать туда каждого, у кого завелась корпоративная почта, нельзя.
Из чего собрано
| Компонент | Что делает | Стек |
|---|---|---|
fleet-api |
Бэкенд. Единственный, кто знает ключи от портала и мониторинга | Python, FastAPI, PostgreSQL |
fleet-agent |
Служба на машине клиента: отметка о жизни и инвентаризация | Go |
fleet-setup |
Мастер развёртывания, запускает инженер руками | Go |
fleet-remove |
Снятие с обслуживания | Go |
fleet-admin |
Десктопное приложение инженера | Go + Wails |
| Zabbix и Grafana | Метрики и графики | своя установка |
| RustDesk | Подключение к пользователю | своя установка |
Почему Go для всего, что ставится клиенту. Один статический бинарь без рантайма. Не нужен ни .NET нужной версии, ни Python, ни интерпретатор — ничего из того, чего может не оказаться на чужой машине и что придётся доставлять. Когда программа должна встать на тысячу компьютеров, за которыми ты не следишь лично, это решает.
Почему Wails для приложения инженера. Интерфейс пишется как веб, живёт в нативном окне, собирается в один exe. При этом язык тот же, что у агента — общий код для разбора инвентаря и работы с API не пришлось писать дважды.
Где всё это живёт. Вся инфраструктура проекта виртуализирована на Proxmox: и бэкенд с базой, и мониторинг, и сборочная машина — отдельными виртуалками. Это даёт снапшот перед каждым обновлением и возможность откатиться за секунды, если релиз оказался неудачным. Для системы, которая раскатывается на тысячу чужих машин, такая страховка на своей стороне — не роскошь.
Правила, от которых не отступали
Их было пять, и все пять держались весь проект.
1. В клиентских бинарях нет ни одного ключа. Только адрес API и токен конкретной машины. Кто-то разобрал агента — получил доступ к одной машине, которая и так его собственная.
2. В Битрикс24 пишем только при изменениях. Агент считает хэш инвентаря и, если тот совпал с прошлым, не отправляет ничего. Иначе тысяча машин превратит портал в свалку однотипных записей за неделю.
3. Приложение инженера ходит в API, а не в базу. Никаких прямых подключений к PostgreSQL с рабочих ноутбуков, которые ездят в метро.
4. Что не разрешено явно — запрещено. Права описываются разрешениями: кому, к какому клиенту, к каким типам устройств, показывать ли пароли. Стажёр получает компьютеры одного клиента и не видит его серверов вовсе.
5. Устройство, к которому нет доступа, не показывается совсем — не серой строкой с замочком. Иначе названия и инвентарные номера серверов утекают тому, кому их знать не надо, а поиск по номеру их всё равно находит.
Устройства и подключения — почему это две разные вещи
Помните те четыре кнопки? Вот здесь они превратились в архитектуру.
Изначально система задумывалась под компьютеры с агентом. Но в парке есть ещё серверы, роутеры, коммутаторы, NAS, принтеры, ИБП и веб-панели — от морды гипервизора до личного кабинета провайдера. Часть заводится автоматически, часть только руками.
Если зашить в карточку компьютера «кнопку RustDesk и кнопку VNC», через полгода упрёшься. Поэтому способ подключения — не свойство устройства, а отдельная сущность.
Есть devices — всё, что мы обслуживаем, с типом, владельцем, инвентарным
номером и местом в дереве. И есть connections — у одного устройства их
сколько угодно: rustdesk, rdp, ssh, winbox, web. У каждой свой ярлык,
адрес и учётка, пароли шифруются, выдача пароля пишется в журнал.
К серверу можно добавить RDP, веб-панель и iLO. К MikroTik — Winbox, веб-морду и SSH. Машина с агентом получает свои подключения сама при регистрации. А новый тип подключения — это строчка в конфиге лаунчера, а не переделка схемы данных.
Те самые четыре кнопки, только теперь они данные, а не код.
Агент, который обновляет сам себя
Тысяча машин — это столько, что обойти их руками ради смены версии уже нельзя. Значит, агент должен уметь обновляться сам. А раз должен — это самое опасное место во всей системе: обновление ставится с максимальными правами на каждой машине каждого клиента.
Два решения, которые считаю правильными.
Связь всегда начинает машина. Отдельного канала команд нет и не нужно: задание на обновление приезжает в ответе на очередную отметку о жизни. Наружу у клиента ничего не открыто — незачем.
Подпись проверяет агент, а не сервер. Если бы агент верил серверу на слово, один взломанный бэкенд означал бы весь парк. Поэтому приватный ключ живёт на сборочной машине и на сервер не попадает никогда, а публичная половина вшита в самого агента.
Проверок несколько, и каждая закрывает свой случай: размер и контрольная сумма ловят оборванную закачку, подпись — подмену на раздаче, пробный запуск — битую сборку, а первая успешная отметка о жизни — версию, которая встала, но не работает. Не ожила — возвращается прежняя, она лежит рядом.
Отдельно: понижение версии у нас штатная операция, а не авария. Выкладываем прежнюю версию как свежий релиз, и парк уезжает назад тем же механизмом. Поэтому версии сравниваются на «не равно», а не на «больше» — мелочь, которая экономит очень нервный вечер.
Раскатка поэтапная: сначала одна пилотная машина, потом весь парк.
Увольнение как штатная операция
Помните проблему из начала — человек уходит, и надо закрыть доступ «везде», а перечислить это «везде» никто не может?
Теперь «везде» — это одно место. Синхронизация с порталом раз в час: уволенный или отключённый в Битрикс24 сотрудник автоматически блокируется в системе. Это не про удобство, это главный практический механизм безопасности — потому что руками отозвать доступ однажды обязательно забудут.
Плюс двухфакторная аутентификация обязательна для всех, кто видит пароли. Приложение с доступами к инфраструктуре десятков компаний, защищённое одним паролем, — слишком заманчивая цель.
Самое неожиданное оказалось не в коде
Сертификат для подписи кода мы не покупали. И вот тут выяснилось, что неподписанный бинарь, который ставится службой на тысячу машин, лезет в сеть и обновляет сам себя, — это с точки зрения антивируса довольно точный портрет вредоноса.
Лечится не хитростями, а дисциплиной сборки:
- ставить только в
Program Files. Запуск из временных и пользовательских папок — самый жирный эвристический признак, одно это решение убирает большую часть срабатываний; - не паковать ни UPX, ни протекторами. Упакованный бинарь без подписи ловится почти гарантированно;
- автозапуск — только службой. Никаких ключей автозагрузки и задач планировщика с обфускацией;
- не инжектиться в чужие процессы, не прятать окна, не трогать защиту;
- не менять имя файла от версии к версии.
И главное: исключения в антивирусе делаются по пути к папке, а не по хэшу файла — тогда они переживают обновление агента. Раскатываются один раз групповой политикой, а не обходом машин.
Что не лечится совсем: RustDesk сам по себе детектится частью антивирусов как средство удалённого управления — независимо от нас. А SmartScreen срабатывает на мастер установки, если качать его браузером; обходится тем, что мастер раздаётся по сетевой шаре и не получает метку загруженного из интернета файла.
Это тот случай, когда неделя борьбы с антивирусами оказалась дороже, чем неделя написания кода.
Где всё это сейчас
Работает. Агенты под Windows и Linux, самообновление, приложение инженера, мониторинг, права, отзыв доступа при увольнении. И та самая кнопка, с которой всё началось: выбрал клиента, выбрал компьютер, поставил задачу.
Оглядываясь — главным решением было не техническое. Не Go, не FastAPI и не схема данных. Главным было признать, что инструмент, который требует усилий на заполнение, не будет заполняться. Никогда, никем, ни при какой мотивации.
Поэтому Fleet собирает про себя всё, что может собрать сам, а у человека спрашивает только то, чего машина знать не может. Похоже, это единственный способ построить справочник, которому можно верить.