Админ уволился, паролей нет: как за день описать чужую Zimbra

Гостиница в Подольске на тридцать рабочих мест осталась с почтовым сервером, к которому никто не знал пароля. Root по SSH был, админ-консоль — закрыта, документации ноль. Разбираю порядок команд, которым я за день восстановил полную картину: домены, ящики, алиасы, рассылки, кроны и бэкапы. И где я в тот раз крепко ошибся.

Сервер работает, а что внутри — не знает никто

Мне позвонили в четверг вечером, в июне 2021 года. Гостиница в Подольске, тридцать рабочих мест, собственный почтовый сервер в серверной рядом со стойкой видеонаблюдения. Системный администратор уволился тремя неделями раньше. Пароль от админ-консоли Zimbra он назвал управляющей устно, та записала его на стикере, стикер уехал вместе со списанным монитором.

Почта при этом ходила. Брони падали в reception@, счета уходили, никто ничего не замечал. Тревога началась в тот момент, когда бухгалтерии понадобился новый ящик — и оказалось, что завести его некому и нечем.

Если у вас сейчас похоже — сервер стоит с позапрошлого директора, работает, трогать боятся, а кто и что на нём настраивал, не помнит никто — дальше про вас. Ситуация чинится. Но чинится не «сбросом пароля», а полной описью того, что там вообще живёт.

Мне отдали одну вещь: root по SSH. Бывший админ передал его вместе с ноутбуком, и это оказалось единственным, что вообще уцелело из доступов. Этого хватило.

Что было на входе

Приехал в пятницу к десяти утра. Сервер — обычный tower, восемь гигабайт памяти, диск на 500 ГБ, занято 78%. CentOS 7.6. Первое, что я делаю на чужой машине, — снимаю версию и историю установок, потому что версия определяет вообще всё остальное.

su - zimbra
zmcontrol -v
# Release 8.8.15_GA_3869.RHEL7_64_20190917004220 RHEL7_64 FOSS edition

zmhostname
# mail.hotel-podolsk.local

cat /opt/zimbra/.install_history

Строку я, к слову, сначала записал в блокнот по памяти и с ошибкой — рука сама дописала привычную убунтовскую сборку вместо RHEL-овской. Через час сверял с экраном и переписывал заново. На чужом сервере такие мелочи потом стоят часа, поэтому дословно: копируем, а не запоминаем.

Файл .install_history — это то место, которому я верю больше, чем людям. Там построчно записано, когда и какой пакет ставили или обновляли. У этого сервера история начиналась 14 марта 2018 года с версии 8.7.11, потом апгрейд до 8.8.8, потом до 8.8.15 в феврале 2020-го. И тишина полтора года.

Дальше — какие роли реально включены. Люди часто думают, что у них «просто почта», а на машине висит ещё и прокси, и LDAP-мастер, и MTA, и всё это надо описывать отдельно.

zmprov gs $(zmhostname) zimbraServiceEnabled
# zimbraServiceEnabled: ldap
# zimbraServiceEnabled: logger
# zimbraServiceEnabled: mailbox
# zimbraServiceEnabled: mta
# zimbraServiceEnabled: proxy
# zimbraServiceEnabled: snmp
# zimbraServiceEnabled: stats

zmcontrol status
zmvolume -l

Одна коробка, все роли на ней. Для тридцати мест это нормально. Тома: primary на /opt/zimbra/store, index там же, secondary нет, HSM никто не настраивал. Значит, весь рост упирается в один физический диск — и 78% занятости из первой строчки внезапно становятся цифрой, о которой стоит подумать отдельно.

Первый час: каркас без всякой админки

Админ-консоль мне не нужна. Вся картина снимается из командной строки, и снимается быстрее, чем кликается.

Домены и учётки:

zmprov gad
# hotel-podolsk.ru
# podolsk-hotel.ru
# hotel-podolsk.local
# restoran-podolsk.ru

zmprov -l gaa | wc -l
# 47

zmprov gaaa
# admin@hotel-podolsk.local
# galsync@hotel-podolsk.local
# it@hotel-podolsk.ru

Сорок семь ящиков на тридцать человек — обычная история: сервисные адреса, отделы, два ящика уволенных, которые никто не выключил. Администраторов трое, и третий, it@, — это личный ящик того самого уволившегося. Права полного администратора на нём остались.

