Читать 6 мин

Ротация TLS-сертификатов требует двух доказательств

Ротация TLS-сертификатов завершена, лишь когда выдача совпала со свежей проверкой каждого рабочего адреса.

Ротация TLS-сертификатов требует двух доказательств

Центр сертификации может выдать именно тот сертификат, который запросил ИИ-агент, а сервис продолжит показывать старый. Я видел, как задания по ротации считали успехом скачанный PEM-файл, обновленный секрет или зеленый результат перезагрузки, а в самый неподходящий момент выяснялось, что один обработчик так и не переключился. Успешная выдача и успешное развертывание означают разное, поэтому для них нужны разные доказательства.

Надежная ротация записывает, что выдал CA, развертывает именно этот артефакт и открывает новое TLS-соединение, чтобы увидеть результат глазами клиента. В итоговом сравнении нужны стабильный идентификатор сертификата, нужное имя хоста и реальный сетевой путь. Пока агент не предъявил обе стороны сравнения, изменение не завершено.

Для завершения нужен явный договор о доказательствах

Ротация завершена, только если запись о выданном сертификате совпала с наблюдениями каждого адреса в области развертывания. Зафиксируйте это правило до того, как дадите агенту учетные данные или команды. Иначе он будет стремиться к тому, что его инструмент называет успехом, хотя это может быть лишь запись файла, а не видимое клиенту изменение.

Запись о выдаче должна содержать идентификатор заказа или запроса, запрошенные DNS-имена, серийный номер, отпечаток SHA-256, срок действия, издателя и хеш открытого ключа. Нужно также указать место хранения полной цепочки и закрытого ключа, не копируя ключ в журнал. Отпечаток идентифицирует весь конечный сертификат, а хеш открытого ключа показывает, сохранила ли повторная выдача прежний ключ.

Запись о развертывании отвечает на другие вопросы: какая цель получила артефакт, какая конфигурация на него ссылается, какой процесс принял перезагрузку и когда. Успешный ответ API управляющего контура относится сюда, но не доказывает состояние сервиса. Он подтверждает лишь то, что попытка изменения дошла до системы развертывания.

Запись наблюдения появляется из нового соединения после развертывания. В ней нужны запрошенное имя хоста, разрешенный или принудительно заданный адрес, порт, увиденные серийный номер и отпечаток, результат проверки, время и расположение проверяющего. Расположение важно: внутренний, внешний и находящийся за корпоративным прокси датчики могут попасть на разные точки завершения TLS.

Условие завершения должно быть механическим:

complete = issuance.valid
        && deployment.accepted
        && every(expected_endpoint,
                 observation.chain_valid
                 && observation.name_valid
                 && observation.leaf_fingerprint == issuance.leaf_fingerprint)

Так становится виден неудобный статус: сертификат существует, развертывание принято, но выдаваемый отпечаток пока отличается. Назовите его deployed_unverified, а не complete. Точные статусы не позволят панели или сводке агента незаметно слить два разных утверждения.

Выдача ACME не доказывает развертывание

ACME доказывает, что CA принял заказ и после требуемой авторизации выдал сертификат, но протокол не доказывает, что приложение его отдает. RFC 8555 описывает четыре основных шага клиента: отправить заказ, доказать контроль над идентификаторами, завершить заказ запросом на подпись, дождаться выдачи и скачать сертификат. Граница протокола заканчивается на получении.

Эту границу легко пропустить, потому что многие клиенты ACME после продления запускают обработчик развертывания. Он находится вне транзакции с CA и может сломаться из-за измененного пути, контейнера со старым секретом, перезагрузки не того процесса, асинхронно принятого обновления или недоступного узла. Заказ при всех этих сбоях остается действительным.

Сохраните итоговый объект заказа ACME или равнозначный ответ CA. Для ACME поля status: valid и заполненный URL сертификата доказывают выдачу. После разбора скачанного сертификата запишите идентификаторы и отпечаток конечного сертификата. Сам факт появления нового файла не подходит как идентификатор: временные файлы, символические ссылки и повторяемые имена дают слабое доказательство.

