DNSSEC для почтового домена: фундамент DANE, страховка MTA-STS и как не отстрелить себе резолвинг
TLSA-запись в неподписанной зоне — мусор: отправляющий MTA её проигнорирует, потому что не сможет доказать подлинность через AD-бит. Разбираем, почему DNSSEC — неделимый контур с DANE, как подписать зону ECDSA P-256 и залить DS в правильном порядке, и где именно ротация ключа или просроченная RRSIG кладут весь домен в SERVFAIL.
EvilMail Team19 июля 2026 г.14 мин чтения
Почему без DNSSEC ваши TLSA-записи — просто мусор в зоне
STARTTLS в SMTP — оппортунистическое шифрование. Отправляющий сервер видит в ответе на EHLO строку 250-STARTTLS, поднимает TLS и шлёт письмо зашифрованным. Проблема в слове «видит». Активный атакующий на пути (BGP-хайджек, скомпрометированный роутер провайдера, злой транзитный оператор) просто вырезает строку 250-STARTTLS из ответа. Ваш MTA не увидит анонса, решит, что приёмник TLS не умеет, и отправит письмо открытым текстом. Это называется STARTTLS-stripping, и это не теория — так утекали письма целых национальных операторов.
Есть ровно два механизма, которые заставляют отправителя требовать TLS и не откатываться в plaintext: DANE и MTA-STS. И вот здесь начинается главное непонимание. DANE публикует отпечаток сертификата приёмника в DNS — в записи TLSA. Но доверять DNS-ответу можно только тогда, когда он криптографически подписан. Иначе тот же атакующий, что вырезает STARTTLS, подделает и TLSA-ответ.
DNSSEC для почты: DANE, MTA-STS и как не сломать резолвинг | EvilMail — EvilMail Blog
Отсюда прямая, неразрывная зависимость: нет валидной цепочки DNSSEC — отправляющий MTA не увидит AD-бит в DNS-ответе — TLSA не применяется — вся ваша DANE-защита фиктивна. TLSA-запись в неподписанной зоне резолвер честно отдаст, но валидирующий MTA пометит её как недоверенную (AD=0) и откатится к обычному оппортунистическому TLS. Вы думаете, что защищены. Вы — нет.
Сразу разведём два контура, потому что их часто путают:
DANE требует DNSSEC. Без подписанной зоны и без DS у регистратора он не работает вообще.
MTA-STS не требует DNSSEC. Его доверие строится на WebPKI — валидном HTTPS-сертификате хоста mta-sts.<domain>. DNSSEC ему желателен, но не обязателен.
Именно поэтому взрослая почтовая инфраструктура держит оба контура. Ниже — как собрать DANE правильно и как MTA-STS страхует вас в тот день, когда DNSSEC у вас развалится (а он развалится).
Как это работает: цепочка доверия от корня до TLSA
DNSSEC не шифрует DNS. Он его подписывает. Каждый набор записей (RRset) в зоне сопровождается подписью RRSIG. Подписи проверяются публичным ключом из записи DNSKEY. На практике ключей два: KSK (Key Signing Key, флаг 257) подписывает только DNSKEY RRset, а ZSK (Zone Signing Key, флаг 256) подписывает все остальные записи. Разделение нужно, чтобы часто ротируемый ZSK не требовал трогать родительскую зону.
А доверять самому DNSKEY заставляет запись DS — хеш вашего KSK, лежащий в родительской зоне у регистратора. Родитель подписывает DS своим ключом, его DS лежит выше, и так до корня, чей ключ прошит в каждом резолвере. Получается цепочка: корень . → .pro → evilmail.pro. На каждом шаге валидирующий резолвер проверяет DS → DNSKEY и идёт ниже. Отсутствие записей (например, что поддомена нет) доказывается NSEC/NSEC3-записями — тоже подписанными.
Когда вся цепочка сошлась, резолвер ставит в ответе флаг AD (Authenticated Data). Вот этот бит и есть весь смысл. Postfix при smtp_tls_security_level = dane смотрит именно на него.
Критично понимать: DANE-валидация в Postfix полагается на локальный валидирующий резолвер, а не на 8.8.8.8. Публичные резолверы AD-бит вам вернут, но по пути к вашему MTA его может снять любой промежуточный форвардер, и доверять ему всё равно нельзя — вы же не проверяете подпись сами. Поэтому на почтовом сервере ставится свой unbound.
Подписываем зону: BIND, Knot или managed DNS
Забудьте про ручной dnssec-signzone из старых мануалов — сегодня подписание автоматическое. Возьмите ECDSA P-256 (алгоритм 13), а не RSA. Причина не в стойкости, а в размере: подпись ECDSA ~64 байта против ~256 у RSA-2048, а размер ответа DNSKEY+RRSIG напрямую влияет на UDP-фрагментацию, о которой ниже.
BIND9 с dnssec-policy — минимум телодвижений. В named.conf.local:
text
zone "evilmail.pro" {
type master;
file "/var/lib/bind/db.evilmail.pro.signed";
dnssec-policy default;
inline-signing yes;
};
Политика default уже использует ECDSA P-256, генерирует ключи, подписывает зону инлайн (исходный файл зоны не трогается) и обслуживает ротацию ZSK автоматически. Ключи лягут в key-directory (обычно /var/cache/bind/keys).
Knot DNS делает то же через KASP-политику и knotc zone-sign evilmail.pro. PowerDNS — самый короткий путь, если вы уже на нём (а на evilmail.pro авторитативный слой именно PowerDNS):
По NSEC против NSEC3: для одного почтового домена opt-out не нужен — он экономит подписи только на огромных делегирующих зонах. Берите NSEC либо NSEC3 без opt-out (0 в третьем параметре выше — количество итераций, ставьте 0, это актуальная рекомендация RFC 9276, а не 10/100 из старых гайдов).
Загрузка DS у регистратора — шаг, на котором всё оживает или умирает
Зона подписана внутри, RRSIG есть, DNSKEY отдаётся. Для внешнего мира DNSSEC при этом выключен: пока в родительской зоне .pro нет вашей DS-записи, ни один резолвер не построит цепочку и не поставит AD-бит. DS — единственное звено, замыкающее контур.
Получить DS из ключа:
bash
dnssec-dsfromkey -2 Kevilmail.pro.+013+12345.key
# evilmail.pro. IN DS 12345 13 2 3A9F...C1 (digest type 2 = SHA-256)
-2 означает digest type 2 (SHA-256). Не используйте type 1 (SHA-1, устарел) и не связывайтесь с type 4/GOST. Многие регистраторы принимают только DNSKEY и считают DS сами — тогда отдайте им запись DNSKEY с флагом 257.
Теперь про порядок, из-за которого люди роняют почту. Заливка DS до того, как подписанная зона реально отдаётся ВСЕМИ авторитативными серверами, = мгновенный SERVFAIL для части резолверов. Если у вас четыре NS (storm/void/kraken/pandora), а подпись доехала только до трёх, то резолвер, попавший на четвёртый, получит неподписанный ответ при наличии DS у родителя — это классифицируется как атака, ответ бракуется, домен в SERVFAIL. Письма перестают ходить в обе стороны.
Правильная последовательность:
1.Подписать зону.
2.Дождаться распространения по всем авторитативным NS (сравните dig +dnssec DNSKEY evilmail.pro @storm.example.net на каждом сервере).
3.Проверить валидность внутренне (delv, dnsviz).
4.Только теперь залить DS у регистратора.
Теперь можно DANE: генерируем и публикуем TLSA
TLSA-запись для SMTP выглядит так:
text
_25._tcp.mail.evilmail.pro. 3600 IN TLSA 3 1 1 <64-hex-sha256>
Три числа — это usage selector matching:
usage 3 (DANE-EE) — пиннинг конечного сертификата самого сервера, без оглядки на публичные CA. Для SMTP это де-факто стандарт (RFC 7672). 2 (DANE-TA, пиннинг центра) хрупок и почти не нужен.
selector 1 (SPKI) — пиннится публичный ключ, а не весь сертификат. Это позволяет переиздать сертификат Let's Encrypt с тем же ключом, не меняя TLSA.
matching 1 (SHA-256) — хеш, а не полный ключ.
Комбинация 3 1 1 — то, что должно быть у 99% почтовых серверов. Генерация отпечатка из цепочки:
Главная операционная ловушка DANE — ротация сертификата Let's Encrypt. Если certbot сгенерит новый ключ, старый отпечаток в TLSA перестанет совпадать, и все валидирующие отправители получат отказ TLS — письма к вам встанут. Два решения: либо жёстко фиксировать ключ (certbot ... --reuse-key, тогда SPKI не меняется), либо публиковать две TLSA сразу — на текущий и на будущий ключ — за пару TTL до деплоя (roll-over 3 1 1 + 3 1 1), а старую снимать после перехода. Проверка вживую:
bash
posttls-finger -l dane -c mail.evilmail.pro
# Verified TLS connection established ... Matched DANE-EE(3): SPKI(1): SHA256(1)
В main.cf:
text
smtp_tls_security_level = dane
smtp_dns_support_level = dnssec
smtpd_tls_security_level = may
smtp_dns_support_level = dnssec включает требование AD-бита — без валидирующего локального резолвера Postfix DANE молча не применит.
MTA-STS и TLS-RPT: страховка, которая переживёт вашу ошибку в DNSSEC
MTA-STS решает ту же задачу downgrade-защиты, но принципиально иначе — без DNSSEC. Нужны три вещи: TXT-запись _mta-sts, политика по HTTPS и валидный WebPKI-сертификат на отдельном хосте mta-sts.<domain>.
text
_mta-sts.evilmail.pro. IN TXT "v=STSv1; id=20260704T120000"
_smtp._tls.evilmail.pro. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]"
Файл политики отдаётся веб-сервером по адресу https://mta-sts.evilmail.pro/.well-known/mta-sts.txt:
Отправитель забирает политику по HTTPS, проверяет её сертификат публичными CA и кэширует на max_age (здесь неделя). Дальше он требует TLS с валидным сертификатом для указанных MX — даже если DNS-ответ подделан.
Ключевая мысль для операционной команды: контуры покрывают разные приёмники и разные отказы. Google и Microsoft полагаются на MTA-STS. Многие немецкие, голландские и скандинавские провайдеры (нередко по требованиям BSI и Internet.nl) — на DANE. DANE ловит downgrade даже при первом в жизни контакте (first-flight), но падает, если у вас протухла подпись. MTA-STS требует хотя бы одного успешного забора политики (TOFU-подобно), зато держится на кэше и переживает день, когда ваш DNSSEC развалился. Держите оба. А TLS-RPT (_smtp._tls с rua) — единственный канал, из которого вы вообще узнаете, что у отправителей ломается TLS к вам: без него сбои DANE/STS невидимы.
Где резолвинг ломается: ротация ключей, истечение RRSIG, фрагментация
Три главных убийцы почты после включения DNSSEC.
Просроченная RRSIG. Подписи имеют срок жизни (BIND по умолчанию signatures-validity ~14 дней с рефрешем раньше). Пока демон подписания жив — он их обновляет. Остановился процесс, сломался inline-signing, забыли перезапустить после апдейта — и через несколько дней подписи истекают. Резолверы начинают браковать ответы, домен уходит в SERVFAIL целиком. Это самый частый инцидент DNSSEC, и он тихий: ничего не «падает», просто в какой-то день письма перестают ходить.
Рассинхрон DS ↔ DNSKEY при ротации KSK. Меняете KSK — обязаны обновить DS у регистратора, но не одномоментно. Правильная схема — double-DS / pre-publish: сначала публикуете новый DNSKEY рядом со старым, ждёте, добавляете новый DS к старому у родителя, выдерживаете max(TTL DS, TTL DNSKEY) (обычно DS TTL 3600–86400 секунд), и только потом убираете старые. Снимете старый DS раньше, чем истечёт его кэш у резолверов, — часть мира провалится в SERVFAIL. Автоматические политики BIND/PowerDNS это делают сами; ручная ротация — минное поле.
UDP-фрагментация. Ответ DNSKEY+RRSIG на RSA-2048 легко переваливает за 1500 байт и фрагментируется на IP-уровне. После DNS Flag Day 2020 рекомендованный EDNS-буфер — 1232 байта; фрагменты режутся на файрволах, и валидация молча ломается для части клиентов. Ровно поэтому — ECDSA P-256, а не RSA: ответы влезают без фрагментации.
Диагностика SERVFAIL:
bash
delv @127.0.0.1 evilmail.pro TLSA +rtrace # покажет, на каком звене рвётся
dig +dnssec DNSKEY evilmail.pro # есть ли RRSIG, не истёк ли
# плюс визуальный разбор всей цепочки на dnsviz.net
Проверка и мониторинг перед тем как считать задачу закрытой
Разовая проверка после включения: delv +rtrace (должен показать fully validated), posttls-finger -l dane -c к вашему MX (должен показать Verified и совпадение по DANE-EE), полный обход цепочки на dnsviz.net, и внешний аудит на Internet.nl или Hardenize — там сразу видно, поднялись ли DANE и MTA-STS.
Но разовая проверка ничего не гарантирует завтра. Обязательный постоянный мониторинг — истечение RRSIG. Минимально это скрипт по крону: dig +dnssec вытаскивает RRSIG, парсит поле Expiration и алертит, если до него меньше 7 дней. По-взрослому — blackbox-exporter с dns-пробой на валидацию, Zonemaster или внешний DNSSEC-монитор. Алерт на «RRSIG истекает через неделю» спасёт вас от того самого тихого SERVFAIL.
Чеклист внедрения
Подписать зону алгоритмом ECDSA P-256 (13), NSEC или NSEC3 без opt-out, 0 итераций.
Проверить, что подпись доехала до всех авторитативных NS, прежде чем трогать регистратора.
Залить DS SHA-256 (digest type 2) у регистратора — порядок: подписать → распространить → проверить → DS.
Дождаться fully validated в dnsviz.net и delv +rtrace.
Поднять на MTA локальный валидирующий unbound (127.0.0.1), не форвардить на 8.8.8.8 с потерей AD.
Опубликовать TLSA 3 1 1 на _25._tcp.mail.evilmail.pro.
Настроить pre-publish ротацию TLSA под обновление Let's Encrypt (или --reuse-key).
Включить в Postfix smtp_tls_security_level = dane и smtp_dns_support_level = dnssec.
Поднять MTA-STS mode: enforce и TLS-RPT _smtp._tls — второй контур и канал отчётности.
Настроить алерт на истечение RRSIG за 7 дней и мониторинг цепочки.
Для ротации KSK — только схема double-DS с выдержкой max(TTL DS, TTL DNSKEY), иначе SERVFAIL.
Честная позиция инженера: включайте DNSSEC ради DANE — это единственная защита транспорта, работающая с первого контакта. Но включайте только с мониторингом подписей и pre-publish ротацией ключей. Без этой дисциплины MTA-STS для вашей операционной команды безопаснее: сломать его сложнее, а последствия ошибки не кладут весь домен в SERVFAIL.