Вкратце:


  1. cftotalcontrol — модуль создания среды специальной учётной записи с SSH доступом к другим узлам инфраструктуры и выполнения массовых рутинных задач параллельно.
  2. Отвлечённая от реализации теория организации безопасного и контролируемого доступа к инфраструктуре.
  3. Описывается практическое применение конкретного решения


Тематический цикл:



Концепция организации доступа к инфраструктуре


Безусловно, данная статья не является исчерпывающих руководством. Тем не менее, для наглядности рассмотрим классический пример админского доступа к серверам, в котором у каждого админа есть свой приватный ключ и прямой доступ на любой подконтрольный ему сервер:

Классический пример организации доступа

У такого подхода есть определённые недостатки:

  1. Необходимость прописывать публичный ключ каждого админа на каждом сервере.
    • Прописать новые ключи через Puppet не проблема, но требуется фактическое изменение конфигурации всех доступных систем.
  2. Необходимость организовать сквозной доступ к каждой системе из любого места нахождения админа.
    • Зачастую для этого опасно используют SSH Agent Forwarding.
    • Иногда можно встретить совершенно не гибкое решение через DNAT.
    • Прописывание всех админских IP и/или открытие портов по стуку на каждой системе тоже несколько сомнительное решение.
  3. Каждому админу приходится вручную содержать общий или собственный SSH config и набор скриптов для рутинный задач, а также вовремя их обновлять. Это гарантированно ведёт к ошибках.
  4. С ростом количества систем аудит организации доступа в инфраструктуре превращается в нетривиальную задачу.
  5. Непосредственно централизованное управление (дирижирование) не решено.
    • Безусловно существует разнообразные решения на основе событийной модели, но их безопасность вызывает некоторые сомнения. У самого Puppet для этого есть MCollective.

Для сравнения рассмотрим доступ с условным администратором инфраструктуры в виде доменной группы:

Организации доступа через доменную группу

Данный подход называют по разному, но так или иначе он уже давно существует в интегрированных коммерческих решениях. Обычно это реализовано через некую доменную группу привилегированных пользователей, которая имеет доступ ко всем или части систем инфраструктуры. У такого подхода есть некоторые (2, 3 и 5) недостатки предыдущего плюс очевидный нюанс — необходимость в постоянной доступности сервисов аутентификации и авторизации (конечно, не без кэширования). К слову, не составляет большого труда организовать нечто подобное с помощью PAM и LDAP.

Итак, мы быстро подобрались к сути реализованной концепции доступа в cftotalcontrol:

Схема доступа через cftotalcontrol

По сути данный подход объединяет два предыдущих:

  1. Для доступа используется исключительно SSH без каких-либо дополнительных сервисов. Puppet используется только в момент распространения ключей авторизации.
  2. Вводится управляющий пользователь — специальная учётная запись на особо безопасных узлах инфраструктуры, к которой подключаются админы. Абстрактно такая учётная запись представляет собой группу админов.
  3. Система сама настраивает промежуточные узлы и SSH клиент управляющего пользователя. В системах прописано минимальное количество ключей авторизации, что хорошо масштабируется с ростом и количества узлом, и количества админов.
  4. На рабочем месте админа не требуется каких-либо конфигураций, кроме подключения к управляющей учётной записи.
  5. Полностью контролируемая учётная запись позволяет легко наладить аудит действий админов с ведением журнала. Особенно полезно для низко привилегированных групп.
  6. Имеются готовые решения для дирижирования большим количеством систем:
    • можно запустить команду на всех доступных узлах.
    • можно определить узлы в группы статически или динамически по запросу в PuppetDB.
    • можно запускать команды последовательно в интерактивном режиме.
    • можно запускать команды параллельно с ограничение максимального количества параллельных соединений и получать вывод команды по завершению.
  7. Для рутинных задач, создаются команды по шаблону для каждого узла, для группы узлов и для всех узлов. В список стандартных команд входят:
    • принудительное развёртывание Puppet.
    • обновление мета-информации из репозиториев пакетов.
    • обновление системы.
    • удаление старых неиспользуемых пакетов.
    • любые произвольные команды, задаваемые в конфигурации.
  8. Есть возможность создать низко привилегированных управляющих пользователей (Scoped Control Users) вместо привилегированных "тотального" контроля (Total Control Users).
    • такие пользователи будут иметь доступ только к тем системам, которые явно помечены как относящиеся к данной сфере (Control Scope).

