Миграция Modoboa → Synology MailPlus без потери почты

  • Автор темы Автор темы mexan
  • Дата начала Дата начала
Сообщения
202
Реакции
0
Часть 1: стратегия и архитектура

Сразу скажу: это не теоретическая инструкция, собранная из документации. Я именно так переносил рабочую почту с Modoboa на Synology MailPlus и по ходу миграции поймал несколько нюансов, о которых в обычных гайдах почти не пишут.

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

Поэтому я изначально отказался от варианта «выключить старый сервер, перенести всё и потом разбираться». Делал всё параллельно: сначала подготовил новую площадку, затем перенёс основную массу почты, отдельно проверил DNS/NAT/TLS, и только потом переключал production.

Задача

Нужно перенести рабочий почтовый сервер с Modoboa на Synology MailPlus Server так, чтобы пользователи продолжали работать, а новые письма не потерялись во время переноса.

Дополнительно переносим общие календари и контакты.

Для примеров:
Код:
example.org             — почтовый домен
mail.example.org        — FQDN почтового сервера
calendar.example.org    — CalDAV/CardDAV

203.0.113.10            — старый Modoboa
203.0.113.20            — новый публичный IP MailPlus

10.20.30.0/24           — MAIL VLAN
10.20.30.1              — шлюз
10.20.30.10             — Synology MailPlus
VLAN ID                  — 30

1. Исходная схема

Код:
                  INTERNET
                     |
                203.0.113.10
                     |
                  Modoboa
             +-------+-------+
             |               |
          Postfix          Dovecot
             |               |
            SMTP             IMAP

Дополнительно:
Radicale
 ├─ CalDAV
 └─ CardDAV

Старый сервер находится в production. Пользователи читают и отправляют почту, новые сообщения приходят постоянно.

Поэтому плохой вариант:
Код:
Выключить старый сервер
→ начать перенос
→ включить новый
→ разбираться с ошибками

2. Итоговая схема

Код:
                         INTERNET
                             |
                       203.0.113.20
                             |
                       Router/Firewall
                             |
                      dst-nat/src-nat
                             |
                         VLAN MAIL
                       10.20.30.0/24
                             |
                       10.20.30.10
                             |
                    Synology MailPlus
                     +------+------+ 
                     |      |      |
                    SMTP   IMAP   HTTPS

3. Основной принцип миграции

Код:
Подготовка Synology
        ↓
Аудит старого сервера
        ↓
Тест одного mailbox
        ↓
Initial IMAP Sync
        ↓
Старый сервер продолжает работать
        ↓
Подготовка DNS/NAT/TLS
        ↓
Проверка нового сервера ДО DNS cutover
        ↓
DNS Cutover
        ↓
Final Delta
        ↓
Реальные входящие/исходящие тесты
        ↓
Остановка старых Postfix/Dovecot
        ↓
Повторный внешний тест
        ↓
Poweroff старого сервера
        ↓
DNS Cleanup

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

4. Почему MX часто можно не менять

Если уже используется:
Код:
example.org MX 10 mail.example.org

а меняется только сервер за mail.example.org, то во время cutover достаточно изменить:
Код:
mail.example.org
203.0.113.10 → 203.0.113.20

Пользовательские настройки остаются прежними:
Код:
IMAP: mail.example.org
SMTP: mail.example.org

Но после смены A-записи Migration по имени mail.example.org уже будет попадать на новый сервер. Поэтому Final Delta выполняется по прямому IP старого сервера.

5. Что подготовить заранее

  • sudo/root на старом сервере;
  • административный доступ DSM;
  • доступ к DNS;
  • возможность настроить PTR у провайдера;
  • новый публичный IP;
  • доступ к маршрутизатору и коммутаторам;
  • резервные копии конфигурации;
  • список mailbox;
  • контрольные размеры/число писем;
  • экспорт Calendar/Contacts, если они используются.

6. Правила безопасности

  • Не мигрировать сразу всех — сначала один mailbox.
  • Не переключать DNS, пока новый SMTP/IMAP не проверен напрямую по IP.
  • Не удалять старый IP из SPF до окончания перехода.
  • Не выключать старый сервер сразу после смены DNS.
  • Не публиковать DSM 5001 напрямую.
  • Не считать проект завершённым только по статусу “Migration completed”.

7. Критерии успеха

Код:
A      mail.example.org → новый IP
MX     example.org → mail.example.org
PTR    новый IP → mail.example.org
SPF    PASS
DKIM   PASS
DMARC  PASS

25/tcp   SMTP
465/tcp  SMTPS
587/tcp  Submission
993/tcp  IMAPS
443/tcp  HTTPS

Open Relay: отсутствует
Unknown recipient: отклоняется
Existing recipient: принимается
Incoming mail: работает
Outgoing mail: работает
Final Delta: завершена
 
Последнее редактирование:
Часть 2: аудит Modoboa, Postfix и Dovecot

Перед тем как что-либо менять, я сначала снял состояние старого сервера. Это реально экономит время. Когда через несколько часов начинаешь разбираться, почему какой-то ящик отличается или откуда берётся конкретный hostname, гораздо проще открыть свои исходные данные, чем вспоминать, «как там вроде было».

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

Перед миграцией я фиксирую текущее состояние старого сервера.

1. Подключение

Код:
ssh admin@203.0.113.10

Зачем я это делаю:
Это обычное SSH-подключение к старому серверу. На этом этапе мне нужен именно shell старой системы, потому что через веб-интерфейс Modoboa я не увижу часть нужной информации: реальные listeners, очередь Postfix, Dovecot sessions и активную auth-схему. Адрес и имя пользователя, естественно, подставляются свои.

Код:
hostnamectl

Что даёт эта команда:
Я начинаю с неё просто для фиксации исходного состояния. Она показывает hostname, ОС, kernel и архитектуру. Во время самой миграции это ничего не переключает и ничего не меняет — команда только читает информацию. Зато потом в документации не приходится вспоминать, на какой именно системе всё работало.

Показывает hostname, ОС, kernel и архитектуру.

2. IP и маршруты

Код:
ip -br addr

Что здесь смотрю:
ip показывает сетевую конфигурацию Linux, а ключ -br делает вывод коротким и читаемым. Мне важно увидеть реальный адрес интерфейса старого сервера и не перепутать его с адресом из DNS. Этот IP позже понадобится для Final Delta, когда публичное имя mail.example.org уже будет указывать на новый MailPlus.

-br — компактный вывод интерфейсов и адресов.

Код:
ip route

Почему это важно:
Эта команда показывает таблицу маршрутизации. В первую очередь я ищу строку default via ... — это шлюз, через который сервер выходит наружу. Если после каких-то сетевых изменений сервер перестанет отправлять почту, здесь сразу видно, куда он вообще пытается отправлять внешний трафик.

Ищу default route:
Код:
default via 203.0.113.9 dev ens18

3. Что сервер сам считает адресом mail.example.org

Код:
getent ahostsv4 mail.example.org

Почему использую именно getent:
dig показывает DNS, а getent показывает, какой адрес реально получит приложение на этом Linux-хосте с учётом /etc/hosts и NSS. Это оказалось важным нюансом: старый сервер вполне может считать, что mail.example.org — это он сам, даже после изменения публичного DNS. Поэтому при странных результатах openssl я первым делом проверяю именно это.

В отличие от dig, getent учитывает /etc/hosts и NSS. Это важно: старый сервер может резолвить собственное имя локально, даже когда публичный DNS уже изменён.

Публичная проверка:
Код:
dig A mail.example.org @1.1.1.1 +short

Что проверяется:
Здесь я спрашиваю A-запись не у локального DNS-кэша, а напрямую у выбранного публичного resolver. Это позволяет отделить публичное состояние зоны от того, что закэшировал конкретный компьютер или внутренний DNS.

4. Какие почтовые порты слушаются

Код:
sudo ss -lntp | grep -E ':(25|465|587|143|993)\b'

Что означает команда:
ss показывает сетевые сокеты. Я использую -l для listeners, -n чтобы не преобразовывать номера портов в названия, -t для TCP и -p чтобы увидеть процесс. То есть эта команда отвечает на очень простой вопрос: «кто реально слушает почтовые порты прямо сейчас?». Если 993 здесь нет, никакая настройка Migration его магически не создаст.

Значения:
Код:
25    SMTP
465   SMTPS
587   Submission
143   IMAP
993   IMAPS

Если 993 не слушается, MailPlus Migration по IMAPS не заработает.

5. Статус сервисов

Код:
sudo systemctl status postfix --no-pager

Что я хочу увидеть:
Это безопасная read-only проверка состояния Postfix. Мне важно не просто знать, что пакет установлен, а убедиться, что служба действительно запущена и systemd не показывает свежую ошибку. --no-pager нужен только для удобства — вывод сразу остаётся в терминале.

Код:
sudo systemctl status dovecot --no-pager

Что я хочу увидеть:
Проверяю, что Dovecot действительно активен. Именно через него будет идти IMAP Migration, поэтому переходить дальше при failed/inactive состоянии смысла нет.

--no-pager выводит результат сразу в консоль.

6. Активная конфигурация Postfix

Код:
sudo postconf -n

Почему использую -n:
Полный postconf выдаёт огромное количество defaults. -n оставляет только реально изменённые параметры. Так проще понять hostname, локальные домены, relay, TLS и ограничения старого сервера, не утонув в сотнях строк. Я ничего отсюда автоматически не переношу в MailPlus — это именно аудит.

Ключ -n показывает параметры, отличающиеся от defaults.

Особенно полезны:
Код:
myhostname
mydomain
mydestination
virtual_mailbox_domains
virtual_transport
relayhost
smtpd_tls_cert_file
smtpd_tls_key_file
smtpd_recipient_restrictions

Мы не копируем main.cf в MailPlus. Нам нужно понять текущую логику.

7. Активная конфигурация Dovecot

Код:
sudo doveconf -n

Что показывает команда:
Это аналогичный «срез» активной конфигурации Dovecot без лишних defaults. Для миграции особенно важны passdb, userdb, mail_location, SSL и auth mechanisms. Именно здесь я понимаю, как старый сервер вообще проверяет пользователей.

Смотрим:
Код:
protocols
listen
mail_location
auth_mechanisms
passdb
userdb
ssl
ssl_cert
ssl_key

Поиск auth-конфигурации:
Код:
sudo grep -R "passdb" /etc/dovecot 2>/dev/null

Почему просто не открываю первый попавшийся конфиг:
Dovecot обычно собирает конфигурацию из нескольких include-файлов. grep -R помогает найти, где реально объявлен нужный параметр. 2>/dev/null убирает второстепенные сообщения о недоступных файлах, чтобы они не мешали читать результат.

