Сергей Попов
Администратор
- 30.12.2015
- 6 265
- 6 980
- Специализация
- OSINT
- Веб-безопасность
- Статус верификации
- ✓ Verified
Понедельник, 9:15 - SOC фиксирует успешный brute-force на SSH-порт сервера в DMZ: root-сессия активна. К 11:00 подключаюсь к постмортему. Выясняется: три недели назад при аварийных работах администратор вернул
PermitRootLogin yes и не откатил. Baseline, настроенный вручную полгода назад, не пережил первого экстренного изменения. Drift не мониторился - никто не заметил. Атакующий подобрал учётные данные и зашёл под root - классическая Local Accounts (T1078.003, Initial Access). С учётом оборотных штрафов за утечку персональных данных отсутствие доказуемого baseline - не только техническая, но и юридическая дыра. Этот постмортем стал переломной точкой: харденинг серверов перестал быть документом и стал кодом.Зачем атакующему ваш дрейф: маппинг CIS на MITRE ATT&CK
Прежде чем писать Ansible роли для безопасности, стоит разобраться - от чего именно защищаемся. Каждый контроль CIS Benchmark закрывает конкретную технику из MITRE ATT&CK. Без этого маппинга харденинг превращается в чеклист ради чеклиста, а SOC не понимает, какой алерт связан с каким контролем. Я не видел ни одного русскоязычного материала, где этот маппинг дан явно - в итоге команды раскатывают CIS Level 1 «как есть», не понимая, какую цепочку атаки разрывают.| Область харденинга | Контроль CIS | Техника MITRE ATT&CK | Тактика |
|---|---|---|---|
| SSH: запрет root-входа | 5.2.4 | Valid Accounts / Local Accounts (T1078, T1078.003) | Initial Access, Persistence |
| Парольная политика | 5.4.x | Password Policy Discovery (T1201) - усложняет разведку | Discovery |
| Права на /etc/shadow, /etc/passwd | 6.1.x | Credentials In Files (T1552.001) | Credential Access |
| Конфигурация sudo | 5.3.x (CIS-5 Account Management) | Sudo and Sudo Caching (T1548.003) | Privilege Escalation |
| Ограничение cron | 5.1.x | Cron (T1053.003) | Execution, Persistence |
| Защита auditd от остановки | 4.1.x | Disable or Modify Tools (T1562.001) | Defense Evasion |
| Отключение лишних сервисов | 2.x (CIS-1 Asset Inventory) | System Information Discovery (T1082) - сужает attack surface | Discovery |
Из таблицы вытекают два практических выхода. Для Blue Team - приоритезация: если в threat model основной риск - Privilege Escalation через sudo (T1548.003), контроли CIS 5.3.x идут первыми в deployment. Для SOC - корреляция: drift в конфигурации sudo + аномальный вызов
sudo -l от непривилегированного пользователя - повод для расследования, а не два несвязанных события в разных дашбордах.CIS Controls v8 требует инвентаризации активов (CIS-1) и управления учётными записями (CIS-5), NIST SP 800-53 Rev 5 закрепляет конфигурационный менеджмент (CM-1) как обязательный элемент compliance-программы. Автоматизация харденинга Ansible покрывает оба стандарта одним pipeline.
Структура Ansible роли для харденинга серверов по CIS Benchmark
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
) и область (
ssh, sysctl, auditd). Запуск ansible-playbook site.yml --tags cis_level1,ssh применяет только SSH-контроли первого уровня.
YAML:
# roles/cis_baseline/tasks/ssh.yml - mitigates T1078.003
- name: "CIS 5.2.4 - Disable root login via SSH"
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PermitRootLogin'
line: 'PermitRootLogin no'
when: cis_5_2_4_ssh_root_login | bool
notify: restart sshd
tags: [cis_level1, ssh]
lineinfile для sshd_config работает предсказуемо, пока в конфиге нет Match-блоков. Если PermitRootLogin задан внутри Match Address, lineinfile добавит дубль в глобальную секцию - директива сработает, но конфиг станет сложнее для аудита. Для конфигов с Match-блоками - ansible.builtin.template с полным шаблоном sshd_config.Вторая частая боль - идемпотентность на разных дистрибутивах. Ubuntu 22.04 создаёт
sshd_config с комментированными директивами (#PermitRootLogin prohibit-password), RHEL 9 - с активными. Regexp '^#?PermitRootLogin' закрывает оба варианта, но если написать без ? - playbook будет менять конфиг при каждом запуске на Ubuntu, ломая идемпотентность и генерируя ложные changed-статусы. Мелочь, но в CI/CD pipeline это красный билд на ровном месте.Для sysctl-параметров (
net.ipv4.conf.all.send_redirects, net.ipv4.tcp_syncookies) модуль ansible.posix.sysctl с reload: true стабилен. Но если в /etc/sysctl.d/ уже валяется конфликтующий файл от другого пакета - значение будет перезаписано при следующем sysctl --system. Задача-предшественник должна искать и удалять конфликты в sysctl.d/.Аудит безопасности Ansible: compliance-проверки после применения
Применить baseline - половина работы. Вторая - непрерывная валидация, что конфигурация не уехала. Два подхода: assert-based проверки внутри Ansible и внешний сканер OpenSCAP.Assert-based валидация
Отдельный playbookvalidate_cis.yml запускается по расписанию или после каждого change window. Каждая задача выполняет команду и через assert проверяет результат:
YAML:
- name: Check SSH config
ansible.builtin.command: sshd -T
register: sshd_cfg
changed_when: false
- name: Assert root login disabled
ansible.builtin.assert:
that: "'permitrootlogin no' in sshd_cfg.stdout.lower()"
fail_msg: "CIS 5.2.4 FAIL - root login permitted"
success_msg: "CIS 5.2.4 PASS"
.lower() - sshd -T в большинстве версий OpenSSH возвращает директивы в lowercase, но на нестандартных сборках регистр может отличаться. Без .lower() проверка даст false negative, и отклонение от baseline пройдёт незамеченным. Неприятный сюрприз, если вы собрали OpenSSH из исходников.Ограничение: assert-based подход хорош для 20–30 контролей. На полном CIS Level 2 (200+ контролей) playbook становится громоздким и тяжёлым в поддержке. Здесь нужен переход на OpenSCAP.
OpenSCAP как compliance-gate
OpenSCAP - сканер, валидированный NIST, с SCAP-профилями под CIS и DISA STIG. Роль устанавливаетopenscap-scanner и scap-security-guide на managed node, запускает проверку командой oscap xccdf eval --profile cis_level1 --results /tmp/oscap-results.xml с указанием пути к XCCDF-файлу дистрибутива. Результат - XML-отчёт с verdict по каждому контролю.Проект Compliance as Code (CaC) поддерживает автоматическую генерацию Ansible remediation-плейбуков из результатов OpenSCAP-сканирования: сканер находит отклонение, генерирует задачу Ansible, задача возвращает конфигурацию в baseline. Петля замыкается - конфигурация описана кодом, проверена сканером, исправлена кодом. Инфраструктура как код security в чистом виде.
Когда OpenSCAP подводит: профили в
scap-security-guide обновляются с задержкой относительно новых релизов CIS Benchmark. Если вы перешли на RHEL 9.4, а SSG содержит профиль для 9.2 - часть проверок даст false positive. Проверяйте версию SSG через rpm -q scap-security-guide прежде чем доверять результатам. Я на этом обжёгся, когда после обновления RHEL pipeline внезапно позеленел - оказалось, SSG просто не знал про новые контроли.Drift detection: автоматизация compliance-проверок до алерта в SIEM
Харденинг без мониторинга - snapshot, который устаревает после первого ручного изменения. Для Blue Team критично не просто применить baseline, а знать, когда он нарушен, и видеть это в SIEM как коррелируемое событие.Audit-правила для отслеживания конфигурационного дрейфа
Auditd - встроенный механизм Linux для отслеживания файловых изменений. Ansible-задача деплоит правила, генерирующие событие при любой записи в критичные конфиги:
YAML:
- name: Deploy drift detection rules (T1562.001)
ansible.builtin.copy:
dest: /etc/audit/rules.d/hardening-watch.rules
content: |
-w /etc/ssh/sshd_config -p wa -k sshd_change
-w /etc/sudoers -p wa -k sudoers_change
-w /etc/passwd -p wa -k identity_change
-w /etc/shadow -p wa -k identity_change
mode: '0640'
notify: restart auditd
-k задаёт метку, по которой SIEM фильтрует события. Администратор правит sshd_config - auditd генерирует запись с ключом sshd_change, SIEM подхватывает по key=sshd_change и создаёт алерт. Просто и надёжно.Корреляционные правила: от drift до incident
Одиночное изменение sshd_config - не инцидент. Но комбинация событий - повод для расследования:- T1078.003 - root brute-force после drift:
key=sshd_change(auditd) + в течение 15 минут новый SSH-сеанс от root (sshd log:Accepted password for root). Эта корреляция покрывает ровно тот сценарий из нашего постмортема. - T1548.003 - эскалация через sudo:
key=sudoers_change+ в течение часа выполнениеsudoот пользователя без истории sudo-вызовов (baseline поведения). - T1552.001 - доступ к credential-файлам:
key=identity_changeна/etc/shadow+ время события вне change window.
event.action: "opened-file" AND auditd.log.key: "sshd_change". Для Splunk - sourcetype=linux:audit key=sshd_change | stats count by host, exe. Конкретную реализацию под ваш стек придётся адаптировать, но принцип общий: auditd-метка из Ansible-роли = фильтр в корреляционном правиле SIEM.Ограничение: auditd-правила не защищают от атакующего с root-привилегиями, который может остановить auditd (T1562.001, Disable or Modify Tools). Контрмера: параметр ядра
audit=1 в GRUB и --loginuid-immutable в правилах auditd - оба контроля должны быть в Ansible-роли как задачи CIS 4.1.x. Без них вся цепочка drift detection рассыпается.CI/CD pipeline для непрерывного аудита безопасности
Compliance-проверки, запускаемые вручную, работают ровно до момента, когда у команды заканчивается время. А оно заканчивается всегда. DevSecOps-подход: харденинг встраивается в CI/CD pipeline.Pipeline из трёх стадий: lint → test → deploy. На стадии lint -
ansible-lint с правилами идемпотентности (rule 301: Commands should not change things) и FQCN-модулей (rule 206). Lint не пройден - merge request заблокирован. На стадии test - Molecule поднимает контейнер с целевым дистрибутивом, применяет роль, затем запускает validate_cis.yml. Assert-проверки падают - pipeline красный. На стадии deploy - ansible-playbook site.yml --diff --check сначала в dry-run на staging, затем после ручного approve - на production. Флаг --diff показывает конкретные строки, которые изменятся в конфигах - точка ревью для security-инженера.GitLab CI поддерживает
schedules: validate_cis.yml - ежедневно, полный site.yml --check - еженедельно, enforce site.yml - после каждого change window. Даже если кто-то изменил конфиг вручную - следующий scheduled run вернёт baseline.Ограничение Molecule с Docker: контейнеры по умолчанию не запускают systemd. Handler'ы
restart sshd и restart auditd пройдут lint, но не будут протестированы. Для полноценного теста systemd-зависимых задач нужен Molecule с драйвером Vagrant или Podman в --privileged режиме. Без этого вы тестируете половину роли и надеетесь на лучшее.Половина инцидентов, которые я разбирал за последний год, начинались не с эксплойта нулевого дня, а с ручного изменения конфига, которого никто не заметил. Временный
PermitRootLogin yes, забытый net.ipv4.ip_forward = 1, отключённый auditd «на время дебага». CIS Benchmark в виде PDF не умеет откатывать изменения и не отправляет алерт. Работает только связка «код + валидация + мониторинг»: Ansible как движок, OpenSCAP как аудитор, auditd + SIEM как сигнализация.Скажу непопулярную вещь: в большинстве команд compliance as code живёт в репозитории, который никто не трогает после первого коммита. Роль написана, pipeline зелёный - и всё. Через полгода SSG-профиль устарел, Molecule-тесты не проходят на новой версии Python, в production добавились дистрибутивы, которых нет в inventory. Реальная зрелость compliance-автоматизации измеряется не первым деплоем, а количеством коммитов в роль за квартал. Ноль коммитов за три месяца - baseline уже дрейфует, просто тихо. Если в вашем SIEM пока нет правил под drift detection из auditd - на codeby.net в тематическом треде по compliance-автоматизации коллеги делятся работающими корреляциями под разные платформы.