CNN TürkMASTERCHEF ELEME ADAYLARI 24 EYLÜL: MasterChef'te dokunulmazlık oyununu kim kazandı?The Jerusalem PostKKL-JNF unveils archival photos showcasing Sukkot celebrations in honor of the holidayPunchEx-PDP, ADC supporters target 100,000 votes for Tinubu, YayiInquirerFarmer with ‘shabu’ caught at Ilocos Sur checkpointESPN Deportes¡En vivo! Tercera práctica en el GP de AzerbaiyánBollywood HungamaLove & War: First look of Alia Bhatt revealed; actress stuns in glamorous cabaret-inspired avatarUOLAnvisa proíbe propaganda da caneta emagrecedora Semavy após anúncios irregularesDaily MailThe Latin American street gang recruiting children in the UK: Feared 'Los Trinitarios' is now linked to three London knife killings... with machetes their weapon of choice to target victimsRTL BoulevardDakota Johnson noemt samenwerking met Taylor Swift 'geweldig'ESPNTransfer value tiers: Which clubs have the most valuable players?Globo EsporteAustrália x Brasil - Amistosos da Seleção Brasileira 2026 - Ao vivo - globoesporte.comPremium TimesCSOs demand disclosure of JBS $2.5bn Nigeria deal, warn of livestock expansion risks
The Daily Newsstand · Free, Always
Friday, September 25, 2026

Russian Trusted Root CA: почему ваш мониторинг сломан в 2026 году и как это починить за 5 минут

Translate

Привет, Хабр! На связи команда платформы внешнего мониторинга pingzen.dev.

Развивая наш сервис, мы ориентировались на классические стандарты веба: проверяли доступность сайтов, мерили скорость TLS-handshake, отправляли алерты админам, когда «всё упало». Но к нам начали приходить пользователи из РФ с одной и той же болью: «Ребята, ваш мониторинг отличный, но он в упор не видит наши сайты на сертификатах Минцифры».

При попытке проверить условный личный кабинет, завязанный на Госуслуги, ЕСИА или СМЭВ, наши распределенные чекеры падали с классической ошибкой:

curl: (60) SSL certificate problem: unable to get local issuer certificate

Или на уровне Python: ssl.SSLCertVerificationError: unable to get local issuer certificate.

Мы поняли, что поддержка национального удостоверяющего центра (Russian Trusted Root CA - НУЦ Минцифры) - это жесткая реальность российского ИТ-рынка. В этой статье мы разберем теорию PKI и цепочек доверия, покажем, как ломается стандартная валидация, и расскажем, как мы интегрировали поддержку российских корневых сертификатов в нашу распределенную систему мониторинга без ущерба для безопасности.

Часть 1. Как работает PKI и почему это важно

Public Key Infrastructure (PKI) - это фундамент современного веба. Каждый раз, когда вы открываете сайт по HTTPS, происходит сложный процесс проверки доверия.

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

Корневые сертификаты (Root CA) - это «якоря доверия». Они предустановлены везде: в браузерах, операционных системах, в библиотеках вроде Python-модуля ssl. Когда ваш клиент проверяет сертификат сайта, он строит путь от leaf-сертификата к одному из этих якорей. Если путь строится успешно - сайт доверенный, если нет - вы видите ошибку.

Международная экосистема строится на нескольких крупных удостоверяющих центрах: DigiCert, Let's Encrypt, GlobalSign, Sectigo. Их корни включены во все хранилища доверия по умолчанию - в Firefox, Chrome, Windows, macOS, Linux, Python-библиотеки и мобильные устройства.

Часть 2. Российская PKI экосистема и НУЦ Минцифры

До 2022 года российские организации спокойно использовали международные УЦ. Но затем начался массовый отзыв сертификатов у отечественных компаний: DigiCert начал отзывать сертификаты у банков и госкорпораций, GlobalSign прекратил выдачу новым клиентам, а Let's Encrypt перестал валидировать некоторые .ru домены. Это создало критическую уязвимость - государственные и коммерческие сервисы теряли возможность выпускать доверенные TLS-сертификаты.

В ответ на это Министерство цифрового развития, связи и массовых коммуникаций РФ создало Национальный Удостоверяющий Центр - Russian Trusted Root CA. Это полностью отдельная цепочка доверия со своим собственным корневым сертификатом.

Технически это стандартный RSA-4096 сертификат, валидный до 2032 года, но он не включен ни в одно международное хранилище доверия. Ни Mozilla, ни Apple, ни Microsoft его не признают.