Код:
sudo grep -R "userdb" /etc/dovecot 2>/dev/null

Почему просто не открываю первый попавшийся конфиг:
Dovecot обычно собирает конфигурацию из нескольких include-файлов. grep -R помогает найти, где реально объявлен нужный параметр. 2>/dev/null убирает второстепенные сообщения о недоступных файлах, чтобы они не мешали читать результат.

8. Очередь Postfix

Код:
sudo postqueue -p

Зачем это проверять:
Postfix queue — это письма, которые сервер ещё не смог окончательно обработать/доставить. Перед выключением старого сервера я хочу видеть Mail queue is empty. Иначе можно выключить машину вместе с письмом, которое всё ещё ждёт повторной доставки.

В финале я хотел увидеть:
Код:
Mail queue is empty

Если есть письма — сначала выясняем причину. Я не стал удалять всю очередь вслепую.

Удаление только конкретного Queue ID:
Код:
sudo postsuper -d QUEUE_ID

Осторожно с этой командой:
Она удаляет сообщение из очереди по конкретному Queue ID. Именно поэтому я указываю ID вручную, а не использую массовое удаление. Сначала обязательно читаю postqueue -p и понимаю, что именно удаляю.

9. Активные Dovecot-сессии

Код:
sudo doveadm who

Что это мне даёт:
Команда показывает активные подключения к Dovecot. До cutover пользователи здесь ожидаемы. А вот перед окончательным отключением старого сервера этот список очень полезен: если кто-то всё ещё сидит на старом IMAP, значит его клиент не переключился или использует старый DNS/профиль.

До cutover активные пользователи нормальны. Перед выключением старого сервера желательно получить пустой вывод.

10. Папки конкретного mailbox

Код:
sudo doveadm mailbox list -u user@example.org

Почему я сохраняю этот список:
INBOX увидеть легко, а вот пользовательские папки часто забывают проверить. Этой командой я получаю реальные IMAP mailboxes конкретного пользователя и потом сравниваю их с MailPlus. Если у человека был Archive/Projects/2025, он тоже должен приехать.

пример:
Код:
INBOX
Drafts
Sent
Junk
Trash
Projects
Archive

11. Количество сообщений и размер

Код:
sudo doveadm mailbox status \
-u user@example.org \
messages vsize \
INBOX Sent Drafts Junk Trash

Что здесь сравниваю:
messages даёт количество сообщений, а vsize — виртуальный размер. Для меня это контроль до/после переноса. Я не требую абсолютного совпадения размера байт в байт, потому что разные системы хранят метаданные по-разному, но огромная разница в количестве писем — стоп-сигнал.

messages — количество сообщений.

vsize — виртуальный размер.

пример:
Код:
INBOX messages=436
Sent messages=32
Drafts messages=1

Для меня это была удобная контрольная точка для проверки после миграции.

12. TLS старого IMAPS

Код:
openssl s_client \
-connect mail.example.org:993 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

Смотрю на:
Код:
subject=
issuer=
Verify return code: 0 (ok)

Короткая проверка сертификата:
Код:
echo | openssl s_client \
-connect mail.example.org:993 \
-servername mail.example.org 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates

Зачем такая длинная конструкция:
Первая часть устанавливает TLS-соединение, а openssl x509 оставляет только основные данные сертификата: subject, issuer и сроки действия. Это удобно, когда полный вывод s_client уже не нужен и надо быстро сравнить сертификаты SMTP/IMAP.

13. Итог аудита

Код:
[ ] IP старого сервера
[ ] Default gateway
[ ] FQDN
[ ] SMTP/IMAP порты
[ ] Postfix работает
[ ] Dovecot работает
[ ] Список пользователей
[ ] Контрольные message counts
[ ] Нестандартные IMAP-папки
[ ] Состояние Postfix queue
[ ] TLS certificate
[ ] Тип passdb/userdb
[ ] Публичные A/MX
 
Часть 3: подготовка Synology MailPlus

Synology я готовил полностью заранее. То есть до того, как менять хоть одну публичную DNS-запись, на новом сервере уже должны существовать пользователи, сам почтовый домен, сетевой адрес, права и хотя бы один тестовый mailbox.

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

Новый сервер должен быть собран и протестирован до DNS cutover.

1. Пакеты

Устанавливаем через Package Center:
  • MailPlus Server;
  • MailPlus;
  • Synology Calendar — если нужен;
  • Synology Contacts — если нужен.

2. Пользователи

Создаю всех пользователей заранее.

Здесь мне важно увидеть:
  • учётная запись DSM активна;
  • MailPlus разрешён;
  • Calendar/Contacts разрешены;
  • квота достаточна.

3. Почтовый домен и FQDN

Код:
Mail domain: example.org
Server FQDN: mail.example.org

Адрес пользователя:
Код:
user@example.org

Не путать с:
Код:
user@mail.example.org

4. Базовые параметры MailPlus

Здесь мне важно увидеть:
  • SMTP AUTH;
  • запрет auth без encryption;
  • relay policy;
  • max message size;
  • antispam;
  • SPF/DKIM/DMARC checks для входящей почты.

Не включаем внешний relayhost, если MailPlus должен доставлять напрямую по MX.

5. Сетевой адрес Synology

пример:
Код:
IP:      10.20.30.10
Mask:    255.255.255.0
Gateway: 10.20.30.1
DNS:     выбранный resolver

Если switch-port — access/untagged VLAN30, на самой Synology VLAN-tagging не нужен.

6. Проверка через SSH

Если SSH временно включён:
Код:
ip addr

Код:
ip route

Почему это важно:
Эта команда показывает таблицу маршрутизации. В первую очередь я ищу строку default via ... — это шлюз, через который сервер выходит наружу. Если после каких-то сетевых изменений сервер перестанет отправлять почту, здесь сразу видно, куда он вообще пытается отправлять внешний трафик.

В норме ожидаю default:
Код:
default via 10.20.30.1

Проверка шлюза:
Код:
ping -c 4 10.20.30.1

Почему начинаю с gateway:
Если Synology не видит свой шлюз, нет смысла сразу проверять SPF или SMTP наружу. Это самый нижний уровень — сначала должна работать локальная L3-связность MAIL VLAN.

Проверка DNS:
Код:
nslookup example.org

Что проверяю:
Смотрю, способен ли сам Synology разрешать DNS-имена. Почтовому серверу DNS нужен постоянно — например, чтобы находить MX удалённых доменов при прямой доставке.

7. MailPlus Web

Пользовательский адрес:
Код:
https://mail.example.org

Не используем для пользователей:
Код:
https://IP:5001

8. Сертификаты

Сертификат mail.example.org должен быть назначен:
  • MailPlus Web;
  • Postfix;
  • Dovecot.

Позже проверяем каждый сервис отдельно через openssl s_client.

9. Максимальный размер письма

Например:
Код:
40 MB

SMTP capability может выглядеть:
Код:
250-SIZE 41943040

Важно помнить про overhead MIME/base64.

10. Антиспам

До production включаем базовую фильтрацию, но не вводим сразу агрессивные HELO/DNSBL-политики без понимания false positive.

11. Локальный тестовый mailbox

Создаю:
Код:
testuser@example.org

Здесь мне важно увидеть:
  • вход в MailPlus Web;
  • локальную отправку;
  • локальное получение;
  • создание mailbox.

12. Резервные копии

Перед сетевыми изменениями сохраняем:
  • DSM Configuration Backup;
  • backup MikroTik;
  • backup конфигурации управляемых коммутаторов.

13. Чек-лист

Код:
[ ] MailPlus Server установлен
[ ] MailPlus установлен
[ ] Домен example.org создан
[ ] FQDN mail.example.org настроен
[ ] Пользователи созданы
[ ] MailPlus permissions выданы
[ ] Calendar/Contacts permissions выданы
[ ] Static IP настроен
[ ] Gateway доступен
[ ] DNS работает
[ ] Тестовый mailbox работает локально
[ ] Relay policy проверена
[ ] SMTP AUTH настроен
[ ] Backup DSM сохранён
 
Часть 4: MikroTik, VLAN, NAT и Firewall

С сетью я решил сразу сделать нормально: вынес MailPlus в отдельный VLAN и дал ему отдельный публичный IP. Это не обязательное требование Synology, но для меня так гораздо понятнее — видно, какой трафик относится к почте, проще писать firewall и проще потом диагностировать NAT.

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

пример:
Код:
Public IP: 203.0.113.20
MAIL VLAN: 30
MAIL subnet: 10.20.30.0/24
Gateway: 10.20.30.1
Synology: 10.20.30.10
WAN: ether1
Bridge: bridge

Команды — шаблон RouterOS 7. Имена интерфейсов и схема WAN у вас могут отличаться.

1. L3 VLAN-интерфейс

Код:
/interface vlan
add interface=bridge name=vlan30-mail vlan-id=30

Что мы создаём:
Это L3 VLAN-interface RouterOS. Сам VLAN-трафик на bridge от этой команды автоматически не «магически настраивается» — она создаёт интерфейс, на который позже можно повесить IP-шлюз. Bridge VLAN table и PVID на switch ports настраиваются отдельно.

2. Шлюз MAIL VLAN

Код:
/ip address
add address=10.20.30.1/24 interface=vlan30-mail comment="MAIL VLAN gateway"

Что делает эта команда:
Она назначает IP конкретному интерфейсу RouterOS. Когда я вешаю 10.20.30.1/24 на MAIL VLAN, MikroTik становится шлюзом этой подсети. Если это публичный /32, он используется как дополнительный адрес сервиса — но такую схему обязательно сверяйте с маршрутизацией вашего провайдера.

Теперь MikroTik — gateway сети 10.20.30.0/24.

3. Access-порт Synology

пример:
Код:
/interface bridge port
set [find interface=ether1] pvid=30

Почему нужен PVID:
Synology в моей схеме получает обычный untagged Ethernet. pvid=30 говорит bridge: кадры без VLAN-tag, пришедшие с этого access-порта, относятся к VLAN30. Поэтому на самой Synology мне не пришлось включать 802.1Q.

VLAN table:
Код:
/interface bridge vlan
add bridge=bridge vlan-ids=30 tagged=bridge,sfp-sfpplus1 untagged=ether1

Что задаёт эта таблица:
Здесь я явно описываю, где VLAN30 должен идти tagged, а где untagged. Trunk сохраняет 802.1Q tag между сетевыми устройствами, access-порт отдаёт Synology обычный Ethernet. Ошибка именно в этой таблице — один из самых быстрых способов потерять связность с VLAN.

