Однажды моей команде довелось организовывать несложную кустовую схему шифрования для компании, у которой было более 2,5 тысяч офисов продаж и около ста региональных центров. Всё техническое описание решения легко излагалось в таблице Excel размером 2 800 строк на 25 столбцов, но где было взять столько эникейщиков, которые бы настроили оборудование без ошибок?
Если бы эта история была про оборудование, не поддерживающее автоматизацию ни в каком формате, именно так нам и пришлось бы поступить: развернуть некий стенд и найти десяток студентов для тестирования корректности настроек. Мы же имели дело с криптошлюзами S-Terra, и в нашем случае всё упиралось в знание Ubuntu и автоматизации по протоколу SSH. Автоматизировать нужно было два отдельных момента: загрузку конфигурации Cisco-like и инициализацию устройств. Для этого мы решили использовать систему управления конфигурациями Ansible.
В статье я расскажу, как мы пытались скрестить Ansible с криптошлюзами S-Terra, и что из этого вышло. Надеюсь, наш опыт будет полезным тем, кто возьмётся за подобный проект на базе решения S-Terra и будет искать способ ускорить конфигурирование оборудования и свести на нет человеческий фактор.
Как мы выбирали средство автоматизации
Одним из вариантов, которые мы рассматривали, было написать собственные скрипты на основе инструментов Netmiko или Paramiko с использованием модуля TextFSM. С одной стороны, это могла быть хорошая возможность отработать на практике знания, полученные при прочтении книги Наташи Самойленко «Python для сетевых инженеров». С другой, несколько лет назад я участвовал в подобной активности и помню, как намучился тогда с обработкой вывода, парсингом и подключением по SSH. Так что в итоге мы признали этот вариант решения слишком долгим и сложным, а к тому же и плохо масштабируемым.
Альтернативным вариантом было использовать бесплатное решение Ansible. В его пользу говорило наличие различных модулей под Cisco и Linux, а также огромная база знаний. Удобным было ещё и то, что в этом случае мы могли не вливать полный файл конфигурации, а отслеживать этапы: настройка интерфейсов, маршрутизации, Crypto, мониторинга и так далее. В итоге этот вариант и выбрали.
Поскольку у Ansible не было готовых модулей под продукты S-Terra, мы решили декомпозировать задачу: а именно разбить playbook на play Cisco-like и play Linux.
Как мы автоматизировали загрузку конфигурации
Шаг 1. Формирование конфигурации
Для начала мы попробовали отдельные task для выполнения отдельных конфигураций, а позже play для выполнения конфигураций в оболочке Cisco-like. Когда мы обкатали первую заливку конфигурации, то пришли к выводу, что наиболее удобно будет делать файлы переменных для каждого устройства в hostvars. Конечно, можно было вывести пароли, переменные мониторинга и серверов инфраструктуры в groupvars, но тогда мы потеряли бы гибкость, а заказчику во многом нужны были индивидуальные настройки. В итоге файл переменных для каждой точки стал выглядеть примерно так:
#
user_sudo: 'root'
password_sudo: 'ххххх'
user_cisco: 'cscons'
password_cisco: 'csp'
#========================
user_cisco_new: 'хххххх'
password_cisco_new: 'хххххххх'
UpLinkBase: 'ethX'
UpLink: 'ethX.100'
UpLinkCisco: 'GigabitEthernet0/X.700'
MGMTLink: 'ethX.300'
MGMTLinkCisco: 'GigabitEthernet0/X.701'
DownLink: 'ethY'
DownLink0: 'ethZ'
DownLinkCisco: 'GigabitEthernet0/Y'
DownLinkCiscoZ: 'GigabitEthernet0/Z'
InterfacesCisco:
#
- name: 'GigabitEthernet0/Z'
ip: '10.10.10.10'
mask: 'XXX.XXX.XXX.XXX'
#
- name: 'GigabitEthernet0/X.700'
ip: '10.10.10.10'
mask: 'XXX.XXX.XXX.XXX'
#
- name: 'GigabitEthernet0/X.701'
ip: '10.10.10.10'
mask: 'XXX.XXX.XXX.XXX'
#
InterfacesCiscoMTU:
- name: 'GigabitEthernet0/Z'
MTU: '1600'
- name: 'GigabitEthernet0/X'
MTU: '1700'
- name: 'GigabitEthernet0/X.700'
MTU: '1700'
- name: 'GigabitEthernet0/X.701'
MTU: '1700'
#
#SNMP
SNMP_IP: 'XXX.XXX.XXX.XXX'
#Rsyslog
SyslogServerIP: 'XXX.XXX.XXX.XXX'
#NTP
NTP_server: 'XXX.XXX.XXX.XXX'
#
route:
- prefix: 0.0.0.0
mask: 0.0.0.0
next_hop: 'XXX.XXX.XXX.XXX'
- prefix: ZZZ.ZZZ.ZZZ.ZZZ
mask: ZZZ.ZZZ.ZZZ.ZZZ
next_hop: 'XXX.XXX.XXX.XXX'
#
ACLS:
- name: 'ACL_XXXX_X'
content:
- ipsrc: 'XXX.XXX.XXX.XXX'
wildmasksrc: 'XXX.XXX.XXX.XXX'
ipdst: 'XXX.XXX.XXX.XXX'
wildmaskdst: 'XXX.XXX.XXX.XXX'
#
MAPS:
- number: '1'
acl: 'ACL_XXXXX_X'
peer: 'XXX.XXX.XXX.XXX'
#
CryptoInterfaces:
- GigabitEthernet0/X.700
Каждая role использовала свою группу переменных (MAPS, ACL, route и т.д.), которая представляла собой отдельный dict и/или list в формате json. Часть переменных, например, Interfaces, использовались на всём протяжении playbook.
Шаг 2. Подготовка конфигурационных файлов
В качестве основного проектного документа мы приняли файл с расширением .xlsx. Для того, чтобы не перебирать каждый раз ячейки таблицы и не тратить на это огромное количество времени, мы разбили скрипты на три части.
Первая часть выгружала нужную информацию из .xlsx в SQL. Это было необходимо для получения выгрузки данных из строк по определенным фильтрам. Про модуль pandas, который помог бы решить эту задачу более простым путём, на момент создания скрипта я не знал.
Вторая часть скриптов проверяла SQL на ошибки: отсутствие пересечений, корректность peer и другие. Всего у нас получилось семь функций проверки. Как говорится, не ошибается тот, кто ничего не делает, и тут это правило работало на 100%.
Третья часть включала в себя основной скрипт формирования конфигурации и скрипт для формирования изменений. Первый представлял собой группу функций, формирующих переменные для print(template.format(var1,….)), которые в свою очередь являются dict и/или list в формате json в конфигурации для Ansible. Второй мы сделали, так как со временем у нас появилась потребность парсинга (textFSM) файлов Cisco-like конфига уже установленных устройств S-Terra и формирования изменений (diff_conf).
Четыре булыжника, о которые мы споткнулись по пути
Вроде всё сделали красиво, но тут нас ждал первый булыжник — уровни вложенности переменных в Ansible. Оказалось, что при использовании модуля cisco.ios.ios_acls сделать перебор переменных аналогично [списку [списков]] не получится. Пролистав N страниц в интернете и протестировав несколько вариантов, мы пришли к использованию конструкции loop + subelements.
- name: acl_config
cisco.ios.ios_acls:
config:
- afi: ipv4
acls:
- name: '{{item.0.name}}'
acl_type: extended
aces:
- grant: permit
protocol:
ip
source:
address: '{{item.1.ipsrc}}'
wildcard_bits: '{{item.1.wildmasksrc}}'
destination:
address: '{{item.1.ipdst}}'
wildcard_bits: '{{item.1.wildmaskdst}}'
loop: "{{ACLS|subelements('content')}}"
Я думаю, что боль использования модуля cisco.ios.ios_acls понимает любой сетевой инженер, который с ним работал. На мой взгляд, так неудобно сделать мог только человек, который никогда это не настраивал. Наши ощущения можно сравнить со случаем, когда решение по маршрутизации трафика в организации реализуется не на основе динамической маршрутизации, а на основе запуска файлов с расширением .bat, меняющих def route на host: администратор сделал, а на сетевом инженере сэкономили.
К сожалению, ход с конструкцией loop + subelements не решил проблему с использованием правил для /32 сетей, так как в Cisco-like их надо вводить как host, а как запустить when по внутреннему списку, я так и не придумал. Тогда я впервые вспомнил фразу из одной статьи «Ansible — это не язык программирования, это инструмент автоматизации». Пришлось потом это реализовывать отдельной role.
Второй булыжник был в том, что модули Ansible работали не совсем так, как нам хотелось бы это видеть. Модуль sudo так и не получилось запустить на Linux S-Terra, модули ACL и cisco.ios.ios_config не могли в цикле анализировать конфигурацию, удалять лишнее и дополнять недостающее (описанная выше ситуация с host). Так мы пришли к выводу, что всё нужно тестировать, причём очень внимательно и скрупулёзно.
Третий булыжник ждал нас в ситуации перехода с одной учётки на другую, когда настроили что-то под root в Linux и теперь то же самое надо сделать под cscons в Cisco-like, а для этого нужно поменять переменные внутри одного playbook. Тут, конечно, всё оказалось гораздо проще, спасибо статье, из которой я вообще очень много почерпнул для общего понимания работы системы. После второго прочтения я всё-таки понял, почему playbook, а не play. Где же здесь book? Ведь переменные используются в пределах одного play, и если их нужно переопросить для SSH-сессии, то есть снова забрать значения из vars, то необходимо сделать новый play. На самом деле до сих пор не понимаю, где и как настраиваются параметры SSH-сессии и почему нельзя было сделать в ansible.conf список параметров, аналогичный Netmiko, но это я просто чего-то не понимаю.
Четвертый булыжник был уже на этапе настройки. При заливке S-Terra с большим количеством MAP, задаваемых в используемом нами формате, Ansible лавинообразно начинал потреблять RAM. Как он умудрился 50-килобайтовый текстовый файлик конфига превратить в 1Гбайт RAM, я не знаю. Но это был очень неожиданный сюрприз. Пришлось выделять 8Гбайт памяти. Наверное, это тоже можно было как-то решить, но я не придумал, что именно надо вбить в Google для поиска решения проблемы.
Впрочем, на этом булыжники закончились и началось довольно приятное сосуществование: конфигурации получились понятными и читаемыми, а все названия стандартизованными. Всплывающие ошибки были достаточно читаемы и связаны с ошибками скрипта создания конфигураций. К тому же мы сэкономили кучу времени и нервов при загрузке custom-скриптов SNMP, ведь за этим бы последовало многочисленное копирование файлов, запуск большого количества скриптов, выполнение проверок и конфигурирование cron. В итоге мы подготовили к установке почти три тысячи устройств.
Как мы автоматизировали инициализацию устройств
Когда я слышу слово «инициализация», мне тут же вспоминается любимый лозунг безопасников «Инженер должен … страдать». Потому как на ПАК серии ниже 3 000 нужно руками вбивать случайную последовательность для генерации Гаммы. Поклонники популярного в начале 2000-ых способа обучения слепому набору текста «СОЛО на клавиатуре» были бы в восторге от такого решения.
Что мы сделали: на S-Terra G-7000 создали нужное количество gamma и загрузили их на SCP-сервер. Далее получили лицензии в текстовом формате от вендора S-Terra и написали скрипт на Python для управления криптошлюзами через COM-порт.
Тут мы столкнулись только с одной проблемой — статикой при переключении USB-COM конвертера. Из-за неё у нас появлялись дополнительные символы при вводе пароля учётки Administrator.
Что по итогам
В процессе запуска на объектах мы также сформировали группу plays и roles для сбора данных о состоянии устройств S-Terra, проверки прохождения пакетов по шифрованным каналам, доступности инфраструктуры заказчика, сбора lic параметров и S/N. Такое управление сетью оказалось довольно удобным.
Если изначально за день нам удавалось настроить два-четыре устройства, и при этом было достаточно много ошибок, то по итогам автоматизации мы ускорили настройку конфигурации криптошлюзов S-Terra серий G-100, G-1000 и G-2000 до 30 устройств в день, а серий G-3000 и G-7000 — до 15 устройств в день. В случае с последними основное ограничение было только в распаковывании больших коробок и настройке «Аккорда».
Автор: Олег Рябикин, руководитель группы сетевой безопасности «Solar Интеграция» компании «Ростелеком-Солар»
Комментарии (5)
DmitryShm
24.03.2022 13:09+2Такое сочетание Ansible и шлюзов - иниересное решение. Но есть вопросы, их минимум два.
1) Почему не воспользовались С-Терра КП (не подождали новой версии, где часть описываемых задач была бы решена) - вам бы наверняка пошли навстречу и дали альфа-версию.
2) При аттестации объекта информатизации не было ли вопросов в части инициализации гамм предлагаемым вами способом? Ручной ввод там довольно-таки обусловлен.
bird1833
24.03.2022 13:23Каждый кто работал с С-террой КП знает что она не экономит, а отнимает время. Исключение генерация пакетов клиентов.
Документации на API КП нету.
Через Ansible большинство проблем можно решить разными путями.
Иногда надо поставить себя на место интегратора и понять что конечному заказчику абсолютно все равно какими путями он будет решать ту или иную задачу.
Сюда же относятся истории некоторых отечественных вендоров про "запрещено регулятором", только этот регулятор не фсб, а то как видят это отделы сертификации внутри...
Искренне надеюсь что сертификация как процесс отойдет на задний план, а вендоров скорее заставят доказывать процент заимствования исходного кода.
jktu100
24.03.2022 13:27+2На счет Гамм вопрос интересный. Но т.к. они индивидуальные проблем не вижу. тот же самый аппаратный способ как на 7000 и 3000.
Sterra КП не резервируется и не является целевой схемой управления, у меня даже письмо где-то есть от S-terra. Кроме того Автоматизация кликов мышкой, это не то чего мы пытались добиться.
bird1833
Спасибо за пост! Поскольку информации по автоматизации отечественных криптошлюзов откровенно мало, считаю что подобные наработки надо выкладывать на какой то общедоступный репозиторий.
P.S для загрузки ipsec конфигов сгенерированных через jinja2 можно воспользоваться таким фокусом, это спасло бы вас от 3го булыжника, думаю что и от 4го тоже... но стоит порой консультироваться с вендором, а не пытаться в гараже сделать звезду смерти.
Зачем нужен был sql? Чем вас не устроило динамическое инвентори из xlsx?
jktu100
Полностью с Вами согласен.
Я считаю. что это должен сделать Вендор. Мы с ними достаточно плотно и дружно работаем и конечно сможем пердоставить или загрузить на GitLab если будет такая потребность.
Хотя по сути это все работа с Cisco и Linux.
SQL был нужен как фильтрация (отбору группы строк по содержанию ячейки) по содержанию ячеек и не более. Решений целая куча, просто выбрали именно это.