TLS-RPT: как узнать о тихих сбоях TLS при доставке к вам
MTA-STS в режиме enforce и DANE ломаются молча: криво провернули ротацию сертификата на MX — и отправители по всему миру отбивают письма к вам, а вы узнаёте об этом последним. TLS-RPT (RFC 8460) — единственный обратный канал, который сообщает, что чужие MTA не могут дойти до вас по защищённому каналу, и, главное, почему. Разбираем приём и парсинг отчётов и декодер result-type.
EvilMail Team17 июля 2026 г.11 мин чтения
Вы полгода назад выкатили MTA-STS в режиме enforce и опубликовали TLSA-записи для DANE. Всё работало. Потом ночью автоматика провернула ротацию TLS-сертификата на MX — Let's Encrypt, обычное дело, — а TLSA-хэш 3 1 1 в зоне остался старым. С этой секунды каждый отправитель, который проверяет DANE (а это Gmail для доменов под DNSSEC, немецкие провайдеры, куча корпоративных релеев), при попытке доставки к вам получает несовпадение сертификата и отбивает письмо. Клиент не получает счёт. Партнёр не получает договор. А у вас на мониторинге — тишина: MX слушает 25-й порт, Postfix жив, диск не забит, аптайм зелёный.
В этом и есть ловушка входящего TLS: у него нет собственного bounce, который лёг бы вам на стол. Ошибку видит отправитель — в своём логе, в своей отложенной очереди. Вам оттуда ничего не прилетает. Вы узнаёте о собственном сбое последним — когда разозлённый клиент позвонит и спросит, почему письмо «не дошло, хотя вы вроде большая почтовая компания». TLS-RPT (RFC 8460, SMTP TLS Reporting) закрывает ровно эту дыру: это единственный канал, по которому чужие MTA сообщают вам, что не смогли дойти до ваших MX по защищённому каналу — и почему именно. Сразу отделю его от DMARC rua: да, там тоже rua=mailto:, но DMARC-агрегаты про SPF/DKIM/выравнивание домена, а TLS-RPT — про транспортный TLS к вашим MX. Их постоянно путают; это разные отчёты о разных вещах.
Что такое TLS-RPT и кто реально шлёт отчёты
TLS-RPT: приём и разбор отчётов о сбоях TLS (RFC 8460) — EvilMail Blog
TLS-RPT — это механизм агрегированной суточной отчётности. Отчёт формирует отправляющая сторона по итогам своих попыток установить STARTTLS, применить политику MTA-STS и проверить DANE к вашим MX за прошедшие сутки. Раз в день она упаковывает статистику в JSON (gzip) и шлёт по адресу, который вы указали в DNS. Успешные сессии тоже попадают в отчёт — это ваша базовая линия, без неё вы не отличите «никто не пишет» от «все отбиваются».
Кто действительно шлёт отчёты по состоянию на 2026 год: Google/Gmail, Microsoft (Outlook.com и Exchange Online), Yahoo, Comcast, LinkedIn и крупные ESP. Этого набора хватает, чтобы поймать почти любой системный сбой — если у вас что-то сломалось для всех, Gmail сообщит об этом в течение суток.
Чего TLS-RPT не даёт: это не realtime — задержка до 24, иногда 48 часов; это не про содержимое писем (в отчёте нет ни темы, ни отправителей конкретных сообщений, только транспортная статистика по MX-хостам и IP); и это не замена мониторингу порта — он рассказывает про TLS, а не про то, жив ли SMTP вообще.
Публикуем запись, чтобы отчёты пошли
Одна TXT-запись на служебном поддомене:
dns
_smtp._tls.evilmail.pro. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]"
Разбор rua. Значение — куда слать отчёты. Два варианта: mailto: (отчёт приходит письмом с вложением) и https: (отправитель делает POST JSON на ваш endpoint, rua=https://tlsrpt.example.net/ingest). Можно указать несколько адресов через запятую и даже смешивать mailto с https — тогда отчёт уйдёт по всем.
Подводные камни, на которых спотыкаются:
Не путайте поддомены. Запись живёт на _smtp._tls, а политика MTA-STS — на _mta-sts. Это разные имена, разные назначения. Опубликовать TLS-RPT под _mta-sts — классическая ошибка «а почему отчёты не идут».
Для `mailto:` нужен реально работающий ящик, который переживёт объём. Крупный домен получает десятки отчётов в сутки; заводите отдельный tlsrpt@, а не личную почту админа.
TLS-RPT самодостаточен. Он работает, даже если у вас нет ни MTA-STS, ни DANE. Тогда в отчётах будет policy-type: no-policy-found, но сбои всё равно видны — например, если STARTTLS вообще не поднялся из-за вырезающего его middlebox.
TLS-RPT ничего не включает. Он только репортинг, без enforcement. Публиковать его абсолютно безопасно — он физически не может сломать доставку, только рассказать о сбоях. Поэтому его ставят первым, ещё до MTA-STS.
Как выглядит отчёт изнутри
Анатомия JSON. Верхний уровень: organization-name, date-range (start-datetime/end-datetime в UTC, обычно окно 24 часа), contact-info, report-id. Дальше массив policies[] — по объекту на каждую применённую политику. Внутри каждого:
Читать отчёт нужно в таком порядке: сначала соотношение success/fail по каждому mx-host (здесь 1841 против 63 — что-то сломалось для части отправителей, но не для всех), потом проваливаться в failure-details и смотреть result-type. Тип sts-webpki-invalid при 63 провалах — это сигнал, что для части сессий сертификат MX не прошёл валидацию по WebPKI в режиме enforce.
Приём и разбор: свой ящик или внешний процессор
По почте отчёт приходит MIME-вложением с Content-Type: application/tlsrpt+gzip (иногда просто application/gzip), тема письма — Report Domain: evilmail.pro Submitter: google.com Report-ID: <...>. По HTTPS тело POST имеет тип application/tlsrpt+json.
Свой парсинг — три шага: вытащить вложение, gunzip, скормить в jq или скрипт. Быстрый однострочник для просмотра распакованного отчёта — строим массив полей и выводим табом:
А это рабочий сборщик, который забирает отчёты прямо из ящика tlsrpt@ и агрегирует сбои по result-type:
python
import imaplib, email, gzip, json, collections
M = imaplib.IMAP4_SSL("mail.evilmail.pro")
M.login("[email protected]", "***")
M.select("INBOX")
_, ids = M.search(None, "UNSEEN")
fails = collections.Counter()
by_mx = collections.Counter()
for num in ids[0].split():
_, data = M.fetch(num, "(RFC822)")
msg = email.message_from_bytes(data[0][1])
for part in msg.walk():
ct = part.get_content_type()
if ct in ("application/tlsrpt+gzip", "application/gzip"):
report = json.loads(gzip.decompress(part.get_payload(decode=True)))
for p in report["policies"]:
mx = p["policy"].get("mx-host", "?")
by_mx[mx] += p["summary"].get("total-failure-session-count", 0)
for fd in p.get("failure-details", []):
fails[fd["result-type"]] += fd.get("failed-session-count", 0)
for rt, n in fails.most_common():
print(f"{n:>6} {rt}")
print("--- по mx-host ---")
for mx, n in by_mx.most_common():
print(f"{n:>6} {mx}")
Когда не городить своё: если не хотите держать парсер и алертинг, есть готовые процессоры — Mailhardener, URIports, Report URI, EasyDMARC. Они дедуплицируют отчёты, строят тренды и шлют алерты. Практический совет: даже с внешним сервисом оставьте mailto: на своём домене вторым адресом в rua, чтобы всегда иметь сырьё под рукой и не зависеть от чужого парсинга.
Декодер: result-type → что сломано → где чинить
Это ядро всей темы. result-type — не абстрактный код ошибки, а точный указатель на то, какой именно слой у вас сломан.
starttls-not-supported — STARTTLS вырезан middlebox/файрволом «для инспекции» или отключён на MX. Чинится в конфиге MTA и на сетевом оборудовании.
certificate-host-mismatch — в сертификате MX нет SAN на имя MX-хоста. Перевыпуск сертификата с правильным subjectAltName.
certificate-expired / certificate-not-trusted / validation-failure — протух, самоподписан или отдаёте неполную цепочку. Проверяйте выпуск и что MTA отдаёт fullchain, а не только листовой сертификат.
sts-policy-fetch-error — отправитель не смог скачать https://mta-sts.<домен>/.well-known/mta-sts.txt. Чаще всего протух сертификат именно на mta-sts-поддомене (не на MX!). При этом почта на MX ходит, и обычный мониторинг молчит.
sts-policy-invalid — файл политики скачался, но синтаксис битый. Проверяйте mta-sts.txt.
sts-webpki-invalid — в режиме enforce сертификат MX не проходит валидацию по WebPKI. Как в примере выше.
tlsa-invalid / dane-required / dnssec-invalid — TLSA не совпал с предъявленным сертификатом, требуется DANE но нет TLSA, или сломан DNSSEC. Правится в TLSA-записи и зоне DNSSEC.
Три аварии, которые ловит только TLS-RPT
1. Ротация MX-сертификата без обновления TLSA. С той самой истории из начала статьи. Симптом — лавина tlsa-invalid, но только от DANE-отправителей (остальным всё равно). Проверяем запись и пересчитываем хэш из живого сертификата:
Если хэш из dig не равен хэшу из openssl — вот вам источник tlsa-invalid. Обновите TLSA. И раз навсегда: делайте roll-over двумя записями 3 1 1 (старая + новая одновременно) за max(TTL)до смены сертификата, а старую убирайте после.
2. Протух сертификат на mta-sts-поддомене.sts-policy-fetch-error растёт, а сама почта на MX ходит нормально — потому что это два разных сертификата на двух разных хостах. Мониторинг MX зелёный, а MTA-STS-политика недоступна:
Запись _smtp._tls опубликована и dig +short TXT _smtp._tls.evilmail.pro её отдаёт.
Ящик из rua реально существует, мониторится и переживёт объём.
TLS-RPT включён раньше, чем MTA-STS переведён в enforce, — сначала обратный канал, потом жёсткая политика.
Сертификат mta-sts-поддомена в том же alerting, что и сертификат MX.
При ротации сертификата TLSA обновляется в одном плейбуке: roll-over 3 1 1 двумя записями, max_age MTA-STS держим 604800 (7 дней) и больше.
Раз в неделю смотрим соотношение fail/success по каждому mx-host.
Настроен алерт на любой ненулевой sts-webpki-invalid и tlsa-invalid.
TLS-RPT ничего не чинит сам. Но без него вы узнаёте о собственных сбоях от разозлённого клиента, а с ним — из суточного отчёта Google, ещё до того как отвалится второй отправитель.