Установка


Подразумевается, что инфраструктура построена на ранее рассмотренных модулях cfnetwork, cfauth и cfpuppetserver, но cftotalcontrol будет прекрасно жить без конфликтов и в смешанной среде.

Во-первых, чтобы спать спокойно лучше всё же иметь хотя бы один SSH ключ, который будет прописан на всех системах через модуль cfauth, но который не будет использоваться для "бытовых" нужд. Разумеется, не стоит забывать про доступ к железу через IPMI и к виртуалкам через serial/GUI консоль на экстренный случай.

Во-вторых, достаточно посмотреть на пример и:

  1. добавить модуль cftotalcontrol в Puppetfile
    mod 'codingfuture/cftotalcontrol'
  2. в список classes всех узлов (файл common.yaml)
    classes:
    # Обозначить узел, как подлежащий "тотальному" управлению
    - cftotalcontrol::auth
  3. в список classes конфигурации узла с управляющим пользователем (далее "управляющий узел")
    classes:
    # Создать управляющего пользователя
    - cftotalcontrol
  4. требуется запустить развёртывание Puppet (/opt/puppetlabs/bin/puppet agent --test) в следующем порядке:
    1. Развернуть Puppet на управляющем узле — создать управляющего пользователя и его среду.
    2. Развернуть Puppet на всех остальных узлах — это зарегистрирует систему в PuppetDB для автоматической генерации конфигурации управляющего пользователя.
    3. Зайти под управляющего пользователя и сгенерировать приватный ключ (будет предложено автоматически). После генерации будет автоматически вызвано развёртывание Puppet чтобы экспортировать публичный ключ в PuppetDB.
    4. Развернуть Puppet на всех системах ещё раз — везде пропишется публичный ключ управляющего пользователя, а у самого пользователя создаст конфигурацию с доступом ко всем узлам.
    5. Такая последовательность действий требуется только при изначальной установке. Далее, обновление ключей автоматизировано при генерации нового.

После этих нехитрых манипуляций, на узлах с классом cftotalcontrol будет создан пользователь cftcuser со специально заточенной средой Bash и конфигурацией SSH клиента. Настоятельно рекомендуется изменить имя пользователя на нестандартной через параметр cftotalcontrol::control_user.

Все стандартные админские ключи из cfauth будут прописаны и данному пользователю, но для организации идеологически правильного доступа, админские ключи должны быть прописаны только в cftotalcontrol::ssh_auth_keys. Разумеется, сначала следует проверить, что всё работает и доступ есть ко всем серверам.

Прокси-доступ по SSH


В крупной инфраструктуре, практически всегда во внешний мир смотрит лишь малая часть систем и через них требуется организовать доступ к остальным системам.

В SSH клиенте, это легко реализуется через опцию ProxyCommand со значением ssh -W target:port proxyhost. Такой подход более безопасен, чем использование SSH Agent Forwarding, позволяющего утащить приватный ключ админа на удалённой системе. В принципе, знание этих технических деталей необязательно для использование модуля cftotalcontrol, т.к. вся необходимая конфигурация генерируется на основе параметра cftotalcontrol::pool_proxy.

Разумеется, перечисление прокси-узла для каждого целевого узла не только не эффективно, но и обязательно ведёт к частым ошибкам при добавлении новых систем. Вместо этого, для "навигации" используются факты cf_location и cf_location_pool, описанные в предыдущих частях. Обычно эти факты задаются при изначальной установке скриптом инициализации клиента cf_gen_puppet_client_init, но могут быть изменены через параметры модуля cfsystem.

При генерации конфига SSH клиента, прокси узлы ищутся в следующем порядке:

  • $cftotalcontrol::pool_proxy["$cf_location/$cf_location_pool"]
  • $cftotalcontrol::pool_proxy["$cf_location"]
  • $cftotalcontrol::pool_proxy["$certname"] — да, для исключительных случаев была оставлена лазейка указать прокси-узел на основе полного имени целевого узла.

Разумеется, всё это дело интегрировано с cfnetwork и без лишних телодвижений добавляет всё, что нужно в конфигурацию сетевого фильтра.

Группы узлов


