Харденинг Zimbra: 16 пунктов, которые закрывают 90% атак

В бухгалтерскую фирму в Одинцово, 20 рабочих мест, я пришёл в ноябре 2023-го принимать на обслуживание чужой почтовый сервер. Первое, что нашёл, — открытый релей: их Zimbra рассылала спам всему миру, и домен уже висел в трёх чёрных списках. Это не теория из вебинара, это список, по которому я с тех пор прохожу каждый принятый сервер. Шестнадцать пунктов, с командами и с честной пометкой, какие из них ломают привычки пользователей и как это объяснить заранее, чтобы не получить бунт на второй день.

С чего начался этот список: открытый релей в Одинцово

Сервер принимал почту от кого угодно и отправлял куда угодно без аутентификации. Классический открытый релей. Проверяется одной командой снаружи, но я увидел причину изнутри — в zimbraMtaMyNetworks был прописан широкий диапазон, оставшийся от давней настройки «чтобы 1С могла слать без пароля».

zmprov gcf zimbraMtaMyNetworks
# 0.0.0.0/0  <- вот она, дыра: доверяем всему интернету

За неделю такого гостеприимства домен фирмы попал в Spamhaus, Barracuda и SORBS. Легитимная почта бухгалтеров начала отбиваться у контрагентов. Чинить репутацию домена — это недели, а не часы. Вот с этого случая и вырос чек-лист: я больше не хотел находить такое постфактум.

Разбил шестнадцать пунктов на четыре блока: периметр, MTA и релей, антиспам и нагрузка, доступ и учётки. Прохожу их именно в таком порядке — от самого критичного к тонкой настройке.

Блок 1. Периметр: что вообще должно быть видно снаружи

Пункт 1. Админ-консоль 7071 — за периметр или за VPN. Это не опция, это база. Порт 7071 наружу — прямое приглашение к брутфорсу админского пароля. Закрывается на файрволе или выносится в VPN.

Пункт 2. Инвентаризация того, что торчит. Прежде чем закрывать, надо знать, что открыто:

ss -tlnp | grep -E ':(25|110|143|443|587|993|995|7071|7025|10027)'
# наружу оправданы 25, 443, 587, 993 — остальное под вопросом

Пункт 3. Postjournal — отключить, если не используется. Служба postjournal слушает на порту 10027 и стала входом для целого класса RCE (позже это подтвердит CVE-2024-45519). Если она вам не нужна — выключите, и снимете вектор целиком.

Пункт 4. Вынос веб-почты за обратный прокси. Не публиковать mailboxd напрямую. Прокси даёт точку, где можно навесить fail2ban, rate-limit и WAF, а сам сервер не светит версией.

Что ломается у пользователей: вынос 7071 за VPN означает, что администратор больше не зайдёт в консоль из дома одним кликом. Это правильно, но админа надо предупредить заранее — иначе первая же его заявка будет «у меня пропал доступ к управлению».

Блок 2. MTA и релей: чтобы вас не использовали для спама

Пункт 5. Сузить zimbraMtaMyNetworks. Самый частый источник открытого релея у унаследованных серверов. Оставить только реально доверенные подсети, а лучше — вообще loopback, а отправку требовать через аутентификацию на 587.

# было широко — стало точечно
zmprov mcf zimbraMtaMyNetworks '127.0.0.0/8 [::1]/128 192.168.10.0/24'
zmmtaconfig   # применить

Пункт 6. cbpolicyd — throttling исходящей почты. Ограничивает, сколько писем в час может отправить один ящик. Спасает, когда один ящик уже увели: вместо рассылки на весь мир атакующий упрётся в лимит, а вы увидите аномалию.

Порог я ставлю не из мануала, а от живого профиля офиса: смотрю по логам, сколько писем в час реально уходит с самого активного ящика в пиковый день, и беру запас втрое. В Одинцово это вышло 150 писем в час на ящик при обычном фоне 30–40. Цифра по умолчанию тут не помогает. Отдельной строкой выношу ящик, с которого первого числа уходит рассылка актов: если про него забыть, лимит сработает ровно в тот день, когда почта нужнее всего, и заявка придёт не «нас взломали», а «бухгалтерия не может отправить закрывающие». Такому ящику лучше выписать собственную политику с большим лимитом, чем поднимать порог всем сразу.