Флаг -l в zmprov -l gaa заставляет утилиту ходить напрямую в LDAP, минуя SOAP-сервис. На вялой машине это разница между двумя секундами и сорока. И работает даже тогда, когда mailboxd прилёг.

Рассылки, алиасы и классы обслуживания:

zmprov gadl                      # все списки рассылки
zmprov gdl reception@hotel-podolsk.ru   # состав конкретного списка
zmprov gac                       # все COS
for u in $(zmprov -l gaa); do
  echo -n "$u : "
  zmprov ga $u zimbraMailAlias | grep -c zimbraMailAlias:
done

Насчитал шесть рассылок и тридцать один алиас. Треть алиасов вела на ящики людей, которые в гостинице уже не работали. Это не проблема безопасности сама по себе, но это ровно тот мусор, который через год превращается в «а почему письмо от турагентства никто не увидел».

Пароли: то, что нельзя прочитать, но можно унести

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

mkdir -p /migration/passwords && cd /migration/passwords
for user in $(zmprov -l gaa); do
  zmprov -l ga $user userPassword | grep userPassword: \
    | awk '{print $2}' > $user.shadow
done
wc -l *.shadow | tail -1

А вот служебный пароль LDAP лежит в открытом виде, и это надо знать про свой сервер:

zmlocalconfig -s zimbra_ldap_password
zmlocalconfig -s ldap_root_password
ldapsearch -x -H ldap://localhost:389 \
  -D "uid=zimbra,cn=admins,cn=zimbra" -w "$(zmlocalconfig -s zimbra_ldap_password | awk '{print $3}')" \
  -b "" -s base "objectclass=*"

Ключ -s у zmlocalconfig показывает секретные значения вместо звёздочек. Любой, кто получил шелл от пользователя zimbra, читает эти пароли за одну команду. Отсюда простое следствие: доступ к учётке zimbra на сервере равен доступу ко всему каталогу целиком. Я к этому вернусь в конце.

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

Четыре команды. Выполняются от пользователя zimbra, ничего не меняют, ничего не перезапускают.

su - zimbra
zmcontrol -v
zmprov -l gaa | wc -l
zmprov gaaa
crontab -l | grep -v '^#' | grep -v '^$'
Что увиделиКак это читать
Версия 8.8.15 без слова Patch в строкеСервер ни разу не патчили после установки. Патч-уровень нулевой
Число ящиков больше числа сотрудников в полтора разаЕсть забытые учётки уволенных — их надо закрыть отдельным списком
В gaaa есть личный ящик человека, который у вас не работаетПрава администратора остались у постороннего. Это первое, что надо снять
В crontab пусто или только строки ZimbraСобственного бэкапа нет вообще. Zimbra OSE своего инструмента резервного копирования не имеет
В crontab есть чужая строка со скриптомНичего не доказывает. Надо открыть скрипт и посмотреть, куда он реально пишет и когда писал в последний раз

Последняя строка таблицы — то, на чём я в тот раз и обжёгся.

Комментарий в crontab, которому я поверил

В кроне у пользователя zimbra висела строка. Аккуратная, с комментарием на русском:

# nightly backup to NAS 192.168.10.20 -- do not touch
0 2 * * * /opt/scripts/zmbackup.sh >> /var/log/zmbackup.log 2>&1

Я прочитал, поставил галочку «бэкап есть» и пошёл дальше — снимать конфиги MTA и сертификаты. Полдня работал в спокойном режиме, потому что считал, что откатиться в случае чего есть куда. Это была ошибка, и довольно дорогая по нервам.

К вечеру дошли руки открыть сам скрипт. Внутри — цикл по ящикам с zmmailbox getRestURL, выгрузка в /mnt/nas/zimbra, потом ротация с удалением архивов старше четырнадцати дней. Написано грамотно. Только вот:

mount | grep nas
# пусто

tail -3 /var/log/zmbackup.log
# 2021-06-09 02:00:04 ERROR: /mnt/nas is not a mount point, abort
# 2021-06-10 02:00:03 ERROR: /mnt/nas is not a mount point, abort
# 2021-06-11 02:00:04 ERROR: /mnt/nas is not a mount point, abort

grep -m1 ERROR /var/log/zmbackup.log
# 2021-02-11 02:00:05 ERROR: /mnt/nas is not a mount point, abort

ls -la /mnt/nas/zimbra 2>/dev/null | head
# ls: cannot access '/mnt/nas/zimbra': No such file or directory

