Найти в Дзене
File Energy

Linux Kernel EVM и защита метаданных файлов через HMAC и portable signatures

Большинство разговоров о целостности файлов крутятся вокруг содержимого: изменился ли байт внутри бинарника, совпадает ли хеш исполняемого файла с эталонным. Это важно, но это лишь половина картины. Файл можно не трогать вовсе, и при этом сломать всё: достаточно изменить его SELinux-метку, убрать capability из security.capability, поменять владельца или биты доступа. Решения на уровне содержимого этого не увидят. Именно здесь начинается территория EVM, Extended Verification Module, компонента ядра Linux, который следит не за тем, что написано внутри файла, а за тем, что написано о файле. EVM попал в основное ядро Linux в версии 3.2 в 2012 году, поддержка цифровых подписей добавилась в 3.3. С тех пор он работает в одной упряжке с IMA, Integrity Measurement Architecture, образуя подсистему целостности ядра. Если IMA отвечает за содержимое файла, то EVM отвечает за его метаданные и расширенные атрибуты. Вместе они закрывают оба вектора офлайн-атак на файловую систему. EVM защищает метадан
Оглавление

Большинство разговоров о целостности файлов крутятся вокруг содержимого: изменился ли байт внутри бинарника, совпадает ли хеш исполняемого файла с эталонным. Это важно, но это лишь половина картины. Файл можно не трогать вовсе, и при этом сломать всё: достаточно изменить его SELinux-метку, убрать capability из security.capability, поменять владельца или биты доступа. Решения на уровне содержимого этого не увидят. Именно здесь начинается территория EVM, Extended Verification Module, компонента ядра Linux, который следит не за тем, что написано внутри файла, а за тем, что написано о файле.

EVM попал в основное ядро Linux в версии 3.2 в 2012 году, поддержка цифровых подписей добавилась в 3.3. С тех пор он работает в одной упряжке с IMA, Integrity Measurement Architecture, образуя подсистему целостности ядра. Если IMA отвечает за содержимое файла, то EVM отвечает за его метаданные и расширенные атрибуты. Вместе они закрывают оба вектора офлайн-атак на файловую систему.

Что именно защищает EVM и как он это хранит

EVM защищает метаданные файла, включая в расчёт HMAC или подписи следующие атрибуты: номер инода, поколение инода, UID, GID, режим доступа, security.selinux, security.SMACK64, security.ima, security.capability. Помимо этого, по умолчанию в расчёт включается UUID файловой системы, что привязывает подпись к конкретному тому.

Результат этих вычислений хранится в расширенном атрибуте security.evm. Каждый раз, когда любой из защищённых атрибутов читается или изменяется, ядро пересчитывает HMAC или проверяет подпись и сравнивает с сохранённым значением. Если значения расходятся, операция отклоняется. Посмотреть текущие расширенные атрибуты файла:

getfattr -m - -d /bin/ls
# Вывод покажет security.ima, security.evm и security.selinux

Проверить конкретное значение security.evm:

getfattr -n security.evm /bin/ls

EVM поддерживает два класса значений для security.evm: HMAC на основе симметричного ключа и асимметричную цифровую подпись. Это не просто два варианта реализации одного и того же, а два принципиально разных режима с разными свойствами безопасности, разными сценариями применения и разными компромиссами.

Режим HMAC и управление симметричными ключами

HMAC-режим это первый и исторически основной способ работы EVM. EVM создаёт криптографический хеш, а именно HMAC, от расширенных атрибутов файла с использованием ключа, загружаемого во время загрузки системы. Ключ никогда не покидает пространство ядра в незашифрованном виде, что делает его недоступным для атак из userspace.

Ключ для EVM HMAC создаётся через механизм encrypted keys ядра. Сначала создаётся мастер-ключ, который шифрует остальные ключи:

# Создать мастер-ключ в keyring пользователя
keyctl add user kmk \
"$(dd if=/dev/urandom bs=1 count=32 2>/dev/null)" @u

# Сохранить мастер-ключ на диск
keyctl pipe $(keyctl search @u user kmk) > /etc/keys/kmk

Затем создаётся сам EVM-ключ, зашифрованный через мастер-ключ:

# Создать зашифрованный EVM-ключ размером 64 байта
keyctl add encrypted evm-key "new user:kmk 64" @u

# Сохранить зашифрованный blob на диск
keyctl pipe $(keyctl search @u encrypted evm-key) > /etc/keys/evm-key

При использовании TPM мастер-ключ создаётся как trusted key, то есть его plaintext-значение никогда не появляется в файловой системе, а хранится запечатанным TPM:

# Создать trusted key через TPM
keyctl add trusted kmk "new 32" @u
keyctl pipe $(keyctl search @u trusted kmk) > /etc/keys/kmk-trusted

После того как HMAC-ключ загружен в keyring, отключить возможность модификации метаданных уже нельзя. Загрузка HMAC-ключа это единственный способ заблокировать изменение метаданных. Активация EVM через запись в управляющий файл:

# Активировать EVM с поддержкой HMAC (значение 1)
echo 1 > /sys/kernel/security/integrity/evm/evm

# Проверить текущий статус
cat /sys/kernel/security/integrity/evm/evm

Значение в этом файле является битовой маской: бит 0 включает HMAC, бит 1 включает только проверку подписей без HMAC, бит 31 блокирует дальнейшие изменения настроек EVM до следующей перезагрузки.

Portable signatures и отличие от непереносимых

Здесь начинается та часть, где многие делают ошибки при развёртывании EVM в реальных системах. По умолчанию EVM включает в расчёт подписи номер инода и поколение инода. Подпись, включающая номер инода и поколение, не является переносимой, потому что эти значения будут различаться на каждой платформе. Portable signature исключает их, что позволяет устанавливать файл на нескольких платформах.

