fix(hermes): доверять внутреннему CA — иначе OIDC-discovery падает на CERTIFICATE_VERIFY_FAILED #76

Merged
hermes merged 1 commit from refs/pull/76/head into main 2026-09-20 19:24:01 +00:00
hermes commented 2026-09-20 19:18:35 +00:00 (Migrated from git.grachevko.ru)

Симптом

Попытка входа в дашборд:

{"detail":"Provider unreachable: OIDC discovery unreachable: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1029)"}

Причина

Провайдер self-hosted в дашборде делает discovery к https://sso.${SECRET_DOMAIN}/.well-known/openid-configuration (httpx), а этот хост отдаёт envoy-internal с сертификатом от нашего internal-ca. В образе системного стора такого CA нет, verify=True не проходит.

Проверено: /opt/data/ca-internal-bundle.pem (151 сертификат из тома) проверяет цепочку sso.grachevko.ru (issuer листа = internal-ca), и discovery через него отдаёт issuer: https://sso.grachevko.ru, authorization_endpoint: .../oauth/v2/authorize.

Почему не «ходить на внутрикластерный http-адрес» (приём из references/oidc-internal-provider.md для Envoy-провайдера): плагин дашборда запрещает такое явно — plugins/dashboard_auth/self_hosted/__init__.py:62-64, _require_https_or_loopback требует https:// (или http только на localhost), и iss пинится к discovery-issuer'у. Вариант с внутренним http-адресом здесь недостижим.

Правка

Три переменные среды в контейнер (и те же три — в .env тома, чтобы совпадало независимо от того, кто из них выигрывает):

SSL_CERT_FILE: /opt/data/ca-internal-bundle.pem
REQUESTS_CA_BUNDLE: /opt/data/ca-internal-bundle.pem
CURL_CA_BUNDLE: /opt/data/ca-internal-bundle.pem

SSL_CERT_FILE — точнo тот рычаг, который читает httpx при verify=True (httpx/_config.py:33-37), то есть именно клиент, которым ходит плагин. Побочно закрывает остальные https-пути агента (requests/curl) к внутренним адресам.

Ограничение, которое надо знать

Бандл — копия в томе, а internal-ca ротируется (~90 дней по references/oidc-internal-provider.md). После ротации файл устареет и вход снова упадёт с той же ошибкой. Правильное решение — trust-manager: Bundle с внутренним CA → ConfigMap в ns grachevko, монтировать в под read-only и указывать SSL_CERT_FILE на его ca.crt. В скилле рецепт есть, но trust-manager в кластере ещё не стоит — предлагаю отдельной задачей, а сейчас закрыть вход копией из тома.

Проверка после мёржа: под пересоздаётся (переменные среды меняют подшаблон), в логе dashboard-auth-self-hosted: registered provider (issuer=https://sso.grachevko.ru, client_id=391638025492299835, scopes='openid profile email', confidential=False), попытка входа больше не отдаёт CERTIFICATE_VERIFY_FAILED.

## Симптом Попытка входа в дашборд: ```json {"detail":"Provider unreachable: OIDC discovery unreachable: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1029)"} ``` ## Причина Провайдер `self-hosted` в дашборде делает discovery к `https://sso.${SECRET_DOMAIN}/.well-known/openid-configuration` (httpx), а этот хост отдаёт `envoy-internal` с сертификатом от нашего `internal-ca`. В образе системного стора такого CA нет, `verify=True` не проходит. Проверено: `/opt/data/ca-internal-bundle.pem` (151 сертификат из тома) проверяет цепочку `sso.grachevko.ru` (issuer листа = `internal-ca`), и discovery через него отдаёт `issuer: https://sso.grachevko.ru`, `authorization_endpoint: .../oauth/v2/authorize`. Почему не «ходить на внутрикластерный http-адрес» (приём из `references/oidc-internal-provider.md` для Envoy-провайдера): плагин дашборда запрещает такое явно — `plugins/dashboard_auth/self_hosted/__init__.py:62-64`, `_require_https_or_loopback` требует `https://` (или http только на localhost), и `iss` пинится к discovery-issuer'у. Вариант с внутренним http-адресом здесь недостижим. ## Правка Три переменные среды в контейнер (и те же три — в `.env` тома, чтобы совпадало независимо от того, кто из них выигрывает): ```yaml SSL_CERT_FILE: /opt/data/ca-internal-bundle.pem REQUESTS_CA_BUNDLE: /opt/data/ca-internal-bundle.pem CURL_CA_BUNDLE: /opt/data/ca-internal-bundle.pem ``` `SSL_CERT_FILE` — точнo тот рычаг, который читает httpx при `verify=True` (`httpx/_config.py:33-37`), то есть именно клиент, которым ходит плагин. Побочно закрывает остальные https-пути агента (requests/curl) к внутренним адресам. ## Ограничение, которое надо знать Бандл — копия в томе, а `internal-ca` ротируется (~90 дней по `references/oidc-internal-provider.md`). После ротации файл устареет и вход снова упадёт с той же ошибкой. Правильное решение — trust-manager: `Bundle` с внутренним CA → ConfigMap в ns `grachevko`, монтировать в под read-only и указывать `SSL_CERT_FILE` на его `ca.crt`. В скилле рецепт есть, но trust-manager в кластере ещё не стоит — предлагаю отдельной задачей, а сейчас закрыть вход копией из тома. Проверка после мёржа: под пересоздаётся (переменные среды меняют подшаблон), в логе `dashboard-auth-self-hosted: registered provider (issuer=https://sso.grachevko.ru, client_id=391638025492299835, scopes='openid profile email', confidential=False)`, попытка входа больше не отдаёт `CERTIFICATE_VERIFY_FAILED`.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
grachevko/home-ops!76
No description provided.