Обычно, специфичные однотипные задачи требуется запустить только на узлах с определёнными признаками (ОС, набор сервисов, роль и т.п.). Чтобы не городить свой огород по сегрегации узлов, это легко сделать через параметр cftotalcontrol::host_groups, который представляет собой ассоциативный массив, где ключ — это название группы, а значение может быть жёстко заданным списком узлом или строкой в виде запроса Puppet DB, о котором есть возможность почитать в описании модуля puppetdbquery.

Пример:

cftotalcontrol::host_groups:
    puppetserver: "Package['puppetserver']"
    infra: "cf_location_pool = 'infra'"
    custom:
        - 'web.example.com'
        - 'db.example.com'

Стандартные рутинные команды


Должно быть понятно без лишних объяснений:

cftotalcontrol::standard_commands:
    helloworld: 'echo "Hello world!"'
    gethostname: 'hostname --fqdn'

Знакомство со средой централизованного управления


Вот тут и начинается виртуальный пост админа. При первом заходе, система любезно предложит сгенерировать приватный ключ, а при последующих запустит SSH Agent и добавит в него приватный ключ, попутно спрашивая его пароль.

А вот чтобы жизнь не казалась мёдом, система будет напоминать о необходимости обновить приватный ключ, если он старше cftotalcontrol::ssh_old_key_days = 180 дней. Если даже не заходить, то же самое будет делать cron, гневно высылая ежедневные письма. Сгенерировать новый ключ можно командой cftc_gen_key, которая ещё и автоматически установит его на всех контролируемых узлах, не требуя иных действий. Если что-то идёт не так, то старый ключ всегда можно найти в каталоге ~/.ssh/ с расширением в виде UNIX timestamp в секундах на момент генерации нового ключа. Такое может произойти из-за ошибки соединения, конфигурации Puppet или банального offline контролируемой системы.

Вообще, под капотом творится следующее:

  • ~/.bash_aliases — обязательно присутствует строка с подключением ~/.cftotalcontrol_aliases
  • ~/.cftotalcontrol_aliases — вся магия Bash среды
  • ~/.ssh/cftotalcontrol_config — специальный конфиг SSH с правильными пользователями, портами и ProxyCommand. Стоит ли говорить, что данные тоже вытащены из PuppetDB.
  • ~/.ssh/cftchostsall — список всех подконтрольных узлов.
  • ~/.ssh/cftchosts_${grp} — список подконтрольных узлов конкретной группы.
  • ~/.ssh/cftc_id_${ssh_key_type} — текущий приватный ключ.
  • ~/.ssh/cftc_id_${ssh_key_type}.${backup_timestamp} — старые приватные ключи, которые в нормальном случае нигде не прописаны.

