Чип TPM2 умеет хранить секреты и отдавать их только после проверки состояния машины, поэтому связка с LUKS-шифрованием даёт то, чего обычно не хватает: диск открывается сам при загрузке, но ключ остаётся недоступным, если носитель вынуть и подключить к чужой системе. Вся настройка сводится к утилите systemd-cryptenroll из состава systemd и к осознанному выбору PCR-регистров, к которым привязан ключ. Ниже разобрана полная цепочка: проверка чипа, регистрация, привязка к измерениям загрузки, работа с crypttab и initramfs, recovery-план и честный разбор границ метода.

Проверка чипа TPM2 и требований к системе

Начать стоит с железа. Подходит только чип поколения 2.0: с устаревшим TPM 1.2 systemd не работает вовсе. Наличие устройства в системе видно простым списком:

ls -l /dev/tpm*

Нормальный вывод содержит два узла, /dev/tpm0 и /dev/tpmrm0. Первый это сырой интерфейс чипа, второй открывает сеансовый канал команд, и именно его использует systemd. Если узлов нет, TPM отключён в UEFI, и его включают в разделе Security настроек прошивки. Свойства чипа в деталях показывает tpm2_getcap properties-fixed из пакета tpm2-tools: там видны производитель, версия прошивки и число регистров. Стандарт TPM 2.0 предусматривает 24 PCR с номерами от 0 до 23, а банков хэшей обычно два: SHA-1 как наследие и SHA-256 как рабочий стандарт, с которым и работает systemd.

Поддержка со стороны системы проверяется так:

systemd-cryptenroll --tpm2-device=list

Утилита появилась в systemd 248, поэтому на дистрибутивах старее 2021 года сначала потребуется обновление systemd. В Debian 12, Ubuntu 22.04 и новее, Fedora и Arch всё нужное есть из коробки. Требования удобнее зафиксировать заранее:

  1. том в формате LUKS2, потому что токен TPM2 живёт в JSON-заголовке, которого у LUKS1 не существует;
  2. systemd версии 248 и новее, а для PIN-кода при разблокировке 251 и новее;
  3. загруженные модули ядра tpm_crb или tpm_tis, они подхватываются автоматически на большинстве плат;
  4. systemd внутри initramfs, иначе на этапе открытия корневого раздела чип окажется недоступен;
  5. зарегистрированный recovery-ключ, который выручит, если чип внезапно перестанет отдавать секрет.

Формат тома и занятые слоты показывает cryptsetup luksDump: в поле Version должно стоять 2, а из тридцати двух слотов LUKS2 хотя бы один остаётся свободным под новый ключ. Recovery-ключ добавляется до экспериментов командой systemd-cryptenroll --recovery-key: значение печатается один раз, и его следует сохранить вне этой машины, например в менеджере паролей организации. Пароль тома при этом тоже остаётся активным и служит запасным входом на случай любой нештатной ситуации.

Регистрация ключа командой systemd-cryptenroll

Регистрация выполняется одной строкой от root:

systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/nvme0n1p2

Утилита спросит действующий пароль тома, сгенерирует новый ключ, запишет его в отдельный keyslot и создаст в JSON-заголовке LUKS2 токен типа tpm2. Значение auto заставляет systemd найти чип самостоятельно; при нескольких TPM в системе вместо него передают явный путь /dev/tpmrm0. Заголовок тома показывает результат:

cryptsetup luksDump /dev/nvme0n1p2 | sed -n '/Tokens:/,$p'

В секции Tokens появится запись с полем tpm2: внутри указаны привязанные PCR-регистры, номер keyslot и двоичные blob-объекты с запечатанным секретом. Старый пароль продолжает работать: его оставляют как резервный канал доступа либо позже снимают через cryptsetup luksRemoveKey, когда новая схема проверена несколькими перезагрузками. Отдельная деталь для systemd 255 и новее: секрет запечатывается под ключом SRK с хэндлом 0x81000001, и этот хэндл создаётся в чипе автоматически, если его ещё нет, а публичный ключ SRK сервис systemd-tpm2-setup.service сохраняет в /run/systemd для последующих операций.

Привязка к PCR-регистрам и поведение при обновлениях

