Сетевик изучил Python
Сетевик изучил Python

TLDR: в этой статье я рассуждаю о подходах к организации валидации конфигураций сетевых железок и презентую свой плагин для NetBox, позволяющий легко создавать эти самые комплаенс‑тесты.

Если вы работаете с сетевой инфраструктурой средних или крупных размеров, то наверняка сталкивались с ситуацией, когда коробка настроена «слегка» неправильно. Ну т. е. прямо сейчас все работает, аварии нет, но все же проблема есть. Это может быть какое‑то неправильно настроенное резервирование, которое выстрелит в самый неподходящий момент (например, VRRP только на одном роутере), либо просто какая‑то штука, которая ухудшает эксплуатацию (на интерфейсе в сторону клиента нет дескрипшена и теперь вам придется лезть в ненавистный GUI: CRM/OSS/BSS/WTF).

Configuration compliance (или валидация конфигураций) — это процесс автоматической проверки конфигурации на соответствие некоторым правилам.

Например, чтобы исключить вышеописанные ситуации, у вас должны быть правила:

  • Один и тот же VRRP‑адрес должен быть настроен на паре устройств.

  • Каждый интерфейс должен иметь description.

Сформулировать правила — это конечно хорошо, но нужно еще научиться их проверять.

Как это чаще всего реализуется

Чаще всего в сетевом отделе ищется сотрудник, лучше других умеющий в написание скриптов, и ответственная задача валидации конфигов ложится на его плечи. В результате создается пачка скриптов (в худшем случае — один гигантский), реализующая проверку нужного набора правил на имеющемся парке оборудования, при этом описание реализованных правил записываются куда‑то отдельно во внутреннюю вики чтобы не пугать сетевиков чтением скрипта для удобства чтения. И вроде бы все хорошо, все работает, но есть проблемы. Вот некоторые из них:

  • Реализация правил (комплаенс‑тестов) в коде хранится отдельно от словесного описания этих правил. В итоге после нескольких изменений реальные правила и их описание начинают разъезжаться.

  • Код проверки правил чаще всего не отделен от кода парсинга конфигурации. В итоге правило, написанное для Cisco, невозможно применить для Juniper (или даже для Cisco другой серии) и т. п. Как только приходит новая модель условного D‑Link с кардинально отличающимся синтаксисом от предыдущей, все правила приходится переписывать под нее.

  • Добавление новых правил затруднено и завязано на автора инструмента.

  • Функционал системы жестко ограничен текущим скоупом правил. Например, все текущие правила работают в рамках одного девайса и его конфига, и если нужно добавить правило, сравнивающее 2 девайса между собой, то нужно переписать систему валидации с нуля.

Я видел несколько самописных реализаций в нескольких различных компаниях. Так или иначе главная проблема самописной системы сводилась к чудовищной неповоротливости — любые изменения правил и/или моделей поддерживаемого оборудования занимали очень много времени. А если к этому добавить еще и щепотку нашей любимой бюрократии, когда составлением правил и их реализацией занимаются два разных отдела... В общем ну их, эти системы, лучше мы конфигурацию глазами посмотрим, оно так как‑то быстрее будет.

Как сделать лучше

Вот несколько принципов, которые, по моему мнению, стоит соблюдать, чтобы построить более гибкую систему configuration compliance:

  • Максимально отделить код комплаенс‑тестов («бизнес‑логику») от всего остального, в первую очередь от парсинга конфигурации.

  • Писать сам тест максимально коротко. Чем короче тест, тем проще его прочитать и понять, что именно проверяется, какие граничные условия и т. д.

  • На уровне системы обеспечить возможность написания тестов, в которых можно сравнить конфигурацию девайса

    • с эталонными данными в инвентори. Так называемый подход desired vs operational

    • с конфигурацией другого девайса

  • Хранить словесное описание теста рядом с его кодом. Так вероятность их рассинхронизации значительно уменьшится.

  • Обеспечить возможность добавления новых комплаенс‑тестов без перезапуска всей системы. Чем легче будет добавить новое правило в систему, тем большее число инженеров может быть вовлечено в написание этих правил (взамен составления ТЗ на тыжпограммиста).

Compliance plugin for NetBox

Посмотрел я на все это и решил запилить Validity — плагин для NetBox, облегчающий написание комплаенс‑тестов. Я думаю, беглое описание плагина проще всего привести в рамках формата вопрос‑ответ.

Q: Как это вообще работает?

A: Вот упрощенный алгоритм использования:

  • создать сериализатор конфигурации с помощью TTP (похоже на Jinja2, только наоборот). Таким образом вы сможете транслировать ваш vendor specific конфиг в JSON;

  • создать комплаенс‑тест в виде Python‑выражения, которое возвращает True или False, т. е. пройден тест или нет. В коде теста также можно использовать JQ‑выражения;

  • Привязать сериализатор и тест к подмножеству девайсов;

  • Запустить расчет тестов, получить результаты.

Q: Откуда берутся файлы конфигураций?

A: Подтягиваются из git‑репозитория, который вы должны прилинковать в рамках настройки плагина. Validity не занимается сбором конфигурации непосредственно с сетевых устройств, т.к. это отдельная большая задача, не входящая в цели плагина, и к тому же многократно решенная (например, можно использовать oxidized)

Q: А пример теста можно?