tagged — trunk.

untagged — access.

Не включайте vlan-filtering удалённо, пока не уверены, что management VLAN настроен правильно.

4. Проверка VLAN

Код:
/interface bridge vlan print

Что задаёт эта таблица:
Здесь я явно описываю, где VLAN30 должен идти tagged, а где untagged. Trunk сохраняет 802.1Q tag между сетевыми устройствами, access-порт отдаёт Synology обычный Ethernet. Ошибка именно в этой таблице — один из самых быстрых способов потерять связность с VLAN.

Код:
/interface bridge port print

Почему нужен PVID:
Synology в моей схеме получает обычный untagged Ethernet. pvid=30 говорит bridge: кадры без VLAN-tag, пришедшие с этого access-порта, относятся к VLAN30. Поэтому на самой Synology мне не пришлось включать 802.1Q.

Код:
/interface vlan print

Что мы создаём:
Это L3 VLAN-interface RouterOS. Сам VLAN-трафик на bridge от этой команды автоматически не «магически настраивается» — она создаёт интерфейс, на который позже можно повесить IP-шлюз. Bridge VLAN table и PVID на switch ports настраиваются отдельно.

Код:
/ip address print

Что делает эта команда:
Она назначает IP конкретному интерфейсу RouterOS. Когда я вешаю 10.20.30.1/24 на MAIL VLAN, MikroTik становится шлюзом этой подсети. Если это публичный /32, он используется как дополнительный адрес сервиса — но такую схему обязательно сверяйте с маршрутизацией вашего провайдера.

5. Публичный IP MailPlus

Если схема провайдера допускает дополнительный host-address:
Код:
/ip address
add address=203.0.113.20/32 interface=ether1 comment="MailPlus public IP"

Что делает эта команда:
Она назначает IP конкретному интерфейсу RouterOS. Когда я вешаю 10.20.30.1/24 на MAIL VLAN, MikroTik становится шлюзом этой подсети. Если это публичный /32, он используется как дополнительный адрес сервиса — но такую схему обязательно сверяйте с маршрутизацией вашего провайдера.

/32 здесь не универсальное правило. Если провайдер выдаёт подсеть иначе — используйте его схему.

6. Исходящий src-nat

Код:
/ip firewall nat
add chain=srcnat \
src-address=10.20.30.10 \
out-interface=ether1 \
action=src-nat \
to-addresses=203.0.113.20 \
comment="MailPlus outbound via dedicated IP"

Почему нужен отдельный src-nat:
Входящий NAT решает только половину задачи. Для почты критично, с какого IP сервер выходит наружу. Я явно подменяю source 10.20.30.10 на выделенный public IP. Иначе общий masquerade может отправить SMTP с другого WAN-адреса, после чего SPF/PTR уже не будут соответствовать реальной отправке.

Это заставляет MailPlus выходить именно с 203.0.113.20.

Правило должно срабатывать раньше общего masquerade.

7. SMTP dst-nat

Код:
/ip firewall nat
add chain=dstnat \
in-interface=ether1 \
dst-address=203.0.113.20 \
protocol=tcp \
dst-port=25 \
action=dst-nat \
to-addresses=10.20.30.10 \
comment="MailPlus SMTP 25"

Что делает dst-nat:
Пакет приходит из Интернета на публичный IP MikroTik, после чего router меняет destination на внутренний адрес Synology. При этом я публикую только нужные сервисы, а не весь NAS целиком. Это принципиально безопаснее, чем делать какой-либо 1:1 «открыть всё».

8. Клиентские сервисы

Код:
/ip firewall nat
add chain=dstnat \
in-interface=ether1 \
dst-address=203.0.113.20 \
protocol=tcp \
dst-port=465,587,993,443 \
action=dst-nat \
to-addresses=10.20.30.10 \
comment="MailPlus client services"

Что делает dst-nat:
Пакет приходит из Интернета на публичный IP MikroTik, после чего router меняет destination на внутренний адрес Synology. При этом я публикую только нужные сервисы, а не весь NAS целиком. Это принципиально безопаснее, чем делать какой-либо 1:1 «открыть всё».

9. Временный HTTP80 для Let's Encrypt

Код:
/ip firewall nat
add chain=dstnat \
in-interface=ether1 \
dst-address=203.0.113.20 \
protocol=tcp \
dst-port=80 \
action=dst-nat \
to-addresses=10.20.30.10 \
comment="MailPlus ACME HTTP 80 temporary"

Что делает dst-nat:
Пакет приходит из Интернета на публичный IP MikroTik, после чего router меняет destination на внутренний адрес Synology. При этом я публикую только нужные сервисы, а не весь NAS целиком. Это принципиально безопаснее, чем делать какой-либо 1:1 «открыть всё».

После выпуска сертификата:
Код:
/ip firewall nat
disable [find comment="MailPlus ACME HTTP 80 temporary"]

Почему правило отключаю, а не обязательно удаляю:
HTTP80 мне нужен только для ACME-проверки сертификата. После выпуска сертификата поверхность атаки лучше снова уменьшить. Отключённое подписанное comment-ом правило удобно быстро включить снова при ручном renewal, если это понадобится.

10. Forward Firewall

Код:
/ip firewall filter
add chain=forward \
in-interface=ether1 \
dst-address=10.20.30.10 \
protocol=tcp \
dst-port=25,465,587,993,443 \
connection-nat-state=dstnat \
action=accept \
comment="Allow published MailPlus services"

Почему NAT недостаточно:
dst-nat только меняет адрес назначения. После этого пакет всё равно проходит цепочку forward, где его может остановить общий WAN drop. Поэтому я явно разрешаю только соединения к опубликованным MailPlus-портам. Порядок rules здесь критичен: allow должен стоять раньше общего deny.

Разрешающее правило должно быть выше общего WAN drop.

11. Изоляция MAIL VLAN

Идея:
Код:
/ip firewall filter
add chain=forward \
src-address=10.20.30.0/24 \
out-interface=!ether1 \
action=drop \
comment="Block MAIL VLAN to internal networks"

Почему NAT недостаточно:
dst-nat только меняет адрес назначения. После этого пакет всё равно проходит цепочку forward, где его может остановить общий WAN drop. Поэтому я явно разрешаю только соединения к опубликованным MailPlus-портам. Порядок rules здесь критичен: allow должен стоять раньше общего deny.

Не вставляйте его вслепую. Сначала разрешите нужные DNS/NTP/backup/LDAP/monitoring потоки.

12. Counters NAT

Код:
/ip firewall nat print stats

Почему смотрю counters:
Это один из самых быстрых способов понять, вообще попадает ли реальный трафик в правило. Во время внешнего подключения packets/bytes нужного NAT rule должны увеличиваться. Ноль — значит искать надо до MailPlus: IP, interface, провайдера или условия правила.

Если при внешнем тесте SMTP счётчик правила 25 не растёт — пакет не соответствует rule или вообще не приходит.

13. Counters Firewall

Код:
/ip firewall filter print stats

Почему NAT недостаточно:
dst-nat только меняет адрес назначения. После этого пакет всё равно проходит цепочку forward, где его может остановить общий WAN drop. Поэтому я явно разрешаю только соединения к опубликованным MailPlus-портам. Порядок rules здесь критичен: allow должен стоять раньше общего deny.

Если dst-nat растёт, а allow-rule нет — проверяйте порядок/условия firewall.

14. Torch

Код:
/tool torch interface=ether1 port=25

Когда использую Torch:
Это быстрый live-view трафика интерфейса. Я запускаю его на WAN и одновременно делаю внешний test connection. Если пакетов вообще нет, проблема находится до MailPlus и зачастую даже до NAT.

Показывает трафик на WAN в реальном времени.

15. Packet Sniffer

Код:
/tool sniffer quick ip-address=10.20.30.10 port=25

Зачем ещё sniffer:
Torch показывает общий поток, а sniffer удобно привязать к внутреннему IP/порту Synology. Если пакет виден уже на 10.20.30.10:25, значит маршрутизатор перевёл его внутрь, и дальше я проверяю firewall/service на NAS.

Если здесь виден трафик — пакет дошёл до внутреннего адреса Synology.

16. Внешняя проверка

Linux:
Код:
nc -vz 203.0.113.20 25

Что показывает тест:
nc здесь проверяет только возможность открыть TCP connection. Он не доказывает правильность SMTP или TLS, зато быстро отвечает на вопрос «порт вообще доступен снаружи?». После него я уже перехожу к openssl s_client.

или:
Код:
openssl s_client \
-connect 203.0.113.20:25 \
-starttls smtp \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

17. DSM 5001

Я не публиковал 5001 напрямую в Интернет. Для пользователей и reverse proxy достаточно 443.

18. Чек-лист

Код:
[ ] VLAN30 существует
[ ] Gateway 10.20.30.1 существует
[ ] Access-порт Synology PVID30
[ ] Trunk несёт VLAN30
[ ] Synology пингует gateway
[ ] Public IP добавлен корректно
[ ] src-nat выше masquerade
[ ] dst-nat 25
[ ] dst-nat 465
[ ] dst-nat 587
[ ] dst-nat 993
[ ] dst-nat 443
[ ] Forward allow выше WAN drop
[ ] NAT counters растут
[ ] Firewall counters растут
[ ] DSM 5001 не опубликован
 
Часть 5: Dovecot Master User без старых паролей

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

Решение нашлось штатное для Dovecot — Master User. По сути это отдельная служебная учётка, которой Dovecot разрешает открыть mailbox нужного пользователя. В результате я смог мигрировать ящики, вообще не зная их старых паролей.

Если пароли пользователей старого Modoboa неизвестны, не надо собирать их у сотрудников.

Dovecot умеет Master User.

1. Идея

Обычная auth:
Код:
user@example.org + USER_PASSWORD

Master auth:
Код:
Target mailbox: user@example.org
Master account: migration
Master password: MASTER_PASSWORD

2. Сначала изучаем текущий Dovecot

Код:
sudo doveconf -n

Что показывает команда:
Это аналогичный «срез» активной конфигурации Dovecot без лишних defaults. Для миграции особенно важны passdb, userdb, mail_location, SSL и auth mechanisms. Именно здесь я понимаю, как старый сервер вообще проверяет пользователей.

Смотрю на:
Код:
auth_mechanisms
passdb
userdb
auth_master_user_separator

Код:
sudo grep -R "passdb" /etc/dovecot 2>/dev/null

