ADR: Доставка сертификатов в компоненты Netgap через конфигурацию
- Status: Accepted by Andrei Ganiushkin
andrey@ganyushkin.ru - Date: 2026-08-07
- Authors: Andrei Ganiushkin
andrey@ganyushkin.ru - Related issues: -
Context
ADR 0009 фиксирует, что все внутренние коммуникации Netgap выполняются по mTLS (TLS 1.3) с авторизацией по SPIFFE ID. Для работы mTLS каждому компоненту требуется набор криптографического материала и политик:
- собственный leaf-сертификат и приватный ключ (с промежуточной цепочкой);
- корневой сертификат (trust anchor), которым проверяются цепочки контрагентов;
- CRL для проверки отзыва;
- список SPIFFE ID сервисов, которым разрешено вызывать данный сервис.
ADR 0002 устанавливает единственный канал передачи рабочей
конфигурации — YAML-документ через stdin процесса, без файлов, переменных окружения и
CLI-аргументов. Требуется зафиксировать, как этот подход применяется к доставке сертификатов,
ключей и связанных политик, включая нерешённый в ADR 0002 вопрос: как доставить в работающий
процесс обновлённый сертификат при ротации (ADR 0009 требует
горячую ротацию без рестарта), если stdin-конфигурация читается при запуске.
Дополнительно компоненты имеют внешние TLS-взаимодействия — входящие (внешние клиенты продукта) и исходящие (внешние системы заказчика). Способ их конфигурирования должен быть единообразен с внутренним mTLS.
Decision
Весь стартовый криптографический материал и политики доступа доставляются в компоненты
исключительно через конфигурацию по ADR 0002: содержимое
сертификатов, ключей, CRL и списки разрешённых SPIFFE ID передаются в YAML-документе через
stdin, который закрывается сразу после чтения стартовой конфигурации. Ротацию сертификатов
выполняет сам компонент по протоколу обновления сертификатов; параметры протокола передаются в
той же стартовой конфигурации.
Правила, вытекающие из решения:
-
Материал передаётся значением, а не ссылкой. Конфигурация содержит PEM-содержимое leaf-сертификата, приватного ключа, цепочки, корневого сертификата и CRL непосредственно в YAML-документе. Пути к файлам, URL и имена переменных окружения как способ указания криптографического материала не поддерживаются — компонент не читает его с диска и из окружения ни при каких настройках.
-
Корни доверия — только из конфигурации. Truststore компонента формируется исключительно из корневых сертификатов, переданных в конфигурации. Системное хранилище доверенных корневых сертификатов ОС не используется, и возможности включить его использование не существует — by design. Это исключает влияние состояния ОС (добавленный атакующим или политикой ОС корень) на границу доверия продукта и делает trust-домен инсталляции полностью определяемым её конфигурацией.
-
Списки разрешённых SPIFFE ID — в конфигурации. Явный список SPIFFE ID сервисов, которым разрешено вызывать данный сервис (deny by default, ADR 0009), задаётся в том же YAML-документе. Ожидаемые SPIFFE ID серверов для исходящих внутренних соединений задаются аналогично.
-
stdinзакрывается после чтения стартовой конфигурации. Компонент читает ровно один YAML-документ до EOF, валидирует его (fail-fast по ADR 0002) и немедленно закрывает дескрипторstdin. Канал ввода конфигурации прекращает существовать на всё оставшееся время жизни процесса. Держатьstdinоткрытым для приёма обновлений запрещено by design: файловый дескриптор pipe не является аутентифицированным каналом — он наследуется дочерними процессами, может быть передан между процессами (SCM_RIGHTS) или получен через отладочные интерфейсы, поэтому записать вstdinработающего процесса способен не только исходный родитель. Долгоживущий открытыйstdinобразовывал бы канал внедрения криптографического материала и политик в работающий компонент. -
Ротация — самим компонентом по протоколу обновления сертификатов. Компонент самостоятельно обновляет свой leaf-сертификат: заблаговременно до истечения (по достижении настраиваемой доли срока действия, по умолчанию 2/3) генерирует новую ключевую пару в памяти процесса, формирует CSR и выполняет обмен с центром выпуска по одному из поддерживаемых протоколов. Параметры обновления задаются в стартовой конфигурации: протокол, endpoint центра выпуска, корень доверия для канала обмена, идентификаторы и порог ротации. Запрос аутентифицируется действующим сертификатом компонента по mTLS (re-enrollment). Полученный сертификат валидируется по цепочке до корня из конфигурации и атомарно подменяется в памяти
rustlsбез рестарта процесса и без разрыва установленных соединений (ADR 0009). Должны быть рассмотрены к реализации протоколы обновления:- EST (RFC 7030) — основной протокол для внутреннего CA инсталляции: re-enrollment через
/simplereenrollс аутентификацией действующим клиентским сертификатом по TLS; - CMP (RFC 4210, RFC 9480) — для интеграции с корпоративными PKI заказчика;
- ACME (RFC 8555) — для серверных сертификатов внешних входящих подключений, выпускаемых публичным CA;
- SCEP (RFC 8894) — только для совместимости с унаследованными PKI, где недоступны EST/CMP.
SPIFFE Workload API сознательно не поддерживается — требует локального агента на узле (см. Alternative 5). CRL компонент обновляет самостоятельно по точке распространения (CDP), заданной в конфигурации. Обновление корней доверия и списков SPIFFE ID — изменение политики инсталляции; оно выполняется штатным перезапуском компонента с новой стартовой конфигурацией.
Ручная ротация сертификата — это всегда рестарт приложения. Поскольку
stdinзакрыт (п. 4) и других входящих каналов управления у процесса нет (п. 6), «горячей» ручной подмены материала в работающем компоненте не существует by design. Если оператору требуется вручную заменить leaf-сертификат, ключ, корень доверия или списки SPIFFE ID (компрометация ключа, недоступность центра выпуска, изменение политики), единственный способ — перезапуск компонента с новой стартовой конфигурацией вstdinнового процесса, с разрывом установленных соединений. Ротация без рестарта возможна только автоматически, по протоколу обновления из настоящего пункта. - EST (RFC 7030) — основной протокол для внутреннего CA инсталляции: re-enrollment через
-
Свойства безопасности каналов доставки. Приватный ключ либо передаётся один раз в стартовой конфигурации, либо генерируется внутри процесса при ротации; он не появляется на диске, в окружении процесса и в аргументах ни в один момент жизненного цикла. После закрытия
stdinу процесса нет ни одного входящего канала управления: единственный канал обновления — исходящее mTLS-соединение к заранее сконфигурированному endpoint центра выпуска, доверие к которому закреплено корнем из конфигурации. -
Внешние взаимодействия конфигурируются так же. Серверные сертификаты для внешних входящих подключений и клиентские сертификаты/доверенные корни для внешних исходящих подключений передаются в тех же секциях конфигурации тем же способом (PEM-содержимое в YAML, ротация по протоколу обновления из п. 5). Отличие одно: для внешних взаимодействий TLS и/или mTLS опционален и определяется требованиями конкретной интеграции (например, внешний клиент без mTLS или исходящее соединение с проверкой по корню, переданному в конфигурации), тогда как для внутренних коммуникаций весь набор из Context обязателен, и его отсутствие — ошибка запуска. Правило п. 2 действует и здесь: доверие для внешних соединений задаётся только корнями из конфигурации, системное хранилище ОС не используется.
-
Материал не журналируется. Приватные ключи и сырое содержимое конфигурационных документов не попадают в журналы и аварийный вывод (panic-hook по ADR 0002); в журнале фиксируется только факт и результат применения обновления (отпечаток сертификата, срок действия, причина отказа).
Considered Alternatives
Основной канал доставки (stdin YAML) зафиксирован в
ADR 0002 и здесь не пересматривается. Альтернативы
рассматривались для двух частных вопросов: источник корней доверия и канал доставки обновлённого
материала при ротации.
Alternative 1: Корни доверия — только из конфигурации, хранилище ОС не используется
- Pros:
- Граница доверия продукта полностью определяется конфигурацией инсталляции и не зависит от состояния ОС; аудит сводится к аудиту одного YAML-документа.
- Добавление корня в хранилище ОС (атакующим, групповой политикой, предустановкой вендора) не расширяет доверие компонентов Netgap.
- Поведение идентично на всех целевых платформах (ADR 0008); нет платформенного кода чтения хранилищ Linux/Windows.
- Cons:
- Оператор обязан явно доставить корень в каждую конфигурацию, включая случаи, когда нужный корень уже есть в ОС.
- Ротация корня требует обновления конфигурации всех компонентов (штатно решается п. 4).
- Reason for selection: trust-домен под полным контролем инсталляции; исключён целый класс атак через хранилище ОС, которое продукт не контролирует.
Alternative 2: Системное хранилище доверенных корней ОС (само по себе или как опция)
- Pros:
- Не нужно передавать корень в конфигурации при использовании публичного CA.
- Привычная модель для администраторов.
- Cons:
- Хранилище ОС — общесистемный ресурс вне контроля продукта: любой субъект с правами на его изменение расширяет границу доверия Netgap незаметно для конфигурации.
- Содержит сотни корней публичных CA — каждый из них становится способным выпустить «валидный» сертификат для внутреннего компонента; это несовместимо с изоляцией trust-домена (ADR 0009).
- Даже как отключённая по умолчанию опция создаёт валидное небезопасное состояние продукта — тот же класс проблем, что отвергнут в Alternative 6 ADR 0009.
- Разное содержимое и API хранилищ на Linux и Windows — платформенно-зависимое поведение безопасности.
- Reason for rejection: передаёт контроль над границей доверия внешней по отношению к продукту системе; отвергнуто без опции включения — by design.
Alternative 3 (ротация): поток YAML-документов в незакрытом stdin
Компонент не закрывает stdin после стартовой конфигурации; supervisor подаёт документы
обновления (разделитель ---) в тот же pipe на протяжении жизни процесса.
- Pros:
- Единственный канал конфигурации на весь жизненный цикл, без сетевых обменов и протоколов PKI.
- Обновлённый ключ проходит тем же путём, что и стартовый.
- Cons:
- Пишущий конец pipe не привязан к исходному родителю: дескриптор наследуется дочерними
процессами, передаётся между процессами (
SCM_RIGHTS), доступен через отладочные механизмы. Субъект, получивший запись вstdin, внедряет в работающий компонент произвольные сертификаты, корни доверия и списки SPIFFE ID — то есть переопределяет границу доверия. - Постоянно открытый канал ввода расширяет поверхность атаки работающего процесса и требует парсинга недоверенного ввода на всём времени жизни.
- Пишущий конец pipe не привязан к исходному родителю: дескриптор наследуется дочерними
процессами, передаётся между процессами (
- Reason for rejection: долгоживущий открытый
stdinобразует неаутентифицированный канал внедрения криптографического материала в работающий процесс; признано нарушением безопасности.stdinобязан закрываться сразу после чтения стартовой конфигурации.
Alternative 4 (ротация): перечитывание файлов сертификатов по сигналу (SIGHUP + пути на диске)
Классическая схема: сертификаты лежат в файлах, компонент перечитывает их по сигналу или inotify.
- Pros:
- Стандартный, широко поддерживаемый автоматизацией паттерн (cert-manager, Vault Agent пишут файлы).
- Cons:
- Требует хранения приватного ключа на диске — прямо нарушает ADR 0002 (отсутствие персистентного следа, уязвимость к LFI/бэкапам/снапшотам).
- Появляется второй канал конфигурации (файловая система) с собственной моделью прав доступа,
который нужно защищать и аудировать отдельно от
stdin.
- Reason for rejection: ломает ключевое свойство принятой модели конфигурации — секреты не существуют на диске.
Alternative 5 (ротация): локальный агент выдачи (SPIFFE Workload API / SPIRE Agent, Vault Agent)
Компонент получает и обновляет SVID через локальный UNIX-сокет агента.
- Pros:
- Зрелый стандарт (SPIFFE Workload API), автоматическая ротация без участия оператора.
- Ключ также не появляется на диске приложения.
- Cons:
- Требует установки и сопровождения агента на каждом узле — противоречит модели поставки «статические бинарные файлы без агентов в ОС» (ADR 0008).
- Агент и его сокет становятся частью границы доверия: локальный UNIX-сокет — входящий канал доставки материала с собственной моделью аутентификации вызывающего процесса, тогда как принятое решение оставляет только исходящее mTLS-соединение к центру выпуска.
- Недоступен или существенно отличается на части целевых сред (Windows, bare-metal без инфраструктуры SPIRE).
- Reason for rejection: внешняя зависимость в целевой среде; при этом идея (короткоживущие сертификаты, автоматическое обновление без участия оператора) реализована принятым решением средствами самого продукта через стандартные протоколы PKI.
Alternative 6 (ротация): рестарт процесса с новой конфигурацией
Ротация выполняется перезапуском компонента: supervisor подаёт новый YAML в stdin нового
процесса.
- Pros:
- Не требует ни одного нового механизма: строго один документ в
stdinза жизнь процесса. - Простейшая для реализации и тестирования модель.
- Не требует ни одного нового механизма: строго один документ в
- Cons:
- Reason for rejection: несовместимо с короткоживущими сертификатами как штатной стратегией автоматической ротации; при этом остаётся единственным механизмом ручной ротации (любая ручная замена материала — обязательно рестарт, п. 5), резервным путём при недоступности протокола обновления и штатным способом изменения политик (корни доверия, списки SPIFFE ID).
Risks and Mitigations
- Risk: центр выпуска недоступен в момент плановой ротации — сертификат рискует истечь.
- Mitigation: ротация начинается заблаговременно (по умолчанию на 2/3 срока действия) и повторяется с экспоненциальным backoff до успеха; компонент экспортирует метрику остатка срока действия сертификата с алёртом заведомо раньше истечения. Резервный путь — ручная ротация, то есть обязательный рестарт с новой стартовой конфигурацией (Alternative 6, п. 5).
- Risk: подмена endpoint центра выпуска или MITM канала обновления.
- Mitigation: канал обмена — mTLS с проверкой сервера по корню, заданному в конфигурации (системное хранилище ОС не используется, п. 2); запрос аутентифицируется действующим сертификатом компонента; полученный сертификат валидируется по цепочке до корня из конфигурации до применения.
- Risk: невалидный, недоверенный или частичный ответ центра выпуска (обрыв обмена, ошибка
PKI-автоматизации).
- Mitigation: новый материал применяется только целиком после успешной валидации; при ошибке действующий материал не изменяется, отказ журналируется без вывода содержимого ответа; применение каждого обновления журналируется с отпечатком сертификата.
- Risk: ошибка в параметрах протокола обновления в стартовой конфигурации обнаруживается
только к моменту ротации.
- Mitigation: fail-fast валидация секции обновления при запуске; компонент выполняет проверочное обращение к endpoint центра выпуска на старте и журналирует его результат.
- Risk: утечка приватного ключа через журналы или аварийный вывод при обмене по протоколу
обновления.
- Mitigation: те же меры, что для стартовой конфигурации по
ADR 0002: кастомный
panic::set_hook, ручные реализацииDebugс редактированием секретов, запрет журналирования сырых документов.
- Mitigation: те же меры, что для стартовой конфигурации по
ADR 0002: кастомный
- Risk: рассинхронизация материала между компонентами при ротации корня (один компонент уже
получил новый корень, другой ещё нет).
- Mitigation: truststore допускает несколько корней одновременно — ротация корня выполняется в две фазы (перезапуск всех компонентов с конфигурацией, содержащей оба корня → перевыпуск leaf-сертификатов → перезапуск без старого корня); процедура фиксируется в эксплуатационной документации.
References
- ADR 0002: Выбор способа конфигурации приложения.
- ADR 0008: Выбор целевых платформ сборки.
- ADR 0009: Доверенная среда передачи данных между компонентами Netgap.
- Методичка Работа с сертификатами в проекте Netgap: Цепочка доверия, mTLS в Rust на rustls, Автоматизация выпуска и ротация.
- RFC 7030: Enrollment over Secure Transport (EST).
- RFC 4210: Certificate Management Protocol (CMP), RFC 9480: CMP Updates.
- RFC 8555: Automatic Certificate Management Environment (ACME).
- RFC 8894: Simple Certificate Enrolment Protocol (SCEP).
- RFC 5280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile.
- SPIFFE Workload API.