← Все заметки
Мониторинг и безопасность

RouterOS и NetBox: безопасный read-only IPAM drift-аудит через SSH и API

Практика автоматической сверки VLAN, Prefix и IPv4 интерфейсов MikroTik RouterOS 7 с NetBox 4.7: SSH, Python, read-only API, контроль неполных данных и исключений IPsec.

Статус: актуальноОпубликовано: 2026-10-21Обновлено: 2026-10-21

Проверено на: MikroTik RouterOS 7 · NetBox 4.7.3 · Python 3.11 · OpenSSH · AlmaLinux 9 · systemd

Задача: знать не только что записано в IPAM, но и что работает на маршрутизаторе

NetBox удобен как источник описания сетевой инфраструктуры: устройств, интерфейсов, VLAN, Prefix и назначенных IP-адресов. Однако наличие записи в IPAM не гарантирует, что конфигурация маршрутизатора совпадает с документацией. Изменение VLAN, перенос IP на другой интерфейс или новая динамическая адресация могут остаться незамеченными.

В эксплуатационном сценарии реализован read-only аудит MikroTik RouterOS 7 ↔ NetBox 4.7. Система обнаруживает отклонения, но не пытается автоматически исправлять конфигурацию. Сначала был проверен ручной сбор каждого источника, затем строгое сравнение и отдельно — способ классифицировать динамический адрес IKEv2/IPsec.

Все адреса, имена, VLAN ID и численные значения в примерах ниже вымышлены. Реальные идентификаторы площадок, токены, SSH-ключи, схемы VPN и конфигурации устройств не публикуются.

Код сравнения и адаптеры: NetBox Inventory Toolkit, PR с RouterOS IPAM Audit. В указанном PR находится библиотечная часть; локальные эксплуатационные обвязки не следует считать опубликованным универсальным установщиком.

01 / Архитектура и границы доверия

MikroTik RouterOS 7                           NetBox 4.7
  SSH, dedicated read-only account             HTTPS GET, token v2 (write disabled)
  pinned SSH host key                          trusted private CA
           |                                          |
           v                                          v
  RouterOSSSHReader                            NetBoxReader
    /interface vlan                              Device / Interface
    /ip address                                  VLAN / Prefix / IPAddress
           |                                          |
           +----------------+-------------------------+
                            v
                   Python snapshot normalize
                            v
                    IPAM compare (read-only)
                            |
                 CLEAN / DRIFT / ERROR

SSH-аудитор использует отдельную учётную запись RouterOS с минимальной группой ssh,read и ограничением по IP-адресу узла мониторинга. В отличие от группы со стандартными расширенными правами, такая запись не должна иметь write, policy, sensitive и возможности вносить изменения. Клиент использует выделенный приватный ключ, IdentitiesOnly=yes, BatchMode=yes, StrictHostKeyChecking=yes, заранее проверенный known_hosts и отключённое перенаправление SSH-агента.

Для NetBox применяется отдельная техническая учётная запись с правами только просмотра Device, Interface, VLAN, Prefix и IPAddress. API Token v2 создаётся с Write enabled = off; по возможности ограничивается исходным IP и сроком действия. HTTPS проверяется по доверенному CA, без verify=False или curl -k.

02 / Как именно собираются данные RouterOS

Не требуется открывать RouterOS www-ssl или включать HTTP REST API. Два заранее разрешённых SSH-запроса сериализуют вывод RouterOS в JSON:

:put [:serialize to=json value=[/interface vlan print as-value] options=json.no-string-conversion]
:put [:serialize to=json value=[/ip address print as-value] options=json.no-string-conversion]

В routeros/ssh_source.py эти команды жёстко заданы. Клиент не принимает произвольную команду от вызывающего кода. Ответ проверяется как массив JSON; сетевые ошибки, некорректный формат и чрезмерный объём данных приводят к ERROR.

Из RouterOS извлекаются VLAN ID, имя и родительский интерфейс VLAN, флаг disabled, а также CIDR-адрес, интерфейс и флаги dynamic/disabled IPv4. Сырые ответы не нужно выводить в Prometheus, логи или публичный CI: они раскрывают внутреннюю топологию.

03 / Читаем NetBox через HTTPS GET

Адаптер NetBox проверяет идентичность маршрутизатора по имени, площадке и роли router, после чего читает интерфейсы, VLAN-группу, связанные префиксы и назначенные интерфейсам IP-адреса. Доступны только заранее разрешённые GET-endpoints:

/api/dcim/devices/
/api/dcim/interfaces/
/api/ipam/vlans/
/api/ipam/prefixes/
/api/ipam/ip-addresses/

Запросы учитывают пагинацию, а итоговые выборки проверяются на полноту. Это принципиально: ошибка API или отсутствие части объектов не должны случайно превращаться в «нулевое расхождение».

04 / Что сравнивается, а что пока не сравнивается

Аудитор проверяет не только количество объектов:

Область Инвариант
VLAN Набор VLAN ID RouterOS соответствует набору VLAN ID выбранной группы NetBox
Prefix Для каждого VLAN сравнивается подсеть активного статического адреса VLAN-интерфейса с Prefix в NetBox
IPv4 Точное совпадение пар (CIDR, interface) для активных статических адресов
Качество данных Повторяющиеся записи, неверные типы, повреждённый или устаревший snapshot не принимаются
Полнота Количество объектов сверяется с заранее согласованным baseline
Динамика Неожиданные динамические и отключённые адреса требуют рассмотрения

При этом действующий алгоритм не сравнивает все поля RouterOS, например bridge VLAN filtering, PVID, trunk/access-порты, маршруты, VRF, DHCP или фактическое состояние VPN. Имя родительского интерфейса VLAN валидируется, но не является самостоятельным инвариантом сравнения с NetBox. Модель рассчитана на один ожидаемый Prefix для каждого выбранного VLAN.

Важная деталь классификации: расхождение с утверждённым количеством VLAN или статических IP — ERROR (недостаточная достоверность baseline), а не DRIFT. Точные несовпадения записей при корректной полноте дают DRIFT.

05 / Пример вызова Python API

Ниже — адаптируемый фрагмент, а не скрипт, который можно применять без настройки. Используются исключительно адреса из диапазонов документации. Код предполагает наличие модулей из PR и уже подготовленных приватных файлов ключа, токена, known_hosts и сертификата CA.

from pathlib import Path
from routeros.ssh_source import RouterOSSSHReader
from routeros.ipam_sources import (
    NetBoxReader, routeros_snapshot, netbox_snapshot,
)
from routeros.ipam_audit import compare

router = RouterOSSSHReader(
    host="192.0.2.1", username="audit",
    identity_file="/etc/ipam-audit/routeros.key",
    known_hosts_file="/etc/ipam-audit/known_hosts",
)
ros = routeros_snapshot(router)

netbox = NetBoxReader(
    origin="https://netbox.example.net",
    token=Path("/etc/ipam-audit/netbox.token").read_text().strip(),
    ca_file="/etc/ipam-audit/trusted-ca.pem",
)
nb = netbox_snapshot(
    netbox, router_name="edge-router-01",
    site_slug="office", vlan_group_slug="office-vlans",
)

result = compare(
    ros, nb,
    expected_vlans=8,
    expected_static_addresses=12,
)
print(result["status"], result["differences"], result["categories"])

8 и 12 — условные значения: их заменяют утверждёнными параметрами конкретной инфраструктуры. Секреты запрещено помещать в Git, командную строку и журналы. Любая ошибка сбора или проверки схемы должна завершаться кодом 2, а не 0.

06 / Динамический IPsec Mode Config: почему нельзя игнорировать весь dynamic

В реальной проверке был обнаружен один dynamic=true IPv4, добавленный в runtime RouterOS. Строгий аудит корректно сообщал DRIFT с категорией dynamic_review_required. При расследовании выяснилось, что такой адрес может быть связан с клиентским IKEv2 Mode Config, а не с DHCP.

Перед согласованием исключения оператор независимо проверяет идентичность IPsec peer/identity, активную Phase 2, динамическую policy с соответствующим host-IP и динамический NAT ipsec mode-config с корректной connection mark. IP-адрес и интерфейс фиксируются как точечное рассмотренное исключение. Если они меняются или исчезают, аудит снова покажет отклонение.

result = compare(
    ros, nb,
    expected_vlans=8,
    expected_static_addresses=12,
    approved_dynamic=(("198.51.100.10/24", "wan1"),),
)

Это условный пример после ручного подтверждения. Текущие snapshots не содержат достаточных данных IPsec policy/NAT, поэтому сам Python-аудитор пока не перепроверяет происхождение адреса при каждом запуске. Исключение нельзя трактовать как автоматическую проверку IPsec, и не следует одобрять все dynamic подряд.

07 / Интерпретация итогов

Код Статус Что означает
0 CLEAN Данные полны; сравниваемые инварианты совпали; рассмотренные исключения учтены
1 DRIFT Есть конкретные расхождения или неожиданные runtime-адреса
2 ERROR Сбой связи, повреждённая/устаревшая выборка, несогласованная полнота или ошибка проверки

Положительный результат означает совпадение в пределах описанной модели, а не полное равенство конфигураций RouterOS и NetBox. Система не меняет маршрутизатор и не выполняет автосинхронизацию IPAM.

Результат и продолжение

Read-only сверка была успешно пройдена вручную, затем вынесена в локальную команду аудитора. Следующий уровень — регулярное выполнение и мониторинг результата через Prometheus. Его разбираем отдельно: IPAM-аудит в Prometheus: Textfile Collector, systemd и Alertmanager.

Практический вывод: надёжный IPAM drift detection начинается не с автоматического исправления сетевой конфигурации, а с проверяемого baseline, узких прав на чтение и явного разделения CLEAN, DRIFT и ERROR.