Почему просто не открываю первый попавшийся конфиг:
Dovecot обычно собирает конфигурацию из нескольких include-файлов. grep -R помогает найти, где реально объявлен нужный параметр. 2>/dev/null убирает второстепенные сообщения о недоступных файлах, чтобы они не мешали читать результат.

Код:
sudo grep -R "userdb" /etc/dovecot 2>/dev/null

Почему просто не открываю первый попавшийся конфиг:
Dovecot обычно собирает конфигурацию из нескольких include-файлов. grep -R помогает найти, где реально объявлен нужный параметр. 2>/dev/null убирает второстепенные сообщения о недоступных файлах, чтобы они не мешали читать результат.

Modoboa часто использует SQL. Не заменяйте рабочий SQL passdb на passwd-file.

3. Backup Dovecot

Код:
sudo tar -czf /root/dovecot-before-master-user.tar.gz /etc/dovecot

Почему сначала backup:
До изменения Dovecot auth я обязательно сохраняю весь каталог конфигурации. tar -czf создаёт gzip-архив. Если master-auth настройка окажется неудачной, я могу восстановить исходное состояние, а не пытаться вспоминать каждую изменённую строку.

4. Типовая схема Master Passdb

Один из стандартных вариантов:
Код:
auth_master_user_separator = *

passdb {
    driver = passwd-file
    master = yes
    args = /etc/dovecot/master-users
}

Логин становится:
Код:
user@example.org*migration

Конкретное место конфигурации зависит от версии Modoboa/Dovecot.

5. Генерируем hash

Код:
sudo doveadm pw -s SHA512-CRYPT

Что делает команда:
Она не меняет пароль пользователя. Она только генерирует безопасный hash для заданного мной master password. В файл Dovecot я записываю именно hash, а открытый пароль храню отдельно и временно — только на период миграции.

В итоге получаю:
Код:
{SHA512-CRYPT}$6$...

6. master-users

Создаю:
Код:
sudo nano /etc/dovecot/master-users

Что происходит:
Команда просто открывает нужный конфигурационный файл в редакторе Nano. Здесь важно не копировать имя файла вслепую: сначала по doveconf -n надо убедиться, что именно эта схема master passdb подходит вашей версии Dovecot.

Строка:
Код:
migration:{SHA512-CRYPT}$6$...

Права:
Код:
sudo chown root:dovecot /etc/dovecot/master-users

Зачем меняю владельца:
Master-user файл содержит credential с очень широкими правами, поэтому он не должен принадлежать случайному пользователю. Этой командой я задаю владельца/группу так, чтобы Dovecot мог читать файл, а обычные пользователи — нет.

Код:
sudo chmod 640 /etc/dovecot/master-users

Что означает 640:
Владелец может читать и писать, группа — только читать, остальные не имеют доступа. Для файла с master credential это гораздо разумнее, чем оставлять обычные 644.

7. Проверка конфигурации

Код:
sudo doveconf -n

Что показывает команда:
Это аналогичный «срез» активной конфигурации Dovecot без лишних defaults. Для миграции особенно важны passdb, userdb, mail_location, SSL и auth mechanisms. Именно здесь я понимаю, как старый сервер вообще проверяет пользователей.

Дополнительно:
Код:
sudo doveconf -a >/dev/null

Зачем запускаю:
Этой командой я заставляю Dovecot разобрать конфигурацию целиком. Сам вывод мне не нужен, поэтому отправляю его в /dev/null. Если конфиг сломан синтаксически, команда завершится ошибкой — и я не делаю reload, пока не исправлю её.

Если syntax error нет — делаем reload.

8. Reload

Код:
sudo systemctl reload dovecot

Почему reload, а не restart:
Сервер ещё production. Reload просит Dovecot перечитать конфигурацию без жёсткой остановки всей службы. После него я сразу проверяю status и auth-test. Если конкретная версия/конфигурация не поддерживает reload, systemd об этом сообщит.

Код:
sudo systemctl status dovecot --no-pager

Что я хочу увидеть:
Проверяю, что Dovecot действительно активен. Именно через него будет идти IMAP Migration, поэтому переходить дальше при failed/inactive состоянии смысла нет.

9. Проверка Master User

Код:
sudo doveadm auth test \
'user@example.org*migration' \
'MASTER_PASSWORD'

Это ключевая проверка Master User:
Здесь Dovecot сам подтверждает, что связка «целевой пользователь + master credential» принимается. Пока эта команда не проходит, я не запускаю MailPlus Migration — иначе потом придётся разбирать десятки одинаковых auth errors.

Пока auth test не успешен, массовую миграцию не запускаем.

10. Проверка через реальный IMAP

Код:
openssl s_client \
-connect 127.0.0.1:993 \
-crlf \
-quiet

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

Затем:
Код:
a1 LOGIN "user@example.org*migration" "MASTER_PASSWORD"

Что это такое:
Это уже не shell-команда, а ручная IMAP-команда внутри TLS-сессии. Метка вроде a1 — произвольный идентификатор запроса. Если сервер отвечает a1 OK, мы доказали работу не только внутреннего doveadm auth test, но и реальной IMAP-аутентификации.

Успех:
Код:
a1 OK ...

Небольшое пояснение:
Строки a1, a2 и т.п. — это команды протокола IMAP, которые вводятся уже после подключения через openssl s_client. Это не команды Linux.

Список папок:
Код:
a2 LIST "" "*"

Зачем этот запрос:
После успешного LOGIN прошу IMAP показать доступные mailbox. Так я вижу, что master-auth не просто прошла, а действительно открыла нужного пользователя и его папки.

Выход:
Код:
a3 LOGOUT

Здесь всё просто:
Корректно закрываю ручную IMAP-сессию. Для диагностики это мелочь, но лучше завершать protocol session нормально, а не просто рвать соединение.

11. Логи Dovecot

Код:
sudo journalctl -u dovecot -n 100 --no-pager

Когда это пригодится:
Если auth-test или reload не проходит, здесь обычно видно настоящую причину: неправильный passdb, права файла, syntax error или отказ авторизации. -n 100 показывает последние записи, а -f удобно использовать во время повторной попытки входа.

Realtime:
Код:
sudo journalctl -u dovecot -f

Когда это пригодится:
Если auth-test или reload не проходит, здесь обычно видно настоящую причину: неправильный passdb, права файла, syntax error или отказ авторизации. -n 100 показывает последние записи, а -f удобно использовать во время повторной попытки входа.

Остановить:
Код:
Ctrl+C

12. Безопасность

Master credential даёт доступ ко всем целевым mailbox.

Поэтому:
  • длинный уникальный пароль;
  • не публиковать в скриншотах;
  • не оставлять в shell history;
  • после Final Delta Master User удалить/отключить.

13. Чек-лист

Код:
[ ] doveconf -n изучен
[ ] Понятен passdb/userdb
[ ] Backup /etc/dovecot сделан
[ ] Master passdb добавлен
[ ] Hash сохранён
[ ] Права файла ограничены
[ ] doveconf без ошибок
[ ] reload успешен
[ ] doveadm auth test успешен
[ ] IMAP LOGIN успешен
[ ] Master User будет удалён после миграции
 
Часть 6: тестовая и основная IMAP-миграция

Я специально не запускал массовую миграцию сразу. Сначала взял один обычный mailbox и прогнал весь путь на нём. Это тот случай, когда лишние полчаса в начале могут сэкономить несколько часов потом.

Мне было важно проверить не только INBOX, но и Sent, Drafts, пользовательские папки, вложения и старые письма. Только когда один ящик выглядел нормально, я запускал остальные.

1. Почему сначала один mailbox

Первый тест проверяет:
  • INBOX;
  • Sent;
  • Drafts;
  • Trash;
  • Junk;
  • пользовательские папки;
  • даты;
  • вложения;
  • кириллицу;
  • read/unread flags.

2. Фиксируем старый mailbox

Папки:
Код:
sudo doveadm mailbox list -u user@example.org

Почему я сохраняю этот список:
INBOX увидеть легко, а вот пользовательские папки часто забывают проверить. Этой командой я получаю реальные IMAP mailboxes конкретного пользователя и потом сравниваю их с MailPlus. Если у человека был Archive/Projects/2025, он тоже должен приехать.

Статистика:
Код:
sudo doveadm mailbox status \
-u user@example.org \
messages vsize \
INBOX Sent Drafts Junk Trash

Что здесь сравниваю:
messages даёт количество сообщений, а vsize — виртуальный размер. Для меня это контроль до/после переноса. Я не требую абсолютного совпадения размера байт в байт, потому что разные системы хранят метаданные по-разному, но огромная разница в количестве писем — стоп-сигнал.

Сохранить:
Код:
sudo doveadm mailbox status \
-u user@example.org \
messages vsize \
INBOX Sent Drafts Junk Trash \
| tee /root/user-before-migration.txt

Что здесь сравниваю:
messages даёт количество сообщений, а vsize — виртуальный размер. Для меня это контроль до/после переноса. Я не требую абсолютного совпадения размера байт в байт, потому что разные системы хранят метаданные по-разному, но огромная разница в количестве писем — стоп-сигнал.

tee показывает вывод и одновременно пишет его в файл.

3. Проверяю источник

Код:
openssl s_client \
-connect mail.example.org:993 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

4. MailPlus Migration

Типовые параметры:
Код:
Protocol: IMAP
Server: mail.example.org
Port: 993
SSL/TLS: enabled

Для Master User используем формат, который требует конкретная версия MailPlus.

Если интерфейс предлагает CSV template — скачиваем его из MailPlus и заполняем именно по шаблону.

5. После Completed

Входим под тестовым пользователем и вручную проверяем:
Код:
INBOX
Sent
Drafts
Trash
Junk
Archive
Custom folders

Открываем старое письмо, письмо с вложением и письмо из нестандартной папки.

6. Сравниваем counts

Снова:
Код:
sudo doveadm mailbox status \
-u user@example.org \
messages vsize \
INBOX Sent Drafts Junk Trash

Что здесь сравниваю:
messages даёт количество сообщений, а vsize — виртуальный размер. Для меня это контроль до/после переноса. Я не требую абсолютного совпадения размера байт в байт, потому что разные системы хранят метаданные по-разному, но огромная разница в количестве писем — стоп-сигнал.

Если на старом INBOX = 436, а на новом 12 — массовую миграцию не продолжаем.

Небольшая разница vsize не всегда проблема: системы могут хранить служебные данные по-разному.

7. Special-use folders

Фактические имена могут отличаться:
Код:
Sent
Sent Items
Отправленные
Trash
Deleted
Корзина

