Автоматическое продление клиентского сертификата (EST) — Versola Docs
/docs
До 1.00.7.5versola.ioGitHub

Автоматическое продление клиентского сертификата (EST)

Пусть клиент tls_client_auth, например банк, сам получает и продлевает свой mTLS-сертификат через EST (RFC 7030). Включите EST, зарегистрируйте клиента, выдайте одноразовый токен, пройдите выдачу, продлевайте, продлевайте цепочку или отзывайте право продления.

По этому руководству API-клиент (например, банк с интеграцией по FAPI 2.0) сам получает клиентский сертификат и продлевает его с ключом, который он создал. В конце клиент аутентифицируется на /token сертификатом, который выпустил central, продлевает его до истечения, а вы знаете, что делать, если его ключ скомпрометирован. Идея, правила цепочки и отличия от RFC описаны в Клиентские сертификаты и их продление и RFC 7030. Если вы выпускаете клиентские сертификаты в собственном УЦ, прочитайте Свой PKI и пропустите остальное.

Нужны auth с listener’ом mutual-tls, central с бэкендом УЦ (client-certificates) и тенант, у которого задан mtlsCertificateHeader (как для любого клиента tls_client_auth).

1. Включите EST в auth

EST выключен, пока его не включат. Добавьте блок в auth.conf:

est {
  enabled         = true
  ca-certificates = "/app/mtls/client-ca.crt"   # только УЦ клиентов
}

ca-certificates обязателен, и в нем должен быть только УЦ, выпускающий клиентские сертификаты, а не весь bundle mutual-tls.trusted-certificates, где может быть УЦ edge (справочник конфигурации). На сгенерированных таргетах задайте для versola-tools EST_ENABLED=true и, кроме local и Kubernetes (где chart сам монтирует только УЦ клиентов в /app/mtls/client-trust/ca.crt), EST_CA_CERTIFICATES=<путь в контейнере auth>; для Kubernetes см. PKI в Kubernetes. Маршруты такие:

ГдеМаршруты
Публичное имя auth (issuer)/.well-known/est/cacerts, csrattrs, simpleenroll
Публичное имя mutual-TLS listener’а/.well-known/est/cacerts, csrattrs, simplereenroll

Поэтому клиенту нужны два базовых URL: выдача на origin issuer’а, продление на origin mutual-TLS. Стандартный EST-клиент с одним базовым URL получит 404 на simplereenroll на origin issuer’а и ошибку TLS handshake на simpleenroll на origin mutual-TLS, поэтому запускайте его отдельно для каждого origin. cacerts и csrattrs на origin issuer’а не требуют учетных данных, и именно там их забирает клиент без сертификата; на origin mutual-TLS им нужен действительный клиентский сертификат.

2. Зарегистрируйте клиента

Зарегистрируйте клиента (веб или сервисного вида) с authMethod: tls_client_auth и estEnrollment: true. Сертификата у него пока нет. Subject его сертификата — это его mtlsAuth (например subject_dn) или, если он не задан, CN=<client>,OU=<tenant>,O=Versola. Subject никогда не берется из запроса клиента и не меняется при продлениях, поэтому регистрация остается прежней. estEnrollment нельзя сочетать с issueEdgeClientCertificate, enrollEdgeClientCertificate и сертификатом, который вы передаете сами. См. Клиент.

3. Создайте токен выдачи

curl -u central:<central secret> -X POST \
  "https://central.example.com/configuration/clients/est/enrollment-tokens?clientId=bank-a&validityHours=48"

В ответе есть token, его expiresAt и subject, который должен назвать запрос. Токен показывается один раз; хранится только его хеш. По умолчанию он живет 24 часа (validityHours до 168) и срабатывает один раз. Новый токен заменяет неиспользованные прежние токены клиента. Передайте токен и subject оператору клиента по защищенному каналу.

4. Клиент проходит выдачу

Клиент создает собственный ключ, формирует PKCS#10 ровно для того subject, который ему передали, и отправляет его на simpleenroll, передавая client id и токен как HTTP Basic (или токен как Bearer). OpenSSL перечисляет subject от самой общей части к самой частной, наоборот относительно строки RFC 4514, которую вам дали:

openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
  -keyout bank-a.key -subj "/O=Versola/OU=acme/CN=bank-a" -outform DER | base64 > request.b64

curl -sS -u "bank-a:<enrolment token>" \
  -H "Content-Type: application/pkcs10" --data-binary @request.b64 \
  -D headers.txt https://id.example.com/.well-known/est/simpleenroll > response.b64

base64 -d response.b64 | openssl pkcs7 -inform DER -print_certs -out chain.pem