Certificate Transparency тоже не замыкает цепочку. Запись в журнале показывает, что публичный сертификат выдан для имени, но не говорит, отдает ли его балансировщик, ingress, веб-сервер или CDN. CT дает независимое доказательство выдачи, а не контроль развертывания.

Это различие меняет обработку ошибок. Сбой выдачи означает, что агент не получил нужные учетные данные. Сбой развертывания означает, что он получил чувствительные данные, которые уже могут храниться в системе, но не обслуживают запросы. Второй случай часто требует очистки, повтора или отката и отдельного оповещения.

Проверьте артефакт до передачи обработчику

До развертывания убедитесь, что конечный сертификат содержит запрошенные имена, ожидаемые срок и издателя, а его открытый ключ соответствует закрытому. Такая проверка находит поврежденные комплекты и неверные файлы без вмешательства в рабочую среду, но не заменяет живой запрос.

RFC 9525 четко формулирует правило идентичности: клиент независимо строит допустимые эталонные идентификаторы и сравнивает их с сертификатом. Для обычных DNS-сервисов имена находятся в расширении subjectAltName. RFC прямо запрещает откатываться к похожему на домен Common Name. Агент, проверяющий только subject=CN=..., может одобрить сертификат, который современные клиенты обязаны отклонить.

Проверьте конечный сертификат с реальными полями вывода:

openssl x509 -in leaf.pem -noout \
  -serial -fingerprint -sha256 -dates -issuer -subject \
  -ext subjectAltName

Типичный вывод выглядит так:

serial=03A17C...
sha256 Fingerprint=6B:19:8A:...
notBefore=Jul 24 08:15:00 2026 GMT
notAfter=Oct 22 08:14:59 2026 GMT
issuer=C=US, O=Example CA, CN=Example Issuing CA
subject=CN=api.example.net
X509v3 Subject Alternative Name:
    DNS:api.example.net, DNS:www.example.net

Если версия OpenSSL поддерживает, используйте openssl x509 -checkhost api.example.net -noout как автоматический барьер. Документация OpenSSL определяет -checkhost именно для сопоставления сертификата с хостом. Проверяйте код завершения, а не удобный текст, который меняется между версиями.

Сравнивайте открытые ключи, не раскрывая закрытые данные. Эти команды хешируют открытый ключ в DER с обеих сторон; одинаковые строки означают, что сертификат и закрытый ключ образуют пару:

openssl x509 -in leaf.pem -pubkey -noout \
  | openssl pkey -pubin -outform DER \
  | openssl dgst -sha256

openssl pkey -in private-key.pem -pubout -outform DER \
  | openssl dgst -sha256

Никогда не помещайте содержимое private-key.pem, трассировку команд или переменную среды с ключом в контекст агента. Пусть ограниченный исполнитель сравнит их и вернет хеш с кодом завершения. Агенту нужен результат действия, а не использованный секрет.

Успешную запись файла еще нужно активировать

Развертывание состоит из двух операций: атомарно разместить новые данные и заставить точку завершения TLS их загрузить. Агенты часто выполняют первую и предполагают вторую. Долго работающие серверы обычно держат разобранные сертификаты в памяти, поэтому замена файла на диске не обязательно меняет новые соединения.

Подготовьте файлы рядом с назначением, задайте владельца и строгие права, проверьте конфигурацию и затем переименуйте. Переименование в одной файловой системе не открывает частично записанный PEM. Сохраняйте прежние рабочие файлы или версию ссылки на секрет до живой проверки: немедленная очистка уничтожает самый быстрый откат.

Активация зависит от сервера. По документации nginx сигнал HUP заставляет главный процесс проверить конфигурацию, запустить новых работников и плавно завершить старых; при ошибке nginx продолжает использовать старую конфигурацию. Это сохраняет доступность, но создает идеальный ложный успех, если агент записал лишь отправку HUP. Плавный перезапуск Apache тоже проверяет синтаксис и запускает новое поколение, пока старые соединения завершаются. Управляемые балансировщики и CDN могут принять обновление и закончить позже.