Служебные команды Bash среды:

  • cftc_ssh — правильный запуск SSH со всеми необходимыми параметрами.
  • cftc_scp — правильный запуск SCP со всеми необходимыми параметрами.
  • cftc_gen_key — ручная перегенерация приватного ключа (вызывается автоматически при заходе в отсутствии ключа).
  • cftc_add_key — запуск SSH Agent'а и добавление приватного ключа (вызывается автоматически при заходе).
  • cftc_check_old_key — ручная проверка старого ключа (вызывается автоматически при заходе и ежедневно по cron'у).

Непосредственно работа с узлами:

  • ssh_${hostname} [$cmd] — зайти на конкретный узел или запустить на нём произвольную команду в интерактивном режиме. Примечание: точки заменяются подчёркиванием в ${hostname}
  • ssh_${hostname}_{stdcmd} [args] — запустить одну из стандартных команд на конкретном узле.
  • ssh_masscmd {cmd} — последовательно запустить {cmd} на всех узлах в интерактивном режиме.
  • ssh_mass_{stdcmd} [args] — последовательно запустить одну из стандартных команд на всех узлах в интерактивном режим.
  • pssh_masscmd {cmd} — параллельно запустить {cmd} на всех узлах.
  • pssh_mass_{stdcmd} [args] — параллельно запустить одну из стандартных команд на всех узлах.
  • sshgrp_{group}_* и psshgrp_{group}_* — то же, что и предыдущие "массовые", но для ограниченной именованной группы узлов.

Значимые переменные среды:

  • PSSH_COUNT=$cftotalcontrol::parallel — максимальное количество параллельных вызовов. Стоит помнить, что на прокси-узлы пойдёт большая нагрузка и соединения могут быть зарублены по опции MaxStartups. К сожалению, подход с ControlMaster не оправдывает себя с большим количеством соединений, т.к. мультиплексирование работает достаточно посредственно — он больше подходит для ускорения работы последовательных вызовов.
  • PSSH_OPTS=-i — дополнительные параметры для parallel-ssh (pssh)
  • SSH_OPTS= — дополнительные параметры для ssh
  • SCP_OPTS= — дополнительные параметры для scp

Стандартные команды — те же, что и добавлены в cfauth для вызова через sudo без пароля:

  • aptupdatesudo /usr/bin/apt-get update
  • aptdistupgradesudo DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get dist-upgrade -o Dpkg::Options::="--force-confold" -qf
  • aptautoremovesudo DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get autoremove
  • puppetdeploysudo /opt/puppetlabs/puppet/bin/puppet agent --test
  • Пример использования: pssh_mass_aptupdate, psshgrp_grpname_aptdistupgrade -sy, ssh_host_example_com_puppetdeploy

Создание админов с ограниченным доступом


Рано или поздно, определённые админские полномочия требуется давать людям. чьей зоной ответственности не является администрирование всей инфраструктуры. В число таких входят DevOps, DBA, Release Manager и прочие. Некоторые средства кластеризации также полагаются на SSH доступ к узлам-собратьям, который удобно настроить этим же модулем, включая автоматическую генерацию приватного ключа без пароля.

Если коротко, то стандартный путь создания непривилегированных админов лежит через параметр cftotalcontrol::extra_users, который правильно создаёт ресурсы типа cftotalcontrol::admin. Особые отличия от пользователя "тотального" управления:

  • Имя пользователя — это и имя соответствующей сферы управления.
  • На всех промежуточных узлах будет создан пользователь с таким же именем, но постфиксом "_proxy", которому будет доступно только прокидывание соединения и принудительное развёртывания Puppet (используется при обновлении ключа).

Остаётся только вопрос как обозначить целевой узел. Это делается через параметр cftotalcontrol::auth::control_scope. На один и тот же узел можно повесить сразу несколько сфер. Пример:

cftotalcontrol::auth::control_scope:
  - web
  - devops

Документация по модулю cftotalcontrol


класс cftotalcontrol


  • pool_proxy = {}. Пары ключ => "имя.прокси.узла". Формат ключей:
    • "${cf_location}/${cf_location_pool}"
    • "${cf_location}"
    • "${certname}"
  • control_user = 'cftcuser' — имя управляющего пользователя.
  • control_home = undef — домашняя папка. По умолчанию: /home/$control_user.
  • host_groups = {} — определение групп узлов для команд типа (p)sshgrp_*. Ключ — название группы. Значение:
    • массив — статичный список узлом в группе
    • string — запрос Puppet DB для динамичного списка
  • parallel = 10 — количество параллельных вызовов SSH.
  • standard_commands = {} — стандартные рутинные команды для добавления в список.
  • ssh_key_type = 'rsa' — типа приватного ключа. Стоит также уделить внимание новому типу 'ed25519'.
  • ssh_key_bits = 4096 — длина приватного ключа, игнорируется для ed25519.
  • autogen_ssh_key = false — автоматически сгенерировать приватный ключ без пароля (не стоит для "тотального" контроля).
  • ssh_old_key_days = 180 — возраст приватного ключа после которого, система начинает плакаться в cron'е и при заходе в учётную запись.
  • ssh_auth_keys = undef — дополнительные ключи доступа к пользователю помимо тех, что заданы в cfauth
  • extra_users = undef — дополнительный пользователи ограниченного доступа к инфраструктуре ИмяДоступа => параметры cftotalcontrol::admin

класс cftotalcontrol::auth


  • control_scope = [] — строка или массив строк с именами зон ограниченного доступа, которые применимы к данной системе

тип cftotalcontrol::admin


Обычно инициализируется через cftotalcontrol::extra_users. Все параметры такие же, как у cftotalcontrol, за исключением самого extra_users.

  • control_scope = undef — название зоны ограниченного доступа, если применимо. При создании через extra_users соответствует названию пользователя. Если не задана, то создаётся пользователь с неограниченным доступом.

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