Ви відправили лист. Клієнти його не отримали. Або ще гірше: він потрапив у спам з ввічливим, але фатальним «DKIM=fail» на видноті. Маркетинг думає, що «пропагація DNS» — це погодна аномалія, і просить «просто відправити ще раз».
Тут настав час припинити гадати. DKIM не ненадійний. Він суворий. Підпис ламається, бо щось змінилося між підписуванням і верифікацією, або перевіряючі не можуть отримати ключ, або ви взагалі підписали не те. Хороша новина: за дисциплінованої діагностики це зазвичай виправляється менше ніж за годину.
DKIM простими словами (що насправді підписується)
DKIM (DomainKeys Identified Mail) — це не «шифрування» і не «доказ того, що відправник добрий». Це криптографічна контрольна сума над вибраними заголовками і (зазвичай) тілом повідомлення, підписана приватним ключем домену відправника. Отримувач завантажує публічний ключ з DNS і перевіряє підпис. Якщо підписані байти не відповідають тому, що прийшло — верифікація провалюється. Якщо публічний ключ не можна отримати — верифікація провалюється. Якщо отримувач не може розібрати підпис — верифікація провалюється.
У заголовку DKIM-підпису (DKIM-Signature:) ви побачите поля, як-от:
d=домен, що підписав (домен, «відповідальний» за DKIM)s=селектор (для пошуку ключа в DNS)h=які заголовки підписаніbh=хеш тіла (base64 від канонічного хешу тіла)b=фактичний підпис над заголовками + хешем тілаc=канонізація (як нормалізуються пробіли/переноси рядків)t=/x=мітка часу / термін дії
Ключовий операційний момент: DKIM валідує конкретне подання повідомлення. Якщо будь-який ретранслятор, шлюз, додавальник підписів у футері, менеджер розсилки, «пристрій безпеки» або корисний хмарний сервіс підправить підписані заголовки чи тіло таким чином, що це не вкладається в канонізацію — підпис зламається. Це не помилка DKIM. Це DKIM, який виконує свою роботу.
Два режими канонізації і безкінечні проблеми
Канонізація регулює, як підписувач і перевіряючий нормалізують контент перед хешуванням і верифікацією. Ви зустрінете:
- simple: мінімальна або ніяка нормалізація. Крихке. Добре для людей, які люблять сторінки виклику on-call.
- relaxed: допускає певні зміни у пробілах і форматуванні заголовків. Зазвичай правильний вибір.
Здорові звичні значення — c=relaxed/relaxed (заголовки/тіло обидва relaxed) або інколи relaxed/simple у деяких системах. Якщо ви використовуєте simple/simple у складному шляху доставки, ви підписуєте піщаний замок і відправляєте його через автомийку.
Реальна обіцянка DKIM
DKIM відповідає на питання: «Чи домен, що має контроль над цим приватним ключем DKIM, підписав це повідомлення, і чи підписані частини дійшли незмінними?» Він не відповідає на питання: «Чи автентична адреса From?» Це робота DMARC, який використовує SPF і DKIM плюс правила вирівнювання. DKIM — лише один із будівельних блоків у великій системі перевірок на вході.
Цікаві факти та коротка історія (бо пошта старша за ваш CI)
- Факт 1: DKIM стандартизували у 2007 році (RFC 4871), об’єднавши ідеї з Yahoo DomainKeys і Cisco Identified Internet Mail.
- Факт 2: Підписи DKIM оцінюють після транспортних змін; навіть «безпечне» додавання футера може недійти до хешу тіла.
- Факт 3: Ранні розгортання широко використовували RSA-ключі 1024 біт; багато приймачів тепер віддають перевагу або вимагають 2048 біт для більшої безпеки.
- Факт 4: Обмеження розміру TXT записів DNS і фрагментація історично спричиняли перебійні помилки отримання ключів, особливо з надмірно великими ключами або некоректними DNS-настройками.
- Факт 5: DKIM може підписувати лише підмножину заголовків; вибір — компроміс між цілісністю і живучістю через ретранслятори.
- Факт 6: Поштові розсилки (mailing lists) відомі як ті, що ламають DKIM: вони модифікують Subject, додають заголовки і додають футери — саме те, що DKIM зазвичай підписує.
- Факт 7: ARC (Authenticated Received Chain) був представлений пізніше, щоб зберегти результати автентифікації через форвардери і списки, бо DKIM сам по собі часто ламається далі в ланцюжку.
- Факт 8: Деякі MTA історично переписували кінці рядків або «dot-stuffing» по-різному; канонізація існує частково, щоб пережити цей хаос.
- Факт 9: Кілька DKIM-підписів можуть співіснувати; системи отримувача зазвичай приймають повідомлення, якщо будь-яка валідна підпис відповідає політиці (особливо для вирівнювання DMARC).
Як ламається DKIM: важливі режими відмови
1) DNS не може віддати ключ (або віддає неправильний)
Отримувачі верифікують DKIM, запитуючи запис DNS за адресою:
<selector>._domainkey.<domain>
Якщо такий запис відсутній, пошкоджений, неправильно розбитий, занадто великий для вашого DNS-шляху або кешований у застарілому стані під час ротації — ви отримаєте помилки DKIM, що виглядають випадковими — бо вони випадкові по різних резолверах.
2) Повідомлення змінилося після підпису
Класичний випадок: повідомлення підписали, потім ретранслятор його модифікував. Поширені винуватці:
- Додавання юридичного футера (виконком, HR або «конфіденційність»)
- Переписування рядка Subject (позначення «EXTERNAL»)
- Модифікація MIME-границь або перемонтування кодування вмісту
- Нормалізація пробілів у способах, не покритих канонізацією
- Антивірусні сканери або DLP-шлюзи, що перемонтовують вкладення
3) Ви підписали неправильні заголовки (або занадто багато)
Підписування нестабільних заголовків — самонанесена рана. Заголовки на кшталт Received міняються на кожному хопі, тому підписувати їх безглуздо. Деякі системи також додають або переписують Message-ID, Date або навіть From у спосіб, який ви не очікували. Підбирайте заголовки, що відображають ідентичність і вміст, але залишаються стабільними після роботи підписувача.
4) Ваш домен підпису не вирівнюється з DMARC
Ви можете мати проходження DKIM, але все одно провалювати DMARC, якщо домен у d= не вирівнюється з видимим доменом у From: згідно з політикою DMARC (строга або релаксована вирівнювання). Це часто виглядає як «DKIM=pass» але «DMARC=fail», що бізнес-стейкхолдери інтерпретують як «пошта зламана». І це справедливо.
5) Час, термін дії та захист від повторного відтворення
Деякі DKIM-підписи містять x= (термін дії). Якщо ваші години систем відрізняються або повідомлення довго стоять у черзі (привіт, зворотний тиск), підписи можуть прострочитися до доставки. Це менш поширено, але надзвичайно дратує, бо все інше «виглядає правильно», окрім часу.
6) Ротація селекторів/ключів як хобі на вихідних
Ротація ключів DKIM — нормальна річ. Ротація без перекриття — як створити інцидент на два дні. DNS-кеші, багаторегіональні MTA і довгоживучі черги означають, що старі селектори можуть використовуватися ще деякий час. Видалити запис занадто рано — і верифікація провалюватиметься для затриманих листів.
Короткий жарт №1: DKIM — це як пломба, що показує втручання. Якщо ви постійно відкриваєте банку, щоб «покращити її», не дивуйтеся, коли пломба загуде.
Швидка методика діагностики (першочергове/друге/третє)
Це послідовність, що найшвидше знаходить вузьке місце в реальному середовищі. Не починайте з редагування конфігів. Почніть з того, що бачили отримувачі.
Перше: підтвердіть, що саме не працює (DNS проти body hash проти парсингу заголовка)
- Отримайте один реальний провалений лист (оригінальні заголовки як отримано). Не скриншот. Не пересилка. Сировинний текст.
- Прочитайте заголовок Authentication-Results від отримувача. Часто він точно скаже, що зламалося: «no key», «body hash mismatch», «bad signature», «invalid header format».
- Перевірте поля DKIM-Signature:
d=,s=,c=,h=,bh=,x=.
Друге: перевірте DNS з кількох точок огляду
- Запитайте запис селектора через відомий резолвер.
- Запитайте через ваш системний резолвер (щоб впіймати split-horizon або корпоративні DNS-аномалії).
- Перевірте, що запис синтаксично валідний і повний (немає зламаних лапок, відсутніх фрагментів).
Третє: локалізуйте, де повідомлення модифікують
- Порівняйте повідомлення на момент підписування і на момент доставки (якщо можете зняти обидва).
- Шукайте проміжні системи, що переписують контент: вихідні шлюзи, поштові сканери, CRM-платформи, менеджери списків.
- Відправте контрольний тест: той самий відправник, той самий отримувач, але обійдіть опціональні компоненти (якщо можливо), щоб знайти хоп, що модифікує.
Четверте: виправте найменше, що поверне pass
Опирайтеся на бажання «перепроєктувати пошту». Спочатку відновіть доставлюваність, потім укріплюйте pipeline. На практиці це часто означає:
- Виправити DNS-запис ключа або використання селектора
- Переключити канонізацію на relaxed
- Зупинити модифікацію тіла після підписування (перенести підписування на останній хоп)
- Налаштувати, які заголовки підписуються
- Вирівняти
d=з From-доменом для DMARC
Практичні завдання: команди, виводи та рішення (12+)
Це реальні завдання, які ви можете виконати на поштовому хості або на машині для розслідувань. Кожне містить: команду, що означає вивід, і яке рішення приймати. Я припускаю Linux-середовище з типовими інструментами. Підлаштуйте шляхи під свій дистрибутив.
Task 1: Extract Authentication-Results and DKIM-Signature from a saved message
cr0x@server:~$ awk 'BEGIN{RS="";FS="\n"} {for(i=1;i<=NF;i++) if($i ~ /^Authentication-Results:/ || $i ~ /^DKIM-Signature:/) print $i}' failed.eml
Authentication-Results: mx.example.net; dkim=fail (body hash did not verify) header.d=example.com header.s=s2025 header.b=Qn...
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=s2025; h=from:to:subject:date:mime-version:content-type; bh=3l8...; b=Qn...
Значення: Ви вже маєте клас помилки: body hash mismatch. Це не «DNS відсутній», не «поганий ключ», не «підпис прострочено». Щось змінилося в тілі після підпису.
Рішення: Припиніть дивитися на DNS. Знайдіть, що змінює тіло (футери, перемонтування, переписування шлюзом) або перенесіть підписування пізніше в pipeline.
Task 2: Check the DKIM public key record exists (dig)
cr0x@server:~$ dig +short TXT s2025._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtY..."
Значення: TXT-запис існує і повертає щось, що виглядає як DKIM-повідомлення.
Рішення: Якщо DKIM все одно падає з повідомленням «no key», підозрюйте видимість DNS (split-horizon), помилки DNSSEC або фрагментацію/квазі-кавітації запису на деяких резолверах.
Task 3: Check the same DKIM record via a specific resolver (catch split-horizon)
cr0x@server:~$ dig @1.1.1.1 +short TXT s2025._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtY..."
Значення: Публічні резолвери погоджуються. Якщо ваш внутрішній резолвер повертає інший результат — у вас split DNS або застарілий кеш.
Рішення: Якщо результати різняться — спочатку виправте авторитетний DNS. Отримувачі електронної пошти не використовують ваш внутрішній погляд на реальність.
Task 4: Detect malformed multi-string DKIM TXT records
cr0x@server:~$ dig TXT s2025._domainkey.example.com +noall +answer
s2025._domainkey.example.com. 300 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..." "G9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtY..."
Значення: Деякі DNS-провайдери розбивають довгі TXT-записи на кілька рядків. Це нормально, якщо зроблено правильно; резолвери конкатенують їх. Але помилки з лапками можуть тихо зламати ключі.
Рішення: Якщо бачите дивні розділові знаки, відсутні лапки або обрізані сегменти — перегенеруйте і опублікуйте запис заново. Не редагуйте base64 вручну в браузері о 2:00 ранку.
Task 5: Verify OpenDKIM sees the key and selector mapping
cr0x@server:~$ sudo opendkim-testkey -d example.com -s s2025 -vvv
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: checking key 's2025._domainkey.example.com'
opendkim-testkey: key OK
Значення: DNS-запис ключа доступний і відповідає очікуванням OpenDKIM.
Рішення: Якщо це невдача — спочатку виправте DNS/селектор. Якщо проходить, а отримувачі все одно падають — підозрюйте поток пошти або модифікацію повідомлення.
Task 6: Confirm the signer is actually signing (mail logs)
cr0x@server:~$ sudo grep -E "opendkim|DKIM-Signature" /var/log/mail.log | tail -n 5
Jan 03 10:14:22 mailgw opendkim[1423]: 6F2C12A0B1: DKIM-Signature field added (s=s2025, d=example.com)
Jan 03 10:14:23 mailgw postfix/qmgr[1111]: 6F2C12A0B1: from=, size=48213, nrcpt=1 (queue active)
Значення: OpenDKIM додав підпис для цього ID повідомлення.
Рішення: Якщо не бачите підписування — перевірте підключення milter, права на сокет або шлях обходу, що пропускає milter.
Task 7: Confirm Postfix is wired to the DKIM milter
cr0x@server:~$ sudo postconf | egrep 'smtpd_milters|non_smtpd_milters|milter_default_action|milter_protocol'
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891
milter_default_action = accept
milter_protocol = 6
Значення: І SMTP-повідомлення, і локально згенерована пошта проходять через DKIM milter. Якщо встановлено лише smtpd_milters, пошта, згенерована локально (cron, додатки), може не підписуватися.
Рішення: Якщо non_smtpd_milters порожній і ви генеруєте пошту локально — додайте його. Непідписана пошта часто вважається підозрілою, коли DMARC суворий.
Task 8: Validate the message body wasn’t rewritten by a gateway (compare hashes via OpenDKIM tools)
cr0x@server:~$ sudo opendkim-testmsg -v < failed.eml
opendkim-testmsg: dkim_eom(): verification failed: body hash did not verify
Значення: Збережене повідомлення (як отримано) не відповідає підписаному хешу тіла.
Рішення: Визначте хоп, що модифікував тіло. Якщо під вашим контролем є downstream шлюз, що додає футери, перенесіть підписування після нього або перестаньте модифікувати підписану пошту.
Task 9: Find which headers you signed (and whether you signed unstable ones)
cr0x@server:~$ grep -i '^DKIM-Signature:' -m 1 failed.eml | sed 's/; /\n/g' | egrep '^(h=|c=|d=|s=)'
c=relaxed/relaxed
d=example.com
s=s2025
h=from:to:subject:date:mime-version:content-type
Значення: Ви підписали доволі стандартний набір заголовків. Добре. Якщо бачите h=...:received:... або інші заголовки, що змінюються на хопах — ви знайшли власну помилку.
Рішення: Якщо підписані нестабільні заголовки — налаштуйте підписувач, щоб підписувати стабільні заголовки.
Task 10: Detect line-ending or MIME transformations (spot the classic “gateway rewrap”)
cr0x@server:~$ python3 - <<'PY'
import sys, email
from email import policy
msg = email.message_from_binary_file(open("failed.eml","rb"), policy=policy.default)
print("Content-Type:", msg.get_content_type())
print("Has multipart:", msg.is_multipart())
print("Transfer-Encoding:", msg.get("Content-Transfer-Encoding"))
PY
Content-Type: multipart/alternative
Has multipart: True
Transfer-Encoding: None
Значення: Мультимедійний лист частіше «дружньо» переписується продуктами безпеки. Не завжди, але це частий сценарій злочину.
Рішення: Якщо у вас є DLP/AV-шлюз, перевірте, чи перемонтовує він частини або нормалізує MIME-границі. Якщо так — підписуйте після цього кроку.
Task 11: Verify DMARC alignment clues (Authentication-Results parsing)
cr0x@server:~$ grep -i '^Authentication-Results:' -m 1 failed.eml
Authentication-Results: mx.example.net; spf=pass smtp.mailfrom=bounces.vendor-mail.net; dkim=pass header.d=vendor-mail.net; dmarc=fail header.from=example.com
Значення: SPF пройшов для домену вендора, DKIM пройшов для домену вендора, але DMARC провалився, бо видимий From — example.com, і ні SPF, ні DKIM не вирівнюються з цим доменом.
Рішення: Змушуйте вендора підписувати з d=example.com (ваш домен) і вирівняйте SPF через власний bounce-домен, або прийміть, що DMARC буде провалюватись і доставлюваність постраждає під строгими політиками.
Task 12: Check for multiple DKIM signatures and decide which one matters
cr0x@server:~$ grep -i '^DKIM-Signature:' failed.eml | wc -l
2
Значення: Існують дві підписи. Часто одна додана вашою системою, інша — upstream-сервісом. Отримувачі можуть валідовувати одну і ігнорувати іншу залежно від вирівнювання і політики.
Рішення: Переконайтеся, що щонайменше одна проходяща підпис вирівнюється з From-доменом для DMARC. Якщо вирівняна підпис не проходить — проблема залишиться, навіть якщо інша проходить.
Task 13: Confirm the selector in outbound mail matches your published record
cr0x@server:~$ grep -i '^DKIM-Signature:' -m 1 failed.eml | tr ';' '\n' | grep -E '^\s*s='
s=s2025
Значення: Повідомлення використало селектор s2025. Якщо в DNS є лише s2024 — ви знайшли невідповідність.
Рішення: Або опублікуйте запис s2025 негайно (і зберігайте його), або поверніть конфіг підписувача на публічний селектор. Не «чекайте пропагацію», якщо запис просто відсутній.
Task 14: Check OpenDKIM key table/signing table mappings (common misroute)
cr0x@server:~$ sudo egrep -v '^\s*(#|$)' /etc/opendkim/SigningTable /etc/opendkim/KeyTable
/etc/opendkim/SigningTable:*@example.com s2025._domainkey.example.com
/etc/opendkim/KeyTable:s2025._domainkey.example.com example.com:s2025:/etc/opendkim/keys/example.com/s2025.private
Значення: SigningTable зіставляє відправників із ім’ям ключа; KeyTable зіставляє це ім’я з доменом/селекторам/шляхом до приватного ключа.
Рішення: Якщо пошта від noreply@sub.example.com не покривається вашим шаблоном, ви можете випадково не підписувати її. Виправте шаблони таблиць, щоб покривати реальні From/адреси відправників.
Task 15: Validate that your private key file is readable by the signing service
cr0x@server:~$ sudo -u opendkim test -r /etc/opendkim/keys/example.com/s2025.private && echo "readable"
readable
Значення: Користувач OpenDKIM може читати ключ. Якщо ні — OpenDKIM може тихо не підписувати або логувати заплутані помилки.
Рішення: Виправте власника/права. Якщо «вирішили» проблему, зробивши ключ читабельним усім, відкотіть це і зробіть правильно.
Task 16: Check for queue delays that can trigger signature expiry
cr0x@server:~$ mailq | head -n 20
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
6F2C12A0B1 48213 Fri Jan 3 10:14:23 noreply@example.com
user@recipient.net
-- 48 Kbytes in 1 Request.
Значення: Якщо ви бачите години/дні черги і ваші підписи мають термін дії (x=), верифікація може провалюватися незважаючи на правильну конфігурацію.
Рішення: Скоротіть час у черзі, уникайте прострочених DKIM-підписів без вагомої причини і вирішіть вузьке місце доставки (обмеження швидкості, проблеми з DNS, заблоковані IP).
Поширені помилки: симптом → корінь → виправлення
Цей розділ навмисно різкий. Більшість проблем з DKIM нудні. Трюк — швидко впізнати форму проблеми.
1) “dkim=fail (no key for signature)”
- Симптом: Authentication-Results каже no key або «key not found».
- Корінь: Відсутній TXT запис селектора, неправильний селектор (
s=) у вихідній пошті, split DNS або пошкоджене форматування TXT. - Виправлення: Опублікуйте правильно
<s>._domainkey.<d>TXT; підтвердіть черезdigпроти публічних резолверів; переконайтеся, що підписувач використовує цей селектор.
2) “dkim=fail (body hash did not verify)”
- Симптом: DKIM падає; часто переривчасто за різними отримувачами або маршрутами.
- Корінь: Тіло повідомлення змінено після підпису: дисклеймер, футер, переписування фільтром, перемонтування кодування, зміни переносів рядків, не покриті канонізацією.
- Виправлення: Перенесіть підписування на останню систему перед Інтернетом; вимкніть пост-підписні модифікації; використовуйте
relaxedканонізацію; уникайте додавання контенту після підпису.
3) “dkim=fail (signature did not verify)” with key present
- Симптом: Ключ є, але підпис не верифікується, навіть якщо тіло виглядає незмінним.
- Корінь: Опубліковано неправильний ключ для селектора (невідповідність ротації), пошкоджений приватний ключ, підписування іншим доменом/селектором, або невідповідність канонізації в окремих реалізаціях.
- Виправлення: Перевірте, що підписувач використовує той самий селектор/ключ, що й DNS; ротація з перекриттям; перегенеруйте ключі, якщо підозрюєте корупцію.
4) DKIM passes, DMARC fails
- Симптом:
dkim=passалеdmarc=fail. - Корінь: DKIM підписано з
d=, що не вирівнюється з видимим From-доменом, або SPF пройшов для іншого домену. - Виправлення: Забезпечте щонайменше одну DKIM-підпис, що вирівнюється з From-доменом; налаштуйте вендорів на підписування з вашим доменом; перевірте режим вирівнювання DMARC (relaxed vs strict).
5) DKIM fails only for mailing lists / forwards
- Симптом: Пряма пошта проходить; пересилки/списки не проходять.
- Корінь: Списки додають футери/теги у Subject; форвардери rewrap; переписування From взаємодіє з DMARC. DKIM ламається у трансфері.
- Виправлення: Віддавайте перевагу ARC на форвардерах/списках; мінімізуйте модифікації; розгляньте DMARC-дружню поведінку списків; підписуйте на останньому контрольованому хопі.
6) DKIM fails only for large emails or certain recipients
- Симптом: Великі розсилки не проходять, короткі сповіщення проходять.
- Корінь: MIME-трансформації, переписування шлюзом, або DNS-проблеми на резолверах, що «ламають» великі TXT/фрагментацію; іноді MTU/EDNS-шляхові проблеми.
- Виправлення: Тримайте DKIM-записи чистими; тестуйте DNS через кілька резолверів; уникайте компонентів шляху, що переформатовують HTML; перевірте, чи ваш ESP не «оптимізує» розмітку після підпису.
7) DKIM fails after a “harmless” infrastructure change
- Симптом: Раптово DKIM падає після міграції на новий шлюз, додавання DLP, вмикання «external» тегування або підключення хмарного коннектора.
- Корінь: Ви змінили байти. DKIM помітив.
- Виправлення: Перенесіть підписування вниз по потоку від компонента, що модифікує, або налаштуйте компонент так, щоб він не змінював вихідну пошту після підпису.
Три корпоративні історії з поля бою
Міні-історія 1: Інцидент через неправильне припущення
Компанія A мала акуратний вихідний стек: аплікаційні сервери віддавали пошту в Postfix, Postfix передавав її в хмарний релей, а релей доставляв у світ. DKIM був «увімкнений», а DMARC — неагресивний. Життя було гаразд, і саме так починається небезпека.
Команда безпеки запровадила новий вихідний банер: додавати «[EXTERNAL]» у початок Subject для будь-якої пошти, що виходить з компанії. Вони зробили це на хмарному релєї, бо там була галочка-фічер. Зміна була швидко затверджена; «лише префікс у Subject».
Через три дні квитанції клієнтів почали потрапляти в спам, а деякі партнери відмовлялися приймати листи. SRE на виклику побачив dkim=fail у кількох отримувачів, і хтось впевнено сказав: «DKIM не має значення для Subject, це ж просто заголовок». Це рішення коштувало їм вечора.
Звісно, DKIM звернув увагу. Їхній підписувач був на Postfix, upstream від хмарного релєя, і вони підписували Subject. Релей змінював Subject після підпису. Кожне повідомлення стало криптографічно «пошкодженим».
Виправлення не було героїчним. Вони перенесли підписування на релей (останній хоп перед Інтернетом) і припинили підписувати заголовки, які переписують downstream-системи. Також додали контроль змін: «Чи змінює це заголовки/тіло після підпису?» Звучить бюрократично. Насправді дешевше, ніж несподівані поїздки в папку спаму.
Міні-історія 2: Оптимізація, що відкотилася
Компанія B мала зрілу вихідну інфраструктуру і ротацію DKIM ключів щоквартально. Хтось помітив, що їх DNS-провайдер бере плату за «розширені записи» і запропонував оптимізацію: уніфікувати селектори й використовувати один DKIM-ключ для кількох субдоменів і підрозділів. Менше записів, менше клопоту. Що могло піти не так.
Вони зробили консолідацію, а потім скоротили TTL, щоб пришвидшити майбутні зміни. Наступна ротація — вони оновили DNS запис селектора та розгорнули новий приватний ключ для половини флоту відправників першою. Інша половина відставала день через зсунуте вікно обслуговування.
Половина флоту підписувала новим ключем, інша — старим, а DNS віддавав лише новий публічний ключ. Листи від запізнілої половини не проходили верифікацію DKIM. Отримувачі не цікавилися внутрішнім розкладом. Вони просто бачили зламані підписи і знижували репутацію.
«Оптимізація» забрала клапан безпеки: наявність декількох селекторів одночасно. Якби вони використовували перекриті селектори — старий і новий — кожен сегмент флоту підписував би тим селектором, що в нього є, а DNS міг би віддавати обидва ключі, поки rollout не завершиться.
Вони відновили ситуацію, опублікувавши старий селектор заново, а потім спланували ротації з вікном перекриття і правилом «не видаляти старий селектор, поки черги не опустилися». Менше записів не завжди перемога.
Міні-історія 3: Сумна, але правильна практика, що врятувала вихідні
Компанія C мала середовище з суворими вимогами збереження, куди проходила кожна вихідна пошта через архівний шлюз. Шлюз час від часу переписував структуру MIME під час «нормалізації» вкладень. Всі ненавиділи це, але воно було не обговорюване.
Раніше інженер наполіг на нудному дизайні: DKIM підписування відбувається після архівного шлюзу, на фінальному вихідному MTA. Це означало, що архівна коробка ніколи не бачила DKIM-підписаних повідомлень і не могла зламати підписи. Люди бурчали, бо це ускладнювало керування ключами на фінальному хопі.
Одної п’ятниці архівна команда виштовхнула оновлення, що змінило генерацію multipart-границь. Семантично нічого не змінилося, але байти для деяких повідомлень змінилися. Якби підписування було вище по потоку — це спричинило би масові збої DKIM і, можливо, DMARC-колапс до понеділка.
Натомість нічого не сталося. DKIM-підписи додавалися після того, як шлюз зробив своє дивне. Отримувачі отримали чисту пошту. Інцидент знизили до «внутрішня зміна змінила MIME» і він ніколи не торкнувся доставлення.
Сумні архітектури не отримують нагород за дизайн. Вони рятують вихідні.
Короткий жарт №2: Доставлення пошти — єдиний вид спорту, де можна програти через два зайві пропуски і футер.
Контрольні списки / поетапний план
Покроково: виправити помилку DKIM за годину
- Заберіть один провалений сирий лист з поштової скриньки отримувача (сирі заголовки + тіло). Збережіть як
failed.eml. - Прочитайте Authentication-Results: вирішіть, чи це «no key», «body hash mismatch», «signature invalid» чи «pass but DMARC fail».
- Витягніть селектор і домен з
DKIM-Signature(s=,d=). - Запитайте DNS для
s._domainkey.dпринаймні через один публічний резолвер і ваш локальний резолвер. - Перевірте підключення сервісу підписування (milter config, логи показують «DKIM-Signature field added»).
- Якщо body hash mismatch: змалюйте шлях виходу і ідентифікуйте хоп, що модифікує контент. Тимчасово обійдіть цей хоп, якщо можливо, щоб підтвердити.
- Застосуйте мінімальну корекцію:
- Виправте DNS запис/невідповідність селектора
- Перенесіть підписування вниз по потоку від модифікаторів
- Припиніть модифікатори, що торкаються вихідної пошти
- Переключіть на
relaxed/relaxed
- Відправте контрольний тест принаймні двом зовнішнім отримувачам і підтвердіть
dkim=pass. - Лише після цього обговорюйте підсилення ротації ключів, ARC і політики DMARC.
Операційна гігієна (те, що робити до того, як щось зламається)
- Підписуйте на останньому контрольованому хопі перед відкритим Інтернетом.
- Використовуйте 2048-бітні ключі, якщо немає сильного обмеження; тримайте TXT-записи чистими і правильно розбитими.
- Ротуйте селектори з перекриттям: опублікуйте новий селектор першим, розгорніть підписувачі, зберігайте старий селектор, поки черги не будуть очищені.
- Підписуйте стабільні заголовки; не підписуйте заголовки, що змінюються на хопах.
- Логуйте події підписування DKIM і ставте алерти на різке падіння обсягу підписаної пошти.
- Відстежуйте всі системи, що можуть переписувати пошту (DLP, AV, тегування external, «розумні» SMTP-релеї, CRM, системи тикетингу).
- Перевіряйте вирівнювання DMARC для пошти вендорів, що відправляють від вашого імені.
Ідея надійності, яку варто пам’ятати (в перефразі)
Перефразована ідея (John Allspaw): Надійність приходить від того, що поведінку системи роблять спостережуваною і вчаться на помилках, а не від того, що вдають, ніби помилок не буде.
Часті питання
1) Чому DKIM іноді проходить, а іноді ні для того самого відправника?
Зазвичай тому, що пошта йшла різними маршрутами. Один шлях модифікує повідомлення (додавання футера, тегування Subject, перемонтування MIME), інший — ні. Менш часто — це несумісність DNS: деякі резолвери бачать запис ключа, інші ні через кеш, split DNS або проблеми з авторитетним сервером.
2) Який найшвидший спосіб з’ясувати, це DNS чи модифікація повідомлення?
Прочитайте заголовок отримувача Authentication-Results. «No key» кричить DNS/селектор. «Body hash did not verify» кричить про модифікацію після підпису. «Signature did not verify» може бути або неправильним ключем/невідповідністю ротації, або змінами у заголовках/тілі.
3) Чи завжди слід використовувати relaxed/relaxed канонізацію?
У виробничих шляхах доставки з реальними проміжними системами — так, частіше так. simple хисткий. Якщо ви контролюєте весь шлях і бажаєте суворості — гаразд, але більшість організацій фактично не контролюють весь шлях, незважаючи на блок-схеми архітектури.
4) Чи можна підписувати менше заголовків, щоб DKIM вижив краще?
Так, але не перевертайтеся в креатив. Підписуйте заголовки, що мають значення для ідентичності: наприклад, From, плюс основні метадані маршруту/вмісту, які не змінюються після підпису. Уникайте підписування заголовків, відомих тим, що змінюються downstream. Мета — виживаність без втрати сенсу підпису.
5) Чи завжди форвардинг ламає DKIM?
Форвардинг часто ламає SPF (бо форвардер стає IP відправника) і може ламати DKIM, якщо форвардер модифікує контент. Чистий SMTP-форвардинг без модифікацій може зберегти DKIM. Багато форвардерів модифікують (перепаковують, додають заголовки, іноді змінюють контент), тут ARC стає корисним.
6) А як щодо mailing lists?
Списки розсилки — природний хижак DKIM. Вони зазвичай додають футери, змінюють Subject і заголовки. Очікуйте провалів DKIM, якщо список не налаштований дружньо до DKIM/DMARC (або не використовує ARC належним чином). Якщо участь у списку важлива, плануйте це, а не звинувачуйте «пошту як вона є».
7) Чи ще прийнятний 1024-бітний DKIM-ключ?
Деякі приймачі його приймають, деякі карають, і безпека слабша. Практична відповідь: використовуйте 2048-бітний RSA, якщо лише ваш DNS-провайдер або платформа не створюють перешкод. Якщо не можете — вирішіть обмеження DNS/провайдера, а не чіпляйтеся за 1024.
8) Ми використовуємо вендора для відправки. Чому ми отримуємо DMARC-провали, хоча вендор каже «DKIM увімкнено»?
Бо вендор, ймовірно, підписує своїм доменом (d=vendor.com), а ваш видимий From — ваш. DKIM може проходити, але він не вирівнюється з вашим доменом, отже DMARC провалюється. Виправлення — підписування з вашим доменом і вирівнювання SPF через кастомний bounce-домен, якщо потрібно.
9) Чи варто мати кілька DKIM-підписів?
Це може бути корисно: одна підпис від вашої інфраструктури, інша — від вендора, або по підпису на кожну доменну ідентичність. Але переконайтеся, що щонайменше одна підпис проходить і вирівнюється з From-доменом. Багато підписів також ускладнюють дебаг; тримайте їх усвідомлено, а не випадково.
10) Як ротацію DKIM-ключів провести без збоїв?
Використовуйте новий селектор, спершу опублікуйте його DNS-запис, розгорніть підписувачі на використання його, і збережіть старий селектор публічним у вікні перекриття. Видаляйте старий запис лише коли впевнені, що затримані листи і повільні MTA більше не його використовують.
Висновок: наступні кроки, які ви реально можете зробити сьогодні
Помилки DKIM здаються містичними, поки ви не поставитесь до них як до будь-якої іншої проблеми цілісності у виробництві: знайдіть, що змінилося, знайдіть, де це сталося, і припиніть це змінювати у неправильному місці.
Зробіть наступне:
- Зберіть один провалений сирий лист і класифікуйте помилку через
Authentication-Results. - Перевірте селектор DNS через публічний резолвер і ваше локальне середовище. Виправте «no key» перш ніж торкатися іншого.
- Якщо це body hash mismatch, припиніть модифікації після підпису: перенесіть DKIM підписування на фінальний хоп або вимкніть функції переписування downstream.
- Забезпечте вирівнювання DMARC для вашого бізнесового From-домену, особливо для пошти, що надсилається вендорами.
- Інституціоналізуйте нудні практики: перекриття ротацій ключів, логування подій підписування і вважайте «додавання футера» як зміну доставності, а не косметичну правку.
Пошта — ворожа розподілена система, замаскована під товар. DKIM — один із небагатьох компонентів, що поводиться передбачувано. Якщо він ламається — щось змінилося. Знайдіть це. Виправте. І запишіть, щоб Майбутній Ви не доводився знову прокидатися о 3:00.