Веб-серверу на виртуальной машине не нужен сертификат, выпущенный вручную на год. Stronghold Agent запрашивает сертификат в механизме секретов PKI, записывает его на диск, перевыпускает до истечения срока действия и перезагружает Nginx. Закрытый ключ создаётся в Stronghold и передаётся только Agent на ВМ.

Цель
Настроить на ВМ Stronghold Agent, который выпускает для Nginx сертификат www.example.com сроком на 72 часа из промежуточного CA pki_int, автоматически перевыпускает его и выполняет перезагрузку Nginx без разрыва соединений.
Предварительные требования
Перед началом подготовьте:
- Промежуточный CA в Stronghold, подключённый по пути
pki_int, как в руководстве «Внутренний PKI на Stronghold». - Виртуальная машина Linux с systemd и Nginx.
- Stronghold Agent, установленный на ВМ и входящий в Stronghold по AppRole, как в руководстве «Приложение на ВМ со Stronghold Agent».
- Токен Stronghold с правами на настройку PKI, AppRole и политик.
Шаг 1. Создайте роль PKI
Роль ограничивает имена и срок жизни сертификатов, которые может получить веб-сервер:
d8 stronghold write pki_int/roles/web-server allowed_domains="example.com" allow_subdomains=true allow_bare_domains=true max_ttl=720hallow_subdomains=trueразрешает имена в доменеexample.com, напримерwww.example.com.allow_bare_domains=trueразрешает само имяexample.com. Без этого параметра Stronghold отклоняет запрос, в которомexample.comуказан вalt_names.
Короткий срок жизни сертификата снижает последствия компрометации ключа: Agent обновляет сертификат автоматически, поэтому ручной перевыпуск не нужен.
Шаг 2. Создайте политику и роль AppRole
Выдайте ВМ доступ только к выпуску сертификатов и доставьте на неё учётные данные AppRole.
Создайте политику, которая разрешает выпуск сертификатов только по роли
web-server:d8 stronghold policy write web-server-tls - <<'POLICY' path "pki_int/issue/web-server" { capabilities = ["update"] } POLICYСоздайте роль AppRole для ВМ:
d8 stronghold write auth/approle/role/web-server token_policies=web-server-tls token_ttl=1h token_max_ttl=24h secret_id_ttl=720h secret_id_bound_cidrs="10.0.10.20/32"Доставьте
role_idи обёрнутыйsecret_idролиweb-serverна ВМ в директорию/etc/stronghold-agent/, как описано в шаге 3 руководства «Приложение на ВМ со Stronghold Agent». В командах этого шага заменитеmyapp-vmнаweb-server.
Шаг 3. Создайте шаблон
Создайте файл /etc/stronghold-agent/templates/www.pem.ctmpl. Шаблон записывает закрытый ключ, сертификат и цепочку промежуточных CA в один файл, поэтому ключ и сертификат всегда соответствуют друг другу:
{{ with secret "pki_int/issue/web-server" "common_name=www.example.com" "alt_names=example.com" "ttl=72h" }}
{{ .Data.private_key }}
{{ .Data.certificate }}
{{ range .Data.ca_chain }}{{ . }}
{{ end }}{{ end }}Каждый вызов secret "pki_int/issue/..." выпускает сертификат с новым ключом. Если нужны отдельные файлы для сертификата и ключа, как в примере из раздела «Основные возможности», используйте в обоих шаблонах вызов с одинаковыми аргументами.
Одинаковые вызовы (тот же путь и те же аргументы) Agent выполняет один раз и использует результат во всех шаблонах, поэтому ключ и сертификат в разных файлах будут парой.
Вместо secret можно использовать функцию шаблона pkiCert. Она читает сертификат из файла destination шаблона и запрашивает новый сертификат, только если файла нет или срок действия сертификата в нём скоро истечёт. Поэтому перезапуск Agent не приводит к выпуску нового сертификата. Функция возвращает поля .Cert, .Key, .CA и .CAChain, которые шаблон записывает в файл destination.
Шаг 4. Настройте Agent
Настройте Agent на выпуск сертификата и перезагрузку Nginx и выдайте Agent нужные права.
Создайте файл
/etc/stronghold-agent/agent.hcl:stronghold { address = "https://stronghold.example.com" } auto_auth { method "approle" { mount_path = "auth/approle" config = { role_id_file_path = "/etc/stronghold-agent/role-id" secret_id_file_path = "/etc/stronghold-agent/secret-id" secret_id_response_wrapping_path = "auth/approle/role/web-server/secret-id" remove_secret_id_file_after_reading = true } } } template { source = "/etc/stronghold-agent/templates/www.pem.ctmpl" destination = "/etc/nginx/tls/www.pem" perms = "0600" command = "/usr/bin/systemctl reload nginx" command_timeout = "30s" error_on_missing_key = true }secret_id_response_wrapping_path— путь создания обёрнутогоsecret_idролиweb-server. Agent проверяет этот путь перед развёртываниемsecret_id.perms = "0600"— файл содержит закрытый ключ. Мастер-процесс Nginx читает его с правами root.command— выполняется после каждой записи файла. При перезагрузке мастер-процесс Nginx проверяет новую конфигурацию, перечитывает сертификат и не разрывает текущие соединения. Если в конфигурации есть ошибка, Nginx продолжает работать с прежней конфигурацией.
Разрешите пользователю
stronghold-agentвыполнять только перезагрузкуnginx.service. Unit Agent запускается от пользователяstronghold-agentсNoNewPrivileges=true, поэтомуsudoвcommandне работает. Создайте файл/etc/polkit-1/rules.d/50-stronghold-agent-nginx.rules:polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units" && subject.user == "stronghold-agent" && action.lookup("unit") == "nginx.service" && action.lookup("verb") == "reload") { return polkit.Result.YES; } });polkit читает файлы правил автоматически. Параметры
unitиverbпередаёт systemd версии 226 и выше.Создайте директорию для сертификата:
sudo install -d -o stronghold-agent -g root -m 0750 /etc/nginx/tlsРазрешите Agent запись в
/etc/nginx/tlsи/etc/stronghold-agent. Unit Agent используетProtectSystem=strict, поэтому добавьте эти директории вReadWritePathsв drop-in-файле. Выполнитеsudo systemctl edit stronghold-agentи добавьте строки:[Service] ReadWritePaths=/etc/stronghold-agent /etc/nginx/tlsВладельцем директории
/etc/stronghold-agentдолжен быть пользовательstronghold-agent. Иначе Agent не сможет удалить файлsecret_idпосле чтения.
Шаг 5. Настройте Nginx
Укажите один и тот же файл в ssl_certificate и ssl_certificate_key:
server {
listen 443 ssl;
server_name www.example.com example.com;
ssl_certificate /etc/nginx/tls/www.pem;
ssl_certificate_key /etc/nginx/tls/www.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
root /var/www/html;
}
}Запустите Agent до первого запуска Nginx с этой конфигурацией, чтобы файл сертификата уже существовал. Затем запустите Nginx:
sudo systemctl restart stronghold-agent
sudo systemctl start nginxЕсли Nginx уже запущен, вместо systemctl start nginx выполните sudo systemctl reload nginx.
Шаг 6. Как работает обновление
Сертификат PKI выдаётся без продлеваемой аренды. Agent отслеживает срок действия сертификата и до его истечения повторно выполняет pki_int/issue/web-server, перезаписывает файл и выполняет command. Выбирайте ttl в шаблоне так, чтобы перевыпуск происходил не реже, чем вы готовы перезагружать Nginx, и не позже, чем вы успеете заметить сбой.
Agent перевыпускает сертификат, когда проходит около 90 % оставшегося срока его действия (со случайным разбросом ±5 %, чтобы клиенты не приходили одновременно). Для ttl = 24h это примерно через 21–22 часа.
Apache HTTP Server
Для Apache используйте тот же шаблон и тот же блок template со следующими отличиями:
в конфигурации виртуального хоста укажите файл в директивах
SSLCertificateFileиSSLCertificateKeyFile. Apache 2.4.8 и выше читает промежуточные сертификаты изSSLCertificateFile:<VirtualHost *:443> ServerName www.example.com SSLEngine on SSLCertificateFile /etc/apache2/tls/www.pem SSLCertificateKeyFile /etc/apache2/tls/www.pem </VirtualHost>в
destinationукажите/etc/apache2/tls/www.pem, а вcommand—/usr/bin/systemctl reload apache2или/usr/bin/systemctl reload httpdв зависимости от дистрибутива. При перезагрузке Apache выполняет плавный перезапуск и не разрывает текущие соединения;в правиле polkit укажите unit
apache2.serviceилиhttpd.serviceвместоnginx.service, а вReadWritePaths— директорию/etc/apache2/tlsвместо/etc/nginx/tls.
Проверка
Проверьте, что Nginx отдаёт сертификат, выпущенный Stronghold, и что сертификат перевыпускается.
Проверьте журнал Agent — должны быть сообщения о рендеринге шаблона и выполнении команды:
sudo journalctl -u stronghold-agent -n 50Проверьте сертификат, который отдаёт Nginx:
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -serial -datesИздатель должен совпадать с промежуточным CA
pki_int, а срок действия — сttlиз шаблона.Проверьте цепочку доверия с корневым сертификатом CA:
curl --cacert root-ca.pem -sSI https://www.example.comПроверьте перевыпуск: временно задайте в шаблоне
ttl=10m, перезапустите Agent и убедитесь, что серийный номер сертификата из шага 2 проверки меняется без ручных действий.
Очистка
Чтобы удалить конфигурацию примера, выполните следующие действия:
Остановите Agent и удалите блок
templateи файл шаблона.Удалите файл
/etc/nginx/tls/www.pemи уберите директивыssl_certificateиз конфигурации Nginx.Удалите файл
/etc/polkit-1/rules.d/50-stronghold-agent-nginx.rules. Уберите/etc/nginx/tlsизReadWritePathsв drop-in-файле Agent: выполнитеsudo systemctl edit stronghold-agent.При необходимости отзовите выпущенный сертификат и удалите роли и политику:
d8 stronghold write pki_int/revoke serial_number=<SERIAL_NUMBER> d8 stronghold delete pki_int/roles/web-server d8 stronghold delete auth/approle/role/web-server d8 stronghold policy delete web-server-tls<SERIAL_NUMBER>— серийный номер сертификата из шага 2 проверки, например39:dd:2e:....