Результат для мониторинга: когда наш чекер пытается подключиться к сайту на сертификате НУЦ, он получает сертификат, пытается построить путь к известному корню - и не может. Криптографически сертификат валиден, но у системы нет пути к trust anchor.

Часть 3. Архитектурные паттерны решения

Есть несколько классических подходов к такой проблеме, но у каждого есть критические недостатки.

  • Подход 1: Модификация системного хранилища доверия. Можно добавить Russian Trusted Root CA в /usr/local/share/ca-certificates/ на каждом сервере. Но это опасно: теперь все outbound TLS соединения будут доверять этому корню - OAuth-клиенты, вебхуки, сторонние API. Если кто-то получит контроль над НУЦ, он сможет выпустить легитимный (для вашей системы) сертификат на условный api.telegram.org, открывая окно для MITM-атак.

  • Подход 2: Локальный прокси-сервер. Между вашей системой мониторинга и целевым сайтом ставится Nginx с КриптоПро CSP, который занимается терминированием и переводом сертификатов. Минусы очевидны: искажается latency (вы мерите скорость до прокси, а не до сайта), появляется единая точка отказа, нужны лицензии СКЗИ.

  • Подход 3: Расширение доверия на уровне приложения. Это то, что выбрали мы в pingzen.dev. НУЦ используется строго для изолированных health-чекеров, вообще не затрагивая другие клиенты системы. Доверие изолировано, предсказуемо и контролируется версиями.

Часть 4. Двухпроходная верификация: концепция и код

Идея проста: сначала пробуем проверить сертификат с «нормальными» публичными корнями, а при отказе - делаем вторую попытку с расширенным набором (certifi + НУЦ). Но с важным архитектурным ограничением: фолбэк срабатывает только для российских доменов.

У НУЦ Минцифры нет встроенных ограничений по именам (Name Constraints), поэтому теоретически он может выдать сертификат на абсолютно любой домен в мире. Мы жестко ограничиваем фолбэк доменными зонами .ru, .su, .рф и их аналогами в punycode. Это полностью соответствует мировым практикам безопасности (например, когда Mozilla привязывает французский ANSSI строго к зоне .fr).

Пример реализации на Python

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

import ssl
import httpx
import certifi
import tempfile
import os
from pathlib import Path
from urllib.parse import urlparse

EXTRA_ANCHOR_TLDS = (".ru", ".su", ".рф", ".xn--p1ai")

def build_extended_bundle(extra_ca_path: str) -> str:
    """Генерирует временный файл с certifi + НУЦ."""
    fd, bundle_path = tempfile.mkstemp(prefix="extended-ca-", suffix=".pem")
    
    with os.fdopen(fd, "w") as out:
        # Сначала стандартный certifi
        out.write(Path(certifi.where()).read_text())
        # Затем добавляем НУЦ
        out.write(Path(extra_ca_path).read_text())
    
    return bundle_path

def is_russian_tld(url: str) -> bool:
    """Проверяет TLD gate для безопасности."""
    parsed = urlparse(url)
    hostname = parsed.hostname or ""
    hostname = hostname.strip().rstrip(".").lower()
    
    # Обработка IDN (internationalized domain names)
    try:
        ascii_host = hostname.encode("idna").decode("ascii")
    except (UnicodeError, ValueError):
        ascii_host = hostname
    
    candidates = {hostname, ascii_host}
    return any(h.endswith(tld) for h in candidates for tld in EXTRA_ANCHOR_TLDS)

async def check_with_two_pass_verification(url: str, nuc_path: str):
    """Двухпроходная проверка с фолбэком на НУЦ."""
    # Пасс 1: стандартные trust anchors
    try:
        async with httpx.AsyncClient(verify=certifi.where(), timeout=5.0) as client:
            response = await client.get(url)
            return response.status_code, "public_ca"
            
    except httpx.ConnectError as e:
        # Если упали не по ошибке валидации сертификата — пробрасываем дальше
        if not isinstance(e.__cause__, ssl.SSLCertVerificationError):
            raise
            
        # Пасс 2: активируем TLD Gate
        if not is_russian_tld(url):
            raise e  # Нероссийский домен — не доверяем НУЦ
        
        # Генерируем расширенный бандл и пробуем снова
        extended_bundle = build_extended_bundle(nuc_path)
        try:
            async with httpx.AsyncClient(verify=extended_bundle, timeout=5.0) as client:
                response = await client.get(url)
                return response.status_code, "russian_nuc"
        except httpx.ConnectError as fallback_err:
            raise fallback_err
        finally:
            # Удаляем временный файл
            os.unlink(extended_bundle)