Запишите команду активации или ID операции API, код завершения и идентичность целевого процесса. Затем проверьте специальный статус или журнал сервиса. Не превращайте фиксированную паузу в доказательство. Ожидание 30 секунд иногда скрывает eventual consistency, а иногда тратит 29 секунд; лучше опрашивать документированный статус до срока.

Сбой активации должен остановить процесс до того, как публичные повторы нагрузят сервис. При ошибке синтаксиса, неожиданной смене процесса или терминальной ошибке управляющего контура сохраните старый сертификат и отметьте развертывание неудачным. Если активация успешна, но адрес отдает старое, продолжайте ограниченные проверки: плавная замена и распределение требуют времени.

Проверьте то, что получает клиент, вместе с именем

Остановите ротацию при блокировке
Заблокированное хранилище отклоняет все действия выдачи и развертывания.

Решающая проверка открывает новое TLS-соединение, отправляет нужный Server Name Indication, проверяет цепочку и имя хоста и записывает конечный сертификат. Локальный файл не показывает клиентский результат. Повторно используемое keep-alive соединение тоже не подходит: его TLS-рукопожатие уже выбрало сертификат.

Используйте OpenSSL с SNI и проверкой имени:

openssl s_client \
  -connect api.example.net:443 \
  -servername api.example.net \
  -verify_hostname api.example.net \
  -verify_return_error \
  </dev/null 2>/dev/null \
| openssl x509 -noout -serial -fingerprint -sha256 -dates -issuer

Параметр -servername управляет SNI, который выбирает виртуальный хост на общих обработчиках. -verify_hostname проверяет идентичность, а -verify_return_error останавливает команду при ошибке вместо простой печати диагностики. -showcerts выводит присланную сервером цепочку, но документация предупреждает: сам список не означает, что цепочка проверена.

Не добавляйте -k, --insecure или аналог, чтобы автоматика прошла. Руководство curl говорит, что обычный TLS-запрос проверяет доверие CA и соответствие имени хоста. Небезопасный датчик может подтвердить отпечаток, пропустив разрыв цепочки или неверное имя, с которым столкнутся пользователи.

Одного отпечатка тоже мало. Сервер может показать нужный конечный сертификат с неполной промежуточной цепочкой, и часть клиентов откажет. Барьер завершения должен требовать правильный путь доверия, имя и ожидаемую идентичность. Если реальный клиент проверяет отзыв или политику CT, запустите этот профиль отдельно: базовый OpenSSL этого не покрывает.

Каждое наблюдение запускайте в новом процессе или явно запрещайте повторное использование соединения. При ошибке сохраняйте stderr, но очищайте его перед передачей агенту. Категории должны быть ясны: соединение, рукопожатие, доверие, идентичность или несовпадение отпечатка.

Каждая точка завершения TLS является отдельной целью

Имя хоста не задает область развертывания. Область включает все места, которые завершают TLS для этого имени: адреса балансировщиков, узлы CDN, реплики ingress, региональные входы, пути IPv4 и IPv6 и доступные исходные обработчики. Одно успешное соединение доказывает одну трассу в один момент.

Стройте список ожидаемых адресов из состояния инфраструктуры, а не из случайного ответа DNS. Динамический DNS и anycast мешают полной публичной выборке, поэтому задайте обоснованный набор: каждый настроенный обработчик или привязку сертификата плюс проверки из важных клиентских регионов. Если краем управляет поставщик, проверьте его статус и сделайте внешние выборки из нескольких мест.

Для проверки конкретного адреса --resolve в curl безопаснее замены имени на IP, потому что сохраняет исходное имя для SNI и проверки сертификата:

curl --fail --silent --show-error \
  --resolve api.example.net:443:192.0.2.18 \
  --output /dev/null \
  https://api.example.net/health

