Конфигурирование внутреннего транспорта
Все коммуникации между компонентами Netgap — сейчас это netgap-gateway ↔ netgap-executor, в будущем и другие компоненты, например входящий балансировщик — выполняются исключительно по mTLS (TLS 1.3) с авторизацией по SPIFFE ID. Незащищённого внутреннего транспорта и опции «отключить проверку сертификата» в продукте не существует — ни как режима по умолчанию, ни как настройки. Это решение зафиксировано в ADR 0009.
Эта статья описывает конфигурирование внутреннего транспорта. Пошаговый разбор того, как компоненты взаимодействуют поверх него — кто кого вызывает, как доставляются запрос и ответ, как проходит mTLS-рукопожатие и какие остаточные риски есть у схемы — вынесен в подстраницу Взаимодействие netgap-gateway и netgap-executor.
Каждое внутреннее соединение одновременно обеспечивает четыре свойства доверенной среды:
- конфиденциальность — трафик недоступен для чтения третьей стороне;
- целостность — модификация трафика в канале обнаруживается и приводит к разрыву соединения;
- взаимная аутентификация — каждая сторона криптографически доказывает свою подлинность до передачи первого байта прикладных данных;
- авторизация отправителя — принимающая сторона проверяет не только валидность цепочки сертификата, но и право контрагента выполнять конкретную операцию: авторизация раздельная по мостам, у каждой внутренней точки свой список разрешённых ролевых SPIFFE ID (deny by default).
Схема соединений
netgap-executor выступает клиентом и подключается к двум внутренним gRPC-мостам netgap-gateway:
- мост запросов — по умолчанию
0.0.0.0:50051; из него исполнитель забирает входящие запросы; - мост ответов — по умолчанию
0.0.0.0:50052; в него исполнитель возвращает ответы целевого сервиса.
Адреса мостов задаются опциями шлюза request_bridge_listen_address и response_bridge_listen_address; со стороны исполнителя те же точки указываются опциями request_bridge_address и response_bridge_address.
SPIFFE ID в Netgap — атрибут роли (операции), а не компонента целиком: роль записывается последним сегментом пути, spiffe://<trust-domain>/<component>/<instance>/<role>. У исполнителя две клиентские роли — http1-request-reader (чтение очереди запросов) и http1-response-writer (доставка ответов), у шлюза две серверные — http1-request-bridge и http1-response-bridge. Один сертификат может нести несколько ролевых SPIFFE ID в SAN (так делает dev-PKI); при необходимости роли можно разнести по разным сертификатам — или в будущем по разным компонентам — без смены схемы конфигурации.
Проверка симметрична — обе стороны проверяют друг друга, и раздельна по операциям — каждый мост авторизуется независимо:
- шлюз после проверки цепочки извлекает множество SPIFFE ID из клиентского сертификата и сверяет его со списком разрешённых идентичностей того моста, к которому подключился клиент (
server.request_bridge.allowed_client_spiffe_idsилиserver.response_bridge.allowed_client_spiffe_ids); клиент авторизован, если хотя бы один из предъявленных ID есть в списке. Сертификат только с рольюhttp1-request-readerпройдёт на мост запросов, но будет отклонён мостом ответов — и наоборот; - исполнитель сверяет множество SPIFFE ID сервера с ожидаемым ролевым ID соответствующего моста (
client.request_bridge.expected_server_spiffe_idиclient.response_bridge.expected_server_spiffe_id) и отказывается работать с узлом, среди идентичностей которого нет ожидаемой. Это исключает перенаправление трафика на подставной узел с любым «валидным» сертификатом того же CA.
Секция конфигурации internal_transport
Секция internal_transport одинакова по структуре для всех компонентов и обязательна: конфигурация без неё отклоняется до запуска сетевых компонентов. Как передать конфигурацию в приложение, описано в статье Конфигурирование приложения.
internal_transport:
identity:
# PEM leaf-сертификата компонента (+ промежуточная цепочка, leaf первым).
# В SAN — один или несколько ролевых URI вида
# spiffe://<trust-domain>/<component>/<instance>/<role>.
certificate_chain_pem: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
# PEM приватного ключа (PKCS#8). Не журналируется, затирается в памяти.
private_key_pem: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
trust:
# Доверенные корни (может быть несколько — двухфазная ротация корня).
trusted_roots_pem: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
# CRL обязателен; для PKI без отозванных сертификатов — пустой CRL.
crl_pem: |
-----BEGIN X509 CRL-----
...
-----END X509 CRL-----
# Серверная роль (компонент публикует внутренние точки, например netgap-gateway).
# Авторизация раздельная по операциям: у каждого моста свой allowlist.
server:
# Мост запросов: кто имеет право читать очередь запросов.
request_bridge:
allowed_client_spiffe_ids:
- "spiffe://netgap.prod/netgap-executor/dc1/http1-request-reader"
# Мост ответов: кто имеет право доставлять ответы.
response_bridge:
allowed_client_spiffe_ids:
- "spiffe://netgap.prod/netgap-executor/dc1/http1-response-writer"
# Клиентская роль (компонент вызывает внутренние точки, например netgap-executor).
# Для каждого моста ожидается свой ролевой SPIFFE ID сервера.
client:
request_bridge:
expected_server_spiffe_id: "spiffe://netgap.prod/netgap-gateway/dc1/http1-request-bridge"
response_bridge:
expected_server_spiffe_id: "spiffe://netgap.prod/netgap-gateway/dc1/http1-response-bridge"
Роли server и client зависят от того, какие внутренние точки компонент публикует и какие вызывает:
- у шлюза обязательна секция
serverс обеими подсекциямиrequest_bridgeиresponse_bridge— он публикует внутренние мосты и авторизует клиентов каждого моста по его собственному allowlist; - у исполнителя обязательна секция
clientс обеими подсекциями — он вызывает мосты шлюза и проверяет ролевую идентичность сервера для каждого подключения; - будущие компоненты (например, входящий балансировщик) могут иметь обе секции, если одновременно публикуют внутренние точки и вызывают чужие.
Доставка криптографического материала
Правила доставки материала зафиксированы в ADR 0010:
- Всё передаётся значением, а не ссылкой. Конфигурация содержит PEM-содержимое сертификатов, ключей и CRL непосредственно в YAML-документе, который поступает через
stdin. Пути к файлам, URL и переменные окружения как способ указания криптографического материала не поддерживаются — компонент не читает его с диска и из окружения ни при каких настройках. - Доверие — только корни из конфигурации. Truststore компонента формируется исключительно из
trusted_roots_pem; системное хранилище доверенных корневых сертификатов ОС не используется, и возможности включить его использование не существует — by design. stdinзакрывается после чтения конфигурации. Компонент читает ровно один YAML-документ до EOF, валидирует его и немедленно закрываетstdin; других входящих каналов управления у процесса нет.- Ручная ротация любого материала — всегда перезапуск процесса с новой конфигурацией в
stdinнового процесса. «Горячей» ручной подмены сертификата, ключа, корня доверия или списков SPIFFE ID в работающем компоненте не существует by design.
Автоматическое обновление сертификатов по протоколам (EST, CMP, ACME, SCEP из ADR 0010) — запланировано, в текущей версии не реализовано. Штатная ротация сегодня — перезапуск компонента с новой конфигурацией.
Требования к сертификатам
- SAN URI — один или несколько SPIFFE ID вида
spiffe://<trust-domain>/<component>/<instance>/<role>, по одному на роль; дубликаты — ошибка. Несколько ID в одном сертификате — осознанное, документированное отступление от строгого X.509-SVID («ровно один ID»): оно позволяет одному сертификату нести несколько ролей и при необходимости разнести роли по разным сертификатам без смены схемы. Проверка по CN и DNS-именам для авторизации не используется. - EKU —
clientAuthи/илиserverAuthпо роли соединения; сертификаты без нужного назначения отклоняются. - Basic Constraints —
CA:FALSEдля leaf-сертификатов. - Алгоритмы: leaf — ECDSA P-256 или Ed25519 (RSA допускается при длине ключа не менее 3072 бит, но не рекомендуется); CA — ECDSA P-384 или RSA 4096.
- Протокол — только TLS 1.3 с AEAD-шифрами (
TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256).
Генерация сертификатов для разработки и тестов
Для разработки, тестов и лабораторных стендов в поставку входит утилита netgap-certgen (чистый Rust, openssl не требуется):
./bin/netgap-certgen dev --out local-pki [--trust-domain netgap.dev] [--instance default] [--days 30]
Команда создаёт в каталоге local-pki:
root-ca.pem/root-ca.key— корневой сертификат и ключ dev-CA;crl.pem— пустой CRL;- сертификаты и ключи
netgap-gatewayиnetgap-executor— каждый несёт по два ролевых SPIFFE ID (у шлюза…/http1-request-bridgeи…/http1-response-bridge, у исполнителя…/http1-request-readerи…/http1-response-writer); - два готовых YAML-фрагмента
netgap-gateway-internal-transport.yamlиnetgap-executor-internal-transport.yamlс секциейinternal_transportцеликом.
Фрагменты дописываются к базовой конфигурации простым объединением:
cat config/netgap-gateway.yaml local-pki/netgap-gateway-internal-transport.yaml | exec env -i ./bin/netgap-gateway run
cat config/netgap-executor.yaml local-pki/netgap-executor-internal-transport.yaml | exec env -i ./bin/netgap-executor run
Сертификат дополнительного компонента выпускается тем же dev-CA командой:
./bin/netgap-certgen component --out local-pki --name <имя> --trust-domain <домен> [--role <роль>]...
Опция --role повторяемая: каждое значение добавляет в SAN ролевой SPIFFE ID spiffe://<домен>/<имя>/<instance>/<роль>. Без опции сертификат несёт один SPIFFE ID без ролевого сегмента.
Важно: сертификаты, сгенерированные
netgap-certgenпредназначены только для разработки и тестов. В продакшене используется PKI заказчика — корпоративный УЦ, Vault PKI или аналогичный механизм. Как развернуть собственную PKI, автоматизировать выпуск и проверять идентичность, описано в методичке Работа с сертификатами: Private PKI, Автоматизация выпуска, Идентичность клиента.
Отказы и диагностика
- Невалидная или неполная секция
internal_transport— отказ запуска (fail-fast): компонент сообщает причину и завершается до открытия сетевых точек. - Отклонённые рукопожатия — невалидная цепочка, отозванный сертификат, неразрешённый SPIFFE ID, истёкший срок действия — журналируются как события безопасности с указанием причины и предъявленной идентичности.
Типовые ошибки:
| Симптом | Причина | Действие |
|---|---|---|
| Рукопожатие отклоняется с причиной «истёкший сертификат» | Истёк срок действия материала dev-стенда (по умолчанию 30 дней) | Перегенерировать dev-PKI: ./bin/netgap-certgen dev --out local-pki и перезапустить компоненты |
| Клиент отклонён с валидным на вид сертификатом | Несовпадение trust-домена: сертификат выпущен PKI другой инсталляции | Выпустить сертификат в PKI той же инсталляции; сверить --trust-domain и корни в trusted_roots_pem |
| Рукопожатие отклоняется с причиной «неразрешённый SPIFFE ID» | Ни один из ролевых SPIFFE ID клиента не входит в allowed_client_spiffe_ids того моста, к которому он подключается (например, сертификат только с ролью http1-request-reader предъявлен мосту ответов) | Добавить нужный ролевой SPIFFE ID в список соответствующего моста (или выпустить сертификат с нужной ролью) и перезапустить компоненты |
Компонент отказывается запускаться после обновления с сообщением о неизвестном ключе в internal_transport | Конфигурация в старом плоском формате: allowed_client_spiffe_ids/expected_server_spiffe_id прямо под server/client | Перевести секцию на подсекции request_bridge/response_bridge (см. шаблон выше) |
Ссылки
- Взаимодействие netgap-gateway и netgap-executor — доставка запроса и ответа через мосты, mTLS-рукопожатие шаг за шагом, анализ остаточных рисков.
- ADR 0009: Доверенная среда передачи данных между компонентами — почему mTLS обязателен и неотключаем.
- ADR 0010: Доставка сертификатов в компоненты через конфигурацию — почему материал передаётся значением через
stdin. - Работа с сертификатами — методичка: теория, private PKI, инспекция, отзыв, автоматизация.
- Конфигурирование приложения — как передать YAML-документ в компонент.