Пункт 7. Требовать SASL-аутентификацию на отправку. Никакой отправки на внешние домены без логина. 587 с обязательным STARTTLS и SASL — единственный путь для клиентов.

Пункт 8. Проверить, что вы не релей, снаружи. После правок — обязательный контроль с внешней машины, а не «на глаз по конфигу».

# с внешнего хоста: сервер обязан отбить relay для чужого домена
swaks --server mail.example.ru --to test@gmail.com --from a@stranger.tld
# ожидаем 554 5.7.1 Relay access denied

Что ломается: старые МФУ и скрипты 1С, которые слали «без пароля через mynetworks», после сужения перестанут отправлять. Их надо заранее перевести на аутентифицированную отправку через 587 — иначе сканирование в почту встанет в первый же рабочий день, и виноваты будете вы.

Блок 3. Антиспам и нагрузка: postscreen, обучение, обновления

Пункт 9. postscreen против ботнетов. Отсекает спам-ботов на входе, до того как они дойдут до amavis и съедят CPU. Это и безопасность, и производительность одновременно. Работает он на двух простых наблюдениях: настоящий почтовый сервер дожидается приветствия и не начинает говорить первым, а ботнет-адреса засвечены в публичных чёрных списках. Первое ловится тестом на «предварительный треп», второе — весовыми DNSBL-проверками:

zmprov mcf zimbraMtaPostscreenGreetAction enforce
zmprov mcf zimbraMtaPostscreenDnsblSites 'zen.spamhaus.org=127.0.0.[2..11]*3'
zmprov mcf zimbraMtaPostscreenDnsblAction enforce
zmmtaconfig

Эффект на маленьком сервере виден сразу: у бухгалтеров в Одинцово после включения нагрузка на amavis упала примерно вдвое, потому что до него перестала доходить половина мусора. Начинать я всегда советую с ignore вместо enforce и недели наблюдения по логу — так вы увидите, кого именно отсекли бы, и не отрежете важного контрагента с кривым сервером.

Пункт 10. Включить автообновление правил SpamAssassin. Мало кто знает, что на части инсталляций оно выключено по умолчанию. Три флага:

zmlocalconfig -e antispam_enable_rule_updates=true
zmlocalconfig -e antispam_enable_restarts=true
zmlocalconfig -e antispam_enable_rule_compilation=true
zmamavisdctl restart

Пункт 11. Обучить байесовский фильтр. zmtrainsa скармливает SpamAssassin размеченные письма. Для старта нужно минимум по ~200 примеров спама и не-спама — иначе фильтр гадает.

Пункт 12. Проверить свежесть ClamAV. Zimbra тянет сигнатуры через freshclam каждые 2 часа, но канал бывает оборван (особенно в РФ). Просроченный ClamAV = вся входящая в deferred. Проверка занимает секунды и делается по датам файлов баз, а не по бодрому статусу службы:

ls -la --time-style=long-iso /opt/zimbra/data/clamav/db/
# daily.cld свежее суток — норма; месячной давности — сигнатуры мёртвые
grep -i freshclam /var/log/zimbra.log | tail -5

Коварство пункта в том, что zmcontrol status при этом честно пишет, что антивирус запущен. Он и запущен. Просто он проверяет письма по базам полугодовой давности, а когда amavis решит, что базы совсем протухли, вся входящая начнёт копиться в очереди — и жалоба придёт не «антивирус сломался», а «нам никто не пишет со вторника».

Что ломается: postscreen в режиме enforce отобьёт письма от контрагентов, чей сервер настроен небрежно, — а объяснять это будете вы, потому что для клиента «письмо от партнёра не дошло» равно «вы что-то сломали». Обучение байесовского фильтра тоже стоит начинать с папки, которую разобрал человек: скормите zmtrainsa сырой ящик с реальной перепиской, помеченной как спам, и через неделю фильтр начнёт резать нормальные счета.