A: Например, представим ситуацию: для доступа на оборудование вы используете RADIUS/TACACS, но при этом обязательно на каждом устройстве должен быть создан локальный админ, на всякий пожарный. Напишем тест, который проверяет, что этот юзер существует и хэш пароля (а значит и сам пароль) имеет строго определенное значение.

device.config["users"]["admin"]["encrypted-password"] == "363a7f2cb1273ea328d"

Код теста, конечно же, зависит от формата сериализованного конфига. В данном случае мы имеем в виду, что конфиг выглядит вот так (пишу в YAML для сокращения объема):

users:
  admin:
    encrypted-password: 363a7f2cb1273ea328d
# остальной конфиг

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

device.config["users"][
   device.get_config_context()["admin_user"]
]["encrypted-password"] == device.get_config_context()["admin_hash"]

Q: Какие еще возможности существуют при написании комплаенс‑тестов?

A:

  • Использование JQ‑выражений с помощью Python‑функций jq.all() и jq.first(). JQ позволяет очень удобно и лаконично извлекать данные из JSON‑подобных структур.

  • (Почти) Полный доступ к Django ORM. device из теста выше — это ORM‑инстанс NetBox Device, у него есть все те же самые поля. Например, IP‑адрес устройства можно получить, использовав device.primary_ip4.

  • Для сравнения пары девайсов между собой можно использовать динамические пары. Например, у вас есть набор коммутаторов, разбитых на MC‑LAG пары и вы хотите удостовериться, что конфигурация в рамках пары совпадает. Для этого можно использовать device.dynamic_pair. На данный момент динамические пары можно формировать только на основе имен устройств. Для этого у пары устройств должна быть общая часть имени, уникальная именно для этой пары. Например, имеем 4 девайса: asw01-primary, asw01-secondary, asw02-primary, asw02-secondary. Эти девайсы можно сгруппировать в динамические пары asw01-* и asw02-*. Подробнее в документации.

  • Контекст комплаенс‑теста можно расширять собственными функциями/классами. Таким образом, например, можно вынести повторяющийся код или сложное JQ‑выражение в отдельную функцию. DRY, все дела.

  • В результате исполнения теста вы получаете не только результат True/False, но и промежуточный результат (почти) каждой отдельной python‑операции по порядку. Это помогает понять, почему тест упал, например.

Q: Дак это что, Arbitrary Code Execution получается?! Опасно!

A: Код тестов исполняются NetBox worker'ом в рамках NetBox Сustom Scripts. Да, код теста исполняется в том же процессе, что и сам скрипт, никакого отдельного контейнера и прочего не создается. Код исполняется с помощью библиотеки simpleeval + добавлены некоторые дополнительные ограничения. Итоговый список ограничений:

  • это eval(), а не exec(), поэтому синтаксис ограничен по умолчанию. Например, нельзя сделать что‑то вроде import sys; sys.exit()

  • Запрещены все свойства/методы, начинающиеся с underscore (например, device._meta)

  • Запрещены опасные built‑ins вроде open() или exec()

  • Запрещены методы .delete(), .save(), .update(), .bulk_create(), .bulk_update()

  • Максимальная длина list comprehension ограничена 100 000

Конечно, это все еще запуск произвольного кода, и способы сделать что‑то плохое по‑прежнему существуют, даже не смотря на вышеуказанные ограничения. Однако, не стоит забывать, что NetBox — система для внутреннего использования, посторонние не должны иметь к ней доступ. Плюс, вы всегда можете отрегулировать, кто из пользователей может добавлять/изменять тесты при помощи системы пермишенов.

Q: Почему NetBox‑плагин, а не отдельное приложение?

A: NetBox — это Source of Truth, т. е. там по определению хранится много достоверных данных, которые можно и нужно использовать в комплаенс‑тестах. Плюс, NetBox предоставляет плагинам почти готовый UI, а автор никогда не питал сильной любви к фронтенду/верстке.

Q: Зачем вообще нужен configuration compliance, если скоро сети захватит тотальная автоматизация, и все будет настраиваться само?

A: Ну пока ж еще не захватила, а проверять конфиг на ошибки будет полезно уже прямо сегодня. И вообще, если инженеры вашей компании не ходят в CLI — можете кинуть в меня камень (надеюсь, в статью не придет слишком много сотрудников гуглов и прочих амазонов:)).

Заключение

Чем больше ваша сеть и чем больше сотрудников занимаются ее эксплуатацией, тем выше шанс совершить незаметную ошибку, которая в дальнейшем может привести к неприятным последствиям. Для исключения подобных ситуаций и существует configuration compliance. Кроме того, compliance‑проверки можно использовать как часть пайплайна автоматизации. Например: пришел запрос на конфигурацию → запустили комплаенс тесты → комплаенс пройден, ошибок нет, можно конфигурировать.

При этом важно сделать систему комплаенс‑проверок достаточно гибкой, чтобы однажды написанные правила не стали дамокловым мечом, висящим над любыми попытками изменить дизайн или принципы конфигурирования вашей сети. Кроме того, чем проще вы организуете написание и добавление новых правил, тем больше людей из числа сетевых инженеров будут реализовывать правила самостоятельно вместо «составления ТЗ на программиста» и прочих эффективнейших коммуникаций.

Ссылки на плагин

Github | Документация

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