PCR это регистры чипа, куда прошивка, загрузчик и systemd записывают хэши каждого этапа загрузки. Значения меняются только при реальном изменении кода или политики, поэтому ключ из TPM отдаётся лишь той системе, чьи измерения совпадают с ожидаемыми. Картина по регистрам такая: PCR0 хранит хэш кода прошивки, PCR1 её конфигурацию, PCR4 загрузчик и вызываемые им PE-образы, PCR5 таблицу разделов GPT, PCR7 состояние Secure Boot вместе с сертификатами PK, KEK, db и dbx, PCR9 отдаёт ядру под измерение полученного initrd, PCR11 достаётся systemd-stub, который кладёт туда образ ядра, initrd и командную строку, PCR14 записывает сертификаты shim и MOK, а systemd-cryptsetup может дополнительно измерить volume key открытого тома в PCR15.

По умолчанию enrollment привязывается только к PCR7, и это разумный компромисс: регистр меняется редко и по осмысленным поводам, при переключении Secure Boot или обновлении баз сертификатов. Менее удачна привязка к PCR0 и PCR2: любое обновление UEFI или опциональных ROM ломает разблокировку до повторной регистрации. Комбинация PCR7, PCR11 и PCR14, рекомендуемая документацией systemd, даёт строгий контроль над тем, что реально загружено, и при этом переживает рутинные обновления, если ядро разворачивается подписанным unified kernel image.

Банк хэшей и точное значение задаются явно:

systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7:sha256 /dev/nvme0n1p2

В свежих выпусках регистры можно перечислять и по именам, например --tpm2-pcrs=secure-boot-policy+kernel-boot, что делает команду читаемой. Пустой список --tpm2-pcrs= привязывает секрет ни к чему: чип отдаёт ключ любой загруженной системе, и такое решение годится разве что для переносных дисков, где предсказать машину загрузки невозможно. Текущие измерения всех регистров смотрятся утилитами tpm2-tools:

tpm2_getcap pcrs

Отдельный нюанс: юнит systemd-pcrphase.service дописывает в PCR11 строки фаз загрузки по мере старта системы, поэтому политика по PCR11 учитывает не только образ ядра, но и точку процесса загрузки, на которой происходит запрос секретов.

Настройка crypttab и initramfs для разблокировки при загрузке

Чтобы systemd-cryptsetup при старте системы обращался к чипу, в /etc/crypttab для тома задаётся опция tpm2-device:

luks-6f1c2a UUID=6f1c2a44-91b7-4f0e-8d52-0a1b2c3d4e5f none tpm2-device=auto

Дальше всё зависит от дистрибутива. В Arch в массив HOOKS конфигурации mkinitcpio добавляют хуки systemd, sd-encrypt и tpm2, после чего образ пересобирается командой mkinitcpio -P. Системы с dracut, включая Fedora, обычно уже тянут модуль tpm2-tss в раннее окружение, достаточно пересобрать образ через dracut -f. В Debian и Ubuntu стоит убедиться, что модули tpm_crb и tpm_tis попадают в initramfs, и запустить update-initramfs -u. После перезагрузки том открывается без вопросов, а в журнале это видно записями юнита systemd-cryptsetup с упоминанием TPM2.

Проверить разблокировку можно и без перезагрузки: cryptsetup вместе с systemd получает плагин для токенов, и том открывается напрямую:

cryptsetup open --token-type systemd-tpm2 /dev/nvme0n1p2 tmpcheck
cryptsetup close tmpcheck

Если команда сработала на работающей системе с теми же измерениями, что и в момент enrollment, раннее окружение тоже справится. Полезный приём для отладки: параметры ядра rd.luks.options переопределяют опции crypttab на один запуск. Убрав tpm2-device из переопределения, администратор загружается с обычным запросом пароля, не трогая файлы на диске, и спокойно ищет причину сбоя.

PIN-код и подписанные политики PCR11

Флаг --tpm2-with-pin=yes добавляет к разблокировке запрос PIN поверх проверки измерений. Это страховка от сценария, когда машина утеряна во включённом состоянии: без PIN чип отдаёт ключ легально загруженной системе, с PIN требуется ещё и фактор знания. PIN может состоять из любых символов, не только цифр. Лимит неверных попыток общий для всего чипа: ошибочные вводы расходуют встроенную защиту от перебора, и TPM уходит в блокировку на время, которое настраивается утилитой tpm2_dictionarylockout, а текущее состояние видно через tpm2_getcap. Плата за защиту понятна: каждая загрузка требует ввода PIN, и часть удобства исчезает, поэтому для ноутбуков компромисс оправдан, а для стоечных серверов в закрытой стойке его нередко считают избыточным.