Пункт 13. Настроить DKIM/SPF/DMARC на стороне DNS. Zimbra подписывает DKIM, но SPF и DMARC живут в DNS. Без них ваши письма режут провайдеры, а ваш домен легче подделать.

/opt/zimbra/libexec/zmdkimkeyutil -q -d example.ru   # текущий ключ
# публичную часть — в TXT-запись селектора в DNS вручную

Блок 4. Доступ, учётки и видимость атакующего

Пункт 14. zimbraMailTrustedIP для прокси. Тонкий, но важный. Если веб-почта за прокси, а этот пункт не выставлен — в mailbox.log в поле oip вы увидите адрес прокси, а не реального клиента. И во время инцидента не поймёте, кто именно ломился.

zmprov mcf +zimbraMailTrustedIP 192.168.10.5   # IP вашего прокси
# теперь в логах видно настоящий адрес источника

Пункт 15. Fail2ban на admin и webmail. Готовые regex ловят брутфорс по mailbox.log и банят IP. В связке с DOS-фильтром Zimbra закрывает перебор паролей.

Пункт 16. Ревизия учёток и sudo. Отдельно проверить: нет ли лишних UID-0 учёток, не разрешает ли sudo пользователю zimbra запускать nginx с произвольными аргументами (путь к записи в sudoers.d), стоит ли PermitRootLogin no. Именно тут прячется повышение привилегий.

awk -F: '$3==0{print $1}' /etc/passwd        # все UID-0 — их должно быть один: root
grep -rn nginx /etc/sudoers /etc/sudoers.d    # правило без аргументов = дыра
grep -E 'PermitRootLogin|PasswordAuthentication' /etc/ssh/sshd_config

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

Проверьте у себя за 2 минуты

Полный чек-лист — работа на несколько часов. Но три вещи, которые чаще всего оказываются пробитыми, проверяются мгновенно. От пользователя zimbra и root.

# 1. вы не открытый релей?
zmprov gcf zimbraMtaMyNetworks

# 2. админка наружу?
ss -tlnp | grep ':7071'

# 3. лишние всесильные учётки и sudo-дыра?
awk -F: '$3==0{print $1}' /etc/passwd
grep -rn nginx /etc/sudoers /etc/sudoers.d 2>/dev/null
РезультатТрактовка
В mynetworks шире, чем ваши реальные подсети (тем более 0.0.0.0/0)Открытый или полуоткрытый релей. Первый приоритет
Порт 7071 слушает на публичном интерфейсеАдминку брутфорсят прямо сейчас. За VPN немедленно
UID-0 не только rootЛишняя всесильная учётка — частый след взлома
sudo на nginx без фиксированных аргументовГотовый путь к записи в sudoers.d и root

Если mynetworks шире, чем нужно, — считайте, что вы уже частично релей. Это самая частая находка на унаследованных серверах, и именно она била по домену в Одинцово.

Сколько стоило бездействие и что из этого следует вам

В цифрах кейс из Одинцово выглядел так. Неделя открытого релея — три чёрных списка. Вывод домена из Spamhaus и Barracuda занял 9 дней переписки и ожидания, всё это время часть писем бухгалтеров не доходила до контрагентов: срывались сроки по актам и счетам. Убыток мы с директором посчитали, и он получился неприятно конкретным. Девять дней трое сотрудников перекладывали отбитые письма руками — через личные ящики, мессенджеры, курьера с флешкой: по два часа в день на человека, итого 54 человеко-часа. По 800 рублей за час со взносами это 43 200 рублей чистого ФОТ, потраченного на пересылку того, что должно уходить само. Хуже другое: два контрагента, не получив вовремя закрывающие документы, сдвинули оплату на следующий месяц — 612 000 рублей кассового разрыва, который фирма закрывала своими деньгами. Полный проход по шестнадцати пунктам обошёлся им потом в 34 000 рублей.