Смотрите на содержимое и IMAP special-use semantics, а не только название.

8. Основная Migration

После успешного теста запускаем остальных пользователей.

DNS пока не меняем.

Код:
External senders
      ↓
Old Modoboa
      ↓
Users still work here

Old Dovecot
      |
      | IMAP Migration
      v
New MailPlus

9. Новые письма во время миграции

Это нормально — пугаться этого не надо.

пример:
Код:
12:00 initial sync закончил INBOX
12:05 пришло новое письмо
12:10 пользователь отправил письмо

Эти изменения доберёт Final Delta.

10. Не разводим пользователей по двум серверам

До официального cutover source of truth остаётся старый сервер.

Не стоит переводить часть сотрудников на новый MailPlus заранее.

11. Большие mailbox

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

12. Статус каждого пользователя

Фиксируем:
Код:
User
Start
End
Success/Failed
Error

Один Failed = задача не закончена.

13. Состояние перед cutover

Код:
Old server:
  Production
  Содержит актуальную почту

New server:
  Содержит initial copy
  Может не иметь последних изменений

Именно это нам нужно.

14. Чек-лист

Код:
[ ] Test mailbox проверен
[ ] Custom folders на месте
[ ] Sent/Drafts на месте
[ ] Attachments открываются
[ ] Основная Migration завершена
[ ] Failed users разобраны
[ ] Большие mailbox завершены
[ ] Старый сервер всё ещё production
[ ] DNS ещё не переключён
 
Часть 7: DNS, SPF, DKIM, DMARC, PTR и TLS

DNS — это тот этап, где почта может вроде бы «работать», но потом половина внешних систем начинает относиться к ней подозрительно. Поэтому я не ограничивался одной MX-записью: отдельно проверял A, PTR, SPF, DKIM, DMARC и сертификаты на каждом сервисе.

Ещё один важный момент: новый MailPlus я тестировал ДО переключения DNS. Для этого очень выручают curl --resolve и openssl s_client — можно проверить реальный новый сервер, пока пользователи всё ещё работают на старом.

1. Итоговая DNS-цепочка

Код:
example.org
   |
   | MX 10
   v
mail.example.org
   |
   | A
   v
203.0.113.20
   |
   | PTR
   v
mail.example.org

2. MX

Код:
example.org. MX 10 mail.example.org.

Linux:
Код:
dig MX example.org @1.1.1.1 +short

Что проверяется:
Команда показывает MX домена — то есть куда внешние почтовые серверы должны доставлять почту. Ключ +short убирает лишний служебный вывод и оставляет только сам результат.

PowerShell:
Код:
Resolve-DnsName example.org -Type MX -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

3. A-record

До cutover:
Код:
mail.example.org → 203.0.113.10

После:
Код:
mail.example.org → 203.0.113.20

Проверяю это так:
Код:
dig A mail.example.org @1.1.1.1 +short

Что проверяется:
Здесь я спрашиваю A-запись не у локального DNS-кэша, а напрямую у выбранного публичного resolver. Это позволяет отделить публичное состояние зоны от того, что закэшировал конкретный компьютер или внутренний DNS.

4. TTL

Заранее уменьшаем, например:
Код:
600

Это 10 минут.

TTL не гарантирует мгновенное обновление всех resolver, но уменьшает время кэширования новых ответов.

5. SPF во время перехода

Код:
v=spf1 ip4:203.0.113.10 ip4:203.0.113.20 -all

После окончания:
Код:
v=spf1 ip4:203.0.113.20 -all

Если есть include: внешних отправителей — сохраняем их.

Проверяю это так:
Код:
Resolve-DnsName example.org -Type TXT -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

6. PTR

Провайдер должен настроить:
Код:
203.0.113.20 → mail.example.org

Linux:
Код:
dig -x 203.0.113.20 @1.1.1.1 +short

Зачем нужна эта проверка:
-x делает reverse lookup — IP превращается обратно в имя. Для почтового сервера я хочу получить mail.example.org. PTR настраивается у владельца IP, а не обычной A-записью в своей DNS-зоне.

Forward и reverse желательно согласовать:
Код:
mail.example.org → 203.0.113.20
203.0.113.20 → mail.example.org

7. DKIM

Selector:
Код:
mailplus

DNS:
Код:
mailplus._domainkey.example.org

TXT:
Код:
v=DKIM1; k=rsa; p=PUBLIC_KEY...

Проверяю это так:
Код:
Resolve-DnsName mailplus._domainkey.example.org -Type TXT -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

8. DMARC

Например:
Код:
_dmarc.example.org
v=DMARC1; p=quarantine; pct=100;

Если DMARC вводится впервые, безопаснее начать с p=none и изучить legitimate senders.

Проверяю это так:
Код:
Resolve-DnsName _dmarc.example.org -Type TXT -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

9. Let's Encrypt

Сертификат:
Код:
mail.example.org

Назначаем:
  • MailPlus Web;
  • Postfix;
  • Dovecot.

Если используется HTTP-01 challenge, TCP80 должен временно доходить до Synology.

10. Проверяю HTTPS ДО cutover

Код:
curl -vk \
--resolve mail.example.org:443:203.0.113.20 \
https://mail.example.org/

Почему это очень удобно до cutover:
--resolve позволяет сказать curl: «для этого hostname используй вот этот IP». При этом URL и TLS SNI остаются правильными. Так я полноценно проверяю новый mail.example.org ещё тогда, когда публичный DNS всё ещё ведёт пользователей на старую систему.

--resolve временно подменяет DNS только для этого curl-запроса.

11. IMAPS нового сервера

Код:
openssl s_client \
-connect 203.0.113.20:993 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

Смотрю на:
Код:
Verify return code: 0 (ok)

12. SMTP STARTTLS

Код:
openssl s_client \
-connect 203.0.113.20:25 \
-starttls smtp \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

13. Submission

Код:
openssl s_client \
-connect 203.0.113.20:587 \
-starttls smtp \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

14. SMTPS

Код:
openssl s_client \
-connect 203.0.113.20:465 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

15. Open Relay

Код:
openssl s_client \
-connect 203.0.113.20:25 \
-starttls smtp \
-servername mail.example.org \
-crlf \
-quiet

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

Далее:
Код:
EHLO test.example
MAIL FROM:<sender@external.example>
RCPT TO:<recipient@another-external.example>

Нормальный отказ:
Код:
554 5.7.1 Relay access denied

16. Existing/Unknown recipient

Существующий:
Код:
RCPT TO:<user@example.org>

должен приниматься.

Несуществующий:
Код:
RCPT TO:<does-not-exist@example.org>

должен получить 550 5.1.1 или эквивалент.

17. SMTP banner

Если всё нормально, получаю:
Код:
220 mail.example.org ESMTP ...

Если сервер представляется случайным hostname — исправляем до production.

18. Чек-лист

Код:
[ ] Public IP доступен
[ ] SMTP 25 работает
[ ] 465 работает
[ ] 587 работает
[ ] 993 работает
[ ] 443 работает
[ ] TLS mail.example.org valid
[ ] PTR нового IP настроен
[ ] SPF содержит новый IP
[ ] DKIM опубликован
[ ] DMARC опубликован
[ ] Open Relay отсутствует
[ ] Existing recipient принимается
[ ] Unknown recipient отклоняется
 
Часть 8: DNS Cutover и Final Delta

Вот здесь начинается собственно переключение. К этому моменту у меня новый MailPlus уже принимал SMTP, отдавал IMAP, имел правильные сертификаты, PTR, SPF/DKIM/DMARC и прошёл тест на Open Relay.

Самый интересный нюанс обнаружился после смены DNS: имя mail.example.org, которое раньше было источником Migration, теперь уже указывает на новый MailPlus. Поэтому финальную синхронизацию я делал напрямую по IP старого сервера.

1. Перед cutover

Старый:
Код:
203.0.113.10

Новый:
Код:
203.0.113.20

DNS пока:
Код:
mail.example.org → 203.0.113.10

Новый сервер уже должен быть протестирован напрямую по IP.

2. Дополнительная pre-cutover sync

Если после initial migration прошло много времени — запускаем повторную синхронизацию ещё до DNS switch. Это уменьшит объём Final Delta.

3. Меняю A-record

Код:
OLD:
mail.example.org → 203.0.113.10

NEW:
mail.example.org → 203.0.113.20

MX при схеме:
Код:
example.org MX 10 mail.example.org

не меняется.

4. Проверяю публичный DNS

Код:
Resolve-DnsName mail.example.org -Type A -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

Если всё нормально, получаю:
Код:
203.0.113.20

5. Главная ловушка

После cutover:
Код:
mail.example.org → NEW

Если Migration source оставить mail.example.org, задача будет попадать на новый сервер.

Для Final Delta указываем:
Код:
203.0.113.10
Port 993
SSL/TLS enabled

6. Hostname mismatch сертификата

Сертификат старого сервера:
Код:
mail.example.org

Connection:
Код:
203.0.113.10

Для контролируемой финальной Migration можно временно отключить проверку имени сертификата, сохранив TLS.

Для обычных клиентов так делать нельзя.

7. Final Delta

Код:
Old Modoboa
203.0.113.10
    |
    | IMAPS 993
    | Final Delta
    v
New MailPlus
203.0.113.20

Проверяю status каждого mailbox. Один Failed = миграция не закончена.

8. Реальный входящий тест

Код:
external@example.net
        ↓
user@example.org

Письмо должно прийти на MailPlus.

9. Реальный исходящий тест

Код:
user@example.org
        ↓
external@example.net

В headers ищем:
Код:
spf=pass
dkim=pass
dmarc=pass

И:
Код:
Received: from mail.example.org [203.0.113.20]

10. Если письмо попало в Spam

При PASS SPF/DKIM/DMARC не надо сразу менять authentication records.

Возможны:
  • новая репутация IP;
  • короткое тестовое содержимое;
  • нулевая история отправки;
  • фильтры получателя.

11. Проверяю реальные клиенты

До отключения старого сервера проверяем несколько рабочих мест:
Код:
IMAP login
Send
Receive
Sent folder
Attachments

12. Чек-лист

Код:
[ ] A переключена
[ ] Public DNS показывает новый IP
[ ] Incoming работает
[ ] Outgoing работает
[ ] SPF PASS
[ ] DKIM PASS
[ ] DMARC PASS
[ ] PTR нового IP корректен
[ ] Migration source изменён на старый IP
[ ] Final Delta Success для всех mailbox
[ ] Последние письма проверены
[ ] Thunderbird проверен
[ ] Старый сервер пока доступен
 
