Механизм секретов PKI Stronghold реализует протокол ACME (RFC 8555). Внутренние серверы получают и продлевают сертификаты стандартными ACME-клиентами — certbot, lego, Caddy — без токена Stronghold и без собственных скриптов выпуска.

Цель
Включить ACME на mount промежуточного CA, ограничить выпуск одной ролью, выдать ключи привязки внешнего аккаунта (EAB) и настроить на сервере автоматический выпуск и продление сертификата.
Предварительные требования
Перед началом подготовьте:
- Mount
pki_intс промежуточным CA и рольexample-com, настроенные по примеру «Внутренний PKI на Stronghold» (шаги 1–5). - Токен Stronghold с правами на запись в
pki_int/config/*,pki_int/roles/*,pki_int/eab/*и на изменение параметров mount (sys/mounts/pki_int/tune). - Внешний адрес Stronghold, доступный ACME-клиентам, — в примерах
https://stronghold.example.com. - Сервер с именем
api.example.com, на котором будет работать ACME-клиент. Stronghold должен иметь сетевой доступ к этому серверу для проверки challenge (дляhttp-01— порт80/TCP). Поддерживаются типы challengehttp-01,dns-01иtls-alpn-01. - Сертификат, которым защищён API Stronghold, должен быть доверенным на сервере-клиенте.
Шаг 1. Задайте внешний адрес mount
ACME-сервер формирует ссылки каталога на основе параметра path из config/cluster. Укажите адрес, по которому клиенты обращаются к mount:
d8 stronghold write pki_int/config/cluster path="https://stronghold.example.com/v1/pki_int" aia_path="https://stronghold.example.com/v1/pki_int"В кластере из нескольких узлов указывайте адрес балансировщика или любого узла этого кластера. Адрес не должен указывать на другой кластер репликации.
Шаг 2. Разрешите заголовки ACME
Протокол ACME передаёт служебные данные в HTTP-заголовках. Разрешите их для mount:
d8 stronghold secrets tune -passthrough-request-headers=If-Modified-Since -allowed-response-headers=Last-Modified -allowed-response-headers=Location -allowed-response-headers=Replay-Nonce -allowed-response-headers=Link pki_intШаг 3. Включите ACME
Включите ACME, разрешите только роль example-com, запретите неограниченный каталог по умолчанию и потребуйте EAB:
d8 stronghold write pki_int/config/acme enabled=true allowed_roles="example-com" default_directory_policy="role:example-com" eab_policy="always-required" max_ttl=720hПараметры:
allowed_roles— роли, через каталоги которых разрешён выпуск. Значение по умолчанию*разрешает все роли, включаяsign-verbatim.default_directory_policy— политика для каталогов без роли (pki_int/acme/directory). По умолчанию используетсяsign-verbatim: выпуск без ограничений роли, но запрос на сертификат (CSR) с SAN типов URI, email и Other отклоняется, потому что проверки ACME подтверждают только DNS-имена и IP-адреса. Значениеsign-verbatim-unsafeвыпускает такие сертификаты с SAN из CSR без проверки. Значениеforbidотключает каталог по умолчанию. Значениеrole:<ROLE_NAME>применяет ограничения роли<ROLE_NAME>; роль должна входить вallowed_roles.eab_policy— требование EAB при регистрации ACME-аккаунта. Допустимые значения:not-required,new-account-required,always-required. По умолчанию —always-required; пример выше задаёт его явно.max_ttl— максимальный срок действия сертификатов, выпускаемых через ACME. По умолчанию2160h(90 дней).challenge_permitted_ip_ranges,challenge_excluded_ip_ranges— CIDR, к которым Stronghold может или не может обращаться при проверке challenge.dns_resolver— DNS-резолвер в формате<HOST>:<PORT>для разрешения имён при проверке challenge. Задайте его, если внутренние имена не разрешаются системным резолвером узлов Stronghold.
Проверьте конфигурацию:
d8 stronghold read pki_int/config/acmeКаталог ACME роли доступен без аутентификации по адресу:
https://stronghold.example.com/v1/pki_int/roles/example-com/acme/directoryШаг 4. Выпустите ключ EAB
EAB связывает ACME-аккаунт с оператором Stronghold: зарегистрировать аккаунт может только тот, кто получил ключ. Ключ одноразовый и используется при первой регистрации аккаунта.
Создайте ключ EAB для каталога роли:
d8 stronghold write -f -format=json pki_int/roles/example-com/acme/new-eab > eab.json jq -r '.data.id, .data.key' eab.jsonОтвет содержит
id(идентификатор ключа,kid) иkey(HMAC-ключ). Передайте их администратору сервера по защищённому каналу.Просмотрите неиспользованные ключи:
d8 stronghold list pki_int/eabПри необходимости удалите неиспользованный ключ:
d8 stronghold delete pki_int/eab/<KEY_ID><KEY_ID>— идентификатор ключа из выводаd8 stronghold list pki_int/eab.
Шаг 5. Настройте ACME-клиент
Выполните шаг на сервере api.example.com. Задайте переменные:
ACME_DIR="https://stronghold.example.com/v1/pki_int/roles/example-com/acme/directory"
EAB_KID="<EAB_KEY_ID>"
EAB_HMAC="<EAB_HMAC_KEY>"Плейсхолдеры:
<EAB_KEY_ID>— значениеidизeab.json;<EAB_HMAC_KEY>— значениеkeyизeab.json.
Если сертификат API Stronghold выпущен внутренним CA, укажите клиенту файл с этим CA. В примерах используется /etc/ssl/stronghold-ca.pem.
certbot
Чтобы получить сертификат через certbot, выполните следующие шаги:
Получите сертификат. Режим
--standaloneподнимает временный веб-сервер на порту80для проверкиhttp-01:REQUESTS_CA_BUNDLE=/etc/ssl/stronghold-ca.pem certbot certonly --standalone --server "$ACME_DIR" --eab-kid "$EAB_KID" --eab-hmac-key "$EAB_HMAC" --agree-tos -m admin@example.com -d api.example.comЕсли на сервере уже работает веб-сервер, используйте
--webroot -w <WEBROOT_DIR>или плагин веб-сервера.<WEBROOT_DIR>— корневая директория сайта.Сертификат и ключ сохраняются в
/etc/letsencrypt/live/api.example.com/:fullchain.pem,privkey.pem. Укажите их в конфигурации сервиса.
lego
Получите сертификат через lego с проверкой http-01:
LEGO_CA_CERTIFICATES=/etc/ssl/stronghold-ca.pem lego --server "$ACME_DIR" --eab --kid "$EAB_KID" --hmac "$EAB_HMAC" --email admin@example.com --accept-tos --domains api.example.com --http runСертификаты сохраняются в директорию .lego/certificates/.
Caddy
Caddy сам получает и продлевает сертификаты для сайтов из Caddyfile. Укажите Stronghold как ACME-сервер в глобальных параметрах:
{
email admin@example.com
acme_ca https://stronghold.example.com/v1/pki_int/roles/example-com/acme/directory
acme_ca_root /etc/ssl/stronghold-ca.pem
acme_eab {
key_id <EAB_KEY_ID>
mac_key <EAB_HMAC_KEY>
}
}
api.example.com {
reverse_proxy 127.0.0.1:8080
}<EAB_KEY_ID> и <EAB_HMAC_KEY> — значения id и key из eab.json.
Шаг 6. Настройте продление
Срок действия сертификата ограничен max_ttl роли и max_ttl из config/acme. Продление выполняется повторным заказом через уже зарегистрированный аккаунт, новый ключ EAB не требуется.
certbot. Пакеты certbot обычно устанавливают systemd-таймер или задание cron, которое запускает
certbot renew. Для коротких сертификатов задайте порог продления в/etc/letsencrypt/renewal/api.example.com.conf, напримерrenew_before_expiry = 3 days, и перезагрузку сервиса через--deploy-hook:certbot renew --deploy-hook "systemctl reload nginx"Файл CA для
REQUESTS_CA_BUNDLEдолжен быть доступен и при автоматическом запуске, например черезEnvironment=в unit-файле таймера.lego. Запускайте продление по расписанию, например ежедневно:
LEGO_CA_CERTIFICATES=/etc/ssl/stronghold-ca.pem lego --server "$ACME_DIR" --email admin@example.com --domains api.example.com --http renew --days 7 --renew-hook "systemctl reload nginx"Caddy продлевает сертификаты автоматически, дополнительная настройка не нужна.
Проверка
Проверьте результат:
Убедитесь, что каталог ACME отвечает:
curl -s --cacert /etc/ssl/stronghold-ca.pem https://stronghold.example.com/v1/pki_int/roles/example-com/acme/directory | jqВ ответе должны быть ссылки
newNonce,newAccountиnewOrderс адресом изconfig/cluster.Проверьте выпущенный сертификат: издатель, имя и срок действия:
openssl x509 -in /etc/letsencrypt/live/api.example.com/fullchain.pem -noout -issuer -subject -datesПроверьте, что сертификат есть в хранилище mount:
d8 stronghold list pki_int/certsПроверьте продление без выпуска сертификата:
REQUESTS_CA_BUNDLE=/etc/ssl/stronghold-ca.pem certbot renew --dry-run
Очистка
Удалите объекты, созданные в этом руководстве.
- Отзовите тестовый сертификат:
certbot revoke --cert-name api.example.com(сREQUESTS_CA_BUNDLE) илиd8 stronghold write pki_int/revoke serial_number=<SERIAL_NUMBER>, где<SERIAL_NUMBER>— серийный номер сертификата. - Удалите неиспользованные ключи EAB:
d8 stronghold delete pki_int/eab/<KEY_ID>. - Удалите локальный файл
eab.json. - Если ACME больше не нужен, отключите его:
d8 stronghold write pki_int/config/acme enabled=false.