Есть два типа инфраструктурных инженеров:
- те, у кого сертификаты лежат в
/root/certs-final-final-REAL/
- и те, кто уже поставил себе нормальный PKI на HashiCorp Vault.
Сегодня как раз про вторых. Ну или про тех, кто очень скоро ими станет. Потому что рано или поздно любая инфраструктура приходит к простой мысли:
“А может хватит руками генерировать сертификаты в три часа ночи через openssl?”
И тут появляется Vault PKI.
Что вообще такое PKI в Vault
Vault умеет быть полноценным центром сертификации:
- выпускать сертификаты;
- отзывать их;
- хранить CRL;
- выдавать short-lived сертификаты;
- автоматически подписывать CSR;
- работать как Root CA и Intermediate CA;
- интегрироваться с Kubernetes, Nomad, Consul, PostgreSQL, Nginx, Envoy, Istio и вообще всем, что умеет TLS.
И самое приятное:
сертификаты становятся частью инфраструктуры, а не ZIP-архивом “certs_new2_ok”.
Что будем строить
Сделаем:
- Root CA;
- Intermediate CA;
- роли для сервисов;
- автоматическую выдачу сертификатов;
- примеры для:
- Kubernetes;
- PostgreSQL;
- Nginx;
- внутреннего API;
- mTLS;
- SSH;
- Docker Registry;
- GitLab;
- VPN.
Да, сегодня будет много Bash. Прям вот реальный “DevOps Bash Developer”.
Почему Root CA нельзя держать онлайн
Вот тут важный момент.
Правильная схема выглядит так:
Root CA (offline) -> Intermediate CA (Vault) -> Service certificates
Потому что если кто-то украдёт ваш Root CA - всё.
Это уже не инцидент. Это лапки к верху и уходим обратно в тех.поддержку.
Поэтому:
- Root CA держим отдельно;
- Intermediate CA - в Vault;
- Vault подписывает сервисные сертификаты.
Поднимаем Vault
Самый простой dev-mode:
docker run \
--cap-add=IPC_LOCK \
-e VAULT_DEV_ROOT_TOKEN_ID=root \
-p 8200:8200 \
hashicorp/vault
Но так делать в production нельзя. Вообще! Совсем нельзя, даже если очень хочется!
Production Vault - это:
- Raft storage;
- auto-unseal;
- TLS;
- HA;
- audit;
- policies;
- backups;
- replication.
Иначе это не Vault. Это “секреты в контейнере”.
Включаем PKI engine
vault secrets enable pki
Увеличиваем TTL:
vault secrets tune -max-lease-ttl=87600h pki
87600 часов = 10 лет.
Теперь генерируем Root CA.
Генерация Root CA
vault write -field=certificate \
pki/root/generate/internal \
common_name="corp.local Root CA" \
ttl=87600h > root_ca.crt
Vault создаст:
- private key;
- root certificate;
- CA chain.
Настройка URL
Очень многие забывают это. А потом CRL и AIA выглядят как “localhost:8200”.
Красиво. Но бесполезно.
Настраиваем URLs
vault write pki/config/urls \
issuing_certificates="https://vault.corp.local/v1/pki/ca" \
crl_distribution_points="https://vault.corp.local/v1/pki/crl"
Создаём роль
Например для внутренних сервисов.
vault write pki/roles/internal-services \
allowed_domains="svc.cluster.local,corp.local" \
allow_subdomains=true \
max_ttl="72h"
Теперь Vault сможет выпускать сертификаты.
Выпуск сертификата
vault write pki/issue/internal-services \
common_name="api.corp.local"
Получаем:
- certificate;
- private_key;
- issuing_ca;
- serial_number.
И всё это JSON’ом. Как мы любим.
Kubernetes + Vault PKI
Вот тут начинается магия.
Вместо:
- cert-manager;
- self-signed chaos;
- “а где ключ?”;
- “а кто обновит ingress?”;
можно использовать Vault напрямую.
ServiceAccount для Vault
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-auth
namespace: vault
Включаем Kubernetes auth
vault auth enable kubernetes
Настраиваем auth
vault write auth/kubernetes/config \
token_reviewer_jwt="$TOKEN" \
kubernetes_host="https://10.96.0.1:443" \
kubernetes_ca_cert=@ca.crt
Политика
path "pki/issue/internal-services" {
capabilities = ["update"]
}
Роль
vault write auth/kubernetes/role/backend \
bound_service_account_names=backend \
bound_service_account_namespaces=prod \
policies=backend-pki \
ttl=1h
Получение сертификата из Pod
curl \
--request POST \
--data '{"common_name":"backend.prod.svc.cluster.local"}' \
$VAULT_ADDR/v1/pki/issue/internal-services
Vault Agent Injector
А теперь красиво.
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "backend"
vault.hashicorp.com/agent-inject-secret-cert.pem: "pki/issue/internal-services"
vault.hashicorp.com/agent-inject-template-cert.pem: |
{{- with secret "pki/issue/internal-services" "common_name=backend.prod.svc.cluster.local" -}}
{{ .Data.certificate }}
{{- end }}
Vault сам:
- выпустит сертификат;
- положит в контейнер;
- обновит его.
Без shell-скриптов из 2017 года.
mTLS между сервисами
Вот это вообще топовая история. Каждый сервис получает свой сертификат:
frontend -> backend
backend -> postgres
worker -> rabbitmq
И всё общение:
- шифруется;
- проверяется;
- валидируется.
Никаких:
ALLOW_ALL_INTERNAL=true
Потому что “мы же внутри сети”.
Да-да. А потом кто-то случайно делает
kubectl port-forward
на прод.
PostgreSQL + Vault PKI
Генерируем cert
vault write pki/issue/internal-services \
common_name="postgres.corp.local"
PostgreSQL config
ssl = on
ssl_cert_file = '/etc/ssl/postgres.crt'
ssl_key_file = '/etc/ssl/postgres.key'
ssl_ca_file = '/etc/ssl/root_ca.crt'
pg_hba.conf
hostssl all all 0.0.0.0/0 cert clientcert=verify-full
Теперь PostgreSQL требует сертификаты клиентов.
Очень бодрит разработчиков. Особенно тех, кто привык подключаться как:
psql -U postgres
Без пароля. Без TLS. Из кофейни. Где-то в Индонезии.
Nginx + mTLS
Сервер
ssl_client_certificate /etc/nginx/root_ca.crt;
ssl_verify_client on;
Проверка CN
if ($ssl_client_s_dn !~ "CN=backend") {
return 403;
}
Docker Registry с Vault CA
Docker registry очень любит TLS.
Генерируем cert
vault write pki/issue/internal-services \
common_name="registry.corp.local"
registry config
http:
addr: :5000
tls:
certificate: /certs/registry.crt
key: /certs/registry.key
GitLab Runner + Vault CA
Когда Runner общается с GitLab через mTLS - это уже прям инфраструктурный luxury mode.
Настройка runner
tls-ca-file = "/etc/gitlab-runner/root_ca.crt"
VPN через Vault PKI
Например OpenVPN. Да-да! Знаю! 2026 год на дворе - какой нафиг OpenVPN...
Сертификат клиента
vault write pki/issue/vpn \
common_name="user01"
Почему short-lived сертификаты - это кайф
Вместо:
cert valid until 2038
делаем:
ttl = 24h
Что это даёт:
- украденный cert быстро умрёт;
- меньше возни с revocation;
- меньше blast radius;
- проще аудит.
И это уже подход больших инфраструктур, а не “ну мы потом поменяем”.
Спойлер: не поменяете.
Intermediate CA
Теперь по-взрослому.
Включаем второй PKI engine
vault secrets enable -path=pki_int pki
CSR
vault write \
-format=json \
pki_int/intermediate/generate/internal \
common_name="corp.local Intermediate CA" \
| jq -r '.data.csr' > pki_intermediate.csr
Подписываем CSR Root CA
vault write \
-format=json \
pki/root/sign-intermediate \
csr=@pki_intermediate.csr \
format=pem_bundle ttl="43800h"
Импортируем сертификат
vault write pki_int/intermediate/set-signed \
certificate=@signed_intermediate.crt
Теперь Vault выпускает сертификаты через Intermediate CA.
Вот так уже делают взрослые инфраструктуры, где слово “compliance” перестало быть мемом.
Автоматическая ротация сертификатов
И вот тут Vault становится особенно прекрасным.
Можно:
- перевыпускать сертификаты автоматически;
- ротировать ключи;
- обновлять sidecar’ы;
- reload’ить Nginx;
- обновлять Envoy;
- интегрировать всё с Kubernetes rollout.
Vault Agent Template
template {
destination = "/etc/tls/tls.crt"
contents = <<EOH
{{ with secret "pki/issue/internal-services" "common_name=api.corp.local" }}
{{ .Data.certificate }}
{{ end }}
EOH
}
Envoy + Vault
Envoy вообще создан для таких историй.
mTLS + SPIFFE + short-lived certs + automatic rotation.
Это уже уровень:
“да, у нас service mesh, observability и zero trust”.
А не:
iptables + надежда на лучшее и вера в людей.
Аудит Vault
Обязательно включаем.
vault audit enable file file_path=/vault/logs/audit.log
Иначе потом: Кто выпустил wildcard *.prod.local ?
Что ещё умеет Vault PKI
Очень много всего:
- OCSP;
- CRL;
- ACME;
- SPIFFE;
- cross-signing;
- multi issuer;
- external CA;
- CSR signing;
- hardware HSM;
- auto renew;
- enterprise replication;
- namespaces;
- PKCS#11.
Vault вообще штука из серии:
“мы хотели хранить секреты, а в итоге построили half of modern infrastructure”.
Где это реально используют
Практически везде:
- Kubernetes;
- Service Mesh;
- internal API;
- PostgreSQL;
- Kafka;
- RabbitMQ;
- NATS;
- MinIO;
- Ceph;
- GitLab;
- Jenkins;
- Docker Registry;
- OpenVPN;
- WireGuard PKI;
- SSH CA;
- Nomad;
- Consul Connect.
И чем больше инфраструктура - тем быстрее приходит понимание:
сертификаты должны жить как сервис, а не как архив на сервере Пети.
Итоги
Vault PKI - это:
- централизованный TLS;
- автоматизация;
- безопасность;
- короткоживущие сертификаты;
- mTLS;
- нормальная ротация;
- меньше ручного ада;
- меньше “ой, сертификат вчера истёк”.
И самое главное:
инфраструктура начинает выглядеть как инженерная система, а не коллекция bash-скриптов с историческим наследием.
И напоследок
Блин… Я же забыл. Опять мысль убежала вперёд головы. Быстрее, чем terraform apply в пятницу вечером.
Мы вообще почти не поговорили про:
- auth methods Vault;
- Transit engine;
- KV v2;
- AppRole;
- Vault Agent;
- Response Wrapping;
- Sentinel;
- Raft storage;
- Seal/Unseal;
- namespaces;
- policies;
- OIDC;
- LDAP;
- dynamic secrets;
- database engine;
- SSH engine;
- audit devices;
- performance replication;
- DR replication;
- HSM;
- plugins;
- CSI provider;
- Vault Secrets Operator;
- Agent Injector;
- integrated storage;
- telemetry и Prometheus.
Короче, исправлюсь…
Давайте в следующей статье я уже нормально и подробно разложу весь HashiCorp Vault по полочкам.
Потому что там реально целая вселенная, и каждая кнопка может либо спасти инфраструктуру, либо подарить очень интересный созвон в четыре часа утра.