Несколько лет назад столкнулся с задачей: защитить серверы от компрометации исполняемых файлов. Антивирусы обнаруживают угрозы постфактум, а файрволы бессильны против атак изнутри. Тогда понял - нужна система, которая на уровне ядра перехватывает каждую попытку доступа к файлу и проверяет: а не изменили ли его? Именно так работает IMA (Integrity Measurement Architecture) в Linux, появившаяся в ядре 2.6.30 ещё в 2009 году.
За годы работы с этой технологией выяснил: IMA - не просто логирование событий. Это многоуровневая защита, где политики appraisal блокируют подозрительные файлы, хеш-листы ускоряют проверку тысяч объектов, а цифровые подписи RSA и ECDSA делают подмену практически невозможной. Разберёмся, как эти механизмы работают вместе, какие подводные камни ждут при внедрении, и почему даже RHEL и Ubuntu включают IMA по умолчанию.
Appraisal: от пассивного логирования к активной блокировке
Помню первую попытку настроить IMA - система загрузилась с ошибками, половина процессов не запустилась. Оказалось, включил режим enforce без предварительной разметки файлов. Вот в чём суть: IMA работает в двух принципиально разных режимах - measurement (просто записывает хеши в журнал) и appraisal (проверяет хеши и блокирует доступ при несовпадении).
При включении appraisal каждый файл получает расширенный атрибут security.ima, где хранится эталонный хеш или цифровая подпись. Когда процесс обращается к файлу, ядро вычисляет его текущий хеш и сравнивает с эталоном. Совпал - доступ разрешён. Не совпал - система возвращает Permission denied и фиксирует инцидент в audit.log. Это мощнее традиционных ACL: даже root не запустит изменённый файл, если его хеш не соответствует политике.
Политики IMA определяют, какие файлы проверять и как реагировать на нарушения. Встроенная политика tcb измеряет все исполняемые файлы, библиотеки, модули ядра и firmware - базовый набор для Trusted Computing Base. Политика appraise_tcb идёт дальше: она проверяет все файлы пользователя root (uid=0) через security.ima. А политика secure_boot требует цифровые подписи (appraise_type=imasig) для модулей, firmware и образов kexec, что критично для систем с UEFI Secure Boot.
Режимы работы настраиваются параметром ядра ima_appraise. В enforce система строго блокирует файлы с неверными хешами - боевой режим. Режим fix автоматически обновляет security.ima при первом обращении к файлу, удобен при начальной разметке: загружаешься с параметрами ima_appraise=fix и ima_policy=appraise_tcb, прогоняешь команду:
find / -type f -uid 0 -exec head -n 1 '{}' \;
Эта команда читает все файлы root, запуская механизм автоматической разметки. После перезагрузки с ima_appraise=enforce система начинает строгий контроль. Режим log только записывает нарушения без блокировки - идеален для тестирования политик перед внедрением в продакшн.
Синтаксис кастомных политик гибкий. Правило вида appraise func=BPRM_CHECK mask=MAY_EXEC appraise_type=imasig означает: проверять цифровые подписи при запуске исполняемых файлов. Условие func задаёт тип операции (BPRM_CHECK - запуск, MMAP_CHECK - маппинг в память, MODULE_CHECK - загрузка модулей, FIRMWARE_CHECK - загрузка прошивок), mask определяет права доступа (MAY_EXEC, MAY_READ, MAY_WRITE). Можно фильтровать по владельцу (fowner=0), файловой системе (fsmagic=TMPFS_MAGIC), SELinux-меткам (obj_type=var_log_t) и ключевым кольцам (keyrings=.ima).
Пример кастомной политики из реальной конфигурации:
dont_appraise fsmagic=PROC_SUPER_MAGIC
dont_appraise fsmagic=SYSFS_MAGIC
dont_appraise fsmagic=TMPFS_MAGIC
appraise func=BPRM_CHECK appraise_type=imasig|modsig
appraise func=MMAP_CHECK mask=MAY_EXEC appraise_type=imasig
appraise func=MODULE_CHECK appraise_type=imasig|modsig
appraise func=FIRMWARE_CHECK appraise_type=imasig
appraise func=KEXEC_KERNEL_CHECK appraise_type=modsig
Здесь исключены псевдофайловые системы (proc, sysfs, tmpfs), где файлы генерируются динамически. Остальные файлы требуют подписей формата imasig (в атрибуте security.ima) или modsig (приложенная к файлу, как у модулей ядра). Политику загружают либо через параметр ядра ima_policy=custom с файлом /etc/ima/ima-policy, либо записывая в /sys/kernel/security/ima/policy через initramfs.
Интеграция с EVM (Extended Verification Module) добавляет защиту самих метаданных. EVM вычисляет HMAC или подпись по всем атрибутам security.* (включая security.ima, security.selinux, security.SMACK64) и хранит в security.evm. Это предотвращает ситуацию, когда злоумышленник просто перезаписывает security.ima, обходя IMA. Режимы enforce-evm и log-evm включают совместную проверку IMA и EVM. Параметр evm=fix размечает security.evm аналогично ima_appraise=fix.
Хеш-листы: производительность через централизацию
Когда впервые запустил IMA на системе с десятками тысяч файлов, столкнулся с проблемой: расширение TPM PCR занимало секунды, загрузка затягивалась. Решение - digest lists (хеш-листы). Вместо проверки каждого файла отдельно, ядро импортирует пул допустимых хешей одним блоком и быстро сверяет с ним.
Хеш-листы - это белые списки эталонных значений, хранящиеся в специальном формате. Compact list - простой перечень хешей файлов, TLV (Type-Length-Value) - расширенный формат с метаданными типа labels и modifiers. Формат файла хеш-листа: #position-digest_list_type_list-format-filename, например 0-metadata_list-compact-systemd.
В openEuler и ALT Linux хеш-листы интегрированы с пакетным менеджером. При установке RPM пакета автоматически генерируется и импортируется digest list с хешами всех файлов пакета. Инструмент gen_digest_lists извлекает хеши из RPM-заголовков:
gen_digest_lists -t metadata -f rpm+db -i l: -o add -p -1 \
-m immutable -i f:compact -i F:/lib/firmware -i F:/lib/modules \
-d /etc/ima/digest_lists -i i: -i x: -i e:
Эта команда создаёт digest lists для всех пакетов в базе RPM, включая только файлы с битом execute и содержимое /lib/firmware, /lib/modules. Модификатор immutable помечает файлы как неизменяемые - в режиме enforce их можно открыть только на чтение.
Импорт в ядро происходит через securityfs:
cat /etc/ima/digest_lists/0-metadata_list-compact-bash > \
/sys/kernel/security/ima/digest_list_data
Ядро сначала проверяет цифровую подпись самого файла digest list (да, списки тоже подписываются!), затем добавляет хеши в внутренний allowlist pool. При доступе к файлу ядро ищет его хеш в пуле: найден - файл корректен, не нужно расширять TPM PCR и писать в measurement log. Не найден - либо блокировка (в appraisal), либо запись в журнал (в measurement).
Производительность впечатляет: вместо n операций асимметричного шифрования (проверка подписи каждого файла) выполняется одна проверка подписи digest list плюс быстрый lookup хешей в хеш-таблице ядра. Для пакета с тысячей файлов это ускорение в ~1000 раз. Предсказуемость PCR тоже улучшается: digest lists измеряются последовательно, независимо от порядка доступа к файлам, что позволяет использовать TPM sealing policies.
Digest lists поддерживают два PCR: стандартный (обычно 10) для обычных измерений и альтернативный (например 11) для digest lists. Параметр ima_digest_list_pcr=11 направляет измерения списков в PCR 11, а ima_digest_list_pcr=+11 дублирует в оба PCR. Так журнал измерений выглядит чище: вместо тысяч записей о файлах - несколько записей о digest lists.
Критичный нюанс: файловые системы нужно монтировать с опцией iversion. Это включает отслеживание версий inode, и ядро пересчитывает хеш только когда i_version изменился, а не при каждом открытии файла. Без iversion производительность деградирует до уровня без digest lists.
Структура директории /etc/ima/digest_lists типична для openEuler:
/etc/ima/digest_lists/
├── parser_metadata # метаданные парсера
├── parser_metadata.sig # подпись метаданных
├── metadata # метаданные digest lists
├── 0-metadata_list-compact-bash
├── 1-metadata_list-compact-systemd
├── 2-file_list-compact-coreutils
└── rsa-key.gpg # публичный ключ для проверки
Удаление digest list:
echo "0-metadata_list-compact-bash" > \
/sys/kernel/security/ima/digest_list_data_del
Цифровые подписи: криптографическая защита от офлайн-атак
Хеши защищают от случайных изменений, но против целевой атаки бессильны. Если злоумышленник загрузится с внешнего носителя и получит доступ к файловой системе офлайн, он изменит и файл, и его security.ima. Цифровые подписи решают эту проблему: подпись создаётся приватным ключом, который хранится в защищённом месте (TPM, HSM или вообще отдельный сервер для подписи), а проверяется публичным ключом в ключевом кольце ядра.
IMA поддерживает несколько форматов подписей. Формат v1 (DIGSIG_VERSION_1) использует чистые RSA PEM ключи и собственную схему подписи, требует флага --rsa в evmctl, работает только с sha1 (sha256 не проверяется ядром корректно). Это устаревший формат, оставленный для совместимости.
Формат v2 (DIGSIG_VERSION_2) использует X.509 DER сертификаты и стандартную схему PKCS#7/CMS. Поддерживает алгоритмы RSA-2048/3072/4096, ECDSA (secp256r1, secp384r1), российский ECRDSA (ГОСТ Р 34.10-2012 с кривыми tc26), китайский SM2. Хеш-алгоритмы: SHA-1/224/256/384/512, Streebog-256/512 (ГОСТ Р 34.11-2012). Подпись включает Public Key Identifier (SKID из сертификата), алгоритм хеширования и саму подпись.
Генерация ключей и сертификатов X.509:
# Приватный ключ RSA
openssl genrsa -out ima_private.pem 2048
# Сертификат X.509 (самоподписанный для теста)
openssl req -new -x509 -key ima_private.pem -out ima_cert.pem \
-days 3650 -subj "/CN=IMA Signing Key"
# Конвертация в DER для импорта в ядро
openssl x509 -in ima_cert.pem -outform DER -out ima_cert.der
Для portable подписей (без привязки к inode и generation, чтобы файл можно было копировать между системами) используют флаг --portable в evmctl.
Подпись файлов утилитой evmctl (из пакета ima-evm-utils):
# Подпись одного файла IMA-подписью
evmctl ima_sign --key ima_private.pem /usr/bin/bash
# Рекурсивная подпись всех модулей ядра
find /lib/modules -name "*.ko" -type f -uid 0 \
-exec evmctl ima_sign --key ima_private.pem '{}' \;
# Подпись с сохранением в отдельный файл (вместо xattr)
evmctl ima_sign --sigfile --key ima_private.pem script.sh
Appended signatures (modsig) используются для модулей ядра: подпись добавляется в конец файла с магической строкой "~Module signature appended~\n" (0x7e4d6f64756c6520...). Формат поддерживается с Linux 5.4, позволяет подписывать модули традиционным способом (scripts/sign-file в исходниках ядра) и проверять через IMA.
Загрузка публичных ключей в ключевое кольцо ядра:
# Создание ключевого кольца _ima
keyctl newring _ima @u
# Импорт сертификата X.509 в кольцо .ima
evmctl import ima_cert.der $(keyctl search @u keyring _ima)
# Альтернатива: импорт через keyctl
keyctl padd asymmetric "" %keyring:.ima < ima_cert.der
Ядро использует три типа keyrings: .builtin_trusted_keys (встроенные в ядро при компиляции), .machine (загружаемые из UEFI db), .platform (из MOK - Machine Owner Keys). В режиме CONFIG_INTEGRITY_TRUSTED_KEYRING=y политика secure_boot требует, чтобы ключи в .ima были подписаны одним из доверенных keyrings, блокируя самоподписанные ключи.
При проверке ядро извлекает из подписи Public Key Identifier, ищет соответствующий сертификат в .ima, проверяет подпись публичным ключом из сертификата. Если проверка не прошла, в audit.log пишется:
type=INTEGRITY_DATA cause="invalid-signature" comm="bash" \
name="/usr/bin/ls" dev="dm-0" ino=131466 res=0
И система возвращает EACCES (Permission denied).
Интеграция EVM добавляет защиту метаданных через security.evm. EVM signature включает в подпись inode number, generation, UID, GID, mode, security.selinux, security.SMACK64, security.ima, security.capability. Portable EVM signatures исключают inode/generation для переносимости между системами. Подпись EVM:
# EVM HMAC (симметричный ключ из TPM)
evmctl sign --imahash /usr/bin/bash /root/private.pem
# EVM signature + IMA signature
evmctl sign --imasig /usr/bin/bash /root/private.pem
Активация EVM:
# Режим 1: HMAC + signature
echo 1 > /sys/kernel/security/evm
# Режим 2: только signature (блокирует HMAC)
echo 2 > /sys/kernel/security/evm
# Режим 0x80000000: lock - запрет дальнейших изменений режима
echo $((2 | 0x80000000)) > /sys/kernel/security/evm
Практическое внедрение в дистрибутивах
В RHEL/CentOS настройка через grubby:
grubby --update-kernel=ALL \
--args="ima_appraise=fix ima_appraise_tcb evm=fix ima_hash=sha256"
После перезагрузки и разметки файлов:
grubby --update-kernel=ALL --remove-args="ima_appraise=fix evm=fix"
grubby --update-kernel=ALL --args="ima_appraise=enforce evm=1"
В Ubuntu/Debian пакет ima-evm-utils предоставляет evmctl, ключи хранятся в /etc/keys/ima/ и /etc/keys/x509_evm.der. Dracut-модуль ima автоматически загружает ключи в initramfs.
В openSUSE/SUSE требуется пакет dracut-ima (zypper in dracut-ima), ключи в /etc/keys/ima/*.der. Systemd по умолчанию не грузит IMA-политику, нужен dracut или ручная загрузка через initramfs. Утилита ima_inspect показывает структуру подписи:
ima_inspect /usr/bin/ls
В Yocto/embedded системах IMA включается через DISTRO_FEATURES:
DISTRO_FEATURES:append = " integrity ima"
IMA_EVM_PRIVKEY = "${TOPDIR}/keys/ima_private.pem"
IMA_EVM_X509 = "${TOPDIR}/keys/ima_cert.pem"
inherit ima-evm-rootfs
Класс ima-evm-rootfs автоматически подписывает все файлы rootfs при сборке образа.
ALT Linux предоставляет systemd-службы для управления IMA/EVM, автоматический импорт digest lists при обновлении пакетов, интеграцию с hasher для изолированной сборки с IMA. openEuler развивает digest_cache LSM - эволюцию digest lists с поддержкой динамической загрузки парсеров форматов через kernel modules.
Системы проверки целостности на базе IMA appraisal policies, hash lists и signature verification - это не академическая технология, а реально работающая защита. При правильной настройке получаешь систему, где даже root не запустит изменённый файл, где офлайн-атаки бессильны благодаря криптографии, где производительность не страдает благодаря хеш-листам. Главное - понимать архитектуру, тестировать политики в log-режиме перед enforce, и не забывать про интеграцию с EVM для полной защиты метаданных.