Перейти к содержимому

Выпуск сертификатов: host_binding и user_binding

Авторизация «какой пользователь на каком хосте» закодирована в двух X.509-расширениях leaf-сертификата, которые PAM-модуль проверяет на этапе аутентификации:

  • pam_cert_host_binding
  • pam_cert_user_binding

Когда оба расширения присутствуют — они и только они определяют область действия сертификата. Список [[user_mapping]] в config.toml остался как legacy fallback для сертификатов, выпущенных без расширения pam_cert_user_binding; на новые выпуски расширения должны проставляться УЦ всегда (см. docs/threat-model.md, mandatory-extension policy).

Этот документ описывает синтаксис расширений и приводит готовые рецепты для openssl.cnf, по которым сертификат можно выпустить штатным openssl x509 -req.

Имя расширенияДотированный OIDASN.1 синтаксис
pam_cert_host_binding2.25.183976554325829274683049824615098extnValue ::= SEQUENCE OF UTF8String
pam_cert_user_binding2.25.215438916728501023845629178354627extnValue ::= SEQUENCE OF UTF8String

OID размещены в нерегистрируемой ветке 2.25.<UUID> (RFC 4530), что гарантирует уникальность без обращения к внешнему реестру. Эти значения зафиксированы в коде (tessera_core::x509::oids) и являются частью on-the-wire X.509-контракта — менять их нельзя.

Каждая запись UTF8String в pam_cert_host_binding интерпретируется так:

ЗаписьЗначение
*разрешено на любом хосте
sha256:<HEX>разрешено только на хосте, чей host_id_hash совпадает с указанным шестидесятичетырёхсимвольным lowercase-hex (case-insensitive)
Любая другая UTF-8 строкастрока интерпретируется как «сырое» machine_id и сравнение идёт через SHA-256 от строки

В pam_cert_user_binding запись либо * (любой PAM-пользователь), либо точное имя пользователя (case-sensitive — Linux usernames регистрозависимы).

Для авторизации сертификата на конкретном хосте/пользователе нужна хотя бы одна совпавшая запись в каждом из двух расширений.

Сценарий 1 — рабочая станция: один хост, один пользователь

Заголовок раздела «Сценарий 1 — рабочая станция: один хост, один пользователь»

Рабочее место конкретного оператора. Сертификат можно использовать только на машине с известным machine_id и только для конкретного PAM-пользователя.

# openssl.cnf — фрагмент
[ user_exts ]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature
extendedKeyUsage = clientAuth
subjectAltName = email:[email protected]
# Хост: SHA-256 от machine-id операторской АРМ
2.25.183976554325829274683049824615098 = ASN1:SEQUENCE:hb_one
# Пользователь: единственное имя
2.25.215438916728501023845629178354627 = ASN1:SEQUENCE:ub_one
[ hb_one ]
e0 = UTF8String:sha256:a1b2c3d4e5f6...64charsTotal...
[ ub_one ]
e0 = UTF8String:ivanov

Команда выпуска:

Окно терминала
openssl req -new -key user.key -subj "/CN=Иванов" \
-reqexts user_exts -config openssl.cnf -out user.csr
openssl x509 -req -in user.csr -CA int.pem -CAkey int.key \
-CAcreateserial -days 365 -sha256 \
-extfile openssl.cnf -extensions user_exts -out user.pem

Сценарий 2 — оператор банкоматов: несколько хостов, один пользователь

Заголовок раздела «Сценарий 2 — оператор банкоматов: несколько хостов, один пользователь»
[ hb_three_atms ]
e0 = UTF8String:sha256:1111111111111111111111111111111111111111111111111111111111111111
e1 = UTF8String:sha256:2222222222222222222222222222222222222222222222222222222222222222
e2 = UTF8String:sha256:3333333333333333333333333333333333333333333333333333333333333333
[ ub_operator ]
e0 = UTF8String:operator

Сценарий 3 — мобильный администратор: любой хост, точный пользователь

Заголовок раздела «Сценарий 3 — мобильный администратор: любой хост, точный пользователь»
[ hb_any ]
e0 = UTF8String:*
[ ub_admin ]
e0 = UTF8String:admin

* в host_binding позволяет сертификату работать на любой машине; в user_binding по-прежнему остаётся жёсткое ограничение на имя пользователя.

