Несколько лет назад столкнулся с задачей: защитить серверы от компрометации исполняемых файлов. Антивирусы обнаруживают угрозы постфактум, а файрволы бессильны против атак изнутри. Тогда понял - нужна система, которая на уровне ядра перехватывает каждую попытку доступа к файлу и проверяет: а не изменили ли его? Именно так работает 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 для полной защиты метаданных.