Сам харденинг всех шестнадцати пунктов — это примерно рабочий день инженера на сервер, из которого половина времени уходит не на команды, а на то, чтобы аккуратно перевести МФУ и скрипты 1С на аутентифицированную отправку и не устроить простой. Один день против девяти дней разгребания последствий — вот вся математика профилактики.

Пройдите у себя хотя бы минимальную проверку из блока выше. Если mynetworks шире нужного, торчит 7071 или в системе завелась лишняя UID-0 учётка — пришлите мне вывод этих команд. За день отвечу, какие из шестнадцати пунктов у вас пробиты, что закрывать первым и что при этом сломается у пользователей, чтобы вы предупредили их заранее, а не задним числом.

Частые вопросы

С какого пункта начинать, если времени в обрез?

С mynetworks и админ-консоли 7071 — это два пункта, которые дают наибольший эффект за наименьшее время. Широкий mynetworks делает вас открытым релеем, и это бьёт по репутации домена так, что потом неделями выбираешься из чёрных списков. Открытая наружу админка 7071 — прямая мишень для брутфорса пароля администратора, а с админскими правами атакующий получает всё. Эти два закрываются буквально за полчаса: сузить доверенную сеть командой zmprov mcf и убрать 7071 за файрвол или VPN. Остальные четырнадцать пунктов важны, но эти два — то, что горит.

Сужу mynetworks — и у меня перестанут слать МФУ и 1С. Как быть?

Это самая частая боль при харденинге, и её надо решить ДО правки, а не после. Устройства и скрипты, которые слали «без пароля через mynetworks», нужно заранее перевести на аутентифицированную отправку через порт 587 с STARTTLS: завести им отдельный служебный ящик с паролем и прописать SMTP-аутентификацию в настройках МФУ и в коде 1С. Только когда все легитимные отправители переведены — сужаете mynetworks. Если сделать наоборот, в первый же рабочий день встанет сканирование в почту и отправка документов, и разбираться будете в аврале. Порядок: сначала перевести отправителей, потом закрыть дыру.

Зачем отключать postjournal, если он работает?

Затем, что работающая, но неиспользуемая служба — это лишняя площадь атаки. Postjournal слушает на порту 10027 и обрабатывает входящие письма, и именно он стал точкой входа для целого класса RCE — уязвимость позволяла передать команды на исполнение через поля письма. Если ваша конфигурация journaling не использует (а большинство небольших офисов не использует), служба просто висит как открытая дверь без нужды. Отключение снимает вектор целиком: нет службы — нет и уязвимости в ней. Проверьте, нужен ли вам journaling вообще, и если нет — выключайте.

Мы за прокси. Почему в логах чужой адрес, а не атакующего?

Потому что не выставлен zimbraMailTrustedIP. Когда веб-почта стоит за обратным прокси, все запросы к Zimbra приходят с адреса прокси, и в mailbox.log в поле oip вы видите именно его, а не реальный источник. Во время инцидента это критично: вы смотрите, кто брутфорсил или откуда зашёл злоумышленник, а видите один и тот же внутренний IP прокси на всех записях. Лечится командой zmprov mcf +zimbraMailTrustedIP с адресом вашего прокси — после этого Zimbra доверяет заголовку X-Forwarded-For и пишет в лог настоящий адрес клиента. Пункт тонкий, но без него форензика слепая.

Fail2ban не забанит случайно наших же сотрудников?

При настройках по умолчанию — вполне может. Человек, который трижды промахнулся паролем перед отпуском или настраивает новый телефон, легко попадает под бан, если порог агрессивный. Поэтому пороги подбираются под конкретный живой офис: сколько неудачных попыток за какое окно времени реально означают атаку, а не рассеянность бухгалтера. Плюс имеет смысл добавить свои подсети в whitelist, чтобы внутренние пользователи не блокировались вовсе. Fail2ban — хороший инструмент, но не из коробки: настроенный без учёта поведения людей, он генерирует поток заявок «не могу войти» и его в итоге просто выключают, что хуже, чем не ставить.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#Zimbra#безопасность#харденинг#Postfix#чек-лист
Комментарии 0

Оставить комментарий

загрузка...

Подпишитесь на рассылку ITfresh

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

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.