Хвост лога — три одинаковые строки за три последние ночи, включая ночь накануне моего приезда. Первую такую строку я нашёл отдельной командой, и она датирована 11 февраля. Между ними — сплошняк, ни одной успешной ночи.

NAS отвалился 11 февраля. Скрипт исправно падал сто двадцать одну ночь подряд, писал ошибку в лог, который никто не читал, и никому ни разу не пожаловался. Последний живой бэкап был датирован 10 февраля 2021 года — то есть на момент моего приезда ему было сто двадцать дней, и лежал он на файловой шаре, которой больше не существовало.

Мораль простая и неприятная. Строка в crontab доказывает существование строки в crontab. Больше ничего. Проверять надо не расписание, а последний успешно созданный файл и его размер.

Как я вернул админку и что при этом сломал

Пароль администратора в Zimbra меняется одной командой и без остановки служб. Никаких single-user режимов, никаких перезагрузок:

zmprov sp admin@hotel-podolsk.local 'НовыйДлинныйПароль2021!'
zmprov ga admin@hotel-podolsk.local zimbraIsAdminAccount
# zimbraIsAdminAccount: TRUE

Зашёл в консоль на https://mail:7071/zimbraAdmin, всё открылось. Красиво.

А в понедельник утром администратор гостиницы написала, что перестала работать выгрузка броней в 1С. Оказалось, интеграционный скрипт бронирования логинился в почту под тем самым admin@ — пароль был зашит в конфиг обменника прямым текстом. Я его сменил, обменник упёрся в 401 и молча перестал забирать письма из booking@. За выходные накопилось 187 непрочитанных писем с заявками.

Ничего не потерялось, письма никуда не делись, но два часа в понедельник ушло на поиск источника и правку конфига. Могло не уйти. С тех пор перед сменой любого пароля на принятом сервере я сначала смотрю, кто этим аккаунтом ходит:

grep -i "admin@" /opt/zimbra/log/mailbox.log | grep -i "oip=" | \
  awk -F'oip=' '{print $2}' | awk -F';' '{print $1}' | sort | uniq -c | sort -rn

Команда вытаскивает из журнала все IP, с которых входили под этим ящиком. Если там кроме рабочих станций светится адрес сервера 1С или какой-нибудь принтер — вы нашли зависимость до того, как её сломали, а не после.

Чем закончилось и во что обошлось

Полная опись заняла девять часов чистого времени, разнесённых на два дня. На выходе клиент получил документ на одиннадцать страниц: четыре домена, 47 ящиков с указанием владельцев, шесть рассылок, 31 алиас, три администратора, полный список сервисов, схема портов, где лежат сертификаты и когда они истекают, где хранятся пароли и что с бэкапом.

Работа обошлась в 34 000 рублей. Теперь считаем альтернативу.

Диск был занят на 78% и рос примерно на два гигабайта в неделю. Zimbra при заполнении диска останавливается сама — не «замедляется», а встаёт: MTA начинает отвечать отправителям «452 4.3.1 Insufficient system storage», а сторож конфигурации по достижении критического порога, а это где-то за 95% занятости, гасит службы. Считаем: из 500 ГБ свободно 110, до тех самых 95% остаётся 85 ГБ, при двух гигабайтах в неделю это сорок с небольшим недель. Меньше года. Живого бэкапа не было сто двадцать дней. Развернуть почту гостиницы с нуля, без архива переписки, — это трое суток работы двух человек плюс безвозвратная потеря истории броней и переписки с турагентствами.

Теперь то же самое в рублях, считаю грубо, но по их же цифрам. Шестьдесят номеров, летняя загрузка около 80%, средний тариф 4 700 ₽ — это примерно 225 000 ₽ выручки в сутки. Почтой к ним приходило меньше половины броней, остальное телефон и агрегаторы, так что трое суток без почты в сезон — порядка 300 000 ₽ несостоявшихся заселений. Плюс само восстановление: 48 человеко-часов по нашей ставке 3 500 ₽ — ещё 168 000 ₽. Итого без малого полмиллиона против 34 000 ₽ за опись, которую можно было заказать в любой спокойный вторник.

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

Что делать до того, как админ уволится

