# Жизненный цикл клиентских сертификатов ## Репозиторий и ветка - Репозиторий: https://github.com/ochenstarik-ui/server-monitor-manager - Локальный клон: `C:\Users\Ochenstarik\projects\server-monitor-manager` - База: `main` @ `b11c277` - Ветка: `hermes/cert-lifecycle` Задание не пересекается по файлам с `b3r-fact-reconciliation` и выполняется параллельно в отдельном worktree. Затрагиваемые файлы (`CertificateAuthority.cs`, `CertificateLifecycleService.cs`, `AgentClient.cs`) веткой B-3R не изменяются. ## Зачем `CertificateAuthority.cs`, выдача клиентского сертификата: ```csharp var requestedNotAfter = DateTimeOffset.UtcNow.AddYears(1); ``` Срок — один год, автопродления нет ни у Agent, ни у Operator. Через год после развёртывания весь парк перестаёт проходить mTLS одновременно, и восстановление требует ручной перерегистрации каждого Node через одноразовый enrollment token. Процедуры ротации Control CA в проекте нет вообще. Отзыв работает через таблицу identity в Control (`ResolveIdentityAsync`), CRL не используется — это осознанное решение и менять его не нужно. ## Работы ### 1. Срок жизни - сократить срок клиентского сертификата до 30–90 дней; - вынести значение в `ControlOptions` с валидацией в общей цепочке `.Validate(...)` в `Program.cs`, по образцу `LinkReconciliationSeconds`; - значение по умолчанию в `appsettings.json` и в `control.env`, который пишет bootstrap; - срок сертификата не может превышать остаточный срок Control CA — существующая логика ограничения сохраняется. ### 2. Автопродление Agent - Agent проверяет остаточный срок собственного сертификата при старте и далее периодически; - при остатке меньше трети срока формирует новый CSR и запрашивает выдачу **по действующему mTLS-каналу**, без enrollment token; - новый эндпоинт выдачи требует действующий сертификат того же `node_id`, idempotency key и запись в audit; - замена `agent.pfx` атомарная: запись во временный файл с правами `0600`, затем rename; при любой ошибке остаётся действующий сертификат; - отозванный сертификат продлению не подлежит — запрос отклоняется, Node требует повторной регистрации. ### 3. Продление Operator-сертификата Desktop - Desktop проверяет остаточный срок при подключении к Control; - продление по тому же принципу, с явным уведомлением пользователя в интерфейсе; - защищённое хранение DPAPI сохраняется, приватный ключ не покидает устройство. ### 4. Уведомление об истечении - событие `certificate.expiring` в поток событий Control при остатке меньше трети срока; - поле остаточного срока в ответе `/api/v1/control/agents`; - отображение в Desktop на странице Servers. ### 5. Ротация Control CA Документ `docs/certificate-rotation.md`: - когда ротация требуется: компрометация, истечение, смена алгоритма; - порядок: выпуск нового CA, период двойного доверия, перевыпуск клиентских сертификатов, вывод старого CA; - что делает оператор руками и что делает bootstrap; - как проверить, что ни один Node не остался на старом CA; - почему ротация CA требует режима подтверждения `two_person` — см. `docs/approval-policies.md`. Реализация автоматической ротации в эту задачу не входит — только процедура и то, что нужно для её ручного выполнения. ## Тесты 1. Сертификат с остатком меньше трети срока продлевается; `agent.pfx` заменён, старый сертификат больше не используется. 2. Сертификат с достаточным остатком не продлевается — лишних запросов нет. 3. Hub недоступен в момент продления: Agent продолжает работать на действующем сертификате, повторяет попытку позже, метрики не теряются. 4. Отозванный сертификат продлить нельзя — запрос отклонён. 5. Запрос продления сертификатом другого `node_id` отклонён. 6. Замена `agent.pfx` прервана на середине: остаётся действующий сертификат, Agent поднимается. 7. Срок вне диапазона в конфигурации: Control не стартует, ошибка валидации внятная. ## Критерий приёмки - Node, проработавший дольше срока сертификата, продолжает работать без вмешательства оператора; - отзыв по-прежнему немедленно закрывает доступ; - существующие тесты `CertificateAuthorityTests` и сценарии перерегистрации зелёные без правок логики; - `docs/certificate-rotation.md` описывает выполнимую процедуру, а не намерение; - Control suite прогнан на Linux; - PR создан, CI зелёный; - независимый review получил полный текст этого задания. ## Границы - не трогать `LinkService.cs`, `LinkPolicyApplier.cs`, `LinkReconciliationBackgroundService.cs`, `ControlStore.cs` в части Links, `Provisioning.Helper/*` и `deploy/ochenstarik-smm-policy-apply` — они заняты заданием B-3R; - CRL не вводить; - автоматическую ротацию CA не реализовывать; - приватный ключ Control CA остаётся на Hub и в enrollment-коды не попадает. ## Отчётность Раздельно перечислить: что запущено локально, что в CI, что не запускалось и почему. Статус после merge — `merged / verified`.