Примечание: это пример реализации, адаптированный для статьи. В реальном проекте pingzen.dev логика интегрирована в http_checker.py и checker_utils.py с дополнительными оптимизациями и обработкой граничных случаев.

Критически важно: расширенный контекст используется только для health checks. OAuth клиенты, Telegram алерты, вебхуки продолжают использовать только публичные корни. Это изоляция доверия - критический элемент безопасности.

Часть 5. Реализация в pingzen

Внутри нашей платформы эта логика распределена между двумя ключевыми компонентами:

Генератор расширенного контекста: Функция в модуле checker_utils.py собирает динамический bundle из дефолтного пакета certifi и дополнительных доверенных сертификатов, хранящихся в изолированной директории /opt/pingzen/ca/. На его основе создается чистый ssl.SSLContext. Bundle кэшируется и пересобирается только при изменении сертификатов.

Retry-логика в HTTP-чекере: При перехвате исключения CERTIFICATE_VERIFY_FAILED  асинхронный воркер моментально валидирует TLD хоста. Если домен прошел проверку безопасности, инициируется второй проход с подгрузкой расширенного контекста.

При этом сохраняется жесткая изоляция доверия: все наши внутренние OAuth-клиенты, коннекторы баз данных, отправка Telegram-алертов и исходящие вебхуки продолжают использовать исключительно доверенные международные корни. НУЦ Минцифры не имеет к ним доступа ни при каких обстоятельствах.

Часть 6. Безопасность и TLD gate

TLD gate - это вопрос безопасности всей системы. Так как Russian Trusted Root CA технически способен подписать rogue-сертификат для любого мирового сервиса, без жесткого ограничения злоумышленник, получивший гипотетический доступ к мощностям НУЦ, мог бы устроить MITM-атаку на нашу платформу (например, перехватив трафик до API Telegram). Ограничение зоны гарантирует, что этого не произойдет. Для редких исключений (когда российская компания вынуждена использовать НУЦ-сертификат на домене .com), оператор может отключить gate вручную на уровне конкретного монитора, подписав явное согласие.

Еще один важный элемент - мы шипим только Root сертификат, намеренно исключая Intermediate CA certificates. Большинство корректно настроенных серверов отправляют промежуточные сертификаты сами в процессе TLS Handshake. Те, кто этого не делают, совершают ошибку конфигурации веб-сервера. Если бы мы слепо добавили промежуточные сертификаты в свой бандл, наш чекер маскировал бы эту ошибку, отдавая статус OK там, где реальный пользователь получил бы ошибку цепочки в браузере. С подходом root-only мы подсвечиваем реальный статус инфраструктуры.

Эксперты по информационной безопасности предлагают использовать nameConstraints - механизм X.509 который позволяет ограничить действие сертификата конкретными доменами. Это встроенная функция стандарта которую мы реализуем де-факто через TLD gate на уровне приложения, что дает нам больше гибкости и прозрачности чем модификация самого сертификата.

Часть 7. Распределенная архитектура pingzen.dev

PingZen - это полноценная распределенная система мониторинга. Архитектура построена на базе CheckWorker процессов, которые асинхронно забирают задачи из очередей Redis, выполняют health-checks и сохраняют результаты в PostgreSQL.

В процессе эксплуатации мы выделили три критически важных компонента производительности:

Connection Pooling: Мы используем пул соединений в httpx (до 200 одновременных коннектов и 50 keep-alive соединений с expiry в 60 секунд). Это позволяет переиспользовать TLS handshake между проверками одного хоста, минимизируя сетевой оверхед.

Multi-probe aggregation: Проверки выполняются параллельно из разных географических локаций (US, EU, RU). Результаты агрегируются по настраиваемым правилам:

  • ANY_FAILS - алерт если любой пров подтверждает N подряд failures

  • MAJORITY_FAILS - алерт если большинство провов fail

  • ALL_FAILS - алерт только если все провы fail

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

Cert Chain Inspector: Это изолированный внутренний сервис диагностики. Он выполняет live TLS handshake, извлекает всю цепочку сертификатов и подсвечивает проблемы: missing intermediate, expired certificates или hostname mismatch. Если на сервере пропущена промежуточная цепочка, инспектор парсит расширение Authority Information Access (AIA) и показывает точный URL, откуда ее нужно скачать для исправления.

Часть 8. Docker и deployment