Если вы читаете это и у вас ещё есть свой администратор — попросите его выполнить пять вещей и приложить вывод в общую папку. Это займёт у него полчаса.

  • Вывод zmcontrol -v и cat /opt/zimbra/.install_history — версия и вся история апгрейдов.
  • Списки: zmprov gad, zmprov -l gaa, zmprov gaaa, zmprov gadl.
  • Пароли zimbra_ldap_password, ldap_root_password, админ-консоли — в парольное хранилище компании, а не в файл на рабочем столе.
  • Скрипт бэкапа целиком плюс листинг каталога назначения с датами. Не расписание — именно файлы.
  • Список того, что ходит на сервер по SMTP и IMAP помимо людей: 1С, CRM, сканеры, сайт.

Последний пункт пропускают всегда. Он же потом стоит дороже всех.

И честно про сложность. Команды выше несложные, любой человек с руками их выполнит. Тяжело не собрать вывод — тяжело правильно прочитать. Понять, что 8.8.15 без патч-уровня в 2021 году это одно, а в 2026-м совсем другое. Увидеть, что secondary-тома нет и рост упрётся в один диск. Заметить, что интеграция ходит под админским аккаунтом. Каждая из этих штук отдельно выглядит безобидно, и опасной становится связка.

Если у вас на руках сервер, который никто не описывал: выполните пять команд из блока самодиагностики и пришлите мне вывод. За день отвечу, что у вас на самом деле, где горит и во что обойдётся привести это в порядок. Без выезда и без обследования на месяц.

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

Можно ли сбросить пароль администратора Zimbra, если нет доступа в консоль?

Да, если у вас есть root по SSH. Заходите на сервер, переключаетесь на пользователя zimbra командой su - zimbra и выполняете zmprov sp admin@вашдомен НовыйПароль. Службы останавливать не нужно, перезагрузка не требуется, пользователи ничего не заметят. Единственное, что я советую сделать до смены, — проверить по mailbox.log, не ходит ли под этим ящиком какая-нибудь интеграция. У меня в Подольске под админской учёткой логинился обменник с 1С, и я его уронил на выходные. Двести с лишним заявок пролежали непрочитанными до понедельника.

Как узнать, есть ли на сервере рабочий бэкап, если документации нет?

Смотреть не на расписание, а на файлы. Откройте crontab пользователя zimbra и root, найдите скрипт, откройте его и определите каталог назначения. Затем сделайте ls -la этого каталога и посмотрите даты и размеры. Если самый свежий архив старше недели или каталога вообще нет — бэкапа у вас нет, независимо от того, что написано в комментарии к строке крона. Отдельно проверьте mount: сетевая шара часто отваливается, а скрипт продолжает падать в лог, который никто не читает. Именно так я потерял полдня спокойствия у гостиницы в Подольске.

Пароли пользователей можно как-то вытащить, чтобы не менять их всем при переезде?

Сами пароли — нет, они хранятся хэшами. А вот хэши переносятся: zmprov -l ga пользователь userPassword отдаёт значение атрибута, которое потом заливается на новый сервер той же командой на запись. Это работает при переезде между серверами Zimbra и при миграции на Carbonio, потому что каталог там устроен так же. При переходе на платформы с другим форматом хранения хэшей номер не пройдёт — там придётся либо генерировать новые пароли, либо заводить единый вход через внешний каталог.

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

У меня уходит от шести до двенадцати часов на сервер уровня малого бизнеса — до сотни ящиков, одна машина, все роли на ней. В Подольске получилось девять часов на два дня. Основное время тратится не на команды, а на сверку: кто владелец каждого ящика, что за скрипты висят в кроне, куда ходит интеграция, живы ли сертификаты. Мультисерверная установка с отдельным LDAP и прокси растягивается на два-три дня, потому что там надо описывать связи между узлами, а не только сами узлы.

Насколько опасно, что пароль LDAP лежит на сервере открытым текстом?

Ровно настолько, насколько защищена учётка zimbra на этой машине. Команда zmlocalconfig -s zimbra_ldap_password отдаёт пароль любому, кто получил шелл под этим пользователем, — это архитектурная особенность, а не дыра. Практический вывод такой: всё, что даёт злоумышленнику выполнение команд от zimbra, автоматически отдаёт ему весь каталог с учётками и хэшами. Поэтому патч-уровень сервера и то, что торчит наружу, важнее любых внутренних разграничений прав.

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

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

📞 Связаться с нами
#Zimbra#аудит#инвентаризация#zmprov#передача дел
Комментарии 0

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

загрузка...

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

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

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

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