Повторите запрос для каждого адреса, включая IPv6 в квадратных скобках, где это поддерживается. Затем проверьте отпечаток OpenSSL по тому же адресу, сохранив -servername api.example.net. Успешный HTTP-ответ проверяет больше TLS, поэтому выбирайте дешевый адрес без изменений; наблюдение сертификата остается доказательством ротации.

Точно учитывайте прокси. Датчик на ноутбуке может видеть сертификат корпоративной TLS-инспекции. Датчик внутри кластера может обойти публичный CDN. Записывайте расположение и сетевой путь, отклоняйте наблюдение, если издатель или отпечаток показывает, что нужная точка не достигнута.

При постепенном обновлении ожидается смесь старых и новых отпечатков. Это допустимый переходный статус, но не основание завершить по большинству. Все цели должны сойтись, либо неисправную нужно явно вывести из обслуживания.

Агент должен выдавать сопоставимые записи

Посредничайте в вызовах API
Sallyport направляет bearer, basic и custom-header данные из хранилища.

ИИ-агент должен создавать структурированные доказательства, которые программа сравнит без разбора прозы. Фразы вроде «продление успешно, сайт выглядит нормально» стирают цель, время и расхождения. Оставьте объяснение людям, но переходы статуса привяжите к типизированным полям.

Компактная пара событий выглядит так:

{"type":"certificate.issued","rotation_id":"rot_7f2","order_id":"ord_91c","dns_names":["api.example.net"],"serial_hex":"03A17C","leaf_sha256":"6B:19:8A:...","spki_sha256":"9f4c...","not_before":"2026-07-24T08:15:00Z","not_after":"2026-10-22T08:14:59Z"}
{"type":"certificate.observed","rotation_id":"rot_7f2","host":"api.example.net","address":"192.0.2.18","port":443,"leaf_sha256":"6B:19:8A:...","chain_valid":true,"name_valid":true,"observed_at":"2026-07-24T08:19:22Z","vantage":"external-us-east"}

Один rotation_id должен охватывать выдачу, попытки развертывания, наблюдения, повторы, откат и завершение. ID заказа CA и операции инфраструктуры храните отдельно. Общий идентификатор задания мешает разбору инцидента.

У повторов должна быть своя семантика. Агент может потерять ответ после принятия заказа CA или привязки управляющим контуром. Слепой повтор создаст лишние сертификаты, лимиты или конкурирующие развертывания. Дайте каждому изменению идемпотентный токен из ID ротации и фазы, а до замены запросите удаленную операцию. Запишите, новая ли это операция или повтор существующей.

Свежесть тоже требует правила. Успешная проверка за час до развертывания ничего не доказывает о результате, даже если при прошлой попытке был тот же отпечаток. observed_at должен быть позже принятой активации, а при завершении нужен предельный возраст наблюдения. Используйте надежные часы процесса или проверяющего, не время из текста модели.

Разделяйте идентичность сертификата и сервиса. Отпечаток отвечает «это выданный конечный сертификат?», имя хоста отвечает «он подходит запрошенному сервису?», цепочка отвечает «строится ли приемлемый путь доверия?». Поле tls_ok уничтожает эти различия и вынуждает повторять проверку после исчезновения доказательств.

У списка адресов тоже должна быть ревизия. Если инфраструктура добавляет обработчик во время ротации, завершение обязано принять новую ревизию и потребовать наблюдение либо остаться на утвержденной старой и открыть последующую задачу. Незаметное чтение меняющегося списка создает гонку. Сохраните ревизию, чтобы восстановить смысл слова «все».

Сбор должен быть безопасно повторяемым. Используйте маршруты только для чтения, короткие тайм-ауты и ограниченную параллельность. Health URL, меняющий кеш или сеанс, не годится: TLS происходит до HTTP, поэтому хватит минимального запроса или прямого рукопожатия. Для тысяч адресов планируйте по цели и повторяйте только ошибки.

Наконец, разделите наблюдение и толкование. Датчик сообщает байты рукопожатия и результат проверки, а модуль сравнения решает, удовлетворяют ли они ротации. Тогда ошибку сравнения можно исправить и пересчитать сохраненные данные без доступа к рабочей среде, а агент не перепишет неудобное расхождение в успокаивающую сводку.