Подписанные политики появились в systemd 252 и снимают главную боль строгой привязки к PCR11: обновление ядра больше не рвёт доступ. Секрет привязывается не к текущим измерениям, а к подписи доверенного ключа, и том открывается, пока политика подписана этим ключом. Схема проста: генерируется пара ключей, утилита systemd-measure подписывает ожидаемые значения PCR11 для каждого нового образа:

systemd-measure sign --current --pcr=11 --private-key=/etc/systemd/measure.key --public-key=/etc/systemd/measure.pem

Публичный ключ кладётся как /etc/systemd/tpm2-pcr-public-key.pem, файл подписи как tpm2-pcr-signature.json рядом, тогда systemd-cryptenroll и systemd-cryptsetup находят оба автоматически. Сборщик unified kernel image ukify умеет подписывать измерения прямо на этапе сборки образа, поэтому в хозяйствах с автоматической сборкой ядер подпись появляется сама собой. Регистрация в такой схеме выглядит иначе:

systemd-cryptenroll --tpm2-device=auto --tpm2-public-key-pcrs=11 /dev/nvme0n1p2

По умолчанию --tpm2-public-key-pcrs привязывает политику именно к PCR11, то есть к любому unified kernel image, под которым есть валидная подпись. Перед записью слота systemd сверяет подпись с текущими измерениями и отказывается от enrollment, если текущее состояние не откроется, так что потерять доступ из-за забытой подписи сложно.

Диагностика сбоев и повторная регистрация слота

Частый сбой выглядит так: система падает в запрос пароля, хотя enrollment сделан. Причины делятся на две группы. Первая это изменившиеся измерения: обновление UEFI, пополнение списка отзыва сертификатов dbx, переключение Secure Boot, смена загрузчика. Вторая это окружение initramfs: чип не успел инициализироваться, не загружены модули ядра, отсутствует systemd-cryptsetup в раннем окружении. Точный ответ даёт журнал:

journalctl -b | grep -i tpm2

В выводе видно, чем закончился запрос к чипу: отказом расшифрования токена при несовпадении политики, отсутствием устройства или ошибкой загрузки модуля. Если измерения разошлись, слот перерегистрируется без потери данных, причём systemd-cryptenroll совместит стирание старой записи с созданием новой в одном вызове:

systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=7 /dev/nvme0n1p2

Аргумент --wipe-slot принимает номер слота или слово all, но с типом tpm2 работать безопаснее: парольные слоты не трогаются даже при ошибке в остальной части команды. В корпоративном парке перерегистрацию после обновлений прошивки прогоняют скриптом по списку машин:

for host in $(cat /root/lukshosts.txt); do
  ssh root@$host systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=7 /dev/disk/by-partlabel/root
done

Команда одна и на данные тома не влияет, весь цикл занимает секунды на узел, а recovery-ключ и пароль остаются страховкой на случай отказа чипа в самый неподходящий момент.

Модель угроз и границы защиты TPM2

Трезвое понимание того, что метод защищает, а что нет, экономит массу заблуждений. Сильные стороны: диск, вынутый из машины, бесполезен, потому что секрет запечатан под SRK конкретного чипа и в другом TPM просто не расшифруется; загрузка посторонней системы с флешки меняет измерения и получает отказ; кража выключенного ноутбука не даёт доступа к данным. Слабые стороны тоже ясны: работающая система с правами root читает расшифрованные файлы напрямую, физический доступ к включённой машине без PIN-кода позволяет дождаться легальной загрузки, а сложные аппаратные способы чтения памяти лежат вне модели угроз TPM. Отсюда практические выводы: Secure Boot стоит держать включённым и защищённым паролем администратора UEFI, иначе привязка к PCR7 теряет смысл, PIN обязателен на переносных машинах, а парольный слот остаётся последним рубежом. Итоговая схема из пароля, TPM2 и recovery-ключа закрывает удобство, отказоустойчивость и базовую защиту от уноса носителя, не требуя от администратора ничего экзотического.