Установка Tessera на Astra Linux SE
Этот документ — пошаговый сценарий установки и базовой настройки
tessera на чистой машине Astra Linux SE 1.7+. В конце — рабочий
вход по удостоверению, проверенный через pamtester (§10). Каждый
раздел заканчивается командой проверки; если проверка не прошла —
см. §11 «Troubleshooting» и troubleshooting.md.
Все команды выполняются от имени
rootили сsudo. На время правки PAM-стека держите открытый рут-shell в другом терминале. Если PAM-стек собьёт авторизацию, второй терминал — единственный способ откатить изменения.
1. Подготовка машины
Заголовок раздела «1. Подготовка машины»1.1 Проверка ОС
Заголовок раздела «1.1 Проверка ОС»cat /etc/astra_version 2>/dev/null || cat /etc/os-releaseОжидаемый вывод: версия 1.7.5 или новее. На других редакциях
Astra Linux (Орёл, Воронеж, Смоленск 1.7+) сценарий идентичен. На
Ubuntu/Debian — best-effort.
1.2 Проверка ядра
Заголовок раздела «1.2 Проверка ядра»uname -rОжидание: 5.15.0-93-generic или новее (необходимо для корректной
доставки udev-событий извлечения USB).
1.3 Установка системных зависимостей
Заголовок раздела «1.3 Установка системных зависимостей»sudo apt updatesudo apt install -y \ libpam0g \ libssl3 \ libudev1 \ libdbus-1-3 \ libsystemd0 \ pcscd \ opensc-pkcs11 \ pamtesterЕсли apt install не находит pamtester (или другие пакеты) на
Ubuntu/Debian — проверьте, включён ли компонент universe/multiverse
(sudo add-apt-repository universe && sudo apt update на Ubuntu):
pamtester — реальная зависимость .deb (см. debian/control), но
живёт не в main. На Astra SE pamtester не пакетирован вообще —
ни в одном компоненте (main/contrib/non-free/non-free-firmware)
ни repository-main, ни repository-extended; apt-cache search pamtester там всегда пуст, никакой репозиторий не поможет. Собирается
из исходников за минуту (upstream не развивается с 2005 года, версия
всегда 0.1.2, зависимость только libpam0g-dev):
sudo apt install -y build-essential libpam0g-devcd /tmpwget -L "https://sourceforge.net/projects/pamtester/files/pamtester/0.1.2/pamtester-0.1.2.tar.gz/download" -O pamtester-0.1.2.tar.gz# запасной источник того же тарбола, если SourceForge отдаёт# промежуточную страницу вместо файла:# wget http://deb.debian.org/debian/pool/main/p/pamtester/pamtester_0.1.2.orig.tar.gz -O pamtester-0.1.2.tar.gztar xzf pamtester-0.1.2.tar.gzcd pamtester-0.1.2./configure && makesudo make installСтавится в /usr/local/bin/pamtester — в PATH попадает автоматически,
дальше используется как в §10.
Разделы 3–4 (выпуск тестового CA и удостоверений) — обычные вызовы
opensslбез какого-либо движка (ECDSA P-256 поддерживается встроеннымdefault-provider’ом OpenSSL везде, включая macOS), не обязаны выполняться на целевой Astra-машине и вынесены в отдельный документ — cert-issuance-lab.md.
1.4 Preflight: USBGuard и Astra ЗПС (DIGSIG)
Заголовок раздела «1.4 Preflight: USBGuard и Astra ЗПС (DIGSIG)»Перед установкой полезно убедиться, что окружение не заблокирует ни
сам токен на USB-шине, ни запуск pam_tessera.so /
tessera через ЭЦП-контроль.
USBGuard
Заголовок раздела «USBGuard»Если на хосте установлен USBGuard в режиме block, USB-токен должен
быть в allowlist — иначе ядро не отдаст устройство udev’у, и
tessera не увидит его.
sudo systemctl is-active usbguard # active / inactive / not-foundsudo usbguard list-devices 2>/dev/null # столбец "block" → токен заблокированРазрешить конкретный токен (по vid:pid или по hash) — отдельным
правилом в /etc/usbguard/rules.conf:
allow id 0aca:0030 name "Rutoken ECP" hash "ABC..."После правки правил — sudo systemctl reload usbguard. Подробности
по runtime-аспекту (порядок старта monitord относительно USBGuard)
— в docs/operations.md §3.5.
Astra ЗПС / DIGSIG (astra-digsig-control)
Заголовок раздела «Astra ЗПС / DIGSIG (astra-digsig-control)»В production-развёртывании на Astra SE требуется одно из двух:
astra-digsig-controlпереведён вlogging-only-режим (модуль не блокирует выполнение неподписанных ELF, но шумит в/var/log/syslogсообщениямиDIGSIG: NOT_ELF_SIGNED); либо- бинари
pam_tessera.soиtesseraподписаны через сервис подписи Astra-партнёра (bsignGPG-ключом из доверенной связки в/etc/digsig/keys/) — обычно это шаг сборки.debв Astra-CI.
sudo astra-digsig-control status # ВКЛЮЧЕНО / НЕАКТИВНО / logging-onlysudo dmesg | grep -i digsig | tail # видны ли отказы по подписиВ режиме enforce без валидной подписи PAM-аутентификация не
проходит — pam_tessera.so просто не загружается. См. также
docs/threat-model.md §3.7.
2. Установка .deb
Заголовок раздела «2. Установка .deb»2.1 Скачивание
Заголовок раздела «2.1 Скачивание»Релизы публикуются на GitHub:
github.com/TesseraLabs/tessera/releases.
В релизе — только .deb в двух вариантах (…-astra.deb и
…-ubuntu.deb), плюс .changes и .buildinfo для аудита; готовых
файлов контрольных сумм там нет, их считает оператор на доверенной
машине (см. §2.2). Скачать нужный вариант — через gh CLI или вручную
из браузера:
gh release download v0.5.0 --repo TesseraLabs/tessera --pattern '*-astra.deb'# или для Ubuntu-цели:# gh release download v0.5.0 --repo TesseraLabs/tessera --pattern '*-ubuntu.deb'2.2 Генерация контрольных сумм (доверенная машина)
Заголовок раздела «2.2 Генерация контрольных сумм (доверенная машина)»Контрольные суммы считает сборщик или оператор на доверенной
машине (не на целевой) скриптом generate-checksums.sh. Скрипт живёт в
репозитории (scripts/generate-checksums.sh), в .deb он не
упаковывается — доверенная машина должна иметь клон tessera нужного
тега (git clone --branch v0.5.0 --depth 1 https://github.com/TesseraLabs/tessera.git) либо скачать сам файл
напрямую:
curl -fsSL -o generate-checksums.sh \ https://raw.githubusercontent.com/TesseraLabs/tessera/v0.5.0/scripts/generate-checksums.shchmod +x generate-checksums.sh./generate-checksums.sh tessera_0.5.0-1_amd64.deb checksumsСкрипт кладёт в каталог checksums/ файл checksums.txt — отчёт по
SHA-256 для самого .deb и каждого файла внутри пакета — плюс
standalone-файлы *.sha256. (Скрипт умеет также писать секцию
Streebog-256 при наличии gost-engine, но в этом сценарии ECDSA-only
она не используется.)
На целевую машину доставляются три вещи: сам .deb, checksums.txt
(генерируется на доверенной машине в §2.2, переносится вручную — scp,
USB и т.п.) и verify-checksums.sh (скрипт самодостаточен — из
внешнего ему нужен только openssl). Сам скрипт, в отличие от
checksums.txt, — статичный файл репозитория,
его проще скачать напрямую на целевой машине, а не копировать с
доверенной:
curl -fsSL -o verify-checksums.sh \ https://raw.githubusercontent.com/TesseraLabs/tessera/v0.5.0/scripts/verify-checksums.shchmod +x verify-checksums.sh2.3 Проверка на целевой машине
Заголовок раздела «2.3 Проверка на целевой машине»./verify-checksums.sh tessera_0.5.0-1_amd64.deb checksums.txtОжидание: OK: N checksum(s) verified. Скрипт (описан в
scripts/verify-checksums.sh) сверяет
SHA-256. Ненулевой код выхода — несовпадение суммы (код 1) либо
проблема запуска: неверные аргументы (2). В любом из этих случаев
пакет не устанавливать, пока причина не разобрана.
2.4 Установка
Заголовок раздела «2.4 Установка»sudo apt install ./tessera_0.5.0-1_amd64.debapt подтянет недостающие зависимости (libpkcs11-helper1,
librtpkcs11ecp).
2.4½ Предполётная проверка (tessera check)
Заголовок раздела «2.4½ Предполётная проверка (tessera check)»Перед systemctl restart tessera или при первой установке прогоните
preflight: он валидирует config.toml и сообщает обо всех ошибках
конфигурации за один проход — без открытия сокета и без рестарта демона.
sudo tessera checkЧто проверяется:
-
PAM-стек. Сканирует
/etc/pam.d/{login,fly-dm,fly-dm-np,sshd,sudo,su}и валит ERROR в трёх случаях:@include tessera-*стоит ПЕРЕДauth required pam_parsec_mac.so(на Astra SE это убивает account-фазу с «Can’t obtain required data»). Check id:pam_stack_misorder.session required pam_tessera.soстоит ПЕРЕДpam_systemd.so/@include common-session—XDG_SESSION_IDещё не доступен на моментpam_sm_open_session,UpdateSessionTargetне отправляется, monitord не умеет вызвать logind Logout/Lock при извлечении USB. Check id:pam_stack_session_misorder.- Нет литеральной строки
auth ... pam_parsec_mac.so(стек без МКЦ или auth-фаза целиком через@include common-auth, как в стоковыхsudo/sshdна Debian/Astra), но@include tessera-*стоит ПОСЛЕ@include common-auth—pam_unix.soизcommon-authуспевает аутентифицировать по паролю раньше, чем вообще выполнится снипет tessera, что срываетcert-only/optional. Чинится через--unintegrateи повторную интеграцию. Check id:pam_stack_common_auth_misorder, INFO-паройpam_stack_common_auth_okпри корректном порядке.
Все ERROR подсказывают команду фикса через
integrate-pam.sh. Хелсчек для session-фазы дополнительно пишетpam_stack_session_ok(INFO) при корректном порядке илиpam_stack_session_no_systemd(INFO) если в стеке вообще нет pam_systemd — типично для sysvinit/OpenRC хостов. -
[mac].runtimevs ядро.runtime=requiredбез активногоparsec_strict_mode()=1— ERROR (requiredв strict-mode без МКЦ ядра делает демона бесполезным).auto+ отсутствующее ядро — WARN (тихий fallback наStubBackend, MAC НЕ enforced).disabled— INFO. -
Trust anchors / intermediates. Каждый путь из
[trust].anchorsи[trust].intermediatesдолжен существовать, быть непустым и содержать хотя бы один-----BEGIN CERTIFICATE-----маркер. Иначе ERROR — демон не может валидировать ни одной цепочки. -
/etc/tessera/ca/. WARN, если world-writable (mode & 0o002 != 0). -
PARSEC_CAP_CHMAC. Если МКЦ ядро активно и[mac].runtime ≠ disabled, но у процесса нет capability — WARN: метки наsessions.jsonне лягут. -
host_identity-источники. По одной INFO/WARN строке на каждый настроенный источник (machine_id,dmi_*,hostname,custom_command) — видно сразу, что резолвится и что падает.
Exit-код: 0 — только INFO/WARN; 1 — есть хотя бы один ERROR. Тот
же check выполняется демоном на старте: при наличии ERROR загрузка
прерывается, в journalctl -u tessera останутся структурные
сообщения с target=tessera.startup_check для каждой проверки.
2.4¾ Сценарий клонированного образа (golden image → устройство)
Заголовок раздела «2.4¾ Сценарий клонированного образа (golden image → устройство)»Если устанавливаете на множество устройств через клон одного образа,
полный end-to-end workflow вынесен в отдельный документ:
docs/clone-image.md — bootstrap-cert на эталоне,
finish-bootstrap.sh на каждом клоне, dump-host-id для CA-админа,
выпуск per-host сертификата, troubleshooting, Ansible-выкатка.
Tldr — два инструмента, поставляемых в .deb:
tessera dump-host-id [--output FILE | --usb]— пробует все известныеhost_identity-источники, пишет TSV-отчёт. Столбецactive_under_current_config=yesотмечает источник, который демон реально использует сейчас.--usbавтоматически монтирует первую USB-флешку r/w и пишетhost-ids-<hostname>-<UTC>.tsv./usr/share/tessera/finish-bootstrap.sh— переход за один проход с bootstrap-state на production: перезаписьconfig.toml(sources = ["override"]→["dmi_board_serial", "machine_id"]),tessera check, рестарт демона, дамп host_id’ов на USB. Идемпотент. Флаги — см. clone-image.md §4.2.
2.5 Проверка systemd-юнита
Заголовок раздела «2.5 Проверка systemd-юнита»systemctl status tesseraОжидание: Active: active (running). Если inactive (dead) —
запустить вручную:
sudo systemctl enable --now tesseraVerification (раздел 2)
Заголовок раздела «Verification (раздел 2)»tessera --versiontest -d /run/tessera && echo "runtime dir OK"test -S /run/tessera/monitord.sock && echo "socket OK"Ожидание: версия 0.5.0. Обе остальные строки на свежей машине,
где ещё нет /etc/tessera/config.toml (см. постинсталл-подсказку
.deb — cp config.toml.example config.toml), закономерно пустые:
демон стартует, но валидный config.toml требует материал из §3
(CA), §7 (расширения удостоверения) и §8 (ролевое хранилище), которого
на этом этапе walkthrough’а ещё нет. Обе строки должны появиться только
после systemctl restart tessera в конце §8 — полная проверка
демона/сокета там же, в Verification (раздел 8) и smoke-тесте §10.
3. Создание тестового CA
Заголовок раздела «3. Создание тестового CA»Вынесено в отдельный документ — cert-issuance-lab.md
§«Создание тестового CA»:
каталог, ключ CA, самоподписанный сертификат, verification. Всё —
обычные вызовы openssl без движка (ECDSA P-256), можно выполнять на
рабочей станции администратора, не на целевой Astra-машине.
Результат раздела — ca.pem и ca.key в /tmp/ca/, нужны дальше в
§4 и §5.
4. Создание тестовой ролевой учётной записи
Заголовок раздела «4. Создание тестовой ролевой учётной записи»Роль на логине — это имя учётной записи входа: инженер входит в
ролевую учётную запись, названную по роли (ssh serv@device), и
запрошенная роль равна имени этой УЗ. Отдельного запроса роли нет.
Дальше по сценарию используется роль serv («сервисный инженер») — для
неё в пакете есть готовый срез dist/roles/serv.toml, он понадобится
в §8. Учётная запись входа тоже называется serv; заводим её в §8,
вместе с ролевым хранилищем.
Ключ, CSR, оба обязательных расширения (pam_cert_host_binding,
pam_cert_allowed_roles) и упаковка в P12 — в отдельном документе:
cert-issuance-lab.md §«Создание удостоверения ролевой
учётной записи».
Там же — таблица OID, fail-closed-поведение при отсутствии расширений
(см. §7 и §10 этого документа) и получение host_id_hash через sudo tessera dump-host-id с целевой машины.
Результат раздела — serv.p12 в /tmp/ca/, готов к переносу на
USB-носитель (§5).
5. Подготовка USB-носителя (режим pkcs12 / Mode A)
Заголовок раздела «5. Подготовка USB-носителя (режим pkcs12 / Mode A)»Mode A: ключ хранится в
.p12на USB-носителе, защищён парольной фразой. Для production выбирать Mode B (PKCS#11-токен).
5.1 Форматирование
Заголовок раздела «5.1 Форматирование»tessera ищет .p12 на любой партиции с FS из allowlist
(vfat, exfat, ext4, ntfs). Метка партиции значения не имеет —
защита обеспечивается на уровне расшифровки .p12 пользовательским
паролем и валидации цепочки сертификатов модулем доверия. Лимит на
число перебираемых партиций задаётся параметром max_usb_partitions
в config.toml (по умолчанию 8, диапазон 1..=64).
Если на USB-флешке несколько разделов и часть содержит посторонние файлы с именем, совпадающим с
pkcs12_path_pattern(типично для Apple-форматированных носителей и USB с несколькими партициями),tesseraраспознаёт их как «не PKCS#12» по ASN.1-конверту (без запроса PIN) и продолжает искать настоящий.p12на следующих разделах. Ошибки, требующие пароля (неверный PIN / MAC verify / decrypt / chain), остаются fail-closed без перебора.
Типовой рецепт (sdX1 — раздел USB-носителя из вывода lsblk | grep -i usb):
# ВНИМАНИЕ: команда УНИЧТОЖАЕТ данные на устройстве /dev/sdX1.# Поддерживаемые FS: vfat, exfat, ext4, ntfs.sudo mkfs.ext4 /dev/sdX1sudo mount /dev/sdX1 /mnt/usbsudo install -m 0600 service.p12 /mnt/usb/service.p12sudo umount /mnt/usbЕсли флешка отформатирована без таблицы разделов (FS лежит прямо на
whole-device), это тоже работает: tessera читает ID_FS_TYPE
udev и монтирует whole-device напрямую.
5.2 Layout
Заголовок раздела «5.2 Layout»/mnt/usb/├─ certs/│ ├─ user.p12│ └─ chain.pem└─ tessera.marker5.3 Копирование
Заголовок раздела «5.3 Копирование»sudo mkdir -p /mnt/usb/certssudo cp /tmp/ca/serv.p12 /mnt/usb/certs/user.p12sudo cp /tmp/ca/ca.pem /mnt/usb/certs/chain.pemsudo touch /mnt/usb/tessera.markersudo umount /mnt/usbVerification (раздел 5)
Заголовок раздела «Verification (раздел 5)»sudo mount /dev/sdX1 /mnt/usbls -la /mnt/usb/certs/sudo umount /mnt/usbОжидание: оба файла присутствуют, размер > 0.
6. Подготовка Рутокен ЭЦП 2.0 (режим pkcs11 / Mode B)
Заголовок раздела «6. Подготовка Рутокен ЭЦП 2.0 (режим pkcs11 / Mode B)»6.1 Установка драйвера
Заголовок раздела «6.1 Установка драйвера»sudo apt install librtpkcs11ecp6.2 Проверка слота
Заголовок раздела «6.2 Проверка слота»pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -LОжидание: вывод вида Slot 0 (0x...): ... с моделью токена.
6.3 Инициализация (только для нового, неинициализированного токена)
Заголовок раздела «6.3 Инициализация (только для нового, неинициализированного токена)»pkcs11-tool --module /usr/lib/librtpkcs11ecp.so \ --init-token --label "serv-token" \ --so-pin '12345678'pkcs11-tool --module /usr/lib/librtpkcs11ecp.so \ --init-pin --so-pin '12345678' --pin '1234567890'6.4 Импорт ключа и сертификата
Заголовок раздела «6.4 Импорт ключа и сертификата»pkcs11-tool ждёт ключ и сертификат отдельными объектами в DER/PEM, а не
PKCS#12-контейнером. Сначала извлекаем их из serv.p12 (пароль — из
cert-issuance-lab.md §«Упаковка в P12»):
openssl pkcs12 -in serv.p12 -nocerts -nodes -passin pass:test \ -out serv.token.key # приватный ключ, PEM, без пароляopenssl pkcs12 -in serv.p12 -clcerts -nokeys -passin pass:test \ -out serv.token.crt # удостоверение, PEM# Часть токенов принимает только DER — при необходимости конвертируем:# openssl pkey -in serv.token.key -outform DER -out serv.token.key.der# openssl x509 -in serv.token.crt -outform DER -out serv.token.crt.derИмпортируем удостоверение и приватный ключ в токен:
pkcs11-tool --module /usr/lib/librtpkcs11ecp.so \ --login --pin '1234567890' \ --write-object serv.token.crt --type cert --label serv --id 01pkcs11-tool --module /usr/lib/librtpkcs11ecp.so \ --login --pin '1234567890' \ --write-object serv.token.key --type privkey --label serv --id 01Стираем временный приватный ключ, лежавший открытым на диске:
shred -u serv.token.key serv.token.key.der 2>/dev/null || shred -u serv.token.keyVerification (раздел 6)
Заголовок раздела «Verification (раздел 6)»pkcs11-tool --module /usr/lib/librtpkcs11ecp.so \ --pin '1234567890' -OОжидание: в выводе присутствуют Private Key Object и
Certificate Object с label=serv.
7. Авторизация: расширения удостоверения
Заголовок раздела «7. Авторизация: расширения удостоверения»Привязка «на каком устройстве и в какой роли» живёт в самом удостоверении. PAM-модуль читает два X.509 v3 расширения листа:
pam_cert_host_binding(OID2.25.183976554325829274683049824615098) — список разрешённых устройств;pam_cert_allowed_roles(OID2.25.185305973969816596290730578528098241367, некритичное) — список ролей, которые удостоверение вправе активировать.
Это две независимые оси: первая отвечает «где», вторая — «кем». Списка
разрешённых учётных записей отдельно нет: имя учётной записи входа и
есть роль, поэтому pam_cert_allowed_roles заодно отвечает и на вопрос
допуска к УЗ. Допуск решается только удостоверением — механизма, которым
устройство разрешало бы вход по своим правилам, в конфигурации не
существует. Отказ по любому из двух — fail-closed.
Готовые рецепты openssl.cnf для выдачи удостоверений с правильными
расширениями приведены в cert-issuance.md.
Verification (раздел 7)
Заголовок раздела «Verification (раздел 7)»openssl x509 -in /tmp/ca/serv.pem -noout -text \ | grep -E '2\.25\.(183976554325829274683049824615098|185305973969816596290730578528098241367)'Ожидание: обе строки с дотированными OID присутствуют в выводе.
8. Ролевое хранилище устройства
Заголовок раздела «8. Ролевое хранилище устройства»Роль требуется на каждом входе, и её описание модуль берёт из ролевого
хранилища — каталога со срезами <role>.toml на самом устройстве.
Пока хранилище пусто или недоступно, устройство не пускает никого:
это намеренное fail-closed-поведение, а не ошибка настройки.
8.1 Установка среза роли
Заголовок раздела «8.1 Установка среза роли»Готовый образец среза serv лежит в репозитории — dist/roles/serv.toml;
в .deb образцы ролей не входят, поэтому на чистой машине срез
проще создать на месте:
sudo install -d -m 0755 -o root -g root /var/lib/tessera/rolessudo tee /var/lib/tessera/roles/serv.toml >/dev/null <<'EOF'role = "serv"version = 1os = "linux"name = "Service Engineer"level = 5description = "Service engineer with sudo and higher resource limits."
[payload]groups = ["service", "wheel"]sudo_role = "service"
[payload.limits]nofile = 4096
[session]max_ttl_seconds = 14400memory_max = "2G"tasks_max = 512EOFsudo chown root:root /var/lib/tessera/roles/serv.tomlsudo chmod 0644 /var/lib/tessera/roles/serv.tomlЕсли репозиторий доступен на этой машине, то же самое одной командой:
sudo install -m 0644 -o root -g root \ dist/roles/serv.toml /var/lib/tessera/roles/serv.tomlДоверие к хранилищу держится на правах файловой системы — той же
модели, что у sudoers.d: каталог, все срезы и все родительские
каталоги обязаны принадлежать root:root и не быть доступны на запись
группе или всем. Срез, лежащий в каталоге непривилегированного
пользователя, продукт отвергает: иначе роль (а с ней группы, sudo и
лимиты сессии) мог бы переопределить тот, кого она ограничивает.
8.2 Настройка [roles] в /etc/tessera/config.toml
Заголовок раздела «8.2 Настройка [roles] в /etc/tessera/config.toml»[roles]dir = "/var/lib/tessera/roles"# Потолок сессии, когда его не задают ни удостоверение, ни роль.# default_session_ttl_seconds = 43200 # 12 ч, значение по умолчаниюПолное описание секции — configuration.md.
8.3 Учётная запись входа
Заголовок раздела «8.3 Учётная запись входа»Имя учётной записи и есть роль, поэтому на устройстве должна
существовать учётная запись serv. В парке ролевые учётные записи
провижинятся отдельно; для лабораторного стенда достаточно:
sudo useradd --create-home --shell /bin/bash servРолевая учётная запись обязана быть обычной, а не системной.
Флага --system здесь нет намеренно: он выдаёт uid вне диапазона
обычных пользователей, а продукт отвергает вход в учётную запись с
таким uid. Такая учётная запись просто не пустит инженера.
Диапазон обычных пользователей задан двумя границами: 1000 и
61183 включительно. Всё, что ниже, — учётные записи, которые
дистрибутив и пакеты завели для себя; нижняя граница совпадает с
UID_MIN из /etc/login.defs в Debian, Ubuntu и Astra. Всё, что выше,
— тоже не обычные пользователи: с 61184 начинается блок, из которого
systemd раздаёт uid юнитам с DynamicUser=yes, а дальше лежат nobody
(65534) и nogroup (65535). Проверка опирается именно на эти две
границы, а не на список зарезервированных имён; uid выше верхней
границы — не арифметическая экзотика, а штатное состояние любой
системы с systemd.
Верхняя граница намеренно не совпадает с UID_MAX (60000 в Debian):
UID_MAX ограничивает лишь то, что useradd раздаёт сам, а ролевая
учётная запись, заведённая провижинингом выше этого значения, обязана
уметь войти. Значение UID_MAX остаётся разумной рекомендацией
провижинингу, но границей отказа не является.
Причина отказа — в том, что пространство имён ролей совпало с
пространством имён учётных записей Unix. root, daemon, bin,
sys, mail, nobody подходят под формат role_id
(^[a-z][a-z0-9-]{0,15}$), и один срез root.toml — опечатка
провижининга или скопированный образец — превратил бы ssh root@device
в штатный ролевой вход с правами, которых ролевая модель никогда не
выдавала. Поэтому продукт смотрит не на имя, а на uid учётной записи на
самом устройстве: опасно не имя, а то, что под ним уже существует
учётная запись с чужими привилегиями. Список имён кодировал бы догадку
о том, какие это имена, расходился бы с дистрибутивом и отвергал бы
законные роли — mail в Debian одновременно системная учётная запись и
осмысленное имя роли.
Границы продукт несёт в себе, а не читает из /etc/login.defs в момент
входа: правка этого файла на устройстве не должна расширять допуск.
Поэтому и приёмочная проверка ниже сверяется с границами продукта, а не
с содержимым login.defs, — иначе она отвечала бы на другой вопрос, чем
модуль в момент входа.
Если учётная запись уже была заведена с --system, менять её uid у
живой записи не стоит — файлы останутся за старым владельцем. Проще
удалить и завести заново:
id -u serv # вне диапазона → так входить нельзяsudo userdel -r servsudo useradd --create-home --shell /bin/bash servПри провижининге через Census этот шаг уже сделан: Census заводит
ролевую учётную запись командой useradd -u <uid> … с uid из
объявленного в декларации диапазона uid_range, который целиком лежит
внутри диапазона обычных пользователей, и проверяет попадание в него
для каждой роли.
8.4 Закрытие остальных путей входа в ролевую учётную запись
Заголовок раздела «8.4 Закрытие остальных путей входа в ролевую учётную запись»Учётная запись из §8.3 создана с домашним каталогом и логин-шеллом.
Отсутствие пароля закрывает ровно один путь — парольный вход; открытыми
остаются ~/.ssh/authorized_keys, su serv, sudo -u serv -i и любой
PAM-стек, в котором нет pam_tessera. Продукт эти пути не закрывает —
он не управляет ни sshd_config, ни sudoers, ни чужими PAM-стеками.
Их закрывают провижининг и администратор устройства.
Цена пропущенного шага выросла вместе с моделью. Раньше обход давал
доступ к личной учётной записи одного инженера; теперь serv — общая
ролевая учётная запись с правами роли, и вход в обход pam_tessera не
оставляет следа ни в role.audit, ни в журнале выдачи: некому записать,
кто именно вошёл.
Ниже — минимум, который нужно выполнить на каждом устройстве. Всё, что
касается sshd, выполнять при открытом втором рут-shell (см.
pam-integration.md §8).
Пароль: заблокировать, а не «не задавать»
Заголовок раздела «Пароль: заблокировать, а не «не задавать»»sudo passwd -l servsudo passwd -S serv # ожидание: во второй колонке Lpasswd -S для чужой учётной записи читает /etc/shadow, поэтому
выполняется от root; без sudo команда откажет.
«Пароль не задан» и «пароль заблокирован» — разные состояния.
useradd оставляет в поле пароля !, и по паролю такая запись
действительно не пускает, но это состояние по умолчанию, а не
объявленное намерение: один passwd serv, набранный при отладке,
молча открывает парольный вход, и в конфигурации устройства это никак
не видно. passwd -l делает блокировку явной, а passwd -S — видимой:
запись L во второй колонке проверяется одной командой и на приёмке, и
позже.
Отдельно: passwd -l не закрывает непарольные методы. Ключ в
authorized_keys, su от root, sudo -u продолжают работать —
именно поэтому дальше идут остальные шаги.
При провижининге через Census passwd -l уже выполнен: он входит в
последовательность заведения ролевой учётной записи. Повторный вызов
безвреден, и на устройстве, заведённом руками, он обязателен.
authorized_keys: не должно быть и не должно появиться
Заголовок раздела «authorized_keys: не должно быть и не должно появиться»Домашний каталог берём из базы, а не угадываем — при провижининге он
задаётся декларацией и не обязан быть /home/serv:
HOME_SERV=$(getent passwd serv | cut -d: -f6)sudo test -e "$HOME_SERV/.ssh/authorized_keys" \ && echo "authorized_keys существует — разобраться, откуда"Ожидание: команда ничего не печатает. Census этот файл не создаёт никогда; если он есть — его создали руками или принесли образом.
Чтобы файл не появился впредь, каталог .ssh отдают root и снимают
с ролевой учётной записи право записи в него:
sudo mkdir -p "$HOME_SERV/.ssh"sudo chown root:root "$HOME_SERV/.ssh"sudo chmod 0500 "$HOME_SERV/.ssh"Честная граница этой меры: она закрывает добавление ключа от имени
самого serv (в том числе изнутри уже открытой ролевой сессии), но не
от root. Более того, sshd со StrictModes yes принимает
authorized_keys, принадлежащий владельцу каталога или root, —
то есть root-owned ключ, положенный сюда, был бы принят. Полностью путь
публичных ключей для этой учётной записи закрывает только настройка
sshd из следующего шага; права на каталог — второй рубеж, а не первый.
sshd: оставить единственный метод аутентификации
Заголовок раздела «sshd: оставить единственный метод аутентификации»Cert-аутентификация Tessera приходит в sshd через
keyboard-interactive и PAM, поэтому закрывать нужно всё остальное, а
UsePAM yes и KbdInteractiveAuthentication yes — сохранить:
sudo tee /etc/ssh/sshd_config.d/50-tessera-roles.conf >/dev/null <<'EOF'Match User serv PubkeyAuthentication no PasswordAuthentication no KbdInteractiveAuthentication yes HostbasedAuthentication no GSSAPIAuthentication no PermitEmptyPasswords noMatch allEOFsudo sshd -tsudo sshd -T -C user=serv,host=localhost,addr=127.0.0.1 \ | grep -E '^(usepam|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|hostbasedauthentication)'sudo sshd -T -C user=<обычная-учётная-запись>,host=localhost,addr=127.0.0.1 \ | grep -E '^(usepam|pubkeyauthentication)'Ожидание первой проверки: usepam yes,
kbdinteractiveauthentication yes, остальные три — no. Ожидание
второй: usepam yes и pubkeyauthentication yes — то есть на другого
пользователя блок не подействовал. Именно sshd -T -C показывает
эффективную конфигурацию для конкретной учётной записи, с учётом всех
Match-блоков и порядка включений; чтение конфигов глазами этого не
заменяет.
Match allв конце — не украшение, не удалять. Область действияMatch-блока тянется до следующегоMatchили до конца всего конфига, а не до конца файла, в котором блок написан. Debian и Ubuntu ставятInclude /etc/ssh/sshd_config.d/*.confпервой строкойsshd_config— то есть без завершающегоMatch allусловиеUser servнакрыло бы весь оставшийся конфиг: и другие включаемые файлы, и всё тело родительскогоsshd_config. Глобальные директивы, объявленные ниже, перестали бы применяться ко всем прочим пользователям — включаяUsePAM yes, который в Debian объявлен именно там. Настройка, закрывающая пути входа в одну учётную запись, отключила бы PAM на всём устройстве.Match allвозвращает разбор в глобальный контекст, и вторая проверкаsshd -T -Cвыше — та самая, что ловит эту ошибку.
Два места, где дистрибутивы расходятся, и универсальной команды нет:
- Каталог
sshd_config.d/. СтрокаInclude /etc/ssh/sshd_config.d/*.confесть в поставке Debian 11+, Ubuntu 20.04+ и Astra с OpenSSH 8.2+. Проверить:grep -n '^Include' /etc/ssh/sshd_config. Если строки нет, файл создавать бессмысленно — блок дописывают в конец/etc/ssh/sshd_config. ЗавершающийMatch allнужен и там: трейлинг-блок безопасен ровно до первой правки после него, а добавить глобальную директиву в конецsshd_config— обычное дело, и она молча попала бы внутрь условияUser serv. - Имя юнита.
sudo systemctl reload sshв Debian/Ubuntu,sudo systemctl reload sshdв Astra и части сборок. Проверить —systemctl list-units 'ssh*'; перезагружать только после успешногоsshd -t.
Если поставленный OpenSSH понимает AuthorizedKeysFile none (8.2+),
директиву стоит добавить в тот же Match-блок как третий рубеж поверх
PubkeyAuthentication no. На более старых сборках она не
поддерживается и ломает старт sshd; проверять — тем же sshd -t
сразу после правки.
su и sudo -u: закрыть переход в ролевую учётную запись
Заголовок раздела «su и sudo -u: закрыть переход в ролевую учётную запись»Единой команды, одинаково работающей на Debian, Ubuntu и Astra, здесь
нет: su и sudo — разные механизмы с разными файлами настройки, и
закрывать их приходится по отдельности.
Для su работает pam_succeed_if. Модуль входит в пакет
libpam-modules, который на всех трёх целевых дистрибутивах стоит из
базовой поставки и не выносится в отдельную зависимость. Убедиться, что
он на месте, стоит до правки стека — строка ниже написана как
requisite, и если модуля нет, su закроется для всех, включая root:
ls /lib/*/security/pam_succeed_if.so /lib/security/pam_succeed_if.so 2>/dev/nullРолевые учётные записи собирают в отдельную группу, и переход в члена этой группы запрещают:
sudo groupadd -f tessera-rolessudo usermod -aG tessera-roles servВ /etc/pam.d/su, выше строки auth sufficient pam_rootok.so:
auth requisite pam_succeed_if.so quiet user notingroup tessera-rolesuser здесь — целевая учётная запись su, поэтому правило читается
как «переход разрешён, только если цель не является ролевой». Выше
pam_rootok.so — потому что иначе root минует проверку. Закрыть
root полностью всё равно нельзя: у него есть chsh, прямая правка
shadow и десяток других путей. Смысл строки в том, чтобы su serv
перестал быть будничной командой для всех остальных.
Проверять нужно обе стороны — и что запрет сработал, и что он не задел остальных. От имени непривилегированной учётной записи:
su - serv # ожидание: отказsu - <обычная-учётная-запись> # ожидание: запрос пароля и входВторая команда обязательна. Если она тоже отказывает, дело не в
правиле, а в самой строке: requisite при отсутствующем или
неверно записанном модуле закрывает su целиком. Разбираться с этим
следует при открытом втором рут-shell — восстановление правкой
/etc/pam.d/su.
Для sudo то же самое делается запретом в runas-списке:
sudo visudo -cf /etc/sudoers.d/40-tessera-roles # после создания файлаСодержимое (создавать через visudo -f, не tee):
%engineers ALL = (ALL, !%tessera-roles) ALLЗдесь важна оговорка. Отрицание в runas-списке действует только на то
правило, где оно написано. Если инженеру где-то ещё выдано
(serv) ALL или более широкое правило ниже по файлу — выиграет
последнее совпавшее, и запрет не сработает. Поэтому проверять нужно не
файл, а результат:
sudo -l -U <учётная-запись-инженера>Ожидание: в выводе нет строки, разрешающей запуск от имени serv.
PAM-стеки без модуля
Заголовок раздела «PAM-стеки без модуля»Любой сервис, который аутентифицирует и открывает сессию, — это вход.
Стек, в котором нет pam_tessera, пустит в ролевую учётную запись по
своим правилам, мимо роли и мимо role.audit. Проверять нужно все
стеки, через которые на устройство вообще заходят: sshd, login,
fly-dm (и экранная блокировка), su, sudo. Что и куда добавлять —
pam-integration.md; tessera check ловит ошибки
порядка модулей в тех стеках, где модуль уже стоит, но не подскажет о
стеке, куда его не добавили.
Verification (раздел 8)
Заголовок раздела «Verification (раздел 8)»ls -la /var/lib/tessera/roles/id serv
# uid в диапазоне обычных пользователей (границы продукта, см. §8.3)u=$(id -u serv)[ "$u" -ge 1000 ] && [ "$u" -le 61183 ] \ && echo "uid в диапазоне обычных пользователей: ок"
sudo passwd -S servsudo test -e "$(getent passwd serv | cut -d: -f6)/.ssh/authorized_keys" \ && echo "authorized_keys существует — разобраться, откуда"sudo sshd -T -C user=serv,host=localhost,addr=127.0.0.1 \ | grep -E '^(usepam|pubkeyauthentication|passwordauthentication)'sudo sshd -T -C user=<обычная-учётная-запись>,host=localhost,addr=127.0.0.1 \ | grep -E '^(usepam|pubkeyauthentication)'sudo tessera checkОжидание: serv.toml присутствует с правами -rw-r--r-- root root,
id serv печатает uid/gid, uid попадает в диапазон обычных
пользователей, passwd -S показывает L во второй колонке, про
authorized_keys не печатается ничего, для serv — usepam yes при
pubkeyauthentication no и passwordauthentication no, для другой
учётной записи — usepam yes и pubkeyauthentication yes (блок не
протёк за свои границы), tessera check завершается без ERROR.
9. Правка /etc/pam.d/*
Заголовок раздела «9. Правка /etc/pam.d/*»Правка PAM-стека вынесена в отдельный документ — docs/pam-integration.md:
integrate-pam.shи поставочный snippet- Two-include pattern (0.3.12+) и порядок
pam_systemd.so - fly-dm (зачем + применение + screen-locker)
- Три режима:
2fa/optional/cert-onlyс lockout-warning - sudo, login, sshd
- PAM-стек с учётом МКЦ → pam-integration.md §7
- Безопасность правки + recovery
ВАЖНО. Перед правкой PAM открыть второй рут-shell. Подробности — pam-integration.md §8 «Безопасность правки».
Verification (раздел 9)
Заголовок раздела «Verification (раздел 9)»sudo tessera checktessera check ловит ошибки порядка PAM-стека (например
pam_stack_session_misorder). Полный smoke-тест аутентификации через
pamtester — в разделе 10.
10. Smoke-тест через pamtester
Заголовок раздела «10. Smoke-тест через pamtester»Имя, передаваемое pamtester, — это имя ролевой учётной записи, оно же
запрошенная роль.
10.1 Полный цикл: auth + account + session
Заголовок раздела «10.1 Полный цикл: auth + account + session»AuthContext, который pam_sm_authenticate кладёт через
pam_set_data, живёт только в рамках одного pam_start()/pam_end()
— то есть одного процесса, читающего/пишущего один и тот же PAM-хендл.
Три отдельных вызова pamtester — это три независимых хендла, и
account/session-фазы такого прогона ничего не скажут о реальной
работе pam_tessera в проверяемой службе: модуль просто не найдёт
контекст, оставленный предыдущим вызовом. Поэтому все операции,
которые должны пройти в одном логине, передаются pamtester одним
списком — тогда используется один pam_start() на всё:
pamtester sudo serv authenticate acct_mgmt open_session close_sessionПоложительный результат: pamtester печатает successfully на
каждую операцию по очереди (четыре строки).
pamtester— не полноценный логин-стек (нет привилегированного разделения процессов, как уsshdилиlogin), поэтому такой smoke-тест подтверждает корректность самого PAM-стека иpam_tessera, но не заменяет проверку через реальный сервис. Разница и порядок дальнейшей проверки — в pam-integration.md §9.
10.2 Negative-тест: извлечь USB
Заголовок раздела «10.2 Negative-тест: извлечь USB»В одном терминале запустить:
pamtester sudo serv authenticateСразу после ввода извлечь USB. Ожидание: monitord пишет в журнал:
sudo journalctl -u tessera -n 20 -g 'medium absent'11. Troubleshooting
Заголовок раздела «11. Troubleshooting»Полный справочник по диагностике — docs/troubleshooting.md:
- Cert/auth-ошибки (
host_binding mismatch, роль внеallowed_roles, общий чек-лист) - USB и токены (
pcscd,Token PIN locked, USBGuard, ЗПС) - monitord и демон (
monitord not reachable,failed-старт) - PAM-стек и lockout (
Logout requested but session has no logind id, recovery из rescue.target) - МКЦ (
pam_parsec_mac: Can't obtain required data,parsec.mac=0,mac_caps_missing,dmi_board_serial = 0) - fly-dm и greeter (wallpaper не виден) — см. также fly-dm-greeter.md
- Clone-image / golden image (
dump-host-idпуст, повторный flip) — см. также clone-image.md - Инциденты безопасности (компрометация cert, потеря токена, CA worst-case, DIGSIG)
12. Хосты без systemd: SysV init
Заголовок раздела «12. Хосты без systemd: SysV init»Пакет ставит оба init-варианта: tessera.service (systemd)
и /etc/init.d/tessera (SysV). На systemd-хостах SysV-скрипт
трогать не нужно. На non-systemd:
sudo update-rc.d tessera defaultssudo service tessera startПодробности (caveats, отсутствие logind logout) — pam-integration.md §10.
Дальнейшие шаги
Заголовок раздела «Дальнейшие шаги»- docs/configuration.md — справочник по всем
параметрам
config.toml. - docs/cert-issuance.md — выдача удостоверений с
расширениями
pam_cert_host_bindingиpam_cert_allowed_roles. - docs/operations.md — runbook эксплуатации и процедуры incident response.
- docs/threat-model.md — модель угроз и какие атаки модуль защищает.
МКЦ (MAC integrity) — опциональная активация
Заголовок раздела «МКЦ (MAC integrity) — опциональная активация»Полная активация МКЦ (capability демону, поставляемый PAM-стек,
systemd drop-in, per-user MNKC, защита config.toml через ilevel=63,
verify, откат) — отдельный документ:
docs/mac-integrity.md.
Краткий путь:
astra-strictmode-control enable+ reboot.usercaps -m "+3" tessera+pdpl-user --ilevel 63 tessera.- Скопировать
tessera.exampleиmac-integrity.conf.exampleиз/usr/share/tessera/в/etc/pam.d/и/etc/systemd/system/tessera.service.d/. pdpl-user --ilevel 63 <pam_user>для каждого end-user.[mac].cert_integrity = "required"+runtime = "required", рестарт демона.
Default (cert_integrity = "ignore", runtime = "disabled")
— production-готов без активации МКЦ. Ничего настраивать не надо.