Модуль 6. Управление политиками ALD Pro. Часть вторая. Групповые политики.
Введение
В этом модуле мы обсудим, что такое групповые политики и как они позволяют экономить время при выполнении задач администрирования большой ИТ-инфраструктуры.
Мы сравним техническую реализацию политик MS AD с политиками FreeIPA и ALD Pro, посмотрим, как создаются дополнительные параметры, с помощью которых можно организовать централизованное управление настройками любых корпоративных приложений.
После изучения материалов этого модуля вы больше никогда не будете «администрировать ногами» то, что можно настроить централизованно через групповые политики.
Концепция групповых политик
Как известно, уникальные продукты никому не нужны, а у всех востребованных решений быстро появляются аналоги, поэтому, создавая политики, разработчики FreeIPA и ALD Pro ориентировались в первую очередь на передовой опыт Microsoft.
Для новых администраторов слово «политика» может показаться странным, т.к. обычно оно ассоциируется с чем-то серьезным про менеджмент на уровне государства, а в английском языке это слово означает просто «правила», например, «Правила поведения учащихся» у них называются «Школьной политикой» (англ. School policies).
Таким образом, «политики» в Windows — это правила, в соответствии с которыми отдельный компьютер выполняет настройку рабочей среды, а «групповыми» они называются потому, что мы можем группировать эти правила в так называемые объекты. Согласитесь, если бы технология называлась «сгруппированные правила», то было бы на порядок проще, хотя и не так красиво.
В домене Active Directory объекты групповой политики можно назначить на домены, сайты и структурные подразделения, настраивая типовым образом окружение для сотен и даже тысяч пользователей и компьютеров сразу. Это позволяет многократно снижать расходы на администрирование ИТ-инфраструктуры, чем и объясняется популярность групповых политик.
В настоящий момент сложно себе представить предприятие, где было бы более тысячи компьютеров и не применялись бы групповые политики. Поэтому навыки управления групповыми политиками являются обязательным минимумом для администратора домена уровня Senior.
Групповые политики
Групповые политики Windows
Первые политики появились в Windows еще в 95-й версии, и создать их можно было с помощью редактора Poledit. Данная операционная система по умолчанию не предоставляла безопасного окружения (пользователи могли входить в систему без паролей, менять многие системные настройки и вообще ни в чем себе не отказывать), поэтому для устранения указанной проблемы и был создан этот дополнительный инструмент.
В дальнейшем технологию политик переняли в NT4, и окончательно она была переосмыслена в Windows 2000 для Active Directory. С тех пор технология поддерживается всеми операционными системами и за прошедшую четверть века нисколько не потеряла своей актуальности.
Локальные политики
Политики бывают локальными и доменными. Локальные политики появились еще в NT 4.0 и представляют собой просто папки с файлами, внутри которых описаны значения параметров:
Windows\System32\GroupPolicy\Machine— настройки компьютера;Windows\System32\GroupPolicy\User— настройки пользователя;Windows\System32\GroupPolicyUsers— настройки для конкретных пользователей и групп пользователей, если используются множественные локальные групповые политики (англ. Multiple Local Group Policy Objects, MLGPO). Данный механизм появился с Windows Vista.
Параметры хранятся в файлах Registry.pol (от англ. policy), максимальный размер этого файла c Windows 2012 увеличен до 100МБ, но это с большим запасом, т.к. до этого хватало и 64 Кб. Файлы Registry.pol содержат бинарные данные, но так как в них много строк unicode, в них можно «заглянуть» даже простым блокнотом.
Первые 4 байта (PReg) определяют сигнатуру, а следующие за ними 4 байта определяют версию. Версия увеличивается на единицу при каждом изменении GPO, и это значение необходимо для сверки данных, находящихся в LDAP-каталоге, локальном кэше и общей сетевой папке. После заголовка идет тело документа со значениями параметров, которые представляют собой просто разделы реестра Windows, которые необходимо доставить и установить на клиенте, поэтому файл называется реестром (англ. Registry). Например, это может быть параметр со ссылкой на файл для установки обоев рабочего стола.
Для построения пользовательского интерфейса редактор групповых политик использует так называемые административные шаблоны, которые представляют собой специальные XML-файлы *.admx. Если на компьютере установлены инструменты администрирования групповых политик, то эти файлы будут находиться в папке C:\Windows\PolicyDefinitions\, но для удобства централизованного доступа к административным шаблонам метафайлы можно скопировать в каталог SYSVOL по адресу: \win.company.lan\SYSVOL\win.company.lan\Policies\PolicyDefinitions. В этом случае интерфейс будет открываться чуть дольше, но зато одинаково на всех компьютерах.
Разработчики стороннего программного обеспечения могут создавать свои собственные административные шаблоны групповых политик для использования штатного механизма централизованного конфигурирования через изменение параметров реестра Windows. Например, так поступают разработчики MS Office, Adobe Reader, Google Chrome и др.
За применение групповых политик на серверах и рабочих станциях, начиная с Windows Vista, отвечает отдельная служба «Клиент групповых политик» (от англ.Group Policy Service Client, GPSVC), ранее эту задачу возлагали на WinLogon. Если клиентская служба групповой политики будет остановлена, то настройки применяться не будут, поэтому в целях безопасности права доступа по умолчанию настроены таким образом, что даже администратору запрещено ее останавливать, см. рис. 313. Текущие права доступа вы можете посмотреть командой sc.exe sdshow gpsvc.
рис. 313 Действия по управлению службой Group Policy Client, доступные администратору
Таким образом, локальная политика включает следующие компоненты: шаблон локальной групповой политики со значением параметров и административные шаблоны для их редактирования. Взаимосвязь компонентов показана на рис. рис. 314.
рис. 314 Компоненты локальной политики Windows
Доменные групповые политики
Групповые политики появились в Windows 2000 с выходом Active Directory и представляют собой множество правил для настройки рабочего окружения, которые группируются в так называемые объекты групповой политики (от англ. Group Policy Object, GPO), см. рис. 315.
рис. 315 Объект групповой политики Default Domain Policy
Как можно заметить, параметры представлены в двух секциях: настройки компьютера (англ. Computer Configuration) и настройки пользователя (англ. User Configuration), см. рис. 316.
рис. 316 Параметры пользователя и компьютера
Для локальных политик такое деление может показаться условным, поскольку параметры из обеих секций применяются в любом случае, и нужно только не забывать, что параметры компьютера могут переопределить значения аналогичных параметров из секции пользователя.
В случае доменных групповых политик вы должны понимать, что сотрудник может сесть за компьютер из другого структурного подразделения, и тогда пользователь и компьютер будут попадать в область действия разных GPO. Компьютер сначала отберет все GPO, в область действия которых попадает текущий пользователь, и применит параметры из секции пользователя. Затем он отберет все GPO, в область действия которых попадает текущий компьютер, и применит параметры из секции компьютера.
Каждый объект групповой политики включает в себя два компонента:
Контейнер групповой политики (Group Policy Container, GPC) — это запись в LDAP-каталоге, которая определяет имя объекта, версию (порядковый номер изменений), ссылку на шаблон групповой политики gPCFileSysPath на диске и т.д.
Например, контейнер GPO «Default Domain Policy» в домене win.company.lan можно найти в записи, см. рис. 317:
CN={31B2F340-...FB984F9},CN=Policies,CN=System,DC=win,DC=company,DC=localШаблон групповой политики (Group Policy Templates, GPT) — это папка на сетевом диске, в которой хранятся значения параметров по аналогии с папкой GroupPolicy локальных политик. Например, шаблон GPO «Default Domain Policy» в домене win.company.lan можно найти в
\win.company.local\SYSVOL\win.company.local\Policies{31B2F340-...FB984F9}, см. рис. 317.
рис. 317 Контейнер групповой политики Default Domain Policy
Примечание
Default Domain Policy:
{31B2F340-016D-11D2-945F-00C04FB984F9}Default Domain Controller Policy:
{6AC1786C-016F-11D2-945F-00C04FB984F9}
рис. 318 Шаблон групповой политики Default Domain Policy
При назначении объекта групповой политики на структурное подразделение в записи LDAP-каталога, соответствующей этому подразделению, создается атрибут gPLink, который является ссылкой на контейнер GPO, см. рис. 319.
Например, в свойствах домена win.company.lan есть gPLink со значением: [LDAP://CN={31B2F340-..B984F9},CN=Policies,CN=System,DC=win,DC=company,DC=lan;0]
рис. 319 Ссылка на контейнер объекта групповой политики Default Domain Policy
Таким образом, доменная групповая политика включает следующие компоненты: контейнер групповой политики, шаблон групповой политики, и административные шаблоны для редактирования параметров. Их взаимосвязь показана на рис. 320.
рис. 320 Компоненты доменной групповой политики Active Directory
Область действия групповых политик, назначенных на домен, сайт или структурное подразделение можно сузить с помощью WMI-фильтров (Windows Management Instrumentation), которые позволяют задать дополнительные условия отбора целевых объектов на SQL-подобном языке WQL (WMI Query Language). Обычно эта технология используется в ситуациях, когда пользователи и компьютеры находятся в общем списке, а не в выделенном подразделении. Этот механизм крайне удобен, если нужно применить политику в зависимости от версии ОС, ее сетевых настроек, наличия определенного установленного ПО и других условий.
Кроме WMI-фильтров область действия политик можно сужать еще и через права доступа. Загрузка параметров, назначенных на компьютер, осуществляется из-под учетной записи компьютера, а загрузка параметров, назначенных на пользователя, соответственно, из-под учетной записи пользователя. Поэтому, если у компьютера/пользователя не будет соответствующих прав на чтение GPO, то служба GPSVC не сможет получить параметры объекта, и они не будут применены в системе. Права доступа назначаются через делегирование с помощью списков доступа (англ. ACL, Access Control List).
Когда мы изменяем права доступа на вкладке делегирования или через фильтры безопасности (англ. security filter), консоль управления выставляет соответствующие ACL в LDAP-каталоге и на сетевом диске. Набор привилегий в каталоге и на диске разный, но это не мешает достигнуть необходимого результата. Администратор может делегировать не только права на чтение, но и права на редактирование GPO, что дает гибкость в вопросах администрирования.
В классической клиент-серверной архитектуре доставка изменений до клиента может быть организована тремя способами:
Метод опроса (англ. pull) — это когда клиент периодически опрашивает сервер о наличии каких-либо изменений. Эта модель основана на классическом подходе запрос/ответ (англ. request/response).
Метод проталкивания (англ. push) — это когда клиент и сервер меняются местами, и уже сервер связывается с клиентом, чтобы передать ему какие-либо изменения.
В этом случае сервер должен знать IP-адрес клиента и иметь к нему доступ по сети, что невозможно при размещении клиентов за NAT-шлюзом.
Метод длинных опросов (англ. long poll) — это когда клиент подключается к серверу, после чего они продолжают удерживать соединение, чтобы у сервера была возможность оперативно передавать клиенту новые сообщения.
Эта модель основана на веб-сокетах (англ. WebSocket), и ее используют, например, мессенджеры, такие как Telegram.
Учитывая, что в домене могут быть сотни серверов, а рабочие станции могут длительное время находиться в выключенном состоянии или вообще эксплуатироваться вне офиса, метод опроса видится наиболее экономичным способом доставки изменений до клиента. Поэтому в MS AD ответственность за извлечение информации о групповых политиках из домена возлагается на клиента, а точнее на его службу GPSVC, которая выполняет процедуру опроса с периодичностью один раз в 90 минут.
Однако во избежание большой нагрузки на контроллеры домена в начале рабочего дня, когда все сотрудники приходят в офис и включают свои рабочие станции, первое применение параметров групповой политики сразу после загрузки компьютера выполняется из кэша, а далее служба выбирает себе случайным образом момент времени для обновления параметров.
Информацию об участии пользователя/компьютера в организационной структуре, релевантные объекты групповой политики и их версии компьютер извлекает по LDAP-протоколу. Шаблоны групповой политики, в которых содержатся значения параметров, компьютер извлекает по SMB. Для отладки групповых политик администратор может отправить команду на принудительное обновление параметров групповой политики конкретному компьютеру через консоль GPMC или командлет Invoke-GPUpdate.
рис. 321 Протоколы, используемые технологией групповых политик для доставки информации до клиента
Механизм репликации
Домен AD представляет собой распределенную систему, работу которой могут обеспечивать десятки или даже сотни контроллеров домена. Для согласования изменений между серверами применяются две технологии репликации:
Репликация LDAP-каталога – используется для согласования изменений в контейнерах групповых политик. В этом случае применяются протоколы RPC (135/TCP) и SMTP (25/TCP).
Репликация папки SYSVOL – используется для согласования изменений в шаблонах групповых политик. В этом случае применяется протокол DFS-R (5722/TCP).
рис. 322 Протоколы, используемые для репликации групповых политик между контроллерами
Вопросы репликации между контроллерами важны для сопровождения ИТ-инфраструктуры, но в контексте групповых политик полезнее рассмотреть более подробно способы доставки групповых политик до клиента.
Механизмы наследования и суммирования параметров
Порядок суммирования параметров групповых политик иногда обозначают аббревиатурой LSDOU, так как сначала применяются локальные политики (L), затем политики сайта (S), домена (D) и в завершение политики организационных подразделений (OU). Таким образом, параметры, назначенные на организационные подразделения, всегда превалируют над параметрами локальных политик.
При назначении GPO на структурное подразделение в область его действия попадают пользователи и компьютеры всех нижестоящих подразделений. На схеме рис. 323 приведен пример, в соответствии с которым целевой компьютер попадает в область действия всех GPO, начиная с ГПО-1 и заканчивая ГПО-8.
рис. 323 Порядок наследования суммирования групповых политик
При этом, чем ближе GPO по иерархии к пользователю/компьютеру, тем позже будут применяться параметры этого объекта, поэтому они смогут переопределить ранее установленные значения. На приведенном ранее примере параметр ГПО-8 сможет переопределить значения, установленные параметром из ГПО-1.
Однако стандартный порядок наследования можно изменить, если поставить в настройках структурного подразделения флаг «Блокировать наследование» (англ. Block Inheritance). Это позволит отменить наследование GPO, назначенных на вышестоящие родительские подразделения. Например, на рисунке рис. 324 показан пример, в соответствии с которым целевой компьютер не попадает в область действия ГПО-1 и ГПО-2, т. к. на OU1 установлен запрет наследования.
рис. 324 Иллюстрация работы блокировки наследования GPO для структурного подразделения
Функция блокировки наследования удобна для отладки, и когда в рамках организационной структуры есть категории пользователей/компьютеров, на которые нужно назначить принципиально иные настройки. Например, в рамках московского офиса у вас может быть открытое пространство с совместно используемыми компьютерами, для которых проще будет задать настройки заново, чем переопределять общие настройки, назначенные в рамках офиса.
Обойти блокировку наследования можно с помощью флажка «Наследовать принудительно» (англ. enforced), который можно установить при назначении GPO на структурное подразделение. Более того, параметры объектов, отмеченных флажком Enforced, суммируются в отдельном цикле после суммирования параметров обычных GPO, поэтому они могут переопределить ранее установленные значения. Например, на рисунке рис. 325 показан пример, в соответствии с которым целевой компьютер попадает в область действия ГПО-1, несмотря на то, что для OU1 установлен запрет наследования.
рис. 325 Иллюстрация работы флага принудительного наследования
Отладка групповых политик
Служба GPSVC применяет параметры групповых политик, но не отчитывается контроллерам о результатах выполнения, поэтому администратор не располагает сводной информацией о фактическом применении параметров на всех хостах в домене.
Более того, параметры на пользователя имеют значение только в контексте конкретного входа в операционную систему, а доставлять на конкретный компьютер все параметры, определенные для всех категорий пользователей, не только бесполезно, но и еще небезопасно.
Для настройки и отладки групповых политик в домене администратор располагает следующими инструментами:
Мастер моделирования групповой политики в GPMC — использует алгоритм службы GPSVC, чтобы спрогнозировать, какие параметры будут применены для конкретного пользователя на конкретном компьютере в заданных условиях (Resultant Set of Policy, RSOP). Отчет выводится в формате HTML-документа.
Утилита
gpresult.exe— показывает фактический результат применения параметров групповой политики на конкретном компьютере. Результаты можно вывести в формате HTML-отчета.Утилита
gpupdate— позволяет форсировать применение параметров групповой политики, чтобы не дожидаться следующей итерации. Довольно часто используется с ключом /force, который позволяет повторно применить все параметры, даже если они не изменились с последнего применения.
Обычно сценарий настройки нового объекта выглядит следующим образом:
Администратор создает новый объект групповой политики и назначает его на выделенное подразделение, в котором обычно находится один тестовый компьютер или виртуальная машина.
Для проверки настроек администратор многократно выполняет в целевой системе команду
gpupdate /forceи сверяет полученный результат со своими ожиданиями.После настройки объекта групповой политики администратор назначает его на целевое структурное подразделение.
Такие инструменты, как RSOP и GPResult, используются обычно для отладки, когда нужно понять, почему та или иная настройка фактически не применилась к конкретной категории пользователей или компьютеров.
Групповые политики ALD Pro
Разработчики службы каталога FreeIPA решили сосредоточиться в большей степени на задачах аутентификации и авторизации, поэтому в части политик предлагают ограниченный набор параметров, имеющих прямое отношение к вопросам безопасности.
Реализованы политики FreeIPA очень хорошо, но их ни разу недостаточно. Поэтому команда ALD Pro выполнила интеграцию продукта с системой конфигурирования SaltStack, что позволило обеспечить централизованное управление настройками любых операционных систем и приложений в стиле групповых политик Microsoft.
Локальные политики
В операционной системе ALSE есть очень удобный инструмент для управления локальной политикой безопасности fly-admin-smc, с помощью которого можно управлять локальными пользователями и группами, настроить основные параметры безопасности, мандатного доступа, аудита, отчуждаемых носителей, см. рис. 326.
Вместе с тем, следует понимать, что это приложение не ведет какую-то собственную базу настроек, а считывает информацию непосредственно из конфигурационных файлов системы, поэтому при изменении параметров с помощью сторонних систем конфигурирования у вас не будет возможности вернуть значения по умолчанию.
рис. 326 Управление локальной политикой безопасности ALSE
Система ALD Pro не предлагает какой-либо реализации локальных политик и не резервирует изменяемые конфигурационные файлы, поэтому для возможности вернуться к исходным значениям вы можете включить версионирование всей папки /etc/ с помощью обычного Git или более специализированного приложения etckeeper.
Доменные групповые политики
Групповые политики ALD Pro во многом похожи на групповые политики AD. Перечислим основные особенности:
Параметры группируются в объекты групповой политики (GPO). В рамках каждого GPO есть две секции параметров — одна для формирования окружения компьютеров и вторая для окружения пользователей, см. рис. 327.
Значения параметров (шаблоны GPO) хранятся в LDAP-каталоге и извлекаются по LDAP-протоколу, то есть обычные файлы и SMB-протокол не используются.
За извлечение параметров из LDAP-каталога, их суммирование и применение отвечает служба aldpro-salt-minion (до версии ALD Pro 2.4.0 salt-minion-standalone). Для взаимодействия с каталогом служба использует учетную запись компьютера из файла
/etc/krb5.keytab.Для применения параметров на рабочих станциях автономная служба aldpro-salt-minion использует Salt-скрипты, тексты которых хранятся также в LDAP-каталоге.
Объекты групповой политики назначаются на организационные подразделения, которые являются собственной доработкой продукта ALD Pro, т.к. в службе каталога FreeIPA этой возможности изначально нет.
Назначить GPO на сайт в настоящий момент невозможно. Для назначения GPO на домен используется корневое подразделение организационной структуры.
рис. 327 Параметры компьютеров объекта групповой политики ALD Pro
В основе механизма групповых политик ALD Pro лежит Salt — система управления конфигурациями и удаленного выполнения операций с открытым исходным кодом, написанная на языке Python. Классическая система Salt работает по модели «издатель — подписчик», см. рис. 328, поэтому базовыми компонентами архитектуры являются:
Мастер (издатель) — это сервер, выполняющий централизованное управление рабочими станциями.
Миньоны (подписчики) — это рабочие станции или серверы, выступающие в качестве клиентов по отношению к Мастеру и принимающие от Мастера команды на удаленное конфигурирование.
Шина данных — это система для передачи сообщений на базе ZeroMQ.
Шина работает на Мастере и реализует модель длинных опросов (англ. long poll). Миньоны устанавливают с Шиной постоянное соединение по порту 4505/TCP (порт издателя, publisher) для получения заданий на конфигурирование от Мастера.
Кроме постоянного соединения Миньоны периодически подключаются к Шине по порту 4506/TCP (порт сервера запросов, request server) для загрузки необходимых файлов и отправки отчетов о выполнении заданий.
рис. 328 Классическая архитектура Salt
Работа групповых политик в ALD Pro до версии 2.2.0
В продукте ALD Pro до версии 2.2.0 групповые политики использовали классическую архитектуру Salt: служба Salt-Minion рабочей станции подключалась к Salt-Master на контроллере домена, забирала справочник Pillar с результирующим набором параметров, загружала все необходимые salt-скрипты и применяла указанные параметры.
Описанная архитектура отлично подходит для управления инфраструктурой на сотни виртуальных машин с высокой сетевой связанностью, что подтверждается успешным использованием Salt в облаке LinkedIn (самое крупное внедрение продукта). Успешно применяют Salt и российские облачные провайдеры. Однако при использовании классического подхода Salt по управлению тысячами рабочих станций мы сталкиваемся с рядом сложностей:
Суммирование параметров групповых политик на стороне сервера для построения результирующего набора параметров требует слишком много ресурсов.
Аутентификация по ключу, которую использует Salt для установления соединения, требует ощутимо больше ресурсов. Это очень заметно в первые минуты после перезапуска службы Salt-Master.
Удержание нескольких тысяч сетевых соединений по модели long poll требует слишком много ресурсов.
Работа групповых политик в ALD Pro с версии 2.2.0
Для того чтобы достичь показателя в 10к рабочих станций на один контроллер домена, в части групповых политик мы отказались от использования Мастера и перешли к модели Standalone Minion. Весь программный код, отвечающий за наследование и суммирование параметров групповых политик, выполняется на стороне клиента, а к контроллеру он ходит только за обновлением данных, см. рис. 329.
Работа групповых политик в ALD Pro с версии 2.4.0
Кардинальных изменений работы ГП в версии 2.4.0 не произошло. Перечислим некоторые значимые моменты:
Появилась возможности влиять на порядок наследования через установку таких флажков, как «Блокировать наследование» (англ. Block Inheritance) и «Наследовать принудительно» (англ. Enforced).
Упрошен синтаксис команд cli для форсирования применения политик и получения настроек результирующей политики (pillar).
Переработаны скрипты коробочных параметров, повышена стабильность их работы.
При изменении доп. параметра групповой политики, новые настройки применяются к клиентам автоматически (версия всех политик в которых используется такой доп. параметр инкриментируется).
рис. 329 Компоненты доменной групповой политики ALD Pro
Скрипты групповых политик находятся в каталоге /srv/aldpro-salt/roots/states/policies/ (до версии 2.4 в каталоге /srv/salt/standalone/roots/states/policies/). В случае коробочных параметров они доставляются через установку и обновление deb-пакетов программного продукта, а в случае дополнительных параметров загружаются через LDAP.
admin@dc-1:~$ sudo tree -L 2 /srv/aldpro-salt/roots/states/policies/
/srv/aldpro-salt/roots/states/policies/
├── audit-policies
│ ├── files
│ └── init.sls
├── host-policies
│ ├── init.sls
│ ├── rbta_ldap_auth_fast
│ ├── rbta_ldap_crontab
│ ├── rbta_ldap_date_time_h
│ ...
│ ├── rbta_ldap_printers
│ ├── rbta_ldap_quotas
│ ├── rbta_ldap_security_policy
│ └── rbta_ldap_system_alternatives
├── sw-policies
│ ├── init.sls
│ └── README.md
├── update-policy.sls
└── user-policies
├── init.sls
├── rbta_ldap_autostart
├── rbta_ldap_date_time_u
...
├── rbta_ldap_power_management
├── rbta_ldap_quick_launch
├── rbta_ldap_start_menu
└── rbta_ldap_windows_settings
Механизм групповых политик ALD Pro заимствует из MS AD также несколько очевидных способов повышения производительности:
Служба aldpro-salt-minion не обращается к контроллеру сразу после включения, а выбирает случайным образом момент времени в окне продолжительностью в один час, чтобы не создавать пиковую нагрузку в начале рабочего дня.
Параметры групповых политик извлекаются из LDAP-каталога только в том случае, если версия в локальном кэше не совпадает с версией GPO из каталога.
Механизм репликации
Информация по групповым политикам ALD Pro находится в LDAP-каталоге, поэтому реплицируется между контроллерами домена в штатном порядке в рамках установленной топологии.
Мы отказались от использования файлов, т.к. производительность современных серверов позволяет передавать всю необходимую информацию по LDAP-протоколу. Довольно долгое время файлы Registry.pol были не больше 64Кб, а записи размером до 100Кб хранить в каталоге считалось допустимым даже 20 лет назад.
Механизмы наследования и суммирования параметров
В части наследования и суммирования параметров система ALD Pro заимствует подходы, которые применяются в MS AD:
Простые параметры суммируются так же, как обычные параметры групповых политик AD, поэтому, чем ближе будет GPO по иерархии к пользователю/компьютеру, тем позже будут применяться параметры этого объекта, поэтому они смогут переопределить ранее установленные значения.
Составные параметры суммируются так же, как Предпочтения групповых политик (Group Policy Preferences), поэтому salt-скрипт через pillar получает таблицу атрибутов, где каждая строка соответствует набору атрибутов данного параметра из отдельно взятого объекта групповой политики. Скрипт должен будет самостоятельно удалить дубликаты строк, руководствуясь бизнес-логикой параметра групповой политики.
С версии 2.4 появилась возможность влиять на порядок наследования через установку таких флажков, как «Блокировать наследование» (англ. Block Inheritance) и «Наследовать принудительно» (англ. Enforced).
Однако есть некоторые отличия:
В соответствии с порядком наследования LSDOU в системе ALD Pro в настоящий момент можно назначать GPO только на домен и организационные подразделения, причем под доменом подразумевается корневое подразделение в иерархии. Локальных политик нет, и назначить GPO на сайт пока не представляется возможным.
Групповые политики ALD Pro не поддерживают технологии WMI-фильтров и не позволяют назначать права доступа на отдельные GPO, поэтому в настоящий момент сузить область действия GPO не представляется возможным.
Настройка групповой политики
Давайте с помощью групповых политик запретим локальным пользователям вход в систему.
В домене MS AD для управления паролями локальных пользователей используется специальное решение LAPS (Local Administrator Password Solution). Для ALD Pro оно разработано в 2025 году и про наш LAPS мы поговорим дальше в этом модуле. Пока узнаем как с помощью политики заблокировать вход локальным пользователям.
Перейдем на страницу и создадим новый объект групповой политики, нажав соответствующую кнопку.
рис. 330 Страница «Групповые политики»
До первого сохранения объекта нам доступна только вкладка Основное, где требуется задать имя объекта и описание. Введем следующие значения:
Имя: Блокировка локальных учетных записей
Описание: Политика запрещает локальным пользователям вход в систему
И нажмем кнопку
рис. 331 Создание новой групповой политики
Теперь нам доступны для редактирования остальные вкладки:
Конфигурация политики – отображает список параметров, которые задействованы в этом объекте групповой политики. Вы можете удалить параметр или быстро перейти к его редактированию.
Параметры компьютеров – содержит параметры для настройки окружения компьютера.
Параметры пользователей – параметры для настройки окружения пользователя.
Подразделения – содержит список подразделений, на которые назначен объект групповой политики. На этой вкладке вы можете удалить назначение или назначить объект на дополнительные подразделения.
На вкладке включим параметр из списка , установим значение «no», установим во «Включено» и нажмем кнопку
рис. 332 Настройка параметров компьютеров групповой политики
Примечание
Подробную справку о параметре групповой политики можно получить, если кликнуть по значку с символом вопроса, который расположен в правом верхнем углу карточки. Как вы можете заметить, значение «no» означает, что вход будет запрещен для всех категорий локальных пользователей, в том числе и по ssh.
рис. 333 Справка настроек параметров компьютеров групповой политики
Примечание
Чтобы закрыть справку и вернуться к форме редактирования, сделайте клик по значку в форме крестика, который расположен в правом верхнем углу карточки.
Перейдем на вкладку и создадим новое назначение кнопкой .
рис. 334 Назначение групповой политики на подразделение
Так как мы хотим применить объект групповой политики на весь домен, то выберем корневое подразделение «ald.company.lan» и нажмем кнопку .
Обратите внимание на поле «Приоритет групповой политики». Это значение позволяет определить порядок применения параметров, если на одно и тоже организационное подразделение назначено несколько объектов групповой политики.
рис. 335 Выбор подразделения для групповой политики
Файлы, которые используются для применения этого параметра, находятся в каталоге
/srv/aldpro-salt/roots/states/policies/host-policies/rbta_ldap_security_policy.Скрипт
init.slsзаменяет содержимое файла/etc/parsec/parsec.confшаблоном из файлаlocal_login_policy/parsec.conf.j2, подставляя значение переменной login_policy.Чтобы не ждать обновления параметров, забежим немного вперед и выполним на компьютере следующие команды:
admin@dc-1:~$ sudo aldpro-gpupdate --gp
admin@dc-1:~$ cat /etc/parsec/parsec.conf
Если мы теперь попытаемся выполнить вход в систему локальным администратором, то получим ошибку от системы Parsec.
рис. 336 Предупреждение при входе в систему
Если вы захотите вернуть как было, то нужно установить параметр all, обновить параметры политики и перезагрузить хост.
Механизм определения пользовательских сессий
Предлагаем немного подробнее рассмотреть реализацию механизма определения активных пользовательских сессий в ALD Pro при применении политики параметров пользователя. Обратите внимание, что параметры пользователя применяются в цикле индивидуально по отношению к каждой учетной записи, с помощью которой был выполнен интерактивный вход в операционную систему. Для операционных систем 1.8_x86-64 список сессий извлекается через интерфейс dbus (org.freedesktop.login1), а для всех остальных ОС с помощью классической утилиты users (см. модули aldpro_user.py и dbus.py).
Система не учитывает вход из-под sudo su, но не ограничивается только графическими сессиями, поэтому, если вам потребуется обогатить данные pillar параметрами другого пользователя, вы можете выполнить вход с помощью простой утилиты login:
admin@pc-1:~$ sudo login testuser
Давайте рассмотрим механизм определения сессий по шагам:
Пользователь подключается к рабочей станции: графическое подключения Fly или консольное подключение Login.
Запускаются модули PAM в зависимости от выбранного метода подключения: fly-dm (/etc/pam.d/fly-dm) или login (/etc/pam.d/login).
Выше упомянутые PAM модули вызывают PAM модуль Session (/etc/pam.d/common-session). В нем содержится опция запуска внешнего скрипта salt-masterless-login:
session optional pam_exec.so /usr/sbin/salt-masterless-login
PAM модуль передает переменную PAM_USER данному Python скрипту, который отделяет доменного пользователя от локального, сравнивая его с /etc/passwd. В случае, если пользователь доменный, то будет запущена следующая команда:
aldpro-salt-call \ --local \ -m \ /srv/aldpro-salt/roots/_modules/ \ state.single \ module.run \ aldpro_user.store_domain_user \ -c \ /srv/aldpro-salt/config \ queue=TrueПримечание
В ванильной версии Salt для выполнения salt-функций на миньоне предназначена утилита salt-call. В продукте ALD Pro мы упаковываем salt-клиент в отдельное python окружение, поэтому для взаимодействия с нашей автономной службой aldpro-salt-minion вам следует использовать утилиту aldpro-salt-call.
Команда, запущенная в предыдущем шаге вызывает функцию store_domain_user из модуля /srv/aldpro-salt/roots/_modules/aldpro_user.py, по окончанию работы которой, у нас формируется файл со списком пользовательских сессий /opt/rbta/aldpro-salt/minion/gp-user_sessions.json
cat /opt/rbta/aldpro-salt/minion/gp-user_sessions.json {"admin": {"last_active": "2025-03-19 14:33:15.275652"}}
Файл gp-user_sessions.json регулярно очищается с помощью функции purge_inactive_sessions. Она удаляет все сессии, которые были неактивны последние 20 дней. Запускается раз в день. Посмотреть следующий запуск можно:
aldpro-salt-call schedule.show_next_fire_time purge_inactive_sessions
На последнем этапе запускается модуль gp_sum.py (/srv/aldpro-salt/roots/_modules/gp_sum.py). Функция build_gp_pillars из модуля gp_sum.py с помощью dbus определяет все текущие сессии, далее сравнивает их со списком gp-user_sessions.json и в случае совпадения строит pillar для этого пользователя.
На рис. 337 представлены описанные шаги в виде схемы для удобства восприятия.
рис. 337 Механизм определения пользовательских сессий
Отладка групповых политик
Служба aldpro-salt-minion применяет параметры групповых политик по расписанию. Сразу после загрузки компьютера служба запускает отложенное задание, таймаут которого складывается из двух значений:
Постоянная часть в 30 минут — определяет минимальный интервал между последующими обновлениями параметров групповых политик.
Случайный разброс до 50 минут — позволяет снизить пиковую нагрузку в начале дня, когда все сотрудники разом включают свои компьютеры.
Таким образом, компьютер должен гарантированно обновить параметры групповых политик в течение 80 минут после загрузки. Приблизительное время очередного обновления по расписанию вы можете узнать командой:
admin@pc-1:~$ sudo aldpro-gpupdate --gp_timer
Таймер заданий ГП
local:
----------
next_fire_time:
2025-03-24T14:43:09
result:
True
Вывод этой команды учитывает только постоянную составляющую таймера next_fire_time, поэтому возможна ситуация, когда указанное время уже истекло, но применение политик еще не состоялось. Фактически команда будет запущена в момент времени splay, значение которого можно узнать с помощью функции schedule.job_status:
admin@pc-1:~$ sudo aldpro-salt-call schedule.job_status build_and_run_gp
local:
----------
_last_run:
2025-03-24T14:13:09
_next_fire_time:
2025-03-24T14:43:09
_next_scheduled_fire_time:
2025-03-24T11:06:27
_seconds:
1800
_splay:
2025-03-24T14:53:30
args:
enabled:
True
function:
gp_sum.build_and_run_gp
jid_include:
True
kwargs:
----------
maxrunning:
1
minutes:
30
name:
build_and_run_gp
return_job:
False
run:
True
run_on_start:
False
splay:
----------
end:
3000
start:
300
Для выполнения отладки новых объектов групповых политик вы можете запускать принудительное обновление параметров командой:
admin@pc-1:~$ sudo aldpro-gpupdate --gp --verbose
Где:
–gp – форсированное применение групповых политик с очисткой кэша и формированием нового набора параметров результирующей политики (pillar).
–verbose – детальный вывод результата выполняемой команды.
Фактически «под капотом» при этом происходит вызов команды:
admin@pc-1:~$ aldpro-salt-call gp_sum.build_and_run_gp force=True verbose=True
Вызов этой команды дает тот же результат, что и gpupdate /force в Windows.
После форсированного применения всех параметров на рабочей станции будет сформирован pillar и загружены все необходимые sls-скрипты, поэтому у вас появится возможность форсировать применение отдельного параметра без полной очистки кэша. Форсированное применение отдельных параметров существенно ускоряет написание скриптов дополнительных параметров, т.к. позволяет вносить изменения не через LDAP каталог, а напрямую в локальные копии init.sls из соответствующих папок в директории /srv/aldpro-salt/roots/states/policies/host-policies/
Для отладки конкретного параметра компьютера используйте функцию state.apply, которой нужно передать имя скрипта из каталога /srv/aldpro-salt/roots/states/policies/host-policies:
$ sudo aldpro-salt-call state.apply <имя_скрипта> force=True verbose=True
Например, чтобы принудительно обновить параметр «Настройки сервиса сетевого времени Chrony», за который отвечает salt-скрипт из каталога rbta_ldap_date_time_h, требуется выполнить следующую команду:
$ sudo aldpro-salt-call state.apply rbta_ldap_date_time_h force=True verbose=True
...
[INFO ] Completed state [/etc/chrony/chrony.conf] at time 07:59:15.835203
(duration_in_ms=1.932)
...
И не забывайте про ключ --log-level, с помощью которого можно выбрать желаемый уровень детализации: all, garbage, trace, debug, profile, info, warning, error, critical, quiet (по умолчанию warning).
$ sudo aldpro-salt-call --log-level=debug state.apply rbta_ldap_date_time_h
Чтобы форсировать применение параметра пользователя, вам потребуется добавить в pillar дополнительный ключ user с идентификатором того пользователя, для которого вы хотите применить этот параметр. Напомним, что каталог с политиками пользователей находится здесь /srv/aldpro-salt/roots/states/policies/user-policies. Например, если параметр с идентификатором rbta_ldap_env_vars_u нужно применить для пользователя admin, то команда будет выглядеть следующим образом:
sudo aldpro-salt-call state.apply rbta_ldap_env_vars_u pillar="{'user':'admin'}"
Примечание
Команда выше применит параметры указанному пользователю только в том случае, если pillar сформирован с участием пользователя. Например, вы создали групповую политику, назначили на нужное подразделение, но при этом служба aldpro-salt-minion его еще не применила, то команда не отработает, так как pillar не содержит информацию для пользователя. Для ускорения процесса обновления pillar можно форсировать применение политик с помощью команды aldpro-gpupdate.
Для вывода параметров результирующей политики компьютера применяется команда:
admin@pc-1:~$ sudo aldpro-gpupdate --gp_host_pillar
Просмотр pillar ГПO компьютера
local:
----------
Так как эту команду мы запустили на компьютере pc-1.ald.company.lan, то под капотом была вызвана функция pillar.get следующим образом:
aldpro-salt-call pillar.get aldpro-hosts:pc-1.ald.company.lan
Точно такая же команда есть для просмотра данных pillar для конкретного пользователя:
aldpro-gpupdate --gp_user_pillar admin
Под капотом эта команда вызывает функцию pillar.get следующим образом:
aldpro-salt-call pillar.get aldpro-users:admin
Разработка дополнительных параметров
Создание дополнительного параметра
Для того чтобы в домене можно было организовать централизованное управление корпоративными приложениями, в системе ALD Pro реализована возможность создания дополнительных параметров групповых политик, которые решают ту же задачу, что и административные шаблоны Windows.
Давайте создадим дополнительный параметр групповых политик, с помощью которого можно будет централизованно управлять источниками программного обеспечения для пакетного менеджера apt.
Чтобы создать дополнительный параметр, нужно перейти на страницу . Вы можете выбрать вкладку или в зависимости от того, хотите ли вы создать параметр для настройки окружения пользователя или компьютера. Будьте внимательны, т.к. перенести параметр из одного раздела в другой после его создания не получится.
На вкладке кликнем по узлу и нажмем кнопку
рис. 338 Доп. параметры групповых политик
До первого сохранения параметра нам будет доступна только вкладка . Заполним ее параметры:
Название параметра: Источники программного обеспечения
Это имя параметра, под которым вы его хотите видеть при редактировании объектов групповых политик на портале управления.
Уникальный идентификатор: repo_source_list
а полный его идентификатор rbta_ldap_custom_gp_host_repo_source_list, который будет использоваться в качестве имени папки на диске и в salt-скриптах.
Тип каталога: Параметр компьютерной групповой политики
Определяет, относится ли данный параметр к настройкам компьютера или пользователя. Параметр недоступен для редактирования, его значение определяется автоматически в зависимости от того, с какой страницы вы перешли к созданию дополнительного параметра.
Тип параметра: Составной параметр
Он позволяет указать способ хранения атрибутов:
Простой параметр — задает плоский список атрибутов. Например, для настройки межсетевого экрана может потребоваться задать уровень детализации журнала, и эта настройка подразумевает одно конкретное значение, поэтому она может быть реализована атрибутом «простого» параметра.
Составной параметр — задает таблицу атрибутов, где в каждой строке представлен типовой набор данных. Если продолжать пример с межсетевым экраном, то для настройки фильтрации может потребоваться разрешить входящие ssh-соединения, запретить исходящие smtp и т.п., поэтому такие настройки нужно реализовывать атрибутами «составного» параметра.
Папка параметра: Дополнительные параметры
Указывает раздел каталога, в котором будет представлен этот параметр.
Назначение параметра (нужно вставить весь текст целиком):
Позволяет централизованно настраивать источники программного
обеспечения пакетного менеджера apt путем изменения
содержания файла /etc/apt/sources.list.d/repo_source.list
Атрибут "Источник" определяет строку в формате source.list:
deb <адрес_репозитория> <код_дистрибутива> <компонент1> <компонентN>
Где:
- deb - указывает на то, что репозиторий соответствует репозиторию бинарных файлов с предварительно скомпилированными пакетами. Для репозиториев с исходными кодами используют «deb-src».
- адрес репозитория - задает источник пакетов, у интернет-репозиториев адрес начинается с «http(s)://», адреса локальных репозиториев начинаются с «file:/». При добавлении репозитория с диска командой apt-cdrom add в файле появится строка «cdrom:[]/».
- код дистрибутива - дополняет адрес, уточняя необходимый релиз продукта, так как в одном репозитории могут находиться пакеты сразу для нескольких релизов.
- компонент - это группа пакетов, объединенная по условиям использования.
Условия использования компонентов следующие:
- non-free - группа содержит пакеты, которые не соответствуют принципам свободного ПО, имеют патенты или другие юридические ограничения;
- contrib - группа содержит пакеты, которые сами по себе соответствуют принципам свободного ПО, но зависят от пакетов из группы «non-free», т.е. не могут без них работать;
- main - группа содержит пакеты свободного ПО, которые не зависят от пакетов из групп «contrib» и «non-free».
Обязательно нажмите кнопку .
На вкладке атрибутов нам нужно добавить один элемент:
Название атрибута: «Источник» Название, под которым вы хотите видеть этот атрибут на карточке параметра.
Уникальный идентификатор: «repo_source_item» Идентификатор атрибута, под которым значение атрибута будет доступно в пилларе для его использования из salt-скрипта.
Описание: «Строка источника для apt» Краткие комментарии, которые помогут упростить поддержку этого атрибута в будущем, когда потребуется внести какие-либо изменения. Данная информация недоступна при настройке групповой политики, поэтому, если вы хотите дать подсказку администратору о возможных значениях этого атрибута, внесите эту информацию в описание параметра.
Осталось только написать скрипт параметра, с помощью которого его значения будут применяться на рабочих станциях.
Скрипты Salt похожи на PHP, в них текст перемежается императивными инструкциями языка программирования, по результату выполнения которых получается итоговый документ. За императивную часть отвечают инструкции шаблонизатора Jinja2, а декларативная часть задается по правилам Salt в YAML-подобном синтаксисе.
Далее мы подробно ознакомимся с синтаксисом Jinja, а пока просто перенесем текст скрипта:
{% set id = 'rbta_ldap_custom_gp_host_repo_source_list' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% set filename = '/etc/apt/sources.list.d/repo_source.list' %}
{% set lines = [] %}
{% if gpo %}
{%- for item in gpo %}
{%- if item.repo_source_item.lower() != 'none' %}
{%- do lines.append(item.repo_source_item) %}
{%- endif %}
{%- endfor %}
{% endif %}
{{ id }}:
{%- if lines|length == 0 %}
file.absent:
- name: {{ filename }}
{%- else %}
file.managed:
- name: {{ filename }}
- user: root
- group: root
- mode: 644
- contents:
{%- for line in lines %}
- {{ line }}
{%- endfor %}
{%- endif %}
Данный скрипт создает файл /etc/apt/sources.list.d/repo_source.list, настраивает права доступа и вносит в него указанные источники программного обеспечения.
Теперь вы можете создать объект групповой политики и с помощью этого параметра централизованно настраивать источники программного обеспечения.
Синтаксис скриптов
Императивные инструкции шаблонизатора Jinja
Скрипт «Hello world»
Создайте файл test.sls с простейшим Jinja-скриптом, чтобы проверить, как это работает:
$ mkdir ~/test_salt && cd ~/test_salt
$ echo '{% do salt.log.info("Hello world!") %}' > hello.sls
Где:
{% … %}— синтаксис для вставки инструкции языка шаблонизатора Jinja;do— оператор, который вызывает указанную функцию;salt.log.info— имя вызываемой функции;("Hello world!")— параметр, передаваемый в функцию.
Выполните его с помощью утилиты aldpro-salt-call:
sudo aldpro-salt-call --local --file-root=. state.sls hello
Где:
aldpro-salt-call — утилита, которая используется для локального запуска функций Salt на Миньоне без участия Мастера;
ключ
--local— исключает обращение к мастеру для получения настроек;ключ
--file-root— устанавливает текущую директорию «.» в качестве рабочего каталога;параметр
state.sls— указывает, что нужно вызывать функцию sls из модуля state;параметр
hello— задает имя скрипта (без расширения).
Результат вывода должен быть таким:
admin@dc-1:~/test_salt$ sudo aldpro-salt-call --local --file-root=. state.sls hello
[INFO ] Loading fresh modules for state activity
[INFO ] Fetching file from saltenv 'base', \*\* done \*\* 'hello.sls'
[INFO ] Hello world!
local:
Summary for local
-----------
Succeeded: 0
Failed: 0
-----------
Total states run: 0
Total run time: 0.000 ms
Максимально подробную информацию о выполнении команды можно получить в режиме
отладки. Для этого нужно добавить ключ --log-level=debug:
sudo aldpro-salt-call --log-level=debug --local --file-root=. state.sls hello
Чтобы вызвать salt-функцию из jinja-инструкций, нужно воспользоваться оператором do и обращаться к модулю log через встроенный объект salt. Для выполнения salt-скрипта из текстовой строки воспользуемся функцией template_str из модуля state:
admin@dc-1:~$ sudo aldpro-salt-call state.template_str '{% do salt.log.info("Привет, мир!") %}'
...
[INFO ] Привет, мир!
...
Разметка Jinja
Инструкции языка Jinja внедряются с помощью фигурных скобок, см. табл. 31:
Jinja |
Аналог PHP |
Комментарий |
|---|---|---|
|
|
Синтаксис для вставки инструкции языка шаблонизатора Jinja |
|
|
Пример инструкции для вывода в документ по месту вызова значения выражения |
|
|
Краткий синтаксис для вывода в документ по месту вызова значения выражения |
|
|
Синтаксис для вставки комментариев средствами языка Jinja2 |
Для повышения читабельности кода управляющие структуры обычно оформляют отступами, но yaml-документы крайне чувствительны к пробелам, поэтому данный инструмент форматирования оказывается недоступен без применения специальных операторов подавления отступов, см. табл. 32. Чтобы убрать лишние пробелы и пустые строки вам нужно всего лишь поставить знак дефиса слева, справа или с обоих концов управляющего блока:
Jinja |
Комментарий |
|---|---|
|
Удаляет пробелы и пустые строки слева |
|
Удаляет перенос текущей строки и переносит ее на предыдущую |
|
Удаляет пробелы и пустые строки слева, в том числе перенос текущей строки, поэтому текст окажется в конце предыдущей строки |
Работа с переменными
Переменные на языке Jinja задаются оператором set. Вам доступен широкий перечень типов данных: булевы, числовые, текстовые, списки, кортежи, словари:
{% set boolean_val = True %}
{% set int_val = 777 %}
{% set string_val = 'hello' %}
{% set list_val = ['h', 'e', 'l', 'l', 'o'] %}
{% set tuple_val = ('h', 'e', 'l', 'l', 'o') %}
{% set dict_val = {'b': True, 'i': 777, 's': 'hello'} %}
При работе с числовыми переменными доступны обычные математические операторы:
{% set a = 5 %}
{% set b = 10 %}
{% set a = a + b %}
{% set b = a - b %}
{% set a = a - b %}
Конкатенация строк возможна как специальным оператором «~», который предварительно конвертирует все значения в строки, так и обычным оператором сложения «+»:
{% set a = 5 %}
{% set b = 10 %}
{% set c = a ~ b %}
{% set d = 'World' %}
{% set e = 'Hello, ' + d %}
Для выполнения более сложных преобразований над переменными вам доступно множество функций, которые можно применять как методы через точку или в стиле конвейеров bash через символ вертикальной черты, за что их также называют фильтрами:
{% set a = 'Hello'.upper() %}
{% set b = 'world' | upper %}
Работа со строками
Строки являются объектами типа str, поэтому вам доступны все методы этого класса docs.python.org
Более того, вы можете работать со строками, как со списками, обращаясь к символам по их индексам:
admin@dc-1:~$ sudo aldpro-salt-call state.template_str '{% do salt.log.info("Привет, большой мир!"[0] ) %}'
...
[INFO ] П
...
В квадратных скобках можно указывать три значения: индекс первого символа (включительно), индекс последнего символа (невключительно) и шаг, который позволяет включать в подстроку не все символы подряд, как по умолчанию (когда шаг=1), а, например, только каждый второй (если шаг=2). Индекс можно задавать с конца строки, в этом случае нужно использовать отрицательные значения. Шаг может тоже иметь отрицательное значение, тогда символы в строке будут идти в обратном порядке. В качестве индекса можно подставлять результаты поиска подстроки с помощью функции find().
Приведем более сложные примеры работы со строками:
# Переменной "s" присвоена ссылка на строку текста
{% set s = "Привет, большой мир!" %}
# Переменной "f" присвоено значение "П"
{% set f = s[0] %}
# Переменной "f" присвоено значение "большой" с 8-го по 16-й символы
{% set f = s[8:16] %}
# Переменной "f" присвоено значение ", большой ", что соответствует фрагменту между указанными словами
{% set f = s[s.find("Привет")+6:s.find("мир")] %}
# Переменной "f" присвоено значение "Привет" с 0-го по 6-й символы
{% set f = s[:6] %}
# Переменной "f" присвоено значение "!", которое соответствует первому символу с конца
{% set f = s[-1] %}
# Переменной "f" присвоено значение исходной строки с обратнымпорядком символов
{% set f = s[::-1] %}
Работа со списками
Для работы с массивами предназначен тип данных list. Списки можно определить с помощью квадратных скобок, причем нумерация элементов будет начинаться с нуля. В работе со списками можно использовать те же приемы, что и в работе со строками:
# Присваиваем переменной "a" ссылку на список из трех элементов
{% set a = ["раз", "два", "три"] %}
# Присваиваем переменной "s" значение первого элемента с индексом 0, т.е. "раз"
{% set s = a[0] %}
# Присваиваем переменной "a2" ссылку на список из первых двух элементов исходного списка, т.е. ["раз", "два"]
{% set a2 = a[0:2] %}
# Присваиваем переменной "a2" ссылку на список из трех элементов исходного списка в обратном порядке
{% set a2 = a[::-1] %}
Чтобы узнать количество элементов в списке, можно воспользоваться фильтрами count или length. Добавить элемент в конец списка можно с помощью метода append(). Если нужно добавить элемент в конкретную позицию списка, используйте метод insert(). Удалить элемент по индексу можно методом pop(), а по значению - методом remove(). В программном коде Python можно заменять значение элемента напрямую по индексу (например, c[1]=»two»), но в Jinja это не работает, поэтому приходится использовать связку методов pop() и insert().
# Присваиваем переменной "a" ссылку на список из трех элементов
{% set a = ["раз", "два", "три"] %}
# Присваиваем переменной "c" значение, равное количеству элементов в списке "a"
{% set c = a | count }
# Добавляем в список "a" еще один элемент
{% do a.append("четыре") %}
# Добавляем в список "a" еще два элемента
{% do a.extend(["пять", "я иду искать"]) %}
# Присваиваем переменной "b" ссылку на список из двух элементов
{% set b = ["кто не спрятался", "я не виноват"] %}
# Присваиваем переменной "c" ссылку на новый список, состоящий из элементов массивов "a" и "b"
{% set c = a + b %}
# Удаляем из списка "c" элемент c индексом 1, под которым сейчас находится значение "два"
{% do c.pop(1) %}
# Вставляем в список "c" новый элемент "two" под индексом 1
{% do c.insert(1, "two") %}
# Удаляем из списка "c" элемент с значением "я иду искать"
{% do c.remove("я иду искать") %}
Полный перечень методов см. docs.python.org
Есть так же еще один способ замены элемента с помощью связки фильтров map и replace, но данный способ следует использовать с осторожностью, понимая его ограничения.
{% set a = ["раз", "два", "три"] %}
{% set a = a | map("replace", "три", "иду искать") | list %}
Работа со словарями
Для работы с ассоциативными массивами, у которых есть ключ и значение, предназначен тип данных dict. Словари можно определить с помощью фигурных скобок, доступ к объектам словаря выполняется не по индексу, а по имени ключа, которое можно указывать в квадратных скобках, через точку как свойство объекта, в качестве параметра безопасной функции get.
# Переменной "user" присвоена ссылка на словарь
{% set user = {"login": "localadmin", "uid": 1000, "gid": 1000} %}
# Переменной "login" присвоено значение ключа "login" из словаря "user", т.е. "localadmin"
{% set login = user["login"] %}
# Переменной "uid" присвоено значение ключа "uid" из словаря "user", т.е. 1000
{% set uid = user.uid %}
# Безопаснее всего использовать метод get, который позволяет определить так же значение по умолчанию
{% set gid = user.get("uid", -1) %}
Узнать количество элементов в списке, можно с помощью фильтров count или length. Чтобы получить список всех ключей, можно воспользоваться методом keys(), чтобы получить список значений - методом values().
# Переменной "user" присвоена ссылка на словарь
{% set user = {"login": "admin", "uid": 1000, "gid": 1000} %}
# Присваиваем переменной "c" значение, равное количеству элементов в словаре "user"
{% set c = user | count %}
# Присваиваем переменной "k" ссылку на объект dict_keys, который содержит список всех ключей словаря
{% set k = user.keys() %}
# Присваиваем переменной "v" ссылку на объект dict_values, который содержит список всех значений словаря
{% set v = user.values() %}
# Присваиваем переменной "i" ссылку на объект dict_items, который содержит список кортежей из ключей словаря
{% set i = user.items() %}
# Обновляем в словаре "a" значение ключа "uid", присваиваем ему значение 500
{% do a.update({"uid":500}) %}
Ветвления и циклы
Если выполнение набора команд должно зависеть от некоторого условия, то ветвление можно задать с помощью операторов if/elif/else/endif:
{% if ram >= 2048 %}
{% set ram_requirement_success = True %}
{% else %}
{% set ram_requirement_success = False %}
{% endif %}
Для упрощения кода условные конструкции записывать в одну jinja-инструкцию (inline if). В этом случае программный код примет вид:
{% set ram_requirement_success = True if ram >= 2048 else False %}
Переменным можно так же присваивать результаты логических операций напрямую. Тот же самый код можно записать следующим образом:
{% set ram_requirement_success = (ram >= 2048) %}
Циклы for являются аналогом конструкций типа foreach других языков программирования и предназначены для выполнения заданного набора инструкций применительно к каждому элементу из списка:
{% set users = ['ivanov', 'petrov', 'kuznetsov'] %}
{% for user in users %}
{% do salt.log.info(user.upper()) %}
{% endfor %}
Если вам нужно будет обработать данные словаря, воспользуйтесь методом items(), чтобы получить список его элементов:
{% set users = {'u1':'ivanov', 'u2':'petrov', 'u3':'kuznetsov'} %}
{% for id, user in users.items() %}
{% do salt.log.info(user.upper()) %}
{% endfor %}
Так как циклы Jinja работают только со списками, то для эмуляции обычных циклов со счетчиком вам нужно воспользоваться функцией range([min], max). Если передать этой функции один параметр, то будет сгенерирован список с указанным количеством элементов, нумерация которых будет начинаться с нуля. Если передать два числа, то нумерация элементов будет в указанном диапазоне.
{% do salt.log.info('Обратный отсчет') %}
{% for i in range(0, 10) %}
{% do salt.log.info(10 - i) %}
{% endfor %}
При работе с циклами важно помнить, что переменные, объявленные с помощью {% set %}, существуют только в рамках текущей интерации цикла. При переходе к следующей итерации они пересоздаются заново и не сохраняют изменения.
{% set boolean_val = False %}
{% for item in ['a', 'b', 'c'] %}
{% if item == 'b' %}
{% set boolean_val = True %}
{% endif %}
{% endfor %}
{% do salt.log.info(boolean_val) %}
В примере выше boolean_val был изменён в итерации на True, при выходе из цикла он сбросился в False, нам это покажет модуль log.
Для решения проблемы нужно использовать объект namespace(), который создает глобальный контейнер. Значение в namespace() не сбрасывается при переходе к новой итерации. В примере ниже значение ns.boolean_val будет равен True.
{% set ns = namespace(boolean_val=False) %}
{% for item in ['a', 'b', 'c'] %}
{% if item == 'b' %}
{% set ns.boolean_val = True %}
{% endif %}
{% endfor %}
{% do salt.log.info(ns.boolean_val) %}
Вывод значений переменных на экран
Работая с salt-скриптами у вас нет возможности использовать отладчик, поэтому для получения дополнительной информации о работе своего программного кода используйте функцию info из метода log, чтобы вывести на экран значения отдельных переменных:
{% set net = "10.0.1.0/24" %}
{% set net_parts = net.split("/") %}
# Покажет список ['10.0.1.0', '24']
{% do salt.log.info(net_parts) %}
{% set ip = net_parts[0] %}
# Покажет значение '10.0.1.0'
{% do salt.log.info(ip) %}
{% set mask = net_parts[1] %}
# Покажет значение '24'
{% do salt.log.info(mask) %}
Быстрая проверка работы salt-функций
Работу функций salt можно проверить напрямую с помощью утилиты aldpro-salt-call. Например, если вы хотите понять, будет ли функция get_str из модуля random использовать другие классы символов, если ей задать только параметр lowercase, вы можете выполнить следующую команду:
admin@dc-1:~$ sudo aldpro-salt-call random.get_str length=20 lowercase=True
local:
Z8Wta..yO|1Hq<>zf4$;
Работу программного кода jinja можно проверить с помощью функции template_str из модуля state. В следующем примере показано, как можно быстро проверить работу функции pop и append для работы со списками. Для вывода отладочной информации на экран используйте оператор do, с помощью которого можно вызвать функцию info из модуля log.
admin@dc-1:~$ sudo aldpro-salt-call state.template_str '{% set myList = ["one","two","three"] %}{% set temp = myList.pop(1) %}{% set temp = myList.append("two") %}{% do salt.log.info(myList) %}'
[INFO ] Loading fresh modules for state activity
[INFO ] ['one', 'three', 'two']
local:
Summary for local----------
Succeeded: 0
Failed:
0----------
Total states run:
0
Total run time: 0.000 ms
Информация о целевой системе (grains)
Разрабатывая скрипты Jinja вы можете опираться на информацию о целевом хосте, на котором этот скрипт будет применен. Данная информация доступна в словаре grains (зерна), тут вы найдете информацию об операционной системе, памяти, дисках, настройках сетевых интерфейсов и многом другом. Например, используя ключ nodename, можно получить имя хоста:
{% set node = salt['grains.get']('nodename') %}
Актуальное содержание словаря grains для конкретного хоста можно посмотреть утилитой aldpro-salt-call с помощью команды sudo aldpro-salt-call grains.items:
admin@dc-1:~$ sudo aldpro-salt-call grains.items
local:
----------
aldpro_dc_list:
- dc-1.ald.company.lan
aldpro_machine_type:
dc
astra_version:
----------
distr:
smolensk
version:
1.7.7
basedn:
dc=ald,dc=company,dc=lan
biosreleasedate:
12/01/2006
biosversion:
VirtualBox
chasis_type:
----------
chasis:
Other
is_notebook:
False
...
Выполним запрос grains.items на pc-1:
admin@dc-1:~$ sudo aldpro-salt-call grains.items
local:
----------
. . .
dns
----------
domain
ip4_nameservers
- 10.0.1.11
ip6_nameservers
nameservers
- 10.0.1.11
options
search
- ald.company.lan
sortlist
domain
ald.company.lan
efi
**False**
efi-secure-boot:
**False**
fqdn
pc-1.ald.company.lan
fqdn_ip4
- 10.0.1.51
. . .
mem_total
**1978**
nodename:
pc-1.ald.company.lan
num_cpus:
**2**
num_gpus
**1**
os:
AstraLinux
os_family
Debian
. . .
Параметры групповых политик в pillar
Скрипт получает значения параметров групповых политик через справочник pillar. Служба Salt-Minion-Standalone извлекает данные из LDAP-каталога с учетом наследования, выполняет суммирование и предоставляет результирующий словарь.
Содержание словаря pillar можно посмотреть с миньона утилитой aldpro-salt-call (данная команда является условным аналогом GPResult в MS AD) с помощью команды
sudo aldpro-salt-call pillar.items
Содержимое справочника определено в файле /srv/aldpro-salt/roots/pillar/top.sls
base:
'*':
- fonts
- policy_pillar_path
- aldpro_audit
- aldpro_gpo
- aldpro_po
- aldpro_user
Поясним ключи этого yaml-документа:
На первом уровне находятся ключи, которые определяют среду окружения. В файле описана только одна среда base, которая является средой окружения по умолчанию.
На втором уровне находятся ключи, которые таргетируют миньоны. В файле задана только одна цель с помощью символа подстановки «звездочка», под которую попадают миньоны с любым именем.
На третьем уровне указаны включаемые sls-файлы, в которых содержатся непосредственно кэшированные значения параметров.
Механизм кэширования данных pillar довольно сложный, поэтому есть сразу несколько файлов, в которых находятся данные параметров компьютера:
/opt/rbta/aldpro-salt/minion/gp-host,user_settings.json- параметры компьютера/пользователя из всех объектов групповых политик, которые назначены на текущий компьютер и всех пользователей, которые выполнили интерактивный вход в операционную систему этого компьютера/opt/rbta/aldpro-salt/minion/gp-host,user_pillar.json- результат суммирования параметров компьютера/пользователей в формате json/srv/aldpro-salt/roots/pillar/aldpro_gpo,user.sls- экспорт результатов суммирования параметров компьютера/пользователей в соответствующие sls-файлы словаря pillar
Если вы захотите изменить параметры компьютера на лету без внесения изменений в LDAP-каталог, это можно сделать в файле aldpro_{gpo,user}.sls. Вместе с тем, учитывайте, что эти изменения будут автоматически удалены при очередном применении групповых политик c обновлением кэша.
Если же вам нужно проверить, будет ли применена конкретная политика на хосте, вы можете применить состояние с параметром test=True. Если по результатам выполнения команды вы увидите сообщение «No changes needed to be made», значит система уже получила pillar с заданной ГП:
# aldpro-salt-call state.apply rbta_ldap_env_vars_h test=True
...
ID: /etc/bash.bashrc
Function: file.blockreplace
Result: True
Comment: No changes needed to be made
...
Более подробную информацию о модулях pillar можно найти в документации по проекту: docs.saltproject.io
Декларативные описания Salt
Декларативная часть скрипта описывает в YAML-формате желаемую конфигурацию компьютера. В структуре документа указано, какие методы нужно вызывать для конфигурирования системы и с какими параметрами. В качестве примера создадим файл software.sls со скриптом, который описывает состояние, после применения которого в системе должен стать доступен диспетчер задач htop.
Создадим файл vim software.sls со следующим содержимым:
software_htop: # Имя состояния
pkg.installed: # Вызов метода installed модуля salt.states.pkg
- pkgs: # Аргументы метода installed
- htop: # Значение аргумента pkgs
Скрипт можно выполнить с помощью команды aldpro-salt-call:
sudo aldpro-salt-call --local --file-root=. state.sls software
Рассмотрим параметры команды подробнее:
–file-root=. — устанавливает рабочую директорию, в которой будет выполняться поиск *.sls файлов
state.sls — указывает, что следует использовать функцию sls из модуля state, которая выполняет sls-скрипт
software — указывает имя sls-файла без расширения
По результатам выполнения команды видно, что установка прошла успешно и htop готов к работе:
...
[INFO ] Loading fresh modules for state activity
[INFO ] Completed state [software_htop] at time 11:39:24.930751 (duration_in_ms=3955.279)
local:
----------
ID: software_htop
Function: pkg.installed
Result: True
Comment: The following packages were installed/updated: htop
Started: 11:39:20.975472
Duration: 3955.279 ms
Changes:
----------
htop:
----------
new:
2.2.0-1
old:
Summary for local
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 3.955 s
Язык YAML, также как и JSON, является языком разметки, который позволяет описывать иерархические структуры данных. Первые три строки в приведенном примере задают пару ключ-значение, между которыми стоит знак двоеточия. Ключи могут состоять из одного и более слов, причем заключать их в кавычки необязательно. В качестве значений поддерживаются как скалярные типы данных (int, float, boolean, string), так и вложенные словари, что позволяет создавать древовидные структуры данных.
Второй и последующий уровни дерева должны обозначаться отступами с помощью двух и более пробелов, использовать символы табуляции вместо пробелов недопустимо. Если требуется прокомментировать какой-то фрагмент документа, то слева от комментария нужно поставить символ решетки.
На первом уровне структуры данных должен быть указан ключ с именем состояния, в нашем случае это software_htop. Имя выбирается произвольно, но в одном документе эти имена не должны повторяться. В предыдущих релизах ALD Pro идентификаторы состояний не должны были повторяться в пределах всех скриптов групповых политик. В актуальных версиях продукта данное ограничение снято, но мы все равно рекомендуем в идентификаторах состояний в качестве префикса использовать значение идентификатора параметра групповой политики.
На втором уровне структуры данных указывается функция, которую нужно вызвать. В нашем случае это функция installed из модуля pkg (искать по имени salt.states.pkg), который является модулем состояния, т. е. его методы приводят систему к заданному состоянию, описывая желаемый конечный результат, а не то, какие действия нужно выполнить. Существуют также модули исполнения, которые выполняют в системе конкретные действия, ранее мы уже показывали пример с использованием метода run модуля cmd для выполнения bash-команды.
В следующих строках определено, что функции нужно передать аргумент pkgs с значением htop. Обратите внимание, что аргумент и его значение являются элементами списков, поэтому в начале этих строк стоит символ дефиса.
Рекомендации к salt-скриптам
Для применения групповых политик у каждого параметра есть свой собственный скрипт, который представляет собой программный код на языке шаблонизатора Jinja. После рендеринга шаблона должен получаться yaml-документ, описывающий некоторую формулу Salt. Каждая формула может описывать один и более состояний Salt (стейтов), а каждое состояние, в свою очередь, определяет вызов одной и более функций из модулей Salt, выполнение которых, собственно, и вносит изменения на целевом хосте. В этом документе приведен ряд рекомендаций, которые помогут сделать ваши скрипты значительно лучше. Для пояснения рекомендаций мы приводим много примеров из проекта aldpro-group-policy-importer, поэтому настоятельно рекомендуем ознакомиться с файлами из этого проекта и использовать их в качестве дополнительного материала.
Ранее служба Salt-Minion выполняла скрипты дополнительных параметров всегда, т.е. вне зависимости от того, назначен этот параметр или нет. Именно поэтому в скрипте дополнительного параметра repo_source_list у нас есть проверка:
{% set id = 'rbta_ldap_custom_gp_host_repo_source_list' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
...
{% if gpo %}
...
{% endif %}
С версии 2.2.0 архитектура ALD Pro в части работы групповых политик изменилась. Теперь скрипты выполняются только в том случае, если на компьютер или пользователя назначен соответствующий параметр, поэтому делать эту проверку уже необязательно, и ее оставляют для обеспечения со старыми версиями.
Идентификаторы состояний должны включать идентификатор параметра групповой политики
В предыдущих версиях продукта скрипты групповых политик объединялись в одно общее дерево состояний, поэтому разработчикам нужно было вручную обеспечивать уникальность всех используемых идентификаторов состояний, иначе могла возникать ошибка «found conflicting ID „имя_идентификатора“». Указанную ошибку было довольно трудно отловить, т.к. содержимое yaml-документов может определяться динамически с помощью управляющих конструкций jinja в зависимости от параметров pillar, которые определяет пользователь при настройке параметров групповых политик. Данная проблема решалась путем добавления к идентификаторам состояний дополнительного префикса. В роли такого префикса выступал идентификатор самого параметра, уникальность которого гарантировалось уже самой системой по умолчанию. Начиная с версии 2.4.0, каждый параметр групповой политики выполняется по отдельности, поэтому скрипты могут содержать повторяющиеся идентификаторы, но этого рекомендуется все равно избегать. Например, если идентификатор параметра называется «rbta_ldap_custom_gp_host_sec_ssh», то у состояния, которое обеспечивает перезапуск службы, идентификатор может иметь значение «rbta_ldap_custom_gp_host_sec_ssh_restart_ssh», а чтобы уменьшить вероятность опечаток, значение идентификатора рекомендовалось формировать через подстановку значения переменной.
Пример скрипта
В следующем примере вы можете увидеть, что в первой строке идентификатор параметра записывается в переменную id, значение которой далее используется в строках 15 и 26 для формирования уникальных идентификаторов состояний.
{% set id = 'rbta_ldap_custom_gp_host_sec_ssh' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% if gpo %}
{%- macro getvalid(value, available) -%}
{{- value if value!='' and value.lower() in available|lower else available[0] -}}
{%- endmacro %}
{%- set permit_root_login = getvalid(gpo['permit_root_login'], ['no','yes','without-password','forced-commands-only']) %}
{%- set client_alive_interval = gpo['client_alive_interval']|int(15) %}
{%- set client_alive_count_max = gpo['client_alive_count_max']|int(1) %}
{%- set protocol = gpo['protocol']|int(2) %}
{{ id }}:
ini.options_present:
- name: '/etc/ssh/sshd_config'
- separator: ' '
- strict: False
- sections:
PermitRootLogin: '{{ permit_root_login }}'
ClientAliveInterval: '{{ client_alive_interval }}'
ClientAliveCountMax: '{{ client_alive_count_max }}'
Protocol: '{{ protocol }}'
{{ id }}_restart_ssh:
service.running:
- name: ssh
- restart: True
- watch:
- file: /etc/ssh/sshd_config
{% endif %}
После шаблонизации в формуле должно оставаться как минимум одно состояние
Управляющие конструкции Jinja позволяют динамически управлять содержанием salt формул, но эти конструкции следует использовать таким образом, чтобы после работы шаблонизатора в salt-формуле оставалось как минимум одно состояние, иначе это сильно затруднит отладку групповых политик. Даже если jinja-код определит, что никаких изменений вносить не требуется, он должен оставить в формуле хотя бы одно состояние, которое будет информировать системного администратора о результатах применения данного параметра групповой политики. Для того чтобы описать такое состояние вы можете использовать одну из функций модуля test, например, show_notification, nop, succeed_without_changes, fail_without_changes, succeed_with_changes или fail_with_changes.
Пример скрипта
В следующем примере вы можете увидеть, что в строках 39-43 описано состояние с использованием функции test.fail_without_changes, которое останется в salt-формуле, если скрипт будет запущен на операционной системе, которая не поддерживается.
{% set id = 'rbta_ldap_custom_gp_host_repo_source_list' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id, default=[]) %}
{% for item in gpo %}
{% do item.update({'os_type': item.get('os_type', '').lower() }) %}
{% endfor %}
{% set os_type = salt['grains.get']('os') | lower %}
{% set lines = gpo | selectattr('os_type', '==', os_type)
| selectattr('repo_source_item', 'string')
| map(attribute='repo_source_item')
| map('trim')
| select('!=', '')
| unique
| list
%}
{{ id }}:
{%- if os_type in ['astra', 'debian', 'alt'] %}
file.managed:
- name: /etc/apt/sources.list.d/repo_source.list
- contents: |
{%- for line in lines %}
{{ line }}
{%- endfor %}
{%- elif os_type in ['red os', 'redhat'] %}
file.managed:
- name: /etc/apt/sources.list.d/repo_source.list
- contents: |
{%- for line in lines %}
{%- set sublines = line.split('|') %}
[customs_{{ loop.index }}]
{%- for subline in sublines %}
{{ subline }}
{%- endfor %}
{% endfor %}
{% else %}
test.fail_without_changes:
- name: "ОС не поддерживается!"
- failhard: False
{% endif %}
Скрипт нужно выполнять только если параметр групповой политики назначен на пользователя/компьютер
В предыдущих версиях продукта все скрипты групповых политик выполнялись вне зависимости от того, назначены они на пользователя/компьютер или нет. По этой причине от разработчиков дополнительных параметров требовалось, чтобы в скрипте выполнялась проверка на наличие соответствующих ключей в данных pillar, что обычно делали следующим образом:
Пример скрипта
В следующем примере используется ветвление {% if gpo %}, которое позволяет выполнить скрипт только в том случае, если соответствующий параметр назначен на компьютер.
{% set id = 'rbta_ldap_custom_gp_host_dyndns' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% if gpo %}
{% macro getvalid(value, available) -%}
{{- value if value!='' and value.lower() in available|lower else available[0] -}}
{%- endmacro %}
{% set domain = salt['grains.get']('domain') %}
{% set dyndns_update = getvalid(gpo['dyndns_update'], ['true', 'false'])|lower %}
{% set dyndns_update_ptr = getvalid(gpo['dyndns_update_ptr'], ['true','false'])|lower %}
{% set dyndns_refresh_interval = gpo['dyndns_refresh_interval'] if gpo['dyndns_refresh_interval'] != '' and gpo['dyndns_refresh_interval'] | int != 0 else '43200' %}
{% set dyndns_ttl = gpo['dyndns_ttl'] if gpo['dyndns_ttl'] != '' and gpo['dyndns_ttl']|int != 0 else '3600' %}
{{id}}_dyndns_update:
ini.options_present:
- name: /etc/sssd/sssd.conf
- separator: '='
- sections:
domain/{{domain}}:
dyndns_update: {{dyndns_update}}
{{id}}_dyndns_update_ptr:
ini.options_present:
- name: /etc/sssd/sssd.conf
- separator: '='
- sections:
domain/{{domain}}:
dyndns_update_ptr: {{dyndns_update_ptr}}
{{id}}_dyndns_refresh_interval:
ini.options_present:
- name: /etc/sssd/sssd.conf
- separator: '='
- sections:
domain/{{domain}}:
dyndns_refresh_interval: {{dyndns_refresh_interval}}
{{id}}_dyndns_ttl:
ini.options_present:
- name: /etc/sssd/sssd.conf
- separator: '='
- sections:
domain/{{domain}}:
dyndns_ttl: {{dyndns_ttl}}
{% endif %}
В последних релизах аналогичная проверка уже встроена в систему, поэтому вне зависимости от настроек групповых политик всегда будут выполняться только несколько избранных скриптов коробочных параметров. Например, всегда будет отрабатывать скрипт chrony, который по умолчанию выполняет настройку службы синхронизации времени в иерархии домена. Не смотря на то, что данная проверка теперь не является обязательной, ее рекомендуется оставлять для обеспечения обратной совместимости.
Скрипт должен гарантировать идемпотентность
Групповые политики применяются на компьютере каждый час, поэтому повторное выполнение скриптов не должно приводить к каким-либо ошибкам, а итоговый результат должен быть таким же, как и при первом запуске. Данное свойство называется идемпотентностью, и оно гарантируется при использовании функций из модулей состояния.
Пример скрипта
В следующем примере в строках 26-29 продемонстрировано использование функции из file.append из модуля состояния, которая не просто добавляет строку в файл каждый раз при ее вызове, а гарантирует, что эта строка будет добавлена, если ее еще нет в файле.
{% set id = 'rbta_ldap_custom_gp_host_sec_print' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% if gpo %}
{%- macro getvalid(value, available) -%}
{{- value if value!='' and value.lower() in available|lower else available[0] -}}
{%- endmacro %}
{%- set deny_print = getvalid(gpo['deny_print'], ['true', 'false'])| lower %}
{%- set filename = '/etc/cups/cupsd.conf' %}
{%- if deny_print=='true' %}
{{ id }}_AllowAll:
file.blockreplace:
- name: {{ filename }}
- marker_start: '<Location />'
- marker_end: '</Location>'
- append_if_not_found: True
- content: |
Order allow,deny
Allow from 127.0.0.1
{{ id }}_DefaultShared:
file.append:
- name: {{ filename }}
- text: 'DefaultShared No'
{%- else %}
{{ id }}_AllowAll:
file.blockreplace:
- name: {{ filename }}
- marker_start: '<Location />'
- marker_end: '</Location>'
- append_if_not_found: True
- content: |
Order allow,deny
Allow all
{{ id }}_DefaultShared:
file.line:
- name: {{ filename }}
- mode: delete
- match: 'DefaultShared'
{%- endif %}
{% endif %}
Ресурсоемкие действия не следует выполнять повторно, если в них нет необходимости
Некоторые действия по конфигурированию целевой системы могут расходовать значительные вычислительные ресурсы, поэтому их выполнения следует избегать, если в этом нет необходимости и система уже находится в целевом состоянии. Например, если в системе уже включен мандатный контроль целостности, то при повторном выполнении скрипта включать его еще раз не требуется. Именно такую логику реализуют функции из модулей состояния, а при использовании функций выполнения, таких как cmd.run, для достижения похожего результата следует использовать такие директивы, как unless и onlyif.
Пример скрипта
В следующем примере в строке 14 продемонстрировано использования директивы unless, которая позволяет избежать повторной записи атрибута nsosversion в LDAP каталог, если его значение не изменилось.
{% set id = 'rbta_ldap_custom_gp_host_update_nsosversion' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% if gpo %}
{%- set host = salt['grains.get']('fqdn') %}
{%- set grains_key = gpo['grains_key'] if grains.get(gpo['grains_key']) else 'lsb_distrib_description' %}
{%- set nsosversion = salt['grains.get'](grains_key) %}
{{ id }}:
cmd.run:
- name: ipa host-mod '{{ host }}' --setattr=nsosversion='{{ nsosversion }}'
- unless: ipa host-show '{{ host }}' --raw --all | grep 'nsosversion' | grep '{{ nsosversion }}'
{% endif %}
Связанные состояния нужно объединять в цепочки
В тех случаях, когда какие-то состояния нужно применять в строго определенной последовательности, их нужно объединять в цепочки с помощью директивы require, т.к. очередность состояний в sls-файле не дает полной гарантии, что их функции будут выполнены в том же порядке. После парсинга yaml-документов формируется низкоуровневая структура данных (low data), которая описывает вызов функций, и к этой структуре применяются различные оптимизации, которые повлиять на порядок выполнения функций.
Дополнительные возможности по формированию цепочек состояний могут предоставить такие директивы, как onchanges, watch, listen, prereq, onfail и use, которые позволяют выполнять функцию не просто после указанной зависимости, но еще и только в том случае, если эта зависимая функция возвращает определенное состояние. Кроме уже обозначенных возможностей есть еще директива order, с помощью которой можно также повлиять на последовательность выполнения состояний, но она существенно уступает по функциональным возможностям, т.к. позволяет поставить выполнение функции только в начало (order: 1) или конец (order:last) очереди, а назначать конкретные значения очередности выполнения крайне не рекомендуется.
В следующем примере в строках 26-31 продемонстрировано использование директивы watch, которая гарантирует, что служба ssh будет перезапущена только после выполнения тех состояний, которые затрагивают изменение файла /etc/ssh/sshd_config, и только в том случае, если файл будет действительно изменен.
{% set id = 'rbta_ldap_custom_gp_host_sec_ssh' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% if gpo %}
{%- macro getvalid(value, available) -%}
{{- value if value!='' and value.lower() in available|lower else available[0] -}}
{%- endmacro %}
{%- set permit_root_login = getvalid(gpo['permit_root_login'], ['no', 'yes','without-password','forced-commands-only']) %}
{%- set client_alive_interval = gpo['client_alive_interval']|int(15) %}
{%- set client_alive_count_max = gpo['client_alive_count_max']|int(1) %}
{%- set protocol = gpo['protocol']|int(2) %}
{{ id }}:
ini.options_present:
- name: '/etc/ssh/sshd_config'
- separator: ' '
- strict: False
- sections:
PermitRootLogin: '{{ permit_root_login }}'
ClientAliveInterval: '{{ client_alive_interval }}'
ClientAliveCountMax: '{{ client_alive_count_max }}'
Protocol: '{{ protocol }}'
{{ id }}_restart_ssh:
service.running:
- name: ssh
- restart: True
- watch:
- file: /etc/ssh/sshd_config
{% endif %}
Используйте возможности Salt на максимум
Штатные модули Salt предоставляют множество функций, с помощью которых можно в несколько строк и с гарантированно высоким качеством настроить практически все, что угодно, поэтому прежде чем начинать работу по созданию собственных модулей, расширяющих возможности Salt, следует тщательным образом изучить те возможности, которые доступны из коробки. По той же причине следует критически анализировать уже написанные скрипты на предмет того, можно ли их дополнительно улучшить.
Резюмируя, можно сказать, что разработка собственных моделей состояния оправдано только в самых редких случаях, когда эти функции будут задействованы как минимум в 3+ скриптах и предполагают реализацию очень сложной логики.
Пример скрипта
Мы уже приводили пример с параметром rbta_ldap_custom_gp_host_sec_ssh, но его скрипт отлично подойдет в том числе и для иллюстрации этого тезиса. В строках 15-24 продемонстрирована возможность настройки обычного конфигурационного файла с помощью модуля ini, когда в файле нет секций, а в качестве разделителя между ключом и значением используется символ пробела.
{% set id = 'rbta_ldap_custom_gp_host_sec_ssh' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% if gpo %}
{%- macro getvalid(value, available) -%}
{{- value if value!='' and value.lower() in available|lower else available[0] -}}
{%- endmacro %}
{%- set permit_root_login = getvalid(gpo['permit_root_login'], ['no', 'yes','without-password','forced-commands-only']) %}
{%- set client_alive_interval = gpo['client_alive_interval']|int(15) %}
{%- set client_alive_count_max = gpo['client_alive_count_max']|int(1) %}
{%- set protocol = gpo['protocol']|int(2) %}
{{ id }}:
ini.options_present:
- name: '/etc/ssh/sshd_config'
- separator: ' '
- strict: False
- sections:
PermitRootLogin: '{{ permit_root_login }}'
ClientAliveInterval: '{{ client_alive_interval }}'
ClientAliveCountMax: '{{ client_alive_count_max }}'
Protocol: '{{ protocol }}'
{{ id }}_restart_ssh:
service.running:
- name: ssh
- restart: True
- watch:
- file: /etc/ssh/sshd_config
{% endif %}
Избегайте повторов
В скриптах групповых политик следует избегать следующих антипаттернов:
дублирования управляющих конструкций jinja;
впечатывания одних и тех же констант вручную;
повторного описания состояний с минимальными отличиями.
Если выбор будет между тем, чтобы повторить управляющую конструкцию jinja или описание состояния, то более желательно избежать дублирования управляющих конструкций, чтобы упростить чтение кода.
Проверяйте данные pillar
Данные pillar, поступающие от пользователей, должны быть проверены тщательным образом, чтобы исключать ошибки ввода или даже злонамеренные инъекции. Например:
Используйте метод get для безопасного извлечения параметров, чтобы исключить появление ошибок в тех случаях, когда параметр не определен. Используйте возможности метода по определению значения по умолчанию {%- set param = gpo.get(„param“, „default value“) -%}
Если метод get использовать невозможно, задействуйте фильтр default для установки значений по умолчанию. Например, {%- set param = gpo[„param“] | default(„installed“) -%}
Целочисленные значения:
Приводите целочисленные атрибуты к целым числам с помощью фильтра int. Например, {%- set param = gpo[„param“] | int(45) -%}
Ограничивайте целочисленные атрибуты допустимым диапазоном значений «сверху» с помощью фильтра min. Например, {%- set param = [100, param] | min -%}
Ограничивайте целочисленные атрибуты допустимым диапазоном значений «снизу» с помощью фильтра max. Например, {%- set param = [0, param] | max -%}
Строковые значения
Удаляйте лишние пробелы в начале и конце строки с помощью фильтра trim. Например, {%- set param = gpo[„param“] | trim -%}
Приводите строку к требуемому регистру символов с помощью фильтров lower и upper. Например, {%- set param = gpo[„param“] | lower -%}
Для более сложной обработки входных данных используйте макросы, например:
{%- macro getvalid(value, available) -%}
{{- value if value!='' and value.lower() in available|lower else available[0] -}}
{%- endmacro %}
...
{%- set pkg_state = getvalid(item.pkg_state, ['installed', 'removed']) %}
Проверяйте неявные преобразования
Язык шаблонизации jinja предоставляет большие возможности, но вместе с тем несет и большие угрозы, т.к. требует тщательной проверки используемых синтаксических конструкций, в первую очередь в отношении неявного преобразования данных. Например, когда вы выполняете ветвление по значению переменной, в которой должен быть предположительно список элементов, вы должны понимать, что этот список неявно преобразуется к переменной Boolean и от того, каким образом выполняется это преобразование, зависит работа вашего алгоритма.
Примечание
Пустые списки интерпретируются в Jinja как False, а списки, в которых есть какие-то значения, как True
{% if items %}
{# ветка, которая будет выполнена, когда в списке items есть элементы #}
{% else %}
{# когда список пуст #}
{% endif %}
Импорт дополнительных параметров
В разделе дополнительных источников информации приведена ссылка на архив с набором дополнительных параметров групповых политик из личного кабинета клиента.
В архиве находится в том числе скрипт policy.py, с помощью которого дополнительные параметры можно автоматически импортировать в домен. Скрипт написан на языке Python 3 и взаимодействует с контроллером домена через REST API.
Перед выполнением импорта следует определить параметры подключения к серверу через переменные окружения. Пароль можно передавать открытым текстом, но скрипт поддерживает в том числе и Kerberos-аутентификацию.
export ALDPRO_API_SERVER_URL='dc-1.ald.company.lan'
export ALDPRO_API_CA_PATH='/etc/ipa/ca.crt'
export ALDPRO_API_LOGIN='admin'
export ALDPRO_API_PASSWORD='Pa$$w0rd'
Для импорта всех параметров сразу можно использовать скрипт import.sh, который по цепочке вызовет остальные sh-скрипты, создающие объекты в каталоге.
admin@dc-1:~$ sh import.sh
Пример создания папки из скрипта import_folder_common.sh:
python3 policy.py folder add \
--host \
--parent-folder 'Дополнительные параметры' \
--folder-name 'Общие параметры'
Пример создания параметра import_parameter_common_firefox_policy.sh:
python3 policy.py parameter add \
--host \
--id 'rbta_ldap_custom_gp_host_firefox_policy' \
--name 'Политика браузера Firefox' \
--parent-folder 'Общие параметры' \
--description 'Позволяет централизованно настраивать параметры браузера Firefox путем изменения содержания файла /usr/lib/firefox/distribution/policies.json
Атрибут "Блокировать доступ к странице addons" устанавливает значение параметра BlockAboutAddons
- true - доступ заблокирован
- false - доступ предоставлен
Если значение не задано, то параметр будет отсутствовать в файле политики, по умолчанию доступ предоставляется.
Атрибут "Блокировать доступ к странице config" устанавливает значение параметра BlockAboutConfig
- true - доступ заблокирован
- false - доступ предоставлен
Атрибут "Список доменов для Kerberos-аутентификации" устанавливает значение параметра Authentication.SPNEGO, элементы списка разделяются запятыми, например:
- ald.company.lan, win.company.lan
Атрибут "Список доверенных сертификатов" устанавливает значение параметра Certificates.Install. Требуется указывать полный путь к файлу на локальном диске целевой машины, элементы списка разделяются запятыми, например:
- /etc/ipa/ca.crt, /etc/ssl/certs/ca-certificates.crt
Атрибут "Адрес домашней страницы" устанавливает значение параметра Homepage.URL, требуется указать полный URL адрес страницы, например
- https://dc-1.ald.company.lan
Если адрес домашней страницы задан, то дополнительно будут установлены параметры Homepage.Locked=true и Homepage.StartPage=homepage-locked' \
--attr 'Блокировать доступ к странице addons':block_about_addons:'' \
--attr 'Блокировать доступ к странице config':block_about_config:'' \
--attr 'Список доменов для Kerberos-аутентификации':authentication_spnego:'' \
--attr 'Список доверенных сертификатов':trusted_certificates:'' \
--attr 'Адрес домашней страницы':homepage_url:'' \
--script 'import_parameter_common_firefox_policy.sls' \
--script-comment 'Версия 1.0'
Управление и настройка ПО с помощью политик ALD Pro
Система ALD Pro позволяет централизовано устанавливать и настраивать программное обеспечение через «Политики ПО» и «Групповые политики». Если первые обеспечивают более удобное управление и простоту настройки, то вторые большую гибкость и сложность.
«Групповые политики» используются для приведения и поддержания в заданном состоянии настроек на доменных компьютерах. Групповые политики не предусматривают возможность установки и настройки ПО «из коробки». Однако, создавая дополнительные параметры групповых политик, мы ограничены только тем, какие модули и методы Salt мы можем использовать.
«Политики ПО» позволяют устанавливать программы только из deb-пакетов доступных в заранее подготовленных репозиториях. Настройка программы тут может выполняется через создание файлов из шаблонов с параметрами. Политики ПО позволяют устанавливать, обновлять и удалять (если ПО было установлено через политику) программы, создавать конфигурационные файлы. Однако, политики ПО не применимы для выполнения более сложных действий при конфигурировании ПО, например, запуск службы, создание символических ссылок, запуск программ. Для этих целей могут быть использованы дополнительные параметры групповых политик.
С одной стороны, управление ПО через «Групповые политики» отрывает почти безграничные возможности по установке ПО. Можно устанавливать программы хоть из пакетов, хоть из исходных кодов, хоть используя AppImage или устанавливать программы, работающие через Wine. Так же мы можем использовать возможности Salt для создания и изменения конфигурационных файлов приложения.
С другой стороны, создание дополнительных параметров групповых политик не тривиальная задача для среднестатистического администратора. Тут необходимо базовое знание Salt и умение писать bash скрипты, императивные шаблоны Jinja2 и декларативные состояния Salt. Это увеличивает порог входа для использования этого подхода. Однако этот порог может быть понижен, если будет подготовлена толковая инструкция по настройке такого дополнительного параметра ГП.
Кроме того, в ALD Pro разработали специальный инструмент командной строки policy.py – утилиту на Python, которая, используя специальные входные файлы определенного формата, автоматически создает в домене ALD Pro дополнительные параметры групповых политик. Администратору остается только создать групповую политику с необходимом дополнительным параметром, корректно задать значения для этого параметра и назначить её на целевые АРМ.
Минусом установки ПО как через «Политики ПО», так и через «Групповые политики» является отсутствии обратной связи со стороны целевого компьютера. Администратор может централизовано назначить установку, обновление, удаление или настройку ПО через интерфейс ALD Pro, но не сможет централизовано собрать данные о том, где настройки применились корректно, а где не применились или применились с ошибками.
Продемонстрируем в данном модуле возможности настройки ПО через групповые политики и дополнительные параметры.
В качестве примера «сложного» ПО, требующего дополнительных шагов для корректной работы, установим и настроим клиент RuBackup. Причем сделаем это с помощью дополнительных параметров групповых политик.
Для установки ПО через политики ПО, в качестве источников пакетов необходимо использовать дополнительную подсистему ALD Pro – сервер репозитория ПО, которая рассмотрена в Модуль 11. Дополнительные подсистемы и интеграции в разделе Подсистема «Репозиторий программного обеспечения», где мы с вами детальнее рассмотрим работу политики ПО, установим сервер репозитрия ПО и разольем Yandex браузер на клиентские рабочие станции. Установка пакетов через групповые политики может осуществляться из любого источника.
Управление через «Групповые политики»
Создание доп. параметров групповой политики для управления и настройки ПО
Для того чтобы в домене можно было организовать централизованное управление корпоративными приложениями, в системе ALD Pro реализована возможность создания дополнительных параметров групповых политик, которые решают ту же задачу, что и административные шаблоны Windows.
Создадим дополнительный параметр групповых политик, с помощью которого можно будет централизованно управлять источниками программного обеспечения для пакетного менеджера apt.
Создание доп. параметров групповых политик доступно в одноименном разделе портала управления . Вы можете выбрать вкладку или в зависимости от того, хотите ли вы создать параметр для настройки окружения пользователя или компьютера. Будьте внимательны, т. к. перенести параметр из одного раздела в другой после его создания не получится.
Так как установка агента RuBackup это настройка общая для всего хоста, на вкладке кликнем по узлу и нажмем кнопку .
рис. 339 Создание нового дополнительного параметра
До первого сохранения параметра нам будет доступна только вкладка . Заполним ее параметры:
Название параметра: .
Это имя параметра, под которым вы его хотите видеть при редактировании объектов групповых политик на портале управления.
Уникальный идентификатор: .
Полное значение идентификатора будет «rbta_ldap_custom_gp_host_manage_rubackup_client». Оно будет использоваться в качестве имени папки на диске и в salt-скриптах.
Тип каталога: .
Определяет, относится ли данный параметр к настройкам компьютера или пользователя. Параметр недоступен для редактирования, его значение определяется автоматически в зависимости от того, с какой страницы вы перешли к созданию дополнительного параметра.
Тип параметра: .
- Позволяет указать способ хранения атрибутов:
— задает плоский список атрибутов. Например, для настройки межсетевого экрана может потребоваться задать уровень детализации журнала, и эта настройка подразумевает одно конкретное значение, поэтому она может быть реализована атрибутом «простого» параметра.
— задает таблицу атрибутов, где в каждой строке представлен типовой набор данных. Если продолжать пример с межсетевым экраном, то для настройки фильтрации может потребоваться разрешить входящие ssh-соединения, запретить исходящие smtp и т.п., поэтому такие настройки нужно реализовывать атрибутами «составного» параметра.
Папка параметра: .
Указывает раздел каталога, в котором будет представлен этот параметр.
Назначение параметра:
Позволяет централизованно управлять клиентом RuBackup: устанавливать, настраивать и удалять.
Атрибут "Требуемое действие" определяет тип требуемого действия над клиентом RuBackup и
может принимать значения:
install – для установки
remove – для удаления
Атрибут "Основной сервер RuBackup" определяет имя или ip адрес мастер-сервера RuBackup.
Используется в случае установки или перенастройки клиента.
оставьте по умолчанию.
Тут задается информация о том, с какими версиями ОС совместим данный параметр.
Обязательно нажмите кнопку .
рис. 340 Вкладка «Основное» дополнительного параметра
На вкладке «Атрибуты параметра» нам нужно добавить два элемента:
«Требуемое действие»
: Требуемое действие.
Название, под которым вы хотите видеть этот атрибут на карточке параметра.
: action.
Идентификатор атрибута, под которым значение атрибута будет доступно в пилларе для его использования из salt-скрипта.
: Требуемое действие: install или remove.
Краткие комментарии, которые помогут упростить поддержку этого атрибута в будущем, когда потребуется внести какие-либо изменения. Данная информация недоступна при настройке групповой политики, поэтому, если вы хотите дать подсказку администратору о возможных значениях этого атрибута, внесите эту информацию в описание параметра.
: отметьте галочкой.
Атрибуты, отмеченные как , должны быть заполнены при применении параметра.
рис. 341 Вкладка «Атрибуты параметра» дополнительного параметра.
«Основной сервер RuBackup».
: «Основной сервер RuBackup».
: «rubackup_master».
: «Имя или ip адрес мастер-сервера RuBackup».
: оставьте пустым.
рис. 342 Вкладка «Атрибуты параметра» дополнительного параметра
На вкладке необходимо внести текст Salt-скрипта и заполнить обязательное поле комментарий, по которому можно будет идентифицировать его версии.
Нажмите кнопку и перенесите текст скрипта, приведённый ниже:
# Определение переменных из параметров задания автоматизации
{% set id = 'rbta_ldap_custom_gp_host_manage_rubackup_client' %}
{% set node = salt['grains.get']('nodename') %}
{% set gpo = salt['pillar.get']('aldpro-hosts:' + node + ':' + id) %}
{% if gpo %}
{% set action = gpo['action'] %}
{% set rubackup_master = gpo['rubackup_master'] %}
# Установка
{% if action.lower() == 'install' %}
{% do salt.log.info("Установка клиента RuBackup") %}
{{ id }}_install_rubackup_client:
pkg.installed:
- name: rubackup-common
- name: rubackup-client
{{ id }}_root_profile_configure:
file.append:
- name: /root/.bashrc
- text:
- "PATH=$PATH:/opt/rubackup/bin"
- "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/rubackup/lib"
- "export PATH"
- "export LD_LIBRARY_PATH"
{{ id }}_configure_rubackup_client:
{% if not salt[ 'file.file_exists' ]('/opt/rubackup/keys/master-key') %}
cmd.run:
- name: '/opt/rubackup/bin/rb_init --agree-with-eula --primary-fqdn "{{ rubackup_master }}" --client-iface eth0 --remote-replica --centr-recovery --local-backup-dir /rubackup-tmp --used-ip-version ipv4 --node client --monitoring'
- require:
- pkg: rubackup-client
{% else %}
file.keyvalue:
- name: /opt/rubackup/etc/config.file
- key: who-is-primary-server
- value: {{ rubackup_master }}
- separator: ' '
- ignore_if_missing: True
{% endif %}
{{ id }}_start_rubackup_client_service:
service.running:
- name: rubackup_client
- enable: true
- require:
- pkg: rubackup-client
# Удаление
{% elif action.lower() == 'remove' %}
{% do salt.log.info("Удаление клиента RuBackup") %}
{{ id }}_stop_rubackup_client:
service.dead:
- name: rubackup_client
- enable: false
{{ id }}_remove_rubackup_client:
pkg.purged:
- name: rubackup-client
- name: rubackup-common
{{ id }}_housekeeping_config_files1:
file.line:
- name: /root/.bashrc
- mode: delete
- match: 'PATH=\$PATH:/opt/rubackup/bin'
- quiet: True
{{ id }}_housekeeping_config_files2:
file.line:
- name: /root/.bashrc
- mode: delete
- match: 'LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/opt/rubackup/lib'
- quiet: True
{{ id }}_housekeeping_config_files3:
file.line:
- name: /root/.bashrc
- mode: delete
- match: 'export PATH'
- quiet: True
{{ id }}_housekeeping_config_files4:
file.line:
- name: /root/.bashrc
- mode: delete
- match: 'export LD_LIBRARY_PATH'
- quiet: True
{{ id }}_housekeeping_config_files5:
file.absent:
- name: /opt/rubackup/
{% else %}
{% do salt.log.info("Ошибка! Выбрано некорректное действие {{ action }}") %}
{% endif %}
{% endif %}
Данный скрипт, в зависимости от типа выбранного действия, установит и настроит или удалит клиент RuBackup.
рис. 343 Вкладка «Конфигурация скрипта» дополнительного параметра
Создание, настройка и применение групповой политики.
Для применения доп. параметра ГП на доменных компьютерах необходимо создать и настроить групповую политику с этим параметром.
Для создания групповой политики перейдите в раздел «Групповые политики» и нажмите соответствующую кнопку .
рис. 344 Создание новой групповой политики
До первого сохранения объекта нам доступна только вкладка «Основное», где требуется задать имя объекта и описание. Введем следующие значения:
: Управление клиентом RuBackup
: Устанавливает, перенастраивает или удаляет клиент RuBackup
И нажмем кнопку .
рис. 345 Групповая политика вкладка «Основное»
Теперь нам доступны для редактирования остальные вкладки:
– отображает список параметров, которые задействованы в этом объекте групповой политики. Вы можете удалить параметр или быстро перейти к его редактированию.
– содержит параметры для настройки окружения компьютера.
– параметры для настройки окружения пользователя.
– содержит список подразделений, на которые назначен объект групповой политики. На этой вкладке вы можете удалить назначение или назначить объект на дополнительные подразделения.
Включим параметр () и установим значение поля:
: install
: имя или ip адрес вашего сервера RuBackup
и нажмем кнопку .
рис. 346 Групповая политика вкладка «Параметры компьютеров»
Подробную справку о параметре групповой политики можно получить, если кликнуть по значку с символом вопроса, который расположен в правом верхнем углу карточки. Как вы можете заметить, это тот самый текст, который мы вводили в поле при создании доп. параметра ГП.
рис. 347 Вызов справки по групповой политке
Чтобы закрыть справку и вернуться к форме редактирования, сделайте клик по значку в форме крестика, который расположен в правом верхнем углу карточки.
Перейдем на вкладку и добавим новое назначение кнопкой .
рис. 348 Групповая политика вкладка «Подразделения»
4.1. Так как мы хотим применить объект групповой политики на клиентские компьютеры, то выберем подразделение, например, и нажмем кнопку .
Обратите внимание на поле . Это значение позволяет определить порядок применения параметров, если на одно и то же организационное подразделение назначено несколько объектов групповой политики.
рис. 349 Групповая политика выбор подразделения
В карточке компьютера из подразделения можно убедиться, что параметр ГП на него назначен и посмотреть значения его атрибутов.
рис. 350 Карточка компьютера
Полный путь до файла скрипта, который используются для применения этого параметра:
/srv/aldpro-salt/roots/states/policies/host-policies/rbta_ldap_custom_gp_host_manage_rubackup_client/init.sls
Чтобы не ждать обновления параметров выполним на компьютере pc-1.ald.company.lan команду:
sudo aldpro-gpupdate --gp
systemctl status rubackup_client
admin@pc-1:~$ sudo aldpro-gpupdate --gp
Форсированное применение групповых политик
[INFO ] Loading fresh modules for state activity
. . .
[INFO ] Running state [run_rbta_ldap_custom_gp_host_manage_rubackup_client] at time 12:38:17.600170
[INFO ] Executing state salt.state for [run_rbta_ldap_custom_gp_host_manage_rubackup_client]
[INFO ] Loading fresh modules for state activity
[INFO ] Установка клиента RuBackup
[INFO ] Running state [rubackup-client] at time 12:38:22.952570
[INFO ] Executing state pkg.installed for [rubackup-client]
[INFO ] Executing command dpkg-query in directory '/root'
[INFO ] Executing command apt-cache in directory '/root'
[INFO ] Executing command apt-get in directory '/root'
[INFO ] Executing command dpkg in directory '/root'
[INFO ] Executing command systemd-run in directory '/root'
[INFO ] Executing command dpkg-query in directory '/root'
[INFO ] Made the following changes:
'rubackup-client' changed from 'absent' to '2.3.0.12-1'
'rubackup-common' changed from 'absent' to '2.3.0.12-1'
. . .
id__': 'rbta_ldap_custom_gp_host_manage_rubackup_client_configure_rubackup_client'}}}}
[INFO ] Completed state [run_rbta_ldap_custom_gp_host_manage_rubackup_client] at time 12:40:12.964257 (duration_in_ms=115364.087)
. . .
admin@pc-1:~$ systemctl status rubackup_client
● rubackup_client.service - RuBackup client
Loaded: loaded (/etc/systemd/system/rubackup_client.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2024-12-28 12:40:10 MSK; 2min 7s ago
Main PID: 1897226 (rubackup_client)
Tasks: 5 (limit: 2250)
Memory: 46.4M
CPU: 23.512s
CGroup: /system.slice/rubackup_client.service
└─1897226 /opt/rubackup/bin/rubackup_client start
Для удаления клиента RuBackup достаточно изменить значение атрибута на
LAPS - Управление паролями локальных администраторов
При установке операционной системы создается учетная запись как минимум одного локального администратора, с помощью которой на этом хосте можно получить права суперпользователя, поэтому жизненный цикл локальных учетных записей на компьютерах в домене требует повышенного внимания со стороны системных администраторов. Существуют разные подходы к решению этой задачи, например, в службе каталога ALD Pro уже был параметр групповой политики, с помощью которого можно было централизованно запретить локальным пользователям вход в операционную систему компьютеров ALSE, но теперь стало доступно еще и специализированное решение по управлению паролями локальных администраторов - Local Admins Password Solution или кратко, LAPS.
Механизм работы LAPS
Локальная учетная запись администратора может потребоваться в тех случаях, когда войти в операционную систему компьютера с помощью учетной записи из домена окажется невозможно. Вместе с тем, администрировать локальные учетные записи вручную представляется крайне сложной задачей, поэтому решение LAPS становится важным компонентом для обеспечения безопасности ИТ-инфраструктуры.
рис. 351 Архитектура LAPS
Решение LAPS и состоит из трех основных компонентов:
1. Каталог LDAP - хранит информацию о пароле локального администратора в защищенных атрибутах учетной записи компьютера, доступ к которым имеют только участники группы admins и сами компьютеры. Решение LAPS использует следующие атрибуты:
Атрибут aldproLAPSPassword – хранит json-объект с информацией о пароле локального администратора, причем компьютер имеет право только на запись этого атрибута, а чтение ему недоступно. Объект json содержит следующие ключи:
name - имя локального администратора, которому был установлен пароль на данном компьютере;
pass - пароль, закодированный в base64;
time - время, когда этот пароль был установлен в системе в GMT;
error - необязательный ключ с текстом ошибки, которая возникла при изменении пароля.
Атрибут aldproLAPSPasswordExpirationTime – хранит срок действия пароля, по истечению которого пароль должен быть обновлен.
Атрибут aldproLAPSCurrentPasswordVersion – хранит случайное число длиной 16 символов, определяющее версию пароля. Это значение позволяет определить, что операционная система целевого хоста была восстановлена из резервной копии и нужно изменить пароль еще раз во внеочередном порядке, чтобы в каталоге было актуальное значение пароля.
Параметр групповой политики - определяет набор настроек и содержит salt скрипт, реализующий логику LAPS. При каждом применении групповых политик скрипт параметра выполняет на компьютере следующие действия:
Извлекает из учетной записи компьютера в домене значения атрибутов aldproLAPSPasswordExpirationTime и aldproLAPSCurrentPasswordVersion с информацией о пароле локального администратора, который был сохранен в LDAP-каталоге.
Извлекает из локального файла /etc/shadow-aldpro-laps.ldif значения атрибутов admin_account_name, password_length, password_complexity и current_password_version с информацией о пароле локального администратора, который был изменен в операционной системе компьютера.
Проверяет список условий, по любому из которых нужно обновлять пароль:
Если параметр групповой политики применяется в первый раз, и ранее пароль локального администратора еще не был установлен.
Если в настройках групповой политики изменилось имя локального пользователя, длина или сложность пароля.
Если срок действия пароля истек или больше допустимого значения, определенного текущими настройками политики.
Если не совпадает версия пароля в LDAP-каталоге и в локальном файле (операционная система компьютера или LDAP-каталог были восстановлены из резервной копии).
Изменяет пароль локальному пользователю и снимает с него блокировку, если это требуется.
Сохраняет информацию о новом пароле в LDAP-каталог и в локальный файл.
Блокирует остальных локальных пользователей системы, если это требуется.
Графический интерфейс - представляет из себя оконное приложение, которое позволяет найти информацию LAPS для конкретного компьютера по его полному доменному имени. Администратор может увидеть текущий пароль локального администратора, а также изменить срок действия этого пароля, если ему потребуется форсировать обновление пароля.
Установка решения
Решение распространяется в виде deb-пакета, который можно установить на любой компьютер в домене, будь то контроллер или рабочее место администратора:
sudo apt install ./aldpro-laps_1.0-1_amd64.deb
В версиях ALD Pro до 3.x.x после установки deb-пакета требовалось расширить схему каталога и импортировать в домен дополнительный параметр групповой политики. Начиная с ALD Pro 3.0.0 эти действия не нужны и можно сразу переходить к разделу Настройка политики LAPS в домене
Импорт переменных окружения (до ALD Pro 3.x.x)
Сначала откройте окно терминала и определите несколько переменных среды окружения в соответствии с параметрами вашего домена. Чтобы эти команды не сохранились в истории, добавьте символ пробела в начало каждой строки:
export SUFFIX='dc=ald,dc=company,dc=lan'
export ALDPRO_API_SERVER='dc-1.ald.company.lan'
export ALDPRO_API_CA_PATH='/etc/ipa/ca.crt'
export ALDPRO_API_LOGIN='admin'
export ALDPRO_API_PASSWORD='Pa$$w0rd'
Расширение схемы каталога (до ALD Pro 3.x.x)
Теперь внесите изменения в схему каталога, для этого выполните следующую команду:
cat /opt/rbta/aldpro/aldpro-laps/schema/update.ldif | envsubst | ldapmodify -c -H ldaps://$ALDPRO_API_SERVER -D 'cn=Directory Manager' -w $ALDPRO_API_PASSWORD
утилита cat считывает содержимое файла update.ldif и передает его в стандартный поток вывода;
утилита envsubst подставляет значения переменных из среды окружения (в файле используется переменная SUFFIX);
утилита ldapmodify вносит изменения в LDAP-каталог.
Проверить наличие изменений в схеме каталога можно командой:
ldapsearch -o ldif-wrap=no -H ldaps://$ALDPRO_API_SERVER -D 'cn=Directory Manager' -w $ALDPRO_API_PASSWORD -b 'cn=schema' + | grep LAPS
objectClasses: ( 1.3.6.1.4.1.52616.100.2.4.3000 NAME 'aldpro-laps' DESC 'Local administrator password object class' SUP top AUXILIARY MAY ( aldproLAPSPassword $ aldproLAPSPasswordExpirationTime $ aldproLAPSCurrentPasswordVersion ) X-ORIGIN ( 'ALD Pro' 'user defined' ) )
attributeTypes: ( 1.3.6.1.4.1.52616.100.2.3.3000 NAME 'aldproLAPSPassword' DESC 'Local administrator name, password and time in json format' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN ( 'ALD Pro' 'user defined' ) )
attributeTypes: ( 1.3.6.1.4.1.52616.100.2.3.3002 NAME 'aldproLAPSCurrentPasswordVersion' DESC 'Local administrator password version' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN ( 'ALD Pro' 'user defined' ) )
attributeTypes: ( 1.3.6.1.4.1.52616.100.2.3.3001 NAME 'aldproLAPSPasswordExpirationTime' DESC 'Local administrator password expiration time' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE X-ORIGIN ( 'ALD Pro' 'user defined' ) )
Изменения в схему вносятся через указанный в параметре ALDPRO_API_SERVER контроллер домена и реплицируются на все остальные серверы автоматически. Команду можно запускать на любом компьютере в домене неограниченное количество раз. При повторном выполнении команды вы будете просто получать предупреждение «Type or value exists» о том, что указанные в файле инструкции доступа aci уже находятся в каталоге.
Импорт дополнительного параметра групповой политики (до ALD Pro 3.x.x)
Следующим действием импортируйте дополнительный параметр групповой политики следующей командой:
cd /opt/rbta/aldpro/aldpro-laps/policy/
sh import.sh
Скрипт создаст в дополнительных параметрах папку LAPS и загрузит в нее параметр «Пароли локальных администраторов».
Настройка политики LAPS в домене
Теперь вы можете создать объект групповой политики в домене и настроить его с использованием нового параметра.
рис. 352 Настройка параметра групповой политики LAPS
Параметр позволяет централизованно управлять паролями локальных администраторов путем внесения изменений в файл /etc/shadow с помощью salt-функции user.present. На сервере в LDAP-каталоге изменения сохраняются с помощью команды ipa host-mod из под учетной записи компьютера.
Атрибут «Имя локального администратора» определяет логин или числовой UID того пользователя, паролем которого нужно управлять. Для корректной работы параметра групповой политики значение является обязательным.
Атрибут «Блокировать остальных» позволяет указать, нужно ли блокировать вход по паролю для остальных локальных пользователей системы.
Допустимые значения:
Lock - параметр блокирует вход по паролю для остальных локальных пользователей системы, в файле /etc/shadow к паролям пользователей будет добавляться восклицательный знак. В качестве синонима Lock может использоваться значение True.
Unlock - параметр снимает блокировку входа по паролю со всех локальных пользователей системы, которым назначен пароль. Если у пользователя нет пароля (в качестве пароля записан восклицательный знак), то блокировка сниматься не будет, так как вход без пароля является небезопасным.
Ignore - параметр не затрагивает остальных пользователей системы. Это поведение определено по умолчанию, поэтому в качестве синонима можно использовать False, пустую строку или любое другое значение.
Атрибут «Срок действия пароля» позволяет задать период, по истечению которого пароль локального администратора на компьютере будет обновлен. Значение представляет собой целое число в диапазоне от 1 до 365, указывающее количество дней. Если значение не определено, по умолчанию устанавливается срок действия пароля в 30 дней.
Атрибут «Ограничить максимальный срок действия пароля» позволяет запретить установку значений, превышающих текущие настройки параметра групповой политики.
Допустимые значения:
True - пароль локального администратора будет автоматически обновлен, если в момент применения параметра групповой политики срок действия пароля в LDAP каталоге окажется больше того значения, которое должно быть установлено в соответствии с текущими настройками параметра групповой политики. Значение True используется по умолчанию.
False - администраторы вправе устанавливать вручную любой срок действия пароля LAPS, превышающий текущую дату и время, и это не приведет к внеочередному обновлению пароля.
Например, если настройками параметра групповой политики определен срок действия пароля в 30 дней, а на календаре уже 1-го августа, то при включенном ограничении администратор будет не вправе установить вручную срок действия пароля на 3-е сентября или более поздние даты, т.к. в этом случае пароль будет автоматически обновлен.
Атрибут «Сложность пароля» позволяет задать желаемый уровень сложности нового пароля для локального администратора. Допустимые значения:
1 - прописные символы;
2 - прописные символы + строчные символы;
3 - прописные символы + строчные символы + цифры (этот уровень используется по умолчанию);
4 - прописные символы + строчные символы + цифры + специальные символы;
5 - предыдущий набор символов за вычетом непечатных знаков, например, I, O, Q, l, o, 0, 1, и др.
Атрибут «Длина пароля» позволяет задать желаемую длину нового пароля для локального администратора. Значение должно представлять собой целое число в диапазоне от 8 до 64. Если значение не определено, по умолчанию пароли генерируются длиной в 14 символов.
Для принудительного применения параметров групповых политик на целевых хостах вы можете использовать следующую команду:
sudo aldpro-gpupdate --gp
Просмотр информации LAPS в графическом интерфейсе
Графическая утилита LAPS предназначена для управления паролями локальных администраторов Linux-компьютеров, которые подключены к домену ALD Pro. Авторизованный пользователь может узнать учетные данные локального администратора, чтобы использовать их для устранения проблем в работе операционной системы в условиях недоступности доменных сервисов. Для работы с утилитой пользователь должен быть участником группы admins. После установки deb-пакета запустить приложение можно из меню ().
рис. 353 Графический интерфейс администратора LAPS
В основном окне приложения доступны следующие элементы интерфейса:
Поле «Имя компьютера» предназначено ввода полного имени компьютера, по которому будет выполнен поиск записи в LDAP-каталоге.
Кнопка «Найти» запускает поиск учетной записи компьютера в LDAP-каталоге.
Для выбора контроллера домена приложение использует результаты автоматического обнаружения сервисов от службы SSSD из файла /var/lib/sss/pubconf/kdcinfo.DOMAIN. Аутентификация выполняется по протоколу Kerberos V5 с использованием билетов из связки ключей Linux. Если в связке ключей не окажется действительного TGT-билета пользователя, то по нажатию кнопки “Найти” откроется окно для ввода пароля от доменной учетной записи, из-под которой был выполнен вход в систему.
В случае неуспешного поиска в строке состояния будет выведена информация об ошибке, а кнопки «Установить», «Копировать пароль» и «Показать пароль» будут неактивны. При успешном поиске в соответствующих полях отобразится найденная информация, как указано на рис. 353.
Поле «Пароль LAPS действителен до» отображает текущее значение срока действия пароля локального администратора для выбранного компьютера.
Далее на форме представлены элементы управления для установки нового срока действия пароля. Вы можете как уменьшить это значение, так и увеличить его. Пароль будет автоматически обновлен при применении параметра групповой политики в следующих двух случаях:
Если срок действия будет установлен меньше текущей даты и времени.
Если срок действия будет больше того значения, которое должно быть установлено в соответствии с текущими настройками параметра групповой политики, и включено ограничение максимального срок действия пароля.
Кнопка «Установить» сохраняет новое значение срока действия пароля.
Поле «Дата последнего изменения пароля LAPS» отображает дату и время, когда пароль был установлен групповой политикой последний раз. Значение отображается в том часовом поясе, который установлен на рабочем месте администратора.
Поле «Имя аккаунта» отображает имя локального администратора, для которого был установлен пароль в системе.
Поле «Пароль» отображает текст пароля, который был установлен локальному администратору в операционной системе компьютера. По умолчанию пароль скрыт символами звездочки. Если при применении параметра групповой политики произошла ошибка, то в поле «Пароль» будет отображаться текст с уточнением причины этой ошибки.
Кнопка «Копировать пароль» позволяет скопировать скрытый пароль локального администратора в буфер обмена.
Кнопка «Показать пароль» - позволяет отключить отображение пароля на экране открытым текстом.
Строка состояния выводит информацию о работе приложения для упрощения отладки.
Практика и тестирование
Заключение
В этом модуле мы прошли тему политик, поэтому можно сказать, что обязательный минимум уже выполнен. Далее мы перейдем к установке и настройке дополнительных подсистем, которые позволят вам быстро закрыть типовые потребности, которые будут возникать на большинстве предприятий.
Дополнительные источники информации
Обратная связь
Если остались вопросы, то их всегда можно задать в специальной теме.