Часть 9: SMTP, IMAP, Thunderbird и диагностика

После переключения сервера почти всегда всплывают не серверные, а клиентские мелочи. У меня, например, часть старых профилей Thunderbird продолжала ходить на IMAP 143 + STARTTLS. Пока не перевёл их на 993 + SSL/TLS, клиент даже нормально не запрашивал новый пароль.

Поэтому здесь я собрал порядок диагностики, которым пользовался сам: от DNS и TCP до сертификата, логина и сохранённого пароля. Он помогает не начинать с «переустановим Thunderbird», когда проблема на самом деле в NAT.

Когда пользователь говорит «почта не работает», нужно определить слой:
Код:
DNS → Network → NAT → Firewall → TLS → SMTP/IMAP → Auth → Client

1. DNS

Код:
Resolve-DnsName mail.example.org -Type A -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

2. Очистка DNS cache Windows

Код:
ipconfig /flushdns

Что делает команда:
Она очищает локальный DNS cache Windows. Это не меняет публичный DNS — просто заставляет конкретный ПК заново запросить имя. После cutover это полезно, если одна рабочая станция упорно продолжает ходить на старый IP.

Полезно после cutover.

3. TCP-проверка Windows

Код:
Test-NetConnection mail.example.org -Port 993

Почему этот тест удобен на Windows:
Он проверяет именно TCP-доступность порта. Если TcpTestSucceeded : False, бессмысленно пока менять пароль Thunderbird — connection ещё даже не дошёл до уровня IMAP/SMTP authentication.

Код:
Test-NetConnection mail.example.org -Port 587

Почему этот тест удобен на Windows:
Он проверяет именно TCP-доступность порта. Если TcpTestSucceeded : False, бессмысленно пока менять пароль Thunderbird — connection ещё даже не дошёл до уровня IMAP/SMTP authentication.

Нужно:
Код:
TcpTestSucceeded : True

Если False — пароль пока вообще не обсуждаем.

4. IMAPS

Код:
openssl s_client \
-connect mail.example.org:993 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

5. SMTP 25

Код:
openssl s_client \
-connect mail.example.org:25 \
-starttls smtp \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

6. Submission 587

Код:
openssl s_client \
-connect mail.example.org:587 \
-starttls smtp \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

7. SMTPS 465

Код:
openssl s_client \
-connect mail.example.org:465 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

8. Короткая проверка IMAP certificate

Код:
echo | openssl s_client \
-connect mail.example.org:993 \
-servername mail.example.org 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates

Зачем такая длинная конструкция:
Первая часть устанавливает TLS-соединение, а openssl x509 оставляет только основные данные сертификата: subject, issuer и сроки действия. Это удобно, когда полный вывод s_client уже не нужен и надо быстро сравнить сертификаты SMTP/IMAP.

SMTP certificate:
Код:
echo | openssl s_client \
-connect mail.example.org:25 \
-starttls smtp \
-servername mail.example.org 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates

Зачем такая длинная конструкция:
Первая часть устанавливает TLS-соединение, а openssl x509 оставляет только основные данные сертификата: subject, issuer и сроки действия. Это удобно, когда полный вывод s_client уже не нужен и надо быстро сравнить сертификаты SMTP/IMAP.

Если HTTPS верный, а SMTP/IMAP другой — сертификат назначен не всем сервисам.

9. Thunderbird IMAP

Код:
Server: mail.example.org
Port: 993
Connection security: SSL/TLS
Authentication: Normal password
Username: user@example.org

10. Thunderbird SMTP

Вариант 1:
Код:
Port: 587
Security: STARTTLS

Вариант 2:
Код:
Port: 465
Security: SSL/TLS

11. Старый профиль 143 + STARTTLS

После миграции старый Thunderbird может продолжать использовать 143. Если production-профиль рассчитан на 993+SSL/TLS — исправляем порт и security.

12. Сохранённые пароли

Thunderbird:
Код:
Settings
→ Privacy & Security
→ Saved Passwords

Удаляю старые IMAP/SMTP credentials, затем клиент запросит новые.

13. 550 5.1.1 User unknown

Значит SMTP доступен, но recipient отсутствует в local recipient table.

Проверяю mailbox/alias.

14. 554 Relay access denied

В Open Relay test это правильный результат.

15. Ручной SMTP

Код:
openssl s_client \
-connect mail.example.org:25 \
-starttls smtp \
-crlf \
-quiet

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

Далее:
Код:
EHLO test.example
MAIL FROM:<sender@external.example>
RCPT TO:<user@example.org>
QUIT

16. MikroTik counters

Код:
/ip firewall nat print stats

Почему смотрю counters:
Это один из самых быстрых способов понять, вообще попадает ли реальный трафик в правило. Во время внешнего подключения packets/bytes нужного NAT rule должны увеличиваться. Ноль — значит искать надо до MailPlus: IP, interface, провайдера или условия правила.

Код:
/ip firewall filter print stats

Почему NAT недостаточно:
dst-nat только меняет адрес назначения. После этого пакет всё равно проходит цепочку forward, где его может остановить общий WAN drop. Поэтому я явно разрешаю только соединения к опубликованным MailPlus-портам. Порядок rules здесь критичен: allow должен стоять раньше общего deny.

Если dst-nat counter не растёт — соединение не попадает под rule.

17. Torch

Код:
/tool torch interface=ether1 port=993

Когда использую Torch:
Это быстрый live-view трафика интерфейса. Я запускаю его на WAN и одновременно делаю внешний test connection. Если пакетов вообще нет, проблема находится до MailPlus и зачастую даже до NAT.

18. Sniffer

Код:
/tool sniffer quick ip-address=10.20.30.10 port=993

Зачем ещё sniffer:
Torch показывает общий поток, а sniffer удобно привязать к внутреннему IP/порту Synology. Если пакет виден уже на 10.20.30.10:25, значит маршрутизатор перевёл его внутрь, и дальше я проверяю firewall/service на NAS.

19. Проверка PTR

Код:
dig -x 203.0.113.20 @1.1.1.1 +short

Зачем нужна эта проверка:
-x делает reverse lookup — IP превращается обратно в имя. Для почтового сервера я хочу получить mail.example.org. PTR настраивается у владельца IP, а не обычной A-записью в своей DNS-зоне.

20. Headers реального письма

Смотрю на:
Код:
spf=pass
dkim=pass
dmarc=pass
Received: from mail.example.org [203.0.113.20]

21. Диагностический алгоритм IMAP

Код:
1. Resolve-DnsName
2. Test-NetConnection :993
3. openssl s_client
4. certificate
5. username
6. password
7. saved credentials
8. Thunderbird profile

22. Диагностический алгоритм входящей почты

Код:
1. MX
2. A
3. TCP25
4. dst-nat counter
5. forward filter counter
6. SMTP RCPT test
7. MailPlus mailbox/alias
8. logs
 
Часть 10: Calendar и Contacts — CalDAV/CardDAV

С календарями и контактами у меня получилось даже интереснее, чем с самой почтой. На старом сервере это было через Radicale, а на Synology — Calendar и Contacts. Сам импорт ICS/VCF простой, а вот shared CalDAV/CardDAV уже имеет свои нюансы.

Особенно важный момент — общий календарь у другого пользователя может подключаться не по URL владельца, а через отдельный WebDAV binding. Поэтому здесь лучше не гадать адреса, а спрашивать сервер через PROPFIND.

Почта часто переносится проще, чем shared Calendar/Contacts.

1. Схема

Код:
Radicale
 ├─ Shared Calendar
 └─ Shared Contacts

        ↓

Synology
 ├─ Calendar
 └─ Contacts

2. Export Calendar

В итоге получаю:
Код:
calendar.ics

Импортирую в Synology Calendar.

3. Export Contacts

В итоге получаю:
Код:
contacts.vcf

Импортирую в Synology Contacts.

Выборочно проверяем кириллицу, телефоны, e-mail, organization и notes.

4. PROPFIND старого Radicale

Без записи пароля в history:
Код:
curl -i \
-u 'user@example.org' \
-X PROPFIND \
-H 'Depth: 1' \
'https://mail.example.org/radicale/user@example.org/'

Что означает такой тест:
Ключ -I запрашивает только HTTP headers. Для CalDAV endpoint ответ 401 Unauthorized без credentials — не обязательно ошибка: он доказывает, что HTTPS и reverse proxy доступны, а сервис просто требует аутентификацию.

curl сам запросит пароль.

PROPFIND — WebDAV method для получения свойств и collections.

Depth: 1 — текущий resource + один уровень ниже.

5. Отдельный hostname

Код:
calendar.example.org

DNS:
Код:
calendar.example.org → 203.0.113.20

6. Reverse Proxy Synology

пример:
Код:
Source:
HTTPS
calendar.example.org
443

Destination:
HTTPS
127.0.0.1
5001

Порт 5001 наружу напрямую не публикуем.

7. Сертификат

Выпускаю:
Код:
calendar.example.org

Назначаем reverse proxy/service.

8. Быстрая проверка CalDAV

Код:
curl -I https://calendar.example.org/caldav/

Что означает такой тест:
Ключ -I запрашивает только HTTP headers. Для CalDAV endpoint ответ 401 Unauthorized без credentials — не обязательно ошибка: он доказывает, что HTTPS и reverse proxy доступны, а сервис просто требует аутентификацию.

Ответ:
Код:
HTTP/2 401
WWW-Authenticate: Basic ...

без credentials может быть правильным: endpoint доступен и требует auth.

9. Ищу реальные CalDAV collections

Код:
curl -i \
-u 'user' \
-X PROPFIND \
-H 'Depth: 1' \
'https://calendar.example.org/caldav.php/user/'

Что означает такой тест:
Ключ -I запрашивает только HTTP headers. Для CalDAV endpoint ответ 401 Unauthorized без credentials — не обязательно ошибка: он доказывает, что HTTPS и reverse proxy доступны, а сервис просто требует аутентификацию.

В XML ищем:
Код:
href
displayname
resourcetype
calendar

10. Точный PROPFIND

Код:
curl -i \
-u 'user' \
-X PROPFIND \
-H 'Depth: 1' \
-H 'Content-Type: application/xml' \
--data-binary '<?xml version="1.0" encoding="utf-8"?>
<d:propfind xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav">
  <d:prop>
    <d:displayname/>
    <d:resourcetype/>
    <d:current-user-privilege-set/>
  </d:prop>
</d:propfind>' \
'https://calendar.example.org/caldav.php/user/'