Модуль сравнения должен отклонять наблюдения до принятого развертывания, для неожиданного имени или адреса и из другой ротации. Он сравнивает нормализованные бинарные отпечатки, а не строки отображения, где различаются двоеточия и регистр. Исходную строку сохраните для оператора.

Не позволяйте агенту выбирать пропускаемые поля. Определите схему и запрещайте завершение при нехватке доказательств. Подписи укрепляют происхождение, но не исправляют слабое утверждение. Даже подписанная фраза «команда завершилась с нулем» не говорит, какой сертификат увидел клиент.

Сохраняйте и неудачные наблюдения. Другой отпечаток, просроченный сертификат или неверное имя объясняют открытый статус и отличают медленное распространение от неверной привязки. Замена ошибки итоговым успехом оставляет журнал без объяснения окна сбоя.

Знакомый сбой возникает при разделенном развертывании

Он начинается тихо. Агент получает сертификат для api.example.net, записывает заказ ACME действительным, обновляет api-tls и получает успех от API оркестрации. Увидев новую версию секрета, он закрывает заявку.

Публичное имя разрешается в два адреса балансировщика. Один контроллер следит за api-tls в рабочем пространстве имен и загружает новый сертификат. Второй обработчик ссылается на секрет с тем же именем в старом пространстве. Ни один API не сломался: агент обновил настоящий объект, но не каждый объект, управляющий именем.

Большинство пользователей попадает на первый адрес и видит новое. Другой путь отдает старый конечный сертификат. Ближе к окончанию срока ошибки выглядят случайными, а повтор словно помогает, потому что DNS или балансировщик выбирает другую цель. Операторы винят кеши, хотя TLS выбирает сертификат при каждом новом рукопожатии.

Два доказательства сразу раскрывают разделение. В записи выдачи стоит 6B:19:8A:..., а два адреса возвращают его и 41:D0:72:.... Ротация остается deployed_unverified, а несовпавшее наблюдение называет адрес для ремонта.

Популярный совет «посмотрите сертификат в браузере» слаб для автоматизации. Браузер может повторно использовать соединение, скрывать адрес, брать другое хранилище доверия или работать за инспекцией. Во время инцидента это полезная независимая проверка, но не условие завершения. Привязанные к адресу датчики с проверкой имени дают повторяемые доказательства.

Исправив старую привязку, снова проверьте оба адреса новыми соединениями. Сохраните первое расхождение, исправляющее развертывание и успешные наблюдения. История докажет, что изменилось и почему процесс закрылся.

Привилегированным действиям нужны узкие права

Подтверждайте каждое важное развертывание
Требуйте нажатие или Touch ID при каждом применении ключа развертывания.

Ротация открывает агенту API центра, хранилище секретов и пути перезагрузки, поэтому исполнитель должен предоставлять узкие действия, а не исходные учетные данные. Подходящие границы: «отправить этот CSR», «привязать артефакт X к обработчику Y», «проверить H через A». Многоразовый токен у модели повышает риск, но не улучшает рассуждения.

Результат каждого действия должен содержать ID удаленной операции и поля доказательств, но не учетные данные и не байты закрытого ключа. Разделите права на выдачу, развертывание, проверку и откат. Возможность запросить сертификат не должна автоматически разрешать привязать его повсюду.

Sallyport хранит учетные данные API и SSH в зашифрованном хранилище, а агент выполняет HTTP- и SSH-действия через приложение и не получает секреты. Журналы Activity и Sessions строятся из зашифрованного, связанного хешами журнала с записью вслепую. Он подходит для установления автора вызовов, но не заменяет наблюдение сертификата.

Ставьте ручное подтверждение там, где растет влияние. Обычное продление заранее объявленных имени и обработчика может работать с авторизацией сеанса, а новое имя, замена ключа, wildcard или откат требуют подтверждения каждого вызова. Подтверждение должно связывать показанные цель и отпечаток. «Разрешить обновление сертификата» слишком расплывчато.