Окно терминала
openssl x509 -in user.pem -noout -text

В выводе должны присутствовать обе строки с дотированными OID:

2.25.183976554325829274683049824615098:
0...sha256:a1b2c3d4...
2.25.215438916728501023845629178354627:
0...ivanov
ЗаписьСовпадает с…
*любым хостом / любым пользователем
sha256:<HEX>хостом, чей host_id_hash равен HEX (без учёта регистра)
<raw> (host_binding)хостом, чей host_id_hash равен sha256(raw)
<name> (user_binding)PAM-пользователем с точным именем <name>
Расширение отсутствуетотказ (HostExtensionMissing / UserExtensionMissing)
Расширение пустое или DER-битоеотказ (*ExtensionMalformed)
Записи есть, но ни одна не совпалаотказ (HostNotAllowed / UserNotAllowed)

См. также docs/configuration.md.

MAX_INTEGRITY — non-critical X.509 v3-расширение, кодирующее максимальную метку целостности (level, categories), до которой сертификат может быть допущен на хосте Astra SE с включённым strict-mode.

OID: 2.25.273824307386008814506455310913083078403

Структура (DER):

IntegrityLabel ::= SEQUENCE {
level INTEGER (-128..127),
categories BIT STRING DEFAULT ''B
}

Семантика на сервере:

  • При open_session PAM-модуль выбирает эффективную метку как intersect(cert, runtime_caps, fallback?).
  • cert_integrity = "required" → сертификат без расширения отвергается.
  • cert_integrity = "optional" → отсутствие расширения допускается; если задан [mac.fallback_max_integrity], применяется он.
  • cert_integrity = "ignore" → расширение игнорируется.

См. docs/configuration.md §«MAC integrity» и docs/threat-model.md §«Privilege-escalation via MAC label».

Готовые шаблоны openssl.cnf для тестовых сертификатов: tests/fixtures/leaf-{l2-c01,l1-empty,no-ext,l3,malformed,l0-fullcats}.cnf. Генерация — tests/fixtures/setup-mac-fixtures.sh.

Пример строки в openssl.cnf для level=2, categories={0}:

2.25.273824307386008814506455310913083078403 = critical,DER:30:06:02:01:02:03:02:00:01

DER здесь — три TLV: SEQUENCE, INTEGER 2, BIT STRING '01'B.

Полный end-to-end runbook (эталон → клон → flip → выпуск per-host) — в docs/clone-image.md. Здесь — только CA-сторона: как читать TSV-дамп и что попадает в выпускаемый сертификат.

Оператор после finish-bootstrap.sh присылает CA-админу файл host-ids-<hostname>-<UTC>.tsv (с USB или через защищённый канал). Колонки:

source status hash_hex hash_prefix raw normalized active_under_current_config reason

Одна строка на каждый известный источник (не только настроенные в [host_identity].sources): machine_id, dmi_board_serial, dmi_system_uuid, dmi_system_serial, hostname, плюс custom_command (если configured).

Строка с active_under_current_config=yes — это тот источник, который daemon сейчас использует. Только её hash_hex идёт в сертификат.

hash_hex подаётся в CA-инструмент выпуска (см. clone-image.md §6.1 — CA-инструменты поставляются отдельно, не в этом репозитории).

Cert получает pam_cert_host_binding = <hash_hex>, pam_cert_user_binding = <service_user> и стандартный extendedKeyUsage = clientAuth, emailProtection (emailProtection требует штатный валидатор Astra — openssl CMS_verify; сам tessera этот EKU не проверяет). На МКЦ-АРМ дополнительно pam_cert_max_integrity (см. §«Поле MaxIntegrity»).

Готовый .p12 упаковывается на ту же флешку CA-инструментом и возвращается на АРМ.

tessera dump-host-id (вызываемый внутри finish-bootstrap.sh или вручную) выходит с ненулевым кодом, если ни один источник не отдал непустое значение. Это однозначный сигнал «не выписывайте сертификат, пока не починён вход» — типичные причины: пустые DMI-поля в VM, очищенный machine_id, неработающий custom_command. См. clone-image.md §8 — troubleshooting.

После уже состоявшегося flip-а:

  • tessera dump-host-id --usb — на USB-флешку;
  • tessera dump-host-id --output /tmp/host.tsv — в файл;
  • tessera dump-host-id (без флагов) — в stdout.