Разница критична для любого сценария, где образ файловой системы собирается централизованно и потом разворачивается на множестве машин. Если использовать обычные подписи, они будут корректны ровно на той машине, где создавались, и немедленно провалят проверку на любой другой. Portable signature решает эту задачу исключением зависящих от конкретной установки полей.

Создание EVM portable signature для файла через evmctl:

# Сгенерировать ключевую пару для подписи
openssl req -new -nodes -utf8 -sha256 -days 3650 -batch \
-x509 \
-out /etc/keys/pubkey_evm.pem \
-keyout /etc/keys/privkey_evm.pem

# Подписать файл с portable-флагом
evmctl sign --portable \
--key /etc/keys/privkey_evm.pem \
/usr/bin/myapp

# Подписать рекурсивно всю директорию
evmctl sign --portable --recursive \
--key /etc/keys/privkey_evm.pem \
/usr/bin/

Верификация подписи без активации EVM в ядре, полезно для автономного аудита:

evmctl verify \
--key /etc/keys/pubkey_evm.pem \
/usr/bin/myapp

Флаг --portable отключает использование UUID файловой системы и номера инода, что делает подпись независимой от конкретного экземпляра установки. UUID файловой системы можно также отключить отдельно через --uuid, если нужна подпись, переносимая между томами, но привязанная к конкретному инод-расположению это редкий, но допустимый сценарий.

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

Публичный ключ для проверки EVM-подписей должен попасть в специальный keyring ядра _evm. Это делается в initramfs до монтирования корневой файловой системы, потому что EVM должен быть активен до того, как начнётся чтение защищённых файлов.

Импортировать публичный ключ в keyring EVM:

# Получить идентификатор keyring _evm
EVM_KEYRING=$(keyctl search @u keyring _evm 2>/dev/null || \
keyctl newring _evm @u)

# Импортировать сертификат
evmctl import /etc/keys/pubkey_evm.pem $EVM_KEYRING

Для систем с Secure Boot и подписанным ядром публичный ключ EVM можно встроить непосредственно в образ ядра. Тогда он окажется в .evm keyring автоматически при загрузке, и никаких дополнительных действий в initramfs не потребуется. Загрузку ключей и инициализацию EVM рекомендуется выполнять как можно раньше, обычно это делается в initramfs, который уже измерен как часть доверенной загрузки.

Проверить, какие ключи присутствуют в keyring _evm:

keyctl show @u
# или через securityfs
cat /proc/keys | grep _evm

Режим fix и первоначальная разметка файловой системы

Перед тем как включить EVM в enforce-режиме, файловой системе нужно проставить атрибуты security.evm на все защищаемые файлы. Для этого существует режим fix, в котором EVM позволяет создавать и обновлять атрибуты, но ещё не блокирует файлы с некорректными значениями.

Активация IMA и EVM в режиме fix через параметры ядра при загрузке:

# Добавить в параметры ядра в /etc/default/grub
GRUB_CMDLINE_LINUX="ima_appraise=fix evm=fix"

# Применить и обновить grub
grub2-mkconfig -o /boot/grub2/grub.cfg
# или для систем с grubby
grubby --update-kernel=$(uname -r) \
--args="ima_appraise=fix evm=fix"

После перезагрузки в fix-режиме проставить EVM-атрибуты на все файлы системы:

# Инициализировать HMAC для всех файлов
evmctl -r ima_fix /

# Только для конкретного типа файлов
evmctl -t f ima_fix /usr/bin/

Убедиться, что атрибуты проставлены корректно, прежде чем переходить в enforce-режим:

# Проверить файл на наличие обоих атрибутов
getfattr -m security -d /usr/bin/ls | grep -E "ima|evm"

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

Практические ограничения и выбор между HMAC и подписями

Выбор между HMAC-режимом и asymmetric signature режимом определяется моделью угроз и операционными требованиями. HMAC быстрее: верификация симметричного хеша значительно дешевле асимметричных криптографических операций на каждое обращение к файлу. Но HMAC требует, чтобы ключ был доступен системе во время записи. Это значит, что любое легитимное обновление пакетов или изменение прав файла автоматически пересчитывает HMAC без участия человека. Если ключ скомпрометирован, атакующий может подделать HMAC для любого файла.

Асимметричные portable signatures лишены этой уязвимости: приватный ключ может находиться полностью офлайн, а система при работе использует только публичный ключ для проверки. Публичный ключ для верификации подписей загружается в keyring _evm, тогда как приватный ключ может оставаться офлайн. Это модель, при которой сервер продакшена физически не способен подписать ни один файл, что принципиально ограничивает радиус атаки при компрометации.

Разумный подход для большинства серверных сценариев: portable signatures для системных бинарников и библиотек, которые меняются только при обновлении пакетов, и HMAC для рабочих файлов приложений, которые легитимно изменяются в процессе работы. Такое разделение позволяет получить жёсткую защиту там, где она критична, без блокировки нормальной работы системы там, где данные динамичны.

EVM не является заменой мониторинга и аудита, это последний рубеж, который делает определённый класс атак физически невозможным даже при наличии физического доступа к диску. Злоумышленник, получивший возможность монтировать диск офлайн, может изменить содержимое файла и даже его хеш в security.ima, но без ключа EVM он не сможет пересчитать security.evm, и при следующей загрузке система откажет в доступе к каждому затронутому файлу. Это именно та гарантия, ради которой стоит разбираться в деталях настройки.

https://fileenergy.com/linux