Аудируйте проверяющего так же строго, как развертывающего. Если один неограниченный агент меняет список целей, развертывает и считает свою неполную выборку достаточной, договор декоративен. Вычисляйте цели из контролируемого источника и отклоняйте отсутствие наблюдений.

Закрывайте только после схождения и сохраняйте откат

Завершение должно детерминированно сравнивать неизменяемые события выдачи, развертывания и наблюдения. Не спрашивайте агента, «похоже ли все готовым». Когда все цели отдают выданный сертификат через действительную цепочку с верным именем, запишите результат условия и закройте ротацию.

Установите сроки распространения. В допустимом окне повторяйте только несошедшиеся цели с ограниченной задержкой. По истечении срока завершите ошибкой со списком целей. Откат зависит от срока и здоровья сервиса: старый сертификат с запасом может временно быть безопаснее, а просроченный или отозванный требует исправления вперед.

Откат несет ту же обязанность доказательства. Повторная привязка прежнего артефакта только попытка, и процесс должен увидеть старый отпечаток в сети до объявления восстановления. Если закрытый ключ скомпрометирован, возврат к нему не является восстановлением: выдайте новый ключ и отзовите старый по плану.

Храните оба сертификата, пока завершение соединений и политика не разрешат очистку. Плавная перезагрузка оставляет старых работников обслуживать существующие соединения, но новые датчики должны попасть в новое поколение. Удалите старые закрытые данные контролируемым путем и запишите очистку после завершения, а не как условие выдачи нового сертификата.

Первое изменение невелико, но строго: уберите «сертификат скачан» и «секрет обновлен» из конечных успешных статусов. С одной стороны требуйте выданный отпечаток, с другой свежие отпечатки с проверенным именем для полного списка целей. Агент может выполнить все шаги, но только совпавшие доказательства вправе объявить ротацию завершенной.

Вопросы и ответы

Что доказывает успешную ротацию TLS-сертификата?

Запись о выдаче и свежее сетевое наблюдение должны совпасть по конечному сертификату. Соединение также обязано проверить цепочку и имя для каждой точки в области.

Достаточно ли успешного продления ACME?

Нет. ACME доказывает, что CA после авторизации выдал сертификат. Он не доказывает, что сервер, ingress, балансировщик или CDN его загрузил и отдает.

Какое поле сертификата сравнивать автоматически?

Сравнивайте нормализованный отпечаток SHA-256 конечного сертификата, а серийный номер храните для людей. Хеш открытого ключа покажет, менялся ли ключ.

Как проверить сертификат на конкретном адресе балансировщика?

Подключитесь к адресу, сохранив имя для SNI и проверки идентичности. Используйте curl --resolve или OpenSSL -connect address:port -servername hostname -verify_hostname hostname.

Почему недостаточно файла сертификата на диске?

Процесс может держать старый сертификат в памяти, читать другой путь или завершать TLS в другом месте. Только новое рукопожатие по реальному пути показывает результат.

Нужно ли датчику отключать проверку TLS?

Нет. Он может найти нужный отпечаток и пропустить неверное имя или сломанную цепочку. Эти ошибки влияют на клиентов и должны блокировать завершение.

Нужно ли проверять каждый адрес CDN или балансировщика?

Проверьте каждую настроенную цель под вашим контролем и выборочно управляемые узлы из нужных регионов. Один DNS-ответ или узел не доказывает схождение.

Доказывает ли Certificate Transparency развертывание?

Нет. Он доказывает публичную выдачу и запись сертификата, но не наблюдает сертификат, который сервис показывает клиенту.

Когда автоматической ротации нужен откат?

Задайте срок и критерии, откатывайтесь лишь когда прежний сертификат приемлем и снижает риск. До объявления восстановления проверьте выдаваемый отпечаток.

Как ИИ-агенту обращаться с закрытым ключом?

Он должен запрашивать ограниченные действия, не получая ключ или многоразовые учетные данные. Для рассуждения достаточно отпечатков, ID, статусов и очищенных ошибок.

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov