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

Автоматизация выпуска и ротация сертификатов

Компоненты Netgap аутентифицируют друг друга по mTLS, а межшлюзовой трафик шифруется TLS 1.3. Ручной выпуск сертификатов через OpenSSL (см. Выпуск сертификатов через OpenSSL) отлично подходит для обучения, тестирования и экспериментальных стендов, но не масштабируется: как только сервисов становится десятки, а сертификаты — короткоживущими (TTL 1–24 часа, см. Отзыв сертификатов), перевыпуск обязан происходить автоматически, без участия человека. Эта статья — обзор инструментов автоматизации: HashiCorp Vault PKI, cert-manager для Kubernetes и Service Mesh, а также разбор «живого» mTLS-соединения двух сервисов с сертификатами от Vault.

1. HashiCorp Vault: динамический PKI

Vault — популярный инструмент управления секретами со встроенным движком PKI, который превращает Vault в полноценный автоматический удостоверяющий центр (CA):

  1. при настройке в Vault загружается (или генерируется) корневой ключ и сертификат;
  2. приложение, скрипт или CI/CD-пайплайн делает HTTP-запрос POST /v1/pki/issue/my-role, передавая имя нужного клиента или сервера;
  3. Vault на лету генерирует приватный ключ и сертификат (например, с TTL 24 часа) и возвращает их в JSON-ответе вместе с корневым ca.crt;
  4. приложение подхватывает файлы; задолго до истечения TTL процесс повторяется автоматически.
Vault не участвует в handshake

Vault нужен только для выпуска сертификатов. Сама проверка подлинности при соединении происходит по классическим правилам асимметричной криптографии между двумя сервисами — в момент handshake Vault не участвует. Это разгружает сеть и не добавляет задержек соединениям.

Главный плюс: сертификаты короткоживущие. Если ключ утек, через несколько часов он сам «превратится в тыкву», и проверки CRL/OCSP часто становятся просто не нужны — подробнее в разделе 6.

2. Как сервисам доверять корню Vault: варианты Truststore

Чтобы сервис принимал сертификаты, выпущенные Vault, его корневой ca.crt нужно добавить в хранилище доверенных сертификатов (Truststore) — на уровне ОС, контейнера, кода или прокси. Теория того, зачем это нужно, — в статье Цепочка доверия.

Вариант 1: на уровне Linux-сервера.

# Ubuntu / Debian
sudo cp ca.crt /usr/local/share/ca-certificates/vault-ca.crt
sudo update-ca-certificates

# CentOS / RHEL / Rocky Linux
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/vault-ca.crt
sudo update-ca-trust extract

Вариант 2: внутри Docker-контейнера.

FROM alpine:latest

COPY certs/ca.crt /usr/local/share/ca-certificates/vault-ca.crt
RUN apk update && apk add --no-cache ca-certificates && update-ca-certificates

# Далее — запуск вашего приложения

Вариант 3: на уровне кода приложения (когда нет прав на изменение ОС):

  • Go — явно создать пул сертификатов x509.NewCertPool() из файла и передать его в tls.Config{RootCAs: certPool};
  • Node.js — указать путь через переменную окружения: export NODE_EXTRA_CA_CERTS="/path/to/vault-ca.crt" перед запуском node app.js;
  • Python (requests) — передать путь к корню в параметре verify: requests.get('https://service-b.internal', verify='/path/to/vault-ca.crt');
  • Java — импортировать корень в TrustStore утилитой keytool: keytool -importcert -trustcacerts -file ca.crt -alias vaultCA -keystore cacerts.jks -storepass changeit и запустить приложение с флагом -Djavax.net.ssl.trustStore=cacerts.jks;
  • Rust (rustls) — добавить ca.crt в RootCertStore, как описано в статье mTLS на rustls.

Вариант 4: на уровне Nginx — если mTLS терминирует прокси перед сервисом:

server {
listen 443 ssl;
server_name service-a.internal;

# Собственные сертификаты этого сервиса (тоже выпущены Vault)
ssl_certificate /etc/nginx/certs/service-a.crt;
ssl_certificate_key /etc/nginx/certs/service-a.key;

# Корень Vault — для проверки клиентов, приходящих к сервису
ssl_client_certificate /etc/nginx/certs/vault-ca.crt;
ssl_verify_client on; # включаем строгий mTLS
}

3. Живое mTLS-соединение двух сервисов с сертификатами от Vault

Разберем, как Сервер А (клиент) и Сервер Б (сервер) проверяют друг друга, когда оба получили сертификаты от Vault, а ca.crt уже лежит в их Truststore:

  1. Сервер А проверяет Сервера Б (стандартный TLS). Сервер Б присылает свой сертификат. Сервер А проверяет его цифровую подпись открытым ключом из локального ca.crt и математически убеждается: «этот сертификат действительно выпущен моим доверенным Vault CA, данные внутри не подменены». Дополнительно проверяются срок действия и совпадение Subject/SAN с именем Сервера Б.
  2. Сервер Б проверяет Сервера А (собственно mTLS). Сервер Б отправляет CertificateRequest; Сервер А присылает свой клиентский сертификат, и Сервер Б точно так же проверяет его своим ca.crt. Если подпись сходится — Сервер А «свой». Дальше идентичность клиента (CN, SPIFFE ID) можно использовать для авторизации — см. Идентичность клиента.
  3. Защита от кражи сертификата (CertificateVerify). Просто предъявить чужой сертификат недостаточно — он передается по сети и его можно перехватить. Сервер А хеширует все предыдущие сообщения сессии, подписывает хеш своим приватным ключом и отправляет CertificateVerify. Сервер Б проверяет подпись открытым ключом из сертификата А: если математика сошлась, перед ним законный владелец ключа, а не злоумышленник со скопированным .crt-файлом.

4. Cert-manager для Kubernetes

Если инфраструктура работает в Kubernetes, стандарт де-факто — оператор cert-manager:

  1. cert-manager разворачивается внутри кластера;
  2. создается объект Issuer — кто подписывает сертификаты (Vault, Let's Encrypt или собственный локальный CA);
  3. создается манифест Certificate: «нужен mTLS-сертификат для api.internal, срок 30 дней, обновлять за 5 дней до окончания»;
  4. cert-manager сам генерирует ключи, обращается к Issuer, забирает сертификат и кладет его в стандартный Kubernetes Secret;
  5. поды или Ingress-контроллер монтируют секрет; при обновлении сертификата приложения подтягивают новые данные автоматически (или после рестарта).

5. Service Mesh (Istio / Linkerd): mTLS «из коробки»

Когда mTLS-шифрованием нужно связать сотни микросервисов, управлять сертификатами каждого даже через cert-manager тяжело. Service Mesh снимает задачу с разработчика полностью:

  • код приложения пишет обычный HTTP и вообще «не знает» о сертификатах;
  • рядом с каждым контейнером mesh автоматически поднимает sidecar-прокси (в Istio — Envoy);
  • компонент istiod работает как локальный CA и выписывает каждому прокси короткоживущие сертификаты (обычно 12–24 часа) с идентичностью в формате SPIFFE (см. Идентичность клиента);
  • весь трафик между сервисами идет через прокси: они сами выполняют mTLS-handshake, проверяют сертификаты и шифруют канал;
  • ротация ключей происходит в памяти прокси, незаметно для приложения.

6. Стратегия отзыва при автоматизации

Раз выпуск автоматический, сертификаты можно делать короткоживущими — и это меняет подход к отзыву:

  • короткий TTL (рекомендуется для Vault): сертификаты живут 1–12 часов и обновляются в фоне. При компрометации сервиса администратор просто блокирует ему доступ к Vault — старый сертификат истечет сам через несколько часов. CRL/OCSP в таком сценарии часто вообще отключают;
  • проверка по CRL: если сертификаты долгоживущие, проверяющая сторона обязана при каждом соединении сверяться со списком отзыва, который хостит Vault.

Сравнение механизмов отзыва и их ограничения — в статье Отзыв сертификатов.

Горячая ротация в Rust-сервисе

Короткий TTL означает, что сервис должен уметь подменять сертификат без перезапуска. В Rust это делается атомарной заменой ServerConfig в памяти (например, через ArcSwap): новые соединения получают свежую конфигурацию, уже открытые спокойно дорабатывают на старой. Рабочий пример — в статье mTLS на rustls.

7. Сравнение подходов

КритерийOpenSSL вручнуюVault PKIcert-managerService Mesh
ВыпускРуками, по командам из private-pkiAPI-запрос, автоматическиДекларативно, операторомПолностью прозрачно
РотацияРучнаяАвтоматическая (короткий TTL)Автоматическая (по манифесту)Автоматическая, в памяти прокси
Типичный TTLМесяцы/годыЧасы/суткиНедели/месяцы12–24 часа
Участие разработчикаПолноеИнтеграция с API/агентомНаписание манифестовНулевое
Область примененияОбучение, стенды, корень PKIВиртуалки, bare metal, гибридKubernetesKubernetes, сотни микросервисов
ОтзывCRL/OCSP вручнуюКороткий TTL или CRLЧерез IssuerКороткий TTL

Общие правила независимо от выбранного инструмента: корень CA должен доставляться во все Truststore как часть процесса деплоя; TTL сертификатов выбирается осознанно, а стратегия отзыва соответствует TTL (Отзыв сертификатов); сервисы обновляют сертификаты в фоне задолго до истечения срока без перезапуска (mTLS на rustls); доступ к API выпуска (роли Vault, Issuer) ограничивается так, чтобы сервис мог запросить сертификат только со своим именем.