Принимаются ключи EC P-256 или RSA не короче 2048 бит. В ответе certs-only PKCS#7 с цепочкой и без ключа; свой сертификат выберите по subject. Запрос, отклоненный с 400, токен не расходует. simpleenroll ограничен 30 попытками в минуту на client id и реплику (429, Retry-After). УЦ, которому доверять, берите из /.well-known/est/cacerts.

5. Клиент продлевает сертификат

Сертификат живет client-certificates.validity (по умолчанию 14 дней) и продлевается только в своей последней трети (client-identity.renew-window); раньше simplereenroll отвечает 403 и сообщает, когда окно откроется. Клиент отправляет новый PKCS#10 с новым ключом на origin mutual-TLS, предъявляя текущий сертификат:

openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
  -keyout bank-a.next.key -subj "/O=Versola/OU=acme/CN=bank-a" -outform DER | base64 > request.b64

curl -sS --cert bank-a.crt --key bank-a.key \
  -H "Content-Type: application/pkcs10" --data-binary @request.b64 \
  https://id-mtls.example.com/.well-known/est/simplereenroll > response.b64

Subject и альтернативные имена должны быть теми же, что у продлеваемого сертификата, а ключ должен отличаться от прежнего: клиент, чей HSM не умеет создавать новый ключ для каждого сертификата, этим профилем пользоваться не может. Запрос с атрибутом challengePassword (channel binding, RFC 7030 §3.5) отклоняется с 400; отправляйте запрос без него. Истекший сертификат сам собой не продлевается: создайте новый токен и пройдите выдачу заново.

Токены не следуют за сертификатом. Токен, выпущенный, когда клиент предъявлял старый сертификат, несет его отпечаток (cnf.x5t#S256) и с продленным не работает. Клиент продолжает предъявлять старый сертификат со старыми токенами, пока они не истекут, а новый использует для новых токенов. Старый сертификат остается действительным, а сертификат по умолчанию продлевается в последней трети жизни, поэтому на перекрытие в эту треть можно рассчитывать. Спланируйте переход так, чтобы новый сертификат использовался начиная со следующего запроса токена (Токены с привязкой к отправителю). Клиент также должен перечитывать новый ключ в свой TLS-стек без перезапуска.

6. Цепочки, продление цепочки и отзыв

Продления образуют цепочку, начатую при выдаче. По достижении client-identity.max-age (по умолчанию год) central отказывает в продлении (403), пока вы не продлите цепочку:

curl -u central:<central secret> -X POST \
  "https://central.example.com/configuration/clients/est/extend?clientId=bank-a"

При client-identity.auto-extend = true central делает это сам. Граница мягкая: сертификат, выпущенный незадолго до нее, живет весь свой срок, так что клиент может работать и после Versola-Chain-Ends-At, который есть в каждом ответе. GET /configuration/clients показывает est.pending, est.certificateExpiresAt и est.chainEndsAt.

Если ключ мог утечь:

curl -u central:<central secret> -X POST \
  "https://central.example.com/configuration/clients/est/revoke?clientId=bank-a"

Отзывается право продлевать, а не сам сертификат: каждый выпущенный до сих пор сертификат относится к поколению цепочки, и ни один не может продлиться (401). Повторная выдача с новым токеном начинает новое поколение. Сами сертификаты продолжают аутентифицировать до истечения; чтобы отрезать и их, смените subject в mtlsAuth клиента или удалите клиента. Сертификат, который заменило продление и который снова приходит продлеваться после 3-минутной отсрочки, означает, что ключ у двоих: цепочка закрывается (403, est_chain_fork_detected), и вы выдаете клиенту сертификат заново. Клиент, который больше не зарегистрирован для EST (estEnrollment выключен), при продлении получает 401.

За чем следить

ГдеЧто
КлиентДни до истечения, последнее продление, перечитывание сертификата после продления
Метрика authest_requests_total{operation,outcome}: рост forbidden, unauthorized или chain_fork
Метрика centralest_certificate_requests_total{operation,outcome}
Состояние клиентаest.certificateExpiresAt и est.chainEndsAt в GET /configuration/clients; настройте оповещение на клиента, чей сертификат в последней трети срока и не продлился

См. Наблюдаемость. Отзыв — это истечение: Versola не читает CRL и OCSP, поэтому украденный сертификат ограничивает короткий срок жизни.

Свой PKI

Если вы не используете EST, выпустите новый сертификат в своем УЦ с тем же subject, по которому клиент зарегистрирован, передайте его клиенту и пусть он запросит новые токены. В Versola ничего не меняется. Смена subject или УЦ не входит в EST: что не охвачено.

Что дальше