Skip to content

Сертификаты

Сертификат — самостоятельная сущность, а не поле ноды. Один сертификат (например wildcard *.example.com) привязывается к любому числу TLS-инбаундов на любых нодах.

flowchart LR
    DP[DNS-провайдер<br/>Cloudflare · зона example.com] --> CERT["Сертификат<br/>*.example.com"]
    CERT --> I1["Инбаунд NL-1:443"]
    CERT --> I2["Инбаунд DE-1:443"]
    CERT --> I3["Инбаунд DE-1:8443"]
Сущность Поля
certificates domains[], kind (acme | manual), acme_account_id (для acme), dns_provider_id (для acme), цепочка PEM, 🔒 приватный ключ, expires_at
dns_providers type (cloudflare, route53, …), zone, 🔒 креденшелы API
acme_accounts directory_url (Let’s Encrypt / ZeroSSL / свой CA), 🔒 ключ аккаунта, 🔒 EAB (если требуется CA)

acme_accounts отделён от сертификатов: ZeroSSL и корпоративные CA требуют EAB-привязку на уровне аккаунта, один аккаунт обслуживает все сертификаты своего CA. dns_providers отделён от сертификатов: один API-токен зоны обслуживает все домены в ней.

Инбаунд с security = tls обязан ссылаться на сертификат, покрывающий его домен (certificate_id, проверяется при создании). Инбаунды с Reality сертификатов не используют.

Весь ACME выполняет backend; ноды и агенты про ACME не знают.

  1. Админ создаёт сертификат: домены + ACME-аккаунт + DNS-провайдер зоны.
  2. Backend проходит DNS-01: создаёт TXT _acme-challenge.<домен> через API DNS-провайдера, дожидается валидации CA, удаляет TXT.
  3. Полученный PEM сохраняется (ключ — зашифрованным), expires_at парсится из сертификата.
  4. Ревизии всех нод с инбаундами этого сертификата инкрементируются; агенты получают новый PEM в составе desired state и раскладывают в файлы, Xray подхватывает без рестарта.

DNS-01 выбран как единственный ACME-механизм: HTTP-01 и TLS-ALPN-01 конфликтуют с Xray за порты 80/443 и не выдают wildcard. Плата — обязательный API-доступ к DNS зоны.

kind = manual: админ вставляет PEM цепочку + ключ (купленный wildcard, внутренний CA). Backend только парсит expires_at и доставляет на ноды тем же путём. Автопродления нет — за 30 дней до истечения админ получает уведомление.

Фоновый процесс (каждые 12 ч) перевыпускает ACME-сертификаты, у которых expires_at < now() + 30 дней, тем же флоу. При ошибке (отозванный DNS-токен, рейт-лимит CA):

  • сертификат помечается renewal_error с текстом ошибки, админ уведомляется;
  • действующий PEM не трогается — инбаунды работают до фактического истечения;
  • попытки повторяются каждые 12 ч.

Сертификаты доставляются только как часть desired state ноды — отдельного RPC «обнови сертификат» нет. Нода получает ровно те сертификаты, на которые ссылаются её инбаунды. Смена сертификата у инбаунда, продление, замена — всё сводится к инкременту ревизии и обычному reconciliation.