Мы используем многоступенчатый Docker build. На этапе сборки образов сертификат Russian Trusted Root CA копируется в /opt/pingzen/ca/, а в окружении выставляется переменная PINGZEN_EXTRA_CA_DIR.

# Фрагмент деплоя воркеров
FROM python:3.11-slim-bookworm
COPY --from=certs_builder /etc/ssl/certs/russian_trusted_root.pem /opt/pingzen/ca/
ENV PINGZEN_EXTRA_CA_DIR=/opt/pingzen/ca

Бэкенд и центральный сервер управления (MCP) имеют этот траст включенным по умолчанию, однако в образах публичных чекеров он выключен. Если сторонний оператор разворачивает нашу приватную ноду (private location), он должен явно передать переменную окружения для активации поддержки НУЦ. Ни один публичный инструмент мониторинга (будь то Datadog или Grafana Probes) не должен включать локальные государственные CA по умолчанию — это железное правило хорошего тона в ИТ-безопасности.

Часть 9. Накладные расходы (Performance implications)

Двухпроходная верификация добавляет latency только в одном случае - когда первая проверка завершается неудачей.

  • Для обычных сайтов (публичный CA): overhead равен нулю, проверка завершается на первом пассе.

  • Для сайтов на НУЦ Минцифры: добавляется вторая попытка с расширенным контекстом, что суммарно дает микроскопические +5–10ms к общему времени проверки.

Потребление памяти стабильно: бандл certifi весит около 280 КБ, корень НУЦ - всего 2 КБ. Контексты кэшируются в рантайме FastAPI-приложения, так что оверхед на один запрос равен нулю. Валидация занимает менее 1ms CPU, а строковые операции для TLD gate практически бесплатны.

Часть 10. Операционные аспекты и Troubleshooting

Корневой сертификат НУЦ Минцифры валиден до 2032 года, поэтому в ближайшие годы плановая ротация файлов конфигурации нам не грозит. Для защиты от случайной или несанкционированной подмены файла в CI/CD пайплайнах мы жестко пингуем SHA-256 отпечаток сертификата в юнит-тестах: при попытке подменить файл сборка моментально упадет.

Все случаи использования расширенного контекста прозрачно логируются. Траблшутинг инцидентов тривиален: при возникновении ошибок мы проверяем issuer в выводе OpenSSL, валидируем доменную зону через TLD gate и проверяем переменные окружения воркера.

Часть 11. ГОСТ TLS - это совершенно другая задача

Важно провести жирную черту: поддержка НУЦ Минцифры - это НЕ ГОСТ TLS. Это две принципиально разные технические проблемы, которые часто путают.

Большинство российских сайтов на сертификатах НУЦ используют стандартные, общемировые шифры RSA/AES. Проблема заключается только в доверии к выпускающему центру (УЦ).

ГОСТ TLS - это когда сервер требует использования российских шифронаборов (Kuznyechik, Magma, Стрибог) вместо AES/SHA. Такие серверы рвут рукопожатие еще до этапа обмена сертификатами, если клиент заявляет поддержку только стандартного AES. Для поддержки ГОСТ TLS внутри Python требуется разворачивать полноценный gost-engine / provider для OpenSSL 3.x, настраивать низкоуровневые конфиги и патчить контексты на C-уровне. Это узкая, специфическая ниша (госсектор), и архитектурно в pingzen.dev эта задача вынесена в отдельный тип проверок.

Часть 12. Заключение

Интеграция Russian Trusted Root CA в распределенную экосистему заставила нас глубоко погрузиться в PKI-архитектуру. Но результатом мы довольны: нам удалось построить предсказуемую, безопасную и производительную модель.

Что это позволило:

  • Российские организации получают надежный мониторинг НУЦ-сайтов

  • Безопасность остальных TLS-соединений не компрометируется

  • Эксплуатационные затраты минимальны

  • Точные метрики задержки до реального endpoint без искажений

В отличие от конкурентов:

  • Zabbix/Uptime Kuma: НУЦ не поддерживается → manual fixes

  • Datadog/New Relic: enterprise tier only, $1000+/месяц

  • PingZen: включено по умолчанию для .ru доменов, zero configuration

Если вы устали вручную прокидывать бандлы сертификатов на ноды мониторинга, воевать с костыльными прокси-серверами и ловить ложные алерты - делегируйте это нам. Регистрируйтесь на pingzen.dev, добавляйте свой .ru сайт и спите спокойно.

Будем рады обсудить нюансы PKI и двухпроходной верификации в комментариях. Поделитесь вашим опытом: как вы дружите свои мониторинговые системы с национальными удостоверяющими центрами?

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.