Что означает такой тест:
Ключ -I запрашивает только HTTP headers. Для CalDAV endpoint ответ 401 Unauthorized без credentials — не обязательно ошибка: он доказывает, что HTTPS и reverse proxy доступны, а сервис просто требует аутентификацию.

11. Shared Binding

Owner может иметь:
Код:
/caldav.php/reception/shared-calendar/

Другой пользователь может видеть ту же collection как:
Код:
/caldav.php/secretary/reception--shared-calendar/

Именно этот binding может быть правильным URL для Thunderbird пользователя.

Не угадываем — получаем href через PROPFIND под credentials конкретного пользователя.

12. Calendar R/W test

Код:
Thunderbird → создать событие → проверить Synology
Synology → создать событие → проверить Thunderbird

Только после двух направлений считаем Calendar рабочим.

13. CardDAV base

Код:
https://calendar.example.org/carddav/user/

PROPFIND:
Код:
curl -i \
-u 'user' \
-X PROPFIND \
-H 'Depth: 1' \
'https://calendar.example.org/carddav/user/'

Что означает такой тест:
Ключ -I запрашивает только HTTP headers. Для CalDAV endpoint ответ 401 Unauthorized без credentials — не обязательно ошибка: он доказывает, что HTTPS и reverse proxy доступны, а сервис просто требует аутентификацию.

14. Address Book collection

Часто path содержит UUID:
Код:
https://calendar.example.org/carddav/user/COLLECTION-ID/

Не копируем чужой UUID. Получаю реальный через PROPFIND.

15. Проверка privileges

Код:
curl -i \
-u 'user' \
-X PROPFIND \
-H 'Depth: 0' \
-H 'Content-Type: application/xml' \
--data-binary '<?xml version="1.0" encoding="utf-8"?>
<d:propfind xmlns:d="DAV:">
  <d:prop>
    <d:current-user-privilege-set/>
  </d:prop>
</d:propfind>' \
'https://calendar.example.org/carddav/user/COLLECTION/'

Что означает такой тест:
Ключ -I запрашивает только HTTP headers. Для CalDAV endpoint ответ 401 Unauthorized без credentials — не обязательно ошибка: он доказывает, что HTTPS и reverse proxy доступны, а сервис просто требует аутентификацию.

16. Contacts R/W test

Код:
Thunderbird → контакт → Synology
Synology → контакт → Thunderbird

17. Mobile CardDAV

Клиенты с autodiscovery часто принимают base URL:
Код:
https://calendar.example.org/carddav/user/

и сами обнаруживают address books.

18. Старые подписки

Удаляю старые Radicale subscriptions только после того, как новые CalDAV/CardDAV полностью проверены.

19. Типичные ошибки

Код:
401 без credentials → часто норма
404 → неправильный path/reverse proxy
Calendar пуст → проверить shared binding
Read есть, Write нет → проверить permissions/privileges
Contacts видны owner, но не shared user → проверить collection этого principal

20. Чек-лист

Код:
[ ] ICS экспортирован/импортирован
[ ] Shared Calendar R/W
[ ] calendar.example.org работает
[ ] TLS valid
[ ] Reverse Proxy работает
[ ] PROPFIND owner работает
[ ] PROPFIND shared user работает
[ ] Binding найден
[ ] Calendar R/W test пройден

[ ] VCF экспортирован/импортирован
[ ] Shared Contacts R/W
[ ] CardDAV collection найдена
[ ] Contacts R/W test пройден
[ ] Mobile CardDAV проверен
[ ] Старые subscriptions удалены после проверки
 
Часть 11: безопасное отключение старого сервера и DNS Cleanup

Старый Modoboa я не выключал сразу после смены DNS. Даже когда уже казалось, что всё работает, он ещё оставался страховкой и источником для последней delta-синхронизации.

Финально я сделал так: ещё раз прогнал миграцию, проверил очередь Postfix и активные Dovecot-сессии, остановил на старом сервере Postfix и Dovecot, отправил настоящее письмо извне — и только после этого выключил машину. Вот такой тест мне кажется намного надёжнее любого «в DNS уже новый IP».

Я не стал выключать Modoboa сразу после cutover.

1. Повторная Final Delta

Если после cutover прошло время — повторяем синхронизацию. Это страхует переходное окно.

2. Status каждого mailbox

Нужно:
Код:
user1 Success
user2 Success
user3 Success
...

3. Очередь Postfix

Код:
sudo postqueue -p

Зачем это проверять:
Postfix queue — это письма, которые сервер ещё не смог окончательно обработать/доставить. Перед выключением старого сервера я хочу видеть Mail queue is empty. Иначе можно выключить машину вместе с письмом, которое всё ещё ждёт повторной доставки.

Если всё нормально, получаю:
Код:
Mail queue is empty

Если есть конкретное явно ненужное письмо:
Код:
sudo postsuper -d QUEUE_ID

Осторожно с этой командой:
Она удаляет сообщение из очереди по конкретному Queue ID. Именно поэтому я указываю ID вручную, а не использую массовое удаление. Сначала обязательно читаю postqueue -p и понимаю, что именно удаляю.

После — снова postqueue -p.

4. Активные IMAP-сессии

Код:
sudo doveadm who

Что это мне даёт:
Команда показывает активные подключения к Dovecot. До cutover пользователи здесь ожидаемы. А вот перед окончательным отключением старого сервера этот список очень полезен: если кто-то всё ещё сидит на старом IMAP, значит его клиент не переключился или использует старый DNS/профиль.

Перед выключением желательно пусто.

5. Локальный DNS старого сервера

Код:
getent ahostsv4 mail.example.org

Почему использую именно getent:
dig показывает DNS, а getent показывает, какой адрес реально получит приложение на этом Linux-хосте с учётом /etc/hosts и NSS. Это оказалось важным нюансом: старый сервер вполне может считать, что mail.example.org — это он сам, даже после изменения публичного DNS. Поэтому при странных результатах openssl я первым делом проверяю именно это.

Старый сервер может резолвить hostname локально через /etc/hosts.

6. Новый IMAP напрямую

Код:
openssl s_client \
-connect 203.0.113.20:993 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

Так тестируется именно новый MailPlus.

7. Останавливаю Postfix

Код:
sudo systemctl stop postfix

Почему сначала просто останавливаю сервис, а не выключаю машину:
Мне нужен контролируемый тест. Если Postfix старого сервера остановлен, а внешнее письмо всё равно приходит на новый MailPlus, я доказал, что старая SMTP-служба больше не участвует в доставке. При проблеме сервер всё ещё можно быстро вернуть.

Код:
sudo systemctl status postfix --no-pager

Что я хочу увидеть:
Это безопасная read-only проверка состояния Postfix. Мне важно не просто знать, что пакет установлен, а убедиться, что служба действительно запущена и systemd не показывает свежую ошибку. --no-pager нужен только для удобства — вывод сразу остаётся в терминале.

8. Останавливаю Dovecot

Код:
sudo systemctl stop dovecot

Почему останавливаю и Dovecot:
Так я исключаю старый IMAP из работы пользователей. После этого doveadm who/listeners и реальный тест показывают, что клиенты уже не зависят от старой системы.

Код:
sudo systemctl status dovecot --no-pager

Что я хочу увидеть:
Проверяю, что Dovecot действительно активен. Именно через него будет идти IMAP Migration, поэтому переходить дальше при failed/inactive состоянии смысла нет.

9. Проверяю listeners

Код:
sudo ss -lntp | grep -E ':(25|143|465|587|993)\b'

Что означает команда:
ss показывает сетевые сокеты. Я использую -l для listeners, -n чтобы не преобразовывать номера портов в названия, -t для TCP и -p чтобы увидеть процесс. То есть эта команда отвечает на очень простой вопрос: «кто реально слушает почтовые порты прямо сейчас?». Если 993 здесь нет, никакая настройка Migration его магически не создаст.

Старых Postfix/Dovecot listeners быть не должно.

10. Самый важный внешний тест

При остановленных старых Postfix/Dovecot:
Код:
external@example.net
        ↓
user@example.org

Если письмо приходит на MailPlus — входящая доставка от старого SMTP уже не зависит.

11. Исходящий контроль

Код:
user@example.org → external@example.net

Headers:
Код:
Received: from mail.example.org [203.0.113.20]
SPF PASS
DKIM PASS
DMARC PASS

12. Poweroff

Код:
sudo poweroff

Почему poweroff только здесь:
К этому моменту я уже сделал Final Delta, проверил очередь, активные сессии, остановил старые почтовые службы и отправил реальные письма. То есть выключение машины — последний шаг, а не эксперимент «посмотрим, что сломается».

13. SPF Cleanup

Было:
Код:
v=spf1 ip4:203.0.113.10 ip4:203.0.113.20 -all

Стало:
Код:
v=spf1 ip4:203.0.113.20 -all

Проверяю это так:
Код:
Resolve-DnsName example.org -Type TXT -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

14. Старый DKIM

Удаляю:
Код:
modoboa._domainkey.example.org

Оставляю:
Код:
mailplus._domainkey.example.org

Проверка старого:
Код:
Resolve-DnsName modoboa._domainkey.example.org -Type TXT -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

В норме ожидаю NXDOMAIN.

15. Root/www A-records

Если есть:
Код:
example.org → 203.0.113.10
www.example.org → 203.0.113.10

не удаляем автоматически.

Сначала выясняем, нет ли там сайта/сервиса.

Если точно не используются — удаляем.

16. Старый PTR

Провайдеру:
Код:
Удалить:
203.0.113.10 → mail.example.org

Оставить:
203.0.113.20 → mail.example.org

17. Проверка нового PTR

Код:
dig -x 203.0.113.20 @1.1.1.1 +short

Зачем нужна эта проверка:
-x делает reverse lookup — IP превращается обратно в имя. Для почтового сервера я хочу получить mail.example.org. PTR настраивается у владельца IP, а не обычной A-записью в своей DNS-зоне.

18. Финальная DNS-проверка

Код:
Resolve-DnsName example.org -Type MX -Server 1.1.1.1
Resolve-DnsName mail.example.org -Type A -Server 1.1.1.1
Resolve-DnsName example.org -Type TXT -Server 1.1.1.1
Resolve-DnsName mailplus._domainkey.example.org -Type TXT -Server 1.1.1.1
Resolve-DnsName _dmarc.example.org -Type TXT -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

19. SSH Synology

Если включали временно — отключаем через DSM либо ограничиваем VPN/firewall.

20. Чек-лист

