Сертификаты
Модель
Section titled “Модель”Сертификат — самостоятельная сущность, а не поле ноды. Один сертификат (например 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-флоу
Section titled “ACME-флоу”Весь ACME выполняет backend; ноды и агенты про ACME не знают.
- Админ создаёт сертификат: домены + ACME-аккаунт + DNS-провайдер зоны.
- Backend проходит DNS-01: создаёт TXT
_acme-challenge.<домен>через API DNS-провайдера, дожидается валидации CA, удаляет TXT. - Полученный PEM сохраняется (ключ — зашифрованным),
expires_atпарсится из сертификата. - Ревизии всех нод с инбаундами этого сертификата инкрементируются; агенты получают новый PEM в составе desired state и раскладывают в файлы, Xray подхватывает без рестарта.
DNS-01 выбран как единственный ACME-механизм: HTTP-01 и TLS-ALPN-01 конфликтуют с Xray за порты 80/443 и не выдают wildcard. Плата — обязательный API-доступ к DNS зоны.
Ручная загрузка
Section titled “Ручная загрузка”kind = manual: админ вставляет PEM цепочку + ключ (купленный wildcard, внутренний CA). Backend только парсит expires_at и доставляет на ноды тем же путём. Автопродления нет — за 30 дней до истечения админ получает уведомление.
Продление
Section titled “Продление”Фоновый процесс (каждые 12 ч) перевыпускает ACME-сертификаты, у которых expires_at < now() + 30 дней, тем же флоу. При ошибке (отозванный DNS-токен, рейт-лимит CA):
- сертификат помечается
renewal_errorс текстом ошибки, админ уведомляется; - действующий PEM не трогается — инбаунды работают до фактического истечения;
- попытки повторяются каждые 12 ч.
Доставка
Section titled “Доставка”Сертификаты доставляются только как часть desired state ноды — отдельного RPC «обнови сертификат» нет. Нода получает ровно те сертификаты, на которые ссылаются её инбаунды. Смена сертификата у инбаунда, продление, замена — всё сводится к инкременту ревизии и обычному reconciliation.