Загрузка сервера выглядит как серия доверительных обещаний: прошивка обещает запустить валидный загрузчик, тот обещает запустить валидное ядро, ядро обещает загрузить только подписанные модули. Secure Boot превращает обещания в проверку подписей, и на платформах AMD к этому добавляется собственный аппаратный корень доверия: процессор безопасности PSP проверяет саму прошивку раньше UEFI, а на серверных EPYC вокруг него выстроена целая система из встроенного TPM, шифрования памяти и подтверждаемой загрузки виртуальных машин. Разница между настроенной и нетронутой цепочкой доверия видна не в интерфейсе, а в последствиях инцидента: в первом случае подмена загрузчика или модуля ловится на месте, во втором остаётся незамеченной. Подписи здесь классические, RSA с хешами SHA-256, сертификаты лежат в переменных прошивки, и каждое звено проверяет следующее, не доверяя соседям словом.
Разберём оба этажа доверия, проверку состояния в Linux, работу с собственными ключами, обновления отзывов и связь с SEV-SNP на EPYC.
Цепочка доверия UEFI от прошивки до ядра
Классический Secure Boot держит четыре хранилища ключей в энергонезависимой памяти прошивки. Platform Key, PK, устанавливает владельца платформы и подписывает ключи обмена, KEK, которые в свою очередь подписывают базы db и dbx. База db содержит доверенные сертификаты и хеши, по которым прошивка допускает загрузчики и ядра, а база dbx хранит отозванные подписи, и запись в неё действует раньше доверия: то, что попало в dbx, не запустится, даже если его сертификат жив в db. Путь стандартной сборки идёт через загрузчик shim, который подписан вендором дистрибутива, прошивка доверяет ему по db, а shim уже проверяет ядро и может опрашивать машинные ключи владельца, MOK, хранящиеся отдельно от прошивочных баз. Путь через shim выбран дистрибутивами не случайно: он позволяет владельцу машины добавлять свои ключи, не касаясь прошивочных баз, которые защищены ключами обмена и обновляются осторожнее. Хранилище переменных при этом само должно быть защищено: записи в энергонезависимой памяти прошивки перечитываются при каждой загрузке, и контроль состояния этих переменных входит в число проверок узла.
В Linux статус виден сразу из нескольких мест, и полезно сверять их между собой:
mokutil --sb-state
bootctl status
cat /sys/kernel/security/lockdown
dmesg | grep -iE 'secure boot|lockdown'
При включённой проверке ядро автоматически поднимает режим lockdown в состоянии integrity: закрыт прямой доступ к памяти ядра через /dev/mem, запрещена загрузка неподписанного кода BPF, ограничен kexec, и эти ограничения отключаются только с отключением самой проверки либо переходом lockdown в none руками, что стоит делать осознанно. Сам режим виден в файле /sys/kernel/security/lockdown, где перечислены уровни none, integrity и confidentiality с пометкой активного, и на серверах, где важна защита памяти ядра от чтения, выбирают и верхний уровень.
Аппаратный корень доверия AMD PSB на EPYC
На серверных EPYC доверие начинается раньше UEFI. В процессоре живёт выделенный сопроцессор безопасности PSP, Platform Security Processor, со собственными ядрами и постоянной памятью, в которую на заводе впаян ключ подписи AMD, ASK. Механизм Platform Secure Boot, PSB, проверяет целостность и подпись всего образа прошивки до того, как управление получит UEFI, и в случае несоответствия загрузка прерывается на уровне сопроцессора, а не на уровне настроек прошивки, которые атака могла бы изменить. Состояние PSB включается один раз и по умолчанию обратного пути не имеет: решение отключить прожигается предохранителем, и эта необратимость стоит осознанного выбора при закупке и подготовке узлов. Ключи в этой схеме двух уровней: ключ AMD подписывает ключи производителей, а производитель подписывает конкретные образы прошивки, и проверка идёт по всей лестнице внутри сопроцессора. Отдельно стоит помнить, что PSB и подсистема шифрования дополняют друг друга: первая защищает прошивку хоста, вторая аттестует память и гостей, и полная вертикаль доверия получается только из обеих.
В системе PSP виден как устройство криптографического сопроцессора на шине, его обслуживает драйвер ccp из состава ядра, а через него же открываются каналы к встроенному TPM и к подсистеме шифрования памяти. Firmware TPM живёт внутри PSP и предъявляется системе как обычный TPM 2.0 с устройством /dev/tpmrm0, и всё, что принято делать с дискретным TPM, от измерений до запечатывания ключей, работает и здесь. На потребительских платформах AMD тоже есть fTPM, и он включается в настройках прошивки тем же пунктом, что и сама проверка загрузки.
Проверка состояния и инструментов на узле
Набор проверок на сервере занимает минуту и показывает всю вертикаль:
ls -l /dev/tpmrm0 /dev/sev
tpm2_pcrread sha256
dmesg | grep -iE 'ccp|psp|sev'
Регистры измерений PCR первых банков показывают измеренную цепочку загрузки, и по их значениям видно, чем именно измерялся путь до ядра: нулевой банк отражает прошивку и её конфигурацию, четвёртый менеджер загрузки, седьмой политики Secure Boot, и сравнение значений между эталонным и рабочим узлом мгновенно показывает разницу в цепочке. Для SEV-стека утилита sevctl из набора инструментов сообщества выводит версию API, состояние платформы и идентификаторы сборки сопроцессора, и расхождение версий между узлами фермы видно мгновенно. Отдельно стоит проверять, что политика подписи модулей действует: параметр module.sig_enforce, выставленный при загрузке, делает отказ от неподписанного модуля жёстким, тогда как при выключенной проверке загрузки ядро лишь предупреждает в журнале.
Собственные ключи и подпись модулей ядра
Как только проверка включена, всё, что грузится в ядро помимо дистрибутивных пакетов, обязано иметь подпись, и здесь у администратора два пути. Первый путь через машинные ключи: создаётся пара ключей, приватная половина бережётся, публичная импортируется в MOK, после перезагрузки и подтверждения в интерфейсе shim ключ попадает в доверенные:
mokutil --import MOK.der
/usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 MOK.key MOK.der mymod.ko
Второй путь через собственные прошивочные базы: утилита sbctl создаёт ключи владельца, подписывает ими нужные файлы и регистрирует их в базах прошивки, и этот путь даёт полный контроль без посредничества shim, за что его любят на минималистичных системах:
sbctl create-keys
sbctl enroll-keys
sbctl sign /boot/vmlinuz-linux
sbctl verify
Последняя команда сверяет подписи всех файлов, о которых знает утилита, и её стоит вписать в процедуру обновления ядра, чтобы незавизированный образ всплывал до перезагрузки, а не после. В обоих случаях золотое правило одно: приватные ключи копируются в защищённое хранилище сразу, потому что потеря ключа означает перезагрузку с перерегистрацией на каждом узле фермы, а компрометация означает перекат всей цепочки с обновлением баз на прошивках. Пара ключей создаётся openssl с алгоритмом RSA и длиной не меньше 2048 бит, и для импорта в MOK сертификат нужен именно в формате DER, а перепутанные форматы остаются частой причиной отказа импорта на ровном месте.
Отзывы dbx и обновления прошивок
База dbx живёт, и обновления для неё приходят по каналам обновлений микропрограмм. fwupd работает с ними как с обычными обновлениями устройств: список показывает запись Secure Boot dbx, применение обновляет содержимое базы, а ядро перечитывает её при следующих загрузках:
fwupdmgr refresh
fwupdmgr get-updates
fwupdmgr update
Есть и специализированная утилита dbxtool, которая работает с базой отзывов напрямую, что удобно, когда обновление нужно доставить вне общего механизма fwupd. С подвохом стоит считаться заранее: обновление dbx отзывает чужие подписи, и если в базе окажется то, чем подписан собственный загрузчик или модуль, узел перестанет грузиться, поэтому массовое применение отзывов идёт сначала на одном узле стенда, и только потом по ферме. Прямое чтение переменной состояния из sysfs тоже годится для проверок:
od -An -t u1 /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c
Единица в выводе означает включённую проверку, ноль выключенную, и такой способ не зависит от наличия вспомогательных утилит в образе. Обновления прошивок через тот же fwupd при работающей проверке проходят только если образ подписан доверенным ключом, и это ещё один слой защиты, который стоит держать включённым намеренно.
SEV-SNP и подтверждённая загрузка виртуальных машин
На EPYC проверка загрузки продолжается внутрь виртуализации. Ступени здесь такие: SME шифрует память по биту в таблицах страниц, SEV выдаёт каждой виртуальной машине собственное пространство шифрования с ключом, недоступным гипервизору, SEV-ES защищает регистры при переходах между гостем и хостом, а SEV-SNP добавляет защиту целостности через таблицу обратных страниц и главное, аттестацию. Гость собирает отчёт, который подписан специфичным для процессора ключом подтверждения VCEK, и сторонняя сторона проверяет подпись и содержимое отчёта, убеждаясь, что работает именно с этой виртуальной машиной на исправном процессоре с ожидаемой прошивкой. В отчёте лежит и измерение начального состояния гостя, и его сверяют с эталоном, полученным при первом развёртывании образа, так что подменённый гипервизор или изменённый образ всплывают на этапе проверки, а не в работе. Доступ к подсистеме открывается устройством /dev/sev, поддержка хоста в KVM дошла до стабильных ядер в серии 6.11, и инструменты сообщества вроде snpguest закрывают цикл аттестации командами. Версии инструментов вроде sevctl и snpguest стоит держать синхронно с версией прошивки PSP на узлах, потому что форматы отчётов аттестации привязаны к поколениям, и расхождение всплывает именно в момент проверки подписи.
Со стороны загрузчика цепочку доверия удобно завязать на TPM: регистр PCR 7 отражает состояние политик Secure Boot, и ключ шифрования раздела, запечатанный в TPM на этом регистре, высвобождается только при неизменной политике:
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/nvme0n1p2
Такая связка означает, что подмена политики проверки на узле не только останавливает загрузку постороннего кода, но и запирает данные диска от того, кто попробует загрузиться со своей системой.
Практика внедрения на ферме
Внедрение полной цепочки умещается в порядок:
- на новых узлах включить fTPM и аппаратную проверку прошивки, а для EPYC зафиксировать решение по PSB до ввода в эксплуатацию, помня о необратимости;
- проверить статус в Linux через mokutil, bootctl и состояние lockdown, зафиксировав ожидаемые значения в профиле узла;
- завести машинные ключи владельца, импортировать их в MOK и настроить подпись модулей в сборочной линии;
- связать шифрование дисков с PCR 7 через systemd-cryptenroll или clevis;
- поставить обновления dbx и прошивок в регулярный цикл со стендом на первом узле;
- для приватных виртуальных машин включить SEV-SNP и выстроить проверку отчётов аттестации в системах управления.
Подводный камень у всего этого один, но большой: цепочка доверия без резервных копий ключей и без документированной процедуры восстановления превращается в одиночную точку отказа всей фермы. Процедура восстановления должна описывать и сброс ключей платформы, и перерегистрацию владельческих, и порядок возврата узлов в строй после такого сброса, и её полезно прогнать на стенде целиком, чтобы в настоящий момент она была привычкой, а не реконструкцией по памяти. Ключ PK, ключи KEK и владельческие MOK хранятся как производственные секреты, а процедура сброса и перерегистрации отрабатывается на стенде до того, как понадобится в продакшене.
Итог у связки Secure Boot и AMD-стека безопасности честный: она не делает систему неуязвимой, но делает подмену кода видимой и громкой, а на EPYC закрывает и слой, куда обычная проверка не дотягивается, от самой прошивки до памяти гостевых машин. Настроенная однажды, эта вертикаль работает годами, и её ценность всплывает именно в тот момент, когда без неё инцидент остался бы незамеченным.