Код:
[ ] Final Delta выполнена
[ ] Все mailbox Success
[ ] postqueue пуст
[ ] doveadm who пуст
[ ] Postfix остановлен
[ ] Dovecot остановлен
[ ] Старые listeners отсутствуют
[ ] External inbound test успешен
[ ] Outgoing test успешен
[ ] Старый сервер выключен
[ ] Старый IP удалён из SPF
[ ] Старый DKIM удалён
[ ] Root/www A проверены
[ ] Старый PTR удалён
[ ] Новый PTR сохранён
[ ] Финальный DNS проверен
[ ] Временный SSH отключён/ограничен
 
Часть 12: типичные ошибки и финальный чек-лист

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

Я бы советовал держать этот пост открытым во время переключения: если что-то перестало работать, идти сверху вниз по слоям — DNS, сеть, NAT, firewall, TLS, протокол, авторизация, клиент — а не менять всё подряд.

Ошибка 1. Migration после cutover подключается не туда

Причина:
Код:
mail.example.org → новый MailPlus

Решение:
Код:
Final Delta source = 203.0.113.10:993

Ошибка 2. Сертификат старого сервера не совпадает с IP

Certificate:
Код:
mail.example.org

Connection:
Код:
203.0.113.10

Для контролируемой final migration временно отключаем hostname verification, но сохраняем TLS.

Ошибка 3. Старый сервер сам резолвит hostname на себя

Код:
getent ahostsv4 mail.example.org

Почему использую именно getent:
dig показывает DNS, а getent показывает, какой адрес реально получит приложение на этом Linux-хосте с учётом /etc/hosts и NSS. Это оказалось важным нюансом: старый сервер вполне может считать, что mail.example.org — это он сам, даже после изменения публичного DNS. Поэтому при странных результатах openssl я первым делом проверяю именно это.

Если видите 127.0.1.1/старый IP — тестируйте новый сервер напрямую:
Код:
openssl s_client \
-connect 203.0.113.20:993 \
-servername mail.example.org

Почему здесь openssl s_client:
Это один из самых полезных инструментов во всей миграции. Он подключается к сервису напрямую и показывает реальный TLS handshake, сертификат и ответ протокола. То есть я проверяю не веб-панель и не «галочку в DSM», а то, что действительно увидит клиент снаружи. Параметр -servername передаёт SNI, чтобы сервер отдал сертификат нужного hostname.

Ошибка 4. 8.8.8.8 timeout

Попробуйте:
Код:
Resolve-DnsName example.org -Type TXT -Server 1.1.1.1

Почему указываю -Server 1.1.1.1:
Я хочу увидеть ответ конкретного публичного resolver, а не локальный DNS-кэш Windows. Это особенно полезно сразу после изменения записи. -Type задаёт тип записи: A, MX, TXT, PTR и т.д.

Timeout одного resolver ещё не означает ошибку DNS-зоны.

Ошибка 5. NAT есть, порт не работает

Код:
/ip firewall nat print stats

Почему смотрю counters:
Это один из самых быстрых способов понять, вообще попадает ли реальный трафик в правило. Во время внешнего подключения packets/bytes нужного NAT rule должны увеличиваться. Ноль — значит искать надо до MailPlus: IP, interface, провайдера или условия правила.

Если counter dst-nat = 0 — пакет не попал под правило.

Если NAT растёт:
Код:
/ip firewall filter print stats

Почему NAT недостаточно:
dst-nat только меняет адрес назначения. После этого пакет всё равно проходит цепочку forward, где его может остановить общий WAN drop. Поэтому я явно разрешаю только соединения к опубликованным MailPlus-портам. Порядок rules здесь критичен: allow должен стоять раньше общего deny.

проверяем forward firewall.

Ошибка 6. MailPlus отправляет с неправильного IP

Headers показывают не 203.0.113.20.

Здесь мне важно увидеть:
Код:
/ip firewall nat print stats

Почему смотрю counters:
Это один из самых быстрых способов понять, вообще попадает ли реальный трафик в правило. Во время внешнего подключения packets/bytes нужного NAT rule должны увеличиваться. Ноль — значит искать надо до MailPlus: IP, interface, провайдера или условия правила.

Dedicated src-nat должен быть выше общего masquerade.

Ошибка 7. HTTPS certificate правильный, IMAP/SMTP другой

IMAP:
Код:
echo | openssl s_client \
-connect mail.example.org:993 \
-servername mail.example.org 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates

Зачем такая длинная конструкция:
Первая часть устанавливает TLS-соединение, а openssl x509 оставляет только основные данные сертификата: subject, issuer и сроки действия. Это удобно, когда полный вывод s_client уже не нужен и надо быстро сравнить сертификаты SMTP/IMAP.

SMTP:
Код:
echo | openssl s_client \
-connect mail.example.org:25 \
-starttls smtp \
-servername mail.example.org 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates

Зачем такая длинная конструкция:
Первая часть устанавливает TLS-соединение, а openssl x509 оставляет только основные данные сертификата: subject, issuer и сроки действия. Это удобно, когда полный вывод s_client уже не нужен и надо быстро сравнить сертификаты SMTP/IMAP.

Назначаем сертификат всем сервисам в DSM.

Ошибка 8. Thunderbird не спрашивает новый пароль

Здесь мне важно увидеть:
Код:
IMAP 993 + SSL/TLS

Удаляю saved credentials.

Ошибка 9. Thunderbird остался на 143 + STARTTLS

Исправляю на production-конфигурацию:
Код:
993 + SSL/TLS

если именно она принята в новой инфраструктуре.

Ошибка 10. 550 5.1.1

SMTP работает, recipient отсутствует. Проверяю mailbox/alias.

Ошибка 11. 554 Relay access denied

В Open Relay test — это правильный результат.

Ошибка 12. SPF/DKIM/DMARC PASS, но Spam

Не ломаем рабочую DNS-authentication. Возможна новая репутация IP или content filtering.

Ошибка 13. Shared Calendar пустой

Выполняем PROPFIND под конкретным shared user и используем его binding URL.

Ошибка 14. CalDAV 401

Без credentials 401 часто означает нормальный работающий endpoint с authentication.

Ошибка 15. Root domain перестал открываться после удаления A

Mail migration не означает, что example.org и www.example.org не используются как сайт. Проверяю назначение до удаления.

Ошибка 16. Старый сервер выключили слишком рано

Правильная последовательность:
Код:
Cutover
→ Test
→ Final Delta
→ Observe
→ Stop Postfix/Dovecot
→ External Test
→ Poweroff

Диагностика входящей почты

Код:
1. MX
2. A mail.example.org
3. TCP25
4. dst-nat counter
5. forward counter
6. SMTP banner
7. RCPT TO existing mailbox
8. MailPlus mailbox/alias
9. logs

Диагностика исходящей почты

Код:
1. Synology default route
2. DNS
3. src-nat
4. outbound TCP25
5. Mail queue/log
6. external recipient
7. Received headers
8. SPF
9. DKIM
10. DMARC
11. PTR

Диагностика IMAP

Код:
1. Resolve-DnsName
2. Test-NetConnection :993
3. openssl s_client
4. certificate
5. username
6. password
7. saved credentials
8. Thunderbird profile

Финальный чек-лист проекта

Код:
========================
OLD SERVER AUDIT
========================
[ ] Старый IP зафиксирован
[ ] FQDN зафиксирован
[ ] Postfix config изучен
[ ] Dovecot config изучен
[ ] Список mailbox получен
[ ] Message counts сохранены
[ ] Custom folders зафиксированы

========================
SYNOLOGY
========================
[ ] MailPlus Server установлен
[ ] MailPlus установлен
[ ] Домен создан
[ ] Пользователи созданы
[ ] Permissions назначены
[ ] FQDN настроен
[ ] Test mailbox работает

========================
NETWORK
========================
[ ] MAIL VLAN создан
[ ] Gateway создан
[ ] Trunk настроен
[ ] Access port настроен
[ ] Synology static IP
[ ] Public IP
[ ] src-nat
[ ] dst-nat 25
[ ] dst-nat 465
[ ] dst-nat 587
[ ] dst-nat 993
[ ] dst-nat 443
[ ] Allow rules выше WAN drop
[ ] DSM 5001 не опубликован

========================
MIGRATION
========================
[ ] Master User работает
[ ] Test mailbox перенесён
[ ] Папки проверены
[ ] Вложения проверены
[ ] Initial Migration завершена
[ ] Failed jobs разобраны

========================
DNS / SECURITY
========================
[ ] MX корректен
[ ] Новый A готов
[ ] PTR готов
[ ] SPF содержит новый IP
[ ] DKIM опубликован
[ ] DMARC опубликован
[ ] Web TLS valid
[ ] SMTP TLS valid
[ ] IMAP TLS valid
[ ] Open Relay отсутствует

========================
CUTOVER
========================
[ ] A переключена
[ ] Public DNS показывает новый IP
[ ] Incoming test
[ ] Outgoing test
[ ] SPF PASS
[ ] DKIM PASS
[ ] DMARC PASS
[ ] Migration source = старый IP
[ ] Final Delta
[ ] Все mailbox Success

========================
CLIENTS
========================
[ ] Thunderbird IMAP 993 SSL/TLS
[ ] SMTP 587 STARTTLS или 465 SSL/TLS
[ ] Старые passwords удалены
[ ] Send/Receive работают

========================
CALENDAR / CONTACTS
========================
[ ] ICS перенесён
[ ] Shared Calendar R/W
[ ] PROPFIND проверен
[ ] Shared binding найден
[ ] Calendar двусторонний test
[ ] VCF перенесён
[ ] Shared Contacts R/W
[ ] CardDAV collection найдена
[ ] Contacts двусторонний test

========================
DECOMMISSION
========================
[ ] Повторный Final Delta
[ ] postqueue пуст
[ ] doveadm who пуст
[ ] Postfix остановлен
[ ] Dovecot остановлен
[ ] Старые ports не слушаются
[ ] External inbound test без старого сервера
[ ] Старый сервер выключен

========================
DNS CLEANUP
========================
[ ] Старый IP удалён из SPF
[ ] Старый DKIM удалён
[ ] Root/www A проверены
[ ] Старый PTR удалён
[ ] Новый PTR сохранён
[ ] MX/A/SPF/DKIM/DMARC финально проверены

Итог

Хорошая миграция — это не просто «письма скопировались».

Это управляемая последовательность:
Код:
Audit
→ Prepare
→ Initial Sync
→ Pre-Cutover Tests
→ DNS Cutover
→ Final Delta
→ Real Mail Tests
→ Client Tests
→ Calendar/Contacts Tests
→ Old Server Isolation
→ DNS Cleanup

Каждый этап должен иметь собственную проверку и понятный критерий успеха.

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