Grommunio: различия между версиями

Материал из wiki.nntc.nnov.ru
Перейти к навигации Перейти к поиску
(→‎Особенности работы Community версии и способы починки)
(→‎Скрипт получения адресных книг из аккаунтов zimbra)
 
(не показаны 32 промежуточные версии этого же участника)
Строка 159: Строка 159:
  
 
  The private key has been generated on the server at
 
  The private key has been generated on the server at
−
  /var/lib/grommunio-admin-api/myorg.net.mailserver.key
+
  /var/lib/grommunio-admin-api/myorg.net.dkim.key
  
 
Этот путь до файла ключа будет нужен потом при настройке сервера по ssh.
 
Этот путь до файла ключа будет нужен потом при настройке сервера по ssh.
  
−
===Настройка сервера по ssh===
+
===Настройка сервера по ssh и настройки конфигов на сервере===
  
 
Для доступа по ssh под root лучше сразу получить на сервер открытую часть ssh ключа и настроить конфиг ssh для быстрого подключения. Под root на сервер по паролю подключиться не получится. А через виртуальный дисплей виртуальной машины нельзя будет скопировать текст на сервер.
 
Для доступа по ssh под root лучше сразу получить на сервер открытую часть ssh ключа и настроить конфиг ssh для быстрого подключения. Под root на сервер по паролю подключиться не получится. А через виртуальный дисплей виртуальной машины нельзя будет скопировать текст на сервер.
Строка 186: Строка 186:
 
2. Копируем тот самый файл, путь к которому нам сообщили в веб интерфейсе в предыдущем пункте в файл с названием селектора в только что созданный каталог
 
2. Копируем тот самый файл, путь к которому нам сообщили в веб интерфейсе в предыдущем пункте в файл с названием селектора в только что созданный каталог
  
−
  cp /var/lib/grommunio-admin-api/myorg.net.mailserver.key /var/lib/grommunio-antispam/dkim/myorg.net/mailserver.key
+
  cp /var/lib/grommunio-admin-api/myorg.net.dkim.key /var/lib/grommunio-antispam/dkim/myorg.net.mailserver.key
  
 
3. Создайм файл
 
3. Создайм файл
Строка 195: Строка 195:
 
sign_authenticated = true;
 
sign_authenticated = true;
 
sign_local = true;
 
sign_local = true;
 +
 +
use_domain = "header";
 +
use_domain_sign_local = "full";
  
 
domain {
 
domain {
 
     "myorg.net" {
 
     "myorg.net" {
−
         path = "/var/lib/grommunio-antispam/dkim/myorg.net/mailserver.key";
+
         path = "/var/lib/grommunio-antispam/dkim/myorg.net.mailserver.key";
 
         selector = "mailserver";
 
         selector = "mailserver";
 
     }
 
     }
 
}
 
}
  
−
use_domain = "header";
+
EOF
 
</pre>
 
</pre>
  
Строка 209: Строка 212:
  
 
  chown -R groas:grommunio /var/lib/grommunio-antispam/dkim/
 
  chown -R groas:grommunio /var/lib/grommunio-antispam/dkim/
−
  chmod 600 /var/lib/grommunio-antispam/dkim/myorg.net/mailserver.key
+
  chmod 600 /var/lib/grommunio-antispam/dkim/myorg.net.mailserver.key
 +
 
 +
5. Оставляем в доверенных сетях только локальную петлю и устанавливаем параметр non_smtpd_milters в файл конфигурации postfix
  
−
5. Перезапустить сервисы
+
echo 'mynetworks = 127.0.0.0/8' >> /etc/postfix/main.cf
 +
echo 'non_smtpd_milters = inet:localhost:11332' >> /etc/postfix/main.cf
 +
 
 +
6. Перезапустить сервисы
  
 
  systemctl restart grommunio-antispam
 
  systemctl restart grommunio-antispam
 
  systemctl restart postfix
 
  systemctl restart postfix
 +
 +
Перезапуска этих сервисов для полноценной работы механизма подписи емэйлов может быть недостаточно. Тогда перезагружаем сервер полностью
 +
 +
reboot
 +
 +
А если и это не помогло, то смотрим что в логах
 +
 +
tail -f /var/log/grommunio-antispam/rspamd.log
 +
 +
====Важный момент при настройке почтового сервера на доменах третьего уровня====
 +
 +
Общая суть в том, что понадобятся параметры
 +
 +
use_esld = false;
 +
use_effective_domain = false;
 +
 +
в файле
 +
 +
/etc/grommunio-antispam/override.d/dkim_signing.conf
 +
 +
Иначе сервер будет схлопывать домен до первого уровня и подставлять это в тело письма вот сюда (это часть письма в сыром виде):
 +
 +
...DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=myorg.net; s=mailserver; t=1785189679;...
 +
 +
В случае с доменом третьего уровня, например, unit.myorg.net, значение должно быть
 +
 +
d=unit.myorg.net
 +
 +
, а без этих параметров оно:
 +
 +
d=myorg.net
 +
 +
Следовательно, принимающие сервера пытаются прочитать ключ для проверки подписи с домена myorg.net, а не с домена unit.myorg.net и не могут её проверить.
 +
 +
 +
 +
Пример рабочего конфига для домена третьего уровня
 +
<pre>
 +
cat << 'EOF' > /etc/grommunio-antispam/override.d/dkim_signing.conf
 +
enabled = true;
 +
sign_authenticated = true;
 +
sign_local = true;
 +
 +
use_domain = "header";
 +
 +
# КРИТИЧЕСКИ ВАЖНО: Выключаем нормализацию eSLD, которая схлопывает домен unit.myorg.net до myorg.net
 +
use_esld = false;
 +
use_effective_domain = false;
 +
 +
selector = "mailserver";
 +
path = "/var/lib/grommunio-antispam/dkim/unit.myorg.net.mailserver.key";
 +
EOF
 +
</pre>
  
 
===Настройка TXT записи для домена===
 
===Настройка TXT записи для домена===
Строка 244: Строка 305:
 
Перед тем, как конфигурировать TXT запись на уровне DNS необходимо свериться: точно ли у ключа, который ранее был скопирован в файл
 
Перед тем, как конфигурировать TXT запись на уровне DNS необходимо свериться: точно ли у ключа, который ранее был скопирован в файл
  
−
  /var/lib/grommunio-antispam/dkim/myorg.net/mailserver.key
+
  /var/lib/grommunio-antispam/dkim/myorg.net/dkim.key
  
 
base64 строка соответствует значению ключа
 
base64 строка соответствует значению ключа
Строка 254: Строка 315:
 
Получить этот ключ можно следующим образом:
 
Получить этот ключ можно следующим образом:
  
−
  openssl rsa -in /var/lib/grommunio-antispam/dkim/myorg.net/mailserver.key -pubout -outform DER 2>/dev/null | base64 -w 0
+
  openssl rsa -in /var/lib/grommunio-antispam/dkim/myorg.net/dkim.key -pubout -outform DER 2>/dev/null | base64 -w 0
  
 
В ответ придёт что-то вроде:
 
В ответ придёт что-то вроде:
Строка 345: Строка 406:
 
Важно! У сервиса этого есть ограничения на кол-во проверок в сутки. Потом он просит денюжку или ждать следующего дня. Поэтому сначала проверяем текст в исходниках письма, как описано выше, и только потом - через сервис.
 
Важно! У сервиса этого есть ограничения на кол-во проверок в сутки. Потом он просит денюжку или ждать следующего дня. Поэтому сначала проверяем текст в исходниках письма, как описано выше, и только потом - через сервис.
  
−
==Пакетное создание ящиков==
+
==Скрипты и автоматизация==
 +
 
 +
Все скрипты, представленные здесь, запускать необходимо с сервера Grommunio!
 +
 
 +
 
 +
===Скрипт создания одного ящика===
 +
 
 +
<pre>
 +
cat << 'EOF' > create_mailbox.sh
 +
#!/bin/bash
 +
USER_GROMMUNIO="$1"
 +
PASS_GROMMUNIO="$2"
 +
 
 +
# Проверка входных параметров
 +
if [ -z "$USER_GROMMUNIO" ] || [ -z "$PASS_GROMMUNIO" ]; then
 +
    echo "Использование: $0 <user_grommunio> <pass_grommunio>"
 +
    echo "Пример: $0 user@domain.ru 'ThePass'"
 +
    exit 1
 +
fi
 +
 
 +
echo "=== 1. Проверка / Создание ящика $USER_GROMMUNIO в Grommunio ==="
 +
 
 +
# Проверяем наличие ящика
 +
if grommunio-admin user show "$USER_GROMMUNIO" >/dev/null 2>&1; then
 +
    echo "i Ящик $USER_GROMMUNIO уже существует. Пропускаем создание."
 +
else
 +
    echo "Пользователь не найден. Создаём ящик $USER_GROMMUNIO..."
 +
 
 +
    DISPLAY_NAME=$(echo "$USER_GROMMUNIO" | cut -d'@' -f1)
 +
 
 +
    # Создаем юзера
 +
    grommunio-admin user create "$USER_GROMMUNIO"
 +
    if [ $? -ne 0 ]; then
 +
        echo "Ошибка создания пользователя $USER_GROMMUNIO!"
 +
        exit 1
 +
    fi
 +
 
 +
    # Устанавливаем свойства пользователю
 +
    grommunio-admin user modify \
 +
--property givenname="$DISPLAY_NAME" \
 +
--property surname="$DISPLAY_NAME" \
 +
--property displayname="$DISPLAY_NAME" \
 +
--property country="Russian Federation" \
 +
"$USER_GROMMUNIO"
 +
    if [ $? -ne 0 ]; then
 +
        echo "Ошибка установки свойств пользователя $USER_GROMMUNIO!"
 +
        exit 1
 +
    fi
 +
 
 +
    # Задаем пароль
 +
    grommunio-admin passwd --password "$PASS_GROMMUNIO" "$USER_GROMMUNIO"
 +
    if [ $? -ne 0 ]; then
 +
        echo "Ошибка установки пароля для $USER_GROMMUNIO!"
 +
        exit 1
 +
    fi
 +
 
 +
    echo "✓ Ящик $USER_GROMMUNIO успешно создан и пароль установлен."
 +
fi
 +
EOF
 +
 
 +
chmod +x create_mailbox.sh
 +
</pre>
 +
 
 +
 
 +
===Скрипт переноса одного ящика из Zimbra в Grommunio===
 +
 
 +
Для работы скрипта необходимо наличие рядом предыдущего скрипта (create_mailbox.sh)
 +
 
 +
А также внутри скрипта необходимо задать адрес сервера Zimbra и логин и пароль мастер-аккаунта Zimbra
 +
 
 +
<pre>
 +
 
 +
cat << 'EOF' > migrate_account_from_zimbra_to_grommunio.sh
 +
#!/bin/bash
 +
 
 +
# Настройки серверов
 +
ZIMBRA_SERVER="192.168.0.1"
 +
GROMMUNIO_SERVER="127.0.0.1"
 +
 
 +
# Мастер-аккаунт с Zimbra с паролем
 +
ZIMBRA_ADMIN_USER="admin@myorg.net"
 +
ZIMBRA_ADMIN_PASS="SuperAdminPassword"
 +
 
 +
# Определяем директорию, в которой расположен текущий скрипт
 +
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 +
CREATE_SCRIPT="$SCRIPT_DIR/create_mailbox.sh"
 +
 
 +
# Чтение аргументов
 +
USER_ZIMBRA="$1"
 +
USER_GROMMUNIO="$2"
 +
PASS_GROMMUNIO="$3"
 +
 
 +
# Проверка входных параметров
 +
if [ -z "$USER_ZIMBRA" ] || [ -z "$USER_GROMMUNIO" ] || [ -z "$PASS_GROMMUNIO" ]; then
 +
    echo "Использование: $0 <mailbox_zimbra> <mailbox_grommunio> <pass_grommunio>"
 +
    echo "Пример: $0 old_user@domain.ru new_user@domain.ru 'GromPass'"
 +
    exit 1
 +
fi
 +
 
 +
# Проверка наличия и прав на выполнение вспомогательного скрипта
 +
if [ ! -f "$CREATE_SCRIPT" ]; then
 +
    echo "Ошибка: Вспомогательный скрипт '$CREATE_SCRIPT' не найден!"
 +
    exit 1
 +
fi
 +
 
 +
if [ ! -x "$CREATE_SCRIPT" ]; then
 +
    echo "Ошибка: Скрипт '$CREATE_SCRIPT' найден, но не имеет прав на выполнение (chmod +x)!"
 +
    exit 1
 +
fi
 +
 
 +
# Вызов внешнего скрипта для создания ящика
 +
"$CREATE_SCRIPT" "$USER_GROMMUNIO" "$PASS_GROMMUNIO"
 +
if [ $? -ne 0 ]; then
 +
    echo "Ошибка при подготовке ящика $USER_GROMMUNIO через $CREATE_SCRIPT"
 +
    exit 1
 +
fi
 +
 
 +
echo "=== 2. Запуск переноса почты из Zimbra в Grommunio ==="
 +
 
 +
set -x
 +
 
 +
# Создаем временные защищенные файлы для паролей
 +
PASSFILE1=$(mktemp)
 +
PASSFILE2=$(mktemp)
 +
 
 +
printf '%s' "$ZIMBRA_ADMIN_PASS" > "$PASSFILE1"
 +
printf '%s' "$PASS_GROMMUNIO" > "$PASSFILE2"
 +
 
 +
imapsync \
 +
  --host1 "$ZIMBRA_SERVER" \
 +
  --user1 "$USER_ZIMBRA" \
 +
  --authuser1 "$ZIMBRA_ADMIN_USER" \
 +
  --passfile1 "$PASSFILE1" \
 +
  --authmech1 PLAIN \
 +
  --ssl1 \
 +
  --sslargs1 SSL_verify_mode=0 \
 +
  --host2 "$GROMMUNIO_SERVER" \
 +
  --user2 "$USER_GROMMUNIO" \
 +
  --passfile2 "$PASSFILE2" \
 +
  --authmech2 LOGIN \
 +
  --ssl2 \
 +
  --sslargs2 SSL_verify_mode=0 \
 +
  --useheader "Message-ID" \
 +
  --automap \
 +
  --f1f2 "Sent=&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-" \
 +
  --f1f2 "Trash=&BCMENAQwBDsENQQ9BD0ESwQ1-" \
 +
  --f1f2 "Junk=&BB0ENQQ2BDUEOwQwBEIENQQ7BEwEPQQwBE8- &BD8EPgRHBEIEMA-" \
 +
  --f1f2 "Drafts=&BCcENQRABD0EPgQyBDgEOgQ4-" \
 +
  --noflags \
 +
  --errorsmax 500000 \
 +
  --noauthmd5
 +
 
 +
# если не надо синхронизировать спам и корзину
 +
#  --exclude "Junk" \
 +
#  --exclude "Trash" \
 +
 
 +
# Удаляем временные файлы с паролями
 +
rm -f "$PASSFILE1" "$PASSFILE2"
 +
EOF
 +
 
 +
chmod +x migrate_account_from_zimbra_to_grommunio.sh
 +
 
 +
</pre>
 +
 
 +
===Скрипт переноса всех ящиков из Zimbra в Grommunio===
 +
 
 +
Для работы скрипта необходимо наличие рядом предыдущего скрипта (migrate_account_from_zimbra_to_grommunio.sh, для которого необходимо наличие предпредыдущего скрипта (create_mailbox.sh))
 +
 
 +
Чтобы скрипт знал какие ящики переносить необходимо создать файл mailboxes.txt, в котором одна строчка - один ящик. Например так:
 +
 
 +
<pre>
 +
cat << 'EOF' > mailboxes.txt
 +
admin@myorg.net
 +
manager@myorg.net
 +
EOF
 +
</pre>
 +
 
 +
 
 +
В результате работы скрипта в Grommunio будут созданы ящики со случайными паролями, в которые будут перемещены все письма из аналогичных аккаунтов Zimbra.
 +
 
 +
Файл с данными новых ящиков будет называться:
 +
 
 +
migrated_passwords.csv
 +
 
 +
При повторном запуске скрипта предварительно будет создаваться резервная копия этого файла, при его наличии.
 +
 
 +
Это сделано чтобы не потерять данные случайно сгенерированных паролей при прошлом запуске скрипта.
 +
 
 +
 
 +
Сам скрипт:
 +
<pre>
 +
cat << 'EOF' > migrate_all_zimbra_accounts.sh
 +
#!/bin/bash
 +
 
 +
TARGET_SCRIPT="./migrate_account_from_zimbra_to_grommunio.sh"
 +
MAILBOXES_FILE="mailboxes.txt"
 +
OUTPUT_CSV="migrated_passwords.csv"
 +
 
 +
if [ ! -f "$TARGET_SCRIPT" ]; then
 +
  echo "Ошибка: Исполняемый файл '$TARGET_SCRIPT' не найден!" >&2
 +
  exit 1
 +
fi
 +
 
 +
if [ ! -x "$TARGET_SCRIPT" ]; then
 +
  echo "Ошибка: Файл '$TARGET_SCRIPT' существует, но у него нет прав на выполнение (chmod +x)!" >&2
 +
  exit 1
 +
fi
 +
 
 +
if [ ! -f "$MAILBOXES_FILE" ]; then
 +
  echo "Ошибка: Файл со списком ящиков '$MAILBOXES_FILE' не найден!" >&2
 +
  exit 1
 +
fi
 +
 
 +
generate_password() {
 +
  # 16 символов: буквы (разного регистра), цифры и безопасные спецсимволы
 +
  LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16
 +
}
 +
 
 +
 
 +
OUTPUT_CSV_BACKUP="${OUTPUT_CSV%.csv}_$(date +%Y-%m-%d-%H-%M-%S).csv"
 +
 
 +
if [ -f "$OUTPUT_CSV" ]; then
 +
  cp "$OUTPUT_CSV" "$OUTPUT_CSV_BACKUP"
 +
  echo "Создан бэкап предыдущего CSV: $OUTPUT_CSV_BACKUP"
 +
fi
 +
 
 +
# Инициализация / очистка CSV файла
 +
echo "Email;Password" > "$OUTPUT_CSV"
 +
 
 +
echo "Все проверки пройдены. Начинаем обработку списков..."
 +
echo "------------------------------------------------"
 +
 
 +
while read ACCOUNT; do
 +
  # Пропускаем пустые строки
 +
  [ -z "$ACCOUNT" ] && continue
 +
 
 +
  NEW_PASS=$(generate_password)
 +
 
 +
  echo "Обработка ящика: $ACCOUNT"
 +
 
 +
  echo "Сохраняем данные аккаунта ${ACCOUNT} в файл $OUTPUT_CSV"
 +
  echo "${ACCOUNT};${NEW_PASS}" >> "$OUTPUT_CSV"
 +
 
 +
  echo "Начата синхронизация аккаунта ${ACCOUNT}..."
 +
  if "$TARGET_SCRIPT" "$ACCOUNT" "$ACCOUNT" "$NEW_PASS"; then
 +
    echo "Успешно: $ACCOUNT"
 +
  else
 +
    echo "ОШИБКА: Сбой при обработке $ACCOUNT" >&2
 +
  fi
 +
 
 +
  echo "------------------------------------------------"
 +
 
 +
done < "$MAILBOXES_FILE"
 +
 
 +
echo "Готово! Сгенерированные пароли сохранены в файл: $OUTPUT_CSV"
 +
EOF
 +
 
 +
chmod +x migrate_all_zimbra_accounts.sh
 +
</pre>
 +
 
 +
===Автоматизация обновления ssl сертификатов, если веб-интерфейс стоит за реверс-прокси nginx, работающим на базе vestacp===
 +
 
 +
 
 +
====Создаём скрипт====
 +
<pre>
 +
cat << 'EOF' > /usr/local/bin/sync-vesta-certs.sh
 +
#!/usr/bin/env bash
 +
set -euo pipefail
 +
 
 +
# === НАСТРОЙКИ ===
 +
# предполагается что доступ без пароля на $VESTA_HOST настроен в файле /root/.ssh/config
 +
VESTA_HOST="vesta"
 +
VESTA_USER="root"
 +
 
 +
# Пути к сертификатам на VestaCP
 +
REMOTE_CERT="/home/admin/conf/web/ssl.mail.myorg.net.pem"
 +
REMOTE_KEY="/home/admin/conf/web/ssl.mail.myorg.net.key"
 +
 
 +
# Пути к сертификатам в Grommunio
 +
# (При необходимости скорректируйте пути под вашу конфигурацию Nginx/Postfix/Grommunio)
 +
LOCAL_CERT="/etc/grommunio-common/ssl/server-bundle.pem"
 +
LOCAL_KEY="/etc/grommunio-common/ssl/server.key"
 +
 
 +
TMP_DIR="/tmp/vesta_ssl_sync"
 +
UPDATED=0
 +
 
 +
# === ЛОГИКА ===
 +
mkdir -p "$TMP_DIR"
 +
chmod 700 "$TMP_DIR"
 +
 
 +
# Функция очистки при завершении
 +
cleanup() {
 +
    rm -rf "$TMP_DIR"
 +
}
 +
trap cleanup EXIT
 +
 
 +
# 1. Скачивание файлов с VestaCP
 +
scp -q "$VESTA_USER@$VESTA_HOST:$REMOTE_CERT" "$TMP_DIR/remote.pem"
 +
scp -q "$VESTA_USER@$VESTA_HOST:$REMOTE_KEY" "$TMP_DIR/remote.key"
 +
 
 +
# 2. Сравнение сертификата
 +
if [ ! -f "$LOCAL_CERT" ] || ! cmp -s "$TMP_DIR/remote.pem" "$LOCAL_CERT"; then
 +
    echo "$(date): Обнаружен новый SSL-сертификат. Обновляем..."
 +
    cp "$TMP_DIR/remote.pem" "$LOCAL_CERT"
 +
    chmod 644 "$LOCAL_CERT"
 +
    UPDATED=1
 +
fi
 +
 
 +
# 3. Сравнение приватного ключа
 +
if [ ! -f "$LOCAL_KEY" ] || ! cmp -s "$TMP_DIR/remote.key" "$LOCAL_KEY"; then
 +
    echo "$(date): Обнаружен новый приватный ключ. Обновляем..."
 +
    cp "$TMP_DIR/remote.key" "$LOCAL_KEY"
 +
    chmod 600 "$LOCAL_KEY"
 +
    UPDATED=1
 +
fi
 +
 
 +
# 4. Перезапуск сервисов Grommunio при обновлении
 +
if [ "$UPDATED" -eq 1 ]; then
 +
    echo "$(date): Сертификаты обновлены. Перезапуск почтовых сервисов..."
 +
   
 +
    # Перезапуск веб-сервера и системных служб Grommunio
 +
    systemctl restart nginx || true
 +
    systemctl restart postfix || true
 +
    systemctl restart "gromox-*" || true
 +
    systemctl restart grommunio-admin-api || true
 +
   
 +
    echo "$(date): Перезапуск успешно завершен."
 +
else
 +
    echo "$(date): Сертификаты не изменились."
 +
fi
 +
EOF
 +
 
 +
 
 +
chmod +x /usr/local/bin/sync-vesta-certs.sh
 +
</pre>
 +
 
 +
 
 +
====Ставим его в планировщик====
 +
 
 +
(crontab -l 2>/dev/null | grep -Fv "/usr/local/bin/sync-vesta-certs.sh"; echo "30 3 * * * /usr/local/bin/sync-vesta-certs.sh >> /var/log/vesta-cert-sync.log 2>&1") | crontab -
 +
 
 +
 
 +
====Если grommunio изначально был установлен в режиме получения сертификата LetsEncrypt...====
 +
 
 +
, а потом появилась необходимость поставить перед ним реверс-прокси, который решает проблемы обновления сертификата, то:
 +
 
 +
Убираем из базы данных сертификатов certbot сертификат
 +
 
 +
certbot delete --non-interactive --cert-name mail.myorg.net
 +
 
 +
Посмотреть список сертификатов в базе данных можно так:
 +
 
 +
certbot certificates
 +
 
 +
===Скрипт получения адресных книг из аккаунтов zimbra===
 +
 
 +
<pre>
 +
cat << 'EOF' > export_all_contacts.sh
 +
#!/bin/bash
 +
 
 +
# Проверяем, запущен ли скрипт от пользователя zimbra
 +
if [ "$(whoami)" != "zimbra" ]; then
 +
    echo "Ошибка: этот скрипт должен запускаться от имени пользователя zimbra."
 +
    echo "Выполните: su - zimbra"
 +
    exit 1
 +
fi
 +
 
 +
OUTPUT_DIR="/tmp/zimbra_all_contacts"
 +
FINAL_FILE="/tmp/zimbra_all_contacts_combined.csv"
 +
 
 +
echo "==> Создаем рабочую директорию: $OUTPUT_DIR"
 +
rm -rf "$OUTPUT_DIR"
 +
mkdir -p "$OUTPUT_DIR"
 +
 
 +
echo "==> Получаем список активных почтовых ящиков..."
 +
ACCOUNTS=$(zmprov -l gaa)
 +
 
 +
if [ -z "$ACCOUNTS" ]; then
 +
    echo "Ошибка: не найдено ни одного аккаунта."
 +
    exit 1
 +
fi
 +
 
 +
COUNT=0
 +
SUCCESS=0
 +
 
 +
for account in $ACCOUNTS; do
 +
    COUNT=$((COUNT + 1))
 +
    # Убираем недопустимые символы для имени файла, если такие вдруг будут
 +
    safe_name=$(echo "$account" | tr '/' '_')
 +
    file_path="$OUTPUT_DIR/${safe_name}.csv"
 +
   
 +
    # Экспортируем контакты конкретного ящика
 +
    zmmailbox -z -m "$account" getRestURL "/Contacts?fmt=csv" > "$file_path" 2>/dev/null
 +
   
 +
    # Проверяем, скачался ли непустой файл (минимальный CSV содержит хотя бы заголовки)
 +
    if [ -s "$file_path" ]; then
 +
        SUCCESS=$((SUCCESS + 1))
 +
    else
 +
        # Удаляем пустые файлы, чтобы они не мешали
 +
        rm -f "$file_path"
 +
    fi
 +
done
 +
 
 +
echo "==> Экспорт завершен. Успешно выгружено ящиков с контактами: $SUCCESS из $COUNT"
 +
 
 +
# Объединяем файлы в один
 +
if [ $SUCCESS -gt 0 ]; then
 +
    echo "==> Объединяем файлы в один общий: $FINAL_FILE"
 +
   
 +
    # Берем заголовки из самого первого попавшегося файла
 +
    FIRST_FILE=$(ls "$OUTPUT_DIR"/*.csv | head -n 1)
 +
    head -n 1 "$FIRST_FILE" > "$FINAL_FILE"
 +
   
 +
    # Склеиваем содержимое всех файлов, пропуская первую строку (заголовки)
 +
    for f in "$OUTPUT_DIR"/*.csv; do
 +
        tail -n +2 "$f" >> "$FINAL_FILE"
 +
    done
 +
   
 +
    echo "==> Готово! Общий файл сохранен как: $FINAL_FILE"
 +
    echo "    Размер файла: $(du -h "$FINAL_FILE" | cut -f1)"
 +
else
 +
    echo "==> Внимание: ни у одного аккаунта не найдено контактов для экспорта."
 +
fi
 +
EOF
 +
</pre>
 +
 
 +
chmod +x export_all_contacts.sh
  
 
==Особенности работы и способы починки==
 
==Особенности работы и способы починки==
Строка 351: Строка 838:
 
===Ошибки входа в веб интерфейс===
 
===Ошибки входа в веб интерфейс===
  
−
После нескольких итерация создания/удаления пользователя с одинаковым емэйлом веб интерфейс почтового ящика (не админский интерфейс на порту 8443, а обычный - на порту 443) показывает невменяемые ошибки прав доступа.
+
После нескольких итераций создания/удаления пользователя с одинаковым емэйлом веб интерфейс почтового ящика (не админский интерфейс на порту 8443, а обычный - на порту 443) показывает невменяемые ошибки прав доступа.
  
 
Как конкретно это решить без перезагрузки сервера до сих пор выяснить не удалось.
 
Как конкретно это решить без перезагрузки сервера до сих пор выяснить не удалось.
Строка 360: Строка 847:
  
 
Перезагрузка сервера решает эту проблему. Поэтому это проблемой можно не считать
 
Перезагрузка сервера решает эту проблему. Поэтому это проблемой можно не считать
−
 
  
 
===Комьюнити версия не разрешает создавать больше 5 учётных записей почты на домен===
 
===Комьюнити версия не разрешает создавать больше 5 учётных записей почты на домен===
Строка 377: Строка 863:
  
 
В этом разделе специально подробно не описан процесс решения этой проблемы - из-за солидарности с разработчиками продукта.
 
В этом разделе специально подробно не описан процесс решения этой проблемы - из-за солидарности с разработчиками продукта.
 +
 +
===Переиндексация SQLite баз данных ящиков===
 +
 +
Есть гипотеза, что это поможет ускорить поиск по письмам в веб интерфейсе почты, если ящик содержит десятки тысяч писем.
 +
 +
Гипотеза пока не проверена до конца.
 +
 +
Индексы лежат здесь
 +
 +
/var/lib/grommunio-web/sqlite-index/
 +
 +
Внутри находятся каталоги, имена которых соответствуют именам ящиков. Внутри этих каталогов лежат файлы
 +
 +
index.sqlite3
 +
 +
Удаляем все индексы
 +
 +
rm -rf /var/lib/grommunio-web/sqlite-index/*
 +
 +
Перезапускаем сервис
 +
 +
systemctl restart grommunio-index.service
 +
 +
За работой сервиса можно проследить так
 +
 +
journalctl -f -u grommunio-index
 +
 +
, либо наблюдать за появлением в каталоге
 +
 +
/var/lib/grommunio-web/sqlite-index
 +
 +
новых каталогов с названиями, идентичными ящикам и файлами внутри них...
 +
 +
====Перенастройка таймера переиндексации====
 +
 +
По умолчанию таймер настроен на запуск раз в 15 минут.
 +
 +
Перенастроить на запуск каждую ночь можно так:
 +
 +
systemctl edit grommunio-index.timer
 +
 +
В открывшемся файле внимательно читаем предостережения сверху и в нужное место вставляем новые параметры
 +
 +
[Timer]
 +
OnCalendar=
 +
OnCalendar=*-*-* 03:00:00
 +
 +
, затем сохраняем файл.
 +
 +
Проверяем
 +
 +
systemctl cat grommunio-index.timer

Текущая версия на 21:10, 16 сентября 2026

Содержание

Развёртывание

Где брать, как ставить

Для развёртывания на PVE можно прямо в хранилище PVE затащить образ по ссылке

https://download.grommunio.com/appliance/grommunio.x86_64-latest.install.iso

Ставить по этой инструкции

https://docs.grommunio.com/admin/installation/

Если ваши будущие емэйлы будут вида

user@myorg.net

То нужно ориентироваться на домены

mail.myorg.net

и

myorg.net

при инсталляции.


Порты 80 и 443 должны быть зарулены на сервер, если предполагается настройка сертификатов инсталлером через LestsEncrypt!


После инсталляции админка будет доступна на порту

8443

и туда входить нужно только по логину (не по полной почте).

Т.е. только по

admin

, а не по

admin@myorg.net


А в веб интерфейсе почты -- наоборот. Он находится по стандартному порту

443

и туда входим по

admin@myorg.net

, а не по

admin


Остальные порты важны будут при эксплуатации и при развёртывании их можно не забрасывать, о них -- ниже.

Порты

Порт Протокол Сервис / Назначение в Grommunio
25 TCP SMTP — входящая почта (передача между почтовыми серверами)
80 TCP HTTP — веб-интерфейс (перенаправление на HTTPS / Let's Encrypt)
110 TCP POP3 — получение почты (незашифрованный)
143 TCP IMAP — получение почты (незашифрованный / STARTTLS)
443 TCP HTTPS — Grommunio Web (Webmail), Outlook (EAS/MAPI over HTTP), Admin API
465 TCP SMTPS — защищённая отправка почты клиентами (SSL/TLS)
587 TCP SMTP Submission — отправка почты клиентами (STARTTLS)
993 TCP IMAPS — защищённое получение почты (SSL/TLS)
995 TCP POP3S — защищённое получение почты (SSL/TLS)
5349 TCP STUN/TURN (coturn) — установка видеосоединений Jitsi за NAT (HTTPS/TLS)
8443 TCP Grommunio Admin Admin Web Console — веб-панель администратора
10000 UDP Jitsi Videobridge — медиапоток (аудио, видео, шеринг экрана)

Записи для домена

Инструкция по проверке DNS-записей почтового сервера (myorg.net)
Назначение Тип Команда для проверки Ожидаемый результат
Маршрутизация почты MX dig MX myorg.net +short 1 mail.myorg.net.
IP-адрес узла A dig A mail.myorg.net +short 37.1.2.3
Обратная зона (PTR) PTR dig -x 37.1.2.3 +short mail.myorg.net.
Разрешенные отправители (SPF) TXT dig TXT myorg.net +short | grep spf "v=spf1 a mx ip4:37.1.2.3 ~all"
Цифровая подпись (DKIM) TXT dig TXT mailserver._domainkey.myorg.net +short "v=DKIM1; k=rsa; p=MIGfMA...AFwIDAQAB"
Политика безопасности (DMARC) TXT dig TXT _dmarc.myorg.net +short "v=DMARC1; p=quarantine; rua=mailto:admin@myorg.net"


БОльшую часть из этой информации можно также получить из формы настроек домена из админки grommunio.

Но есть нюанс с двумя записями:

1. Обратная зона (PTR) -- её невозможно настроить в админке провайдера днс зоны. Её должен настроить провайдер, выдающий интернет для почтового сервера и статический айпи адрес для него. Здесь необходимо добиться от интернет-провайдера, чтобы на его стороне была прописана эта самая обратная зона PTR. Суть в том, чтобы происходил процесс наоборот: по айпи адресу -- выдавался домен. Для почтовых серверов это важно!

2. Цифровая подпись (DKIM) -- чтобы корректно работала эта запись, сначала её необходимо настроить. Об этом подробно написано ниже!

3. Разрешенные отправители (SPF) -- здесь также должен фигурировать статический айпи адрес почтового сервера.

В примере это адрес 37.1.2.3, в реальном кейсе это должен быть везде айпи адрес конкретного сервера

Настроить подпись писем через DKIM

Действия мышкой в админке

Сначала внутри админки нужно создать домен и перейти в форму управления этим доменом.

Там нужно настроить всякие умолчания для вновь создаваемых пользователей этого домена.

С этими умолчаниями пользователи будут создаваться, если их создавать из админки.


Для настройки подписи через DKIM нужно нажать соответствующую кнопку в форме управления доменом. В открывшемся окне выполнить создание ключевой пары для подписи.

Выбрать алгоритм RSA и selector. По умолчанию selector имеет значение

dkim

, но можно установить свой. Этот селектор будет использован при настройке TXT записи для почтового домена.

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

mailserver


В ответ на запрос генерации ключей придёт примерно такой шаблон для настройки TXT записи для домена:

mailserver._domainkey IN TXT ( "v=DKIM1; k=rsa; "
	"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/D2gdESmBR0TqqeZ42wXCsEjGpPOB7uxp4uY0De+297hKs3XV+1CehUu5BxXxKKeJJIKRzogvzzOj76AXWMpiF5LS4oOUd3I+I+H8aCAC69QuV/TKHuB0Qqv7c8S54dw4210fHOSMMwVYlzfRGeyPfrKBcfZi4v98j7jh2on5f5btiaeppkUgUxMOPjTRepl3VzxTkID9cLrIEDc"
	"iA3nVMXbkj4wULMNSL3IjfuW4MRIJ6a4zZZqOECjrhBiSgAies+l0yh/+mn3LQmVcjba0RTuxa7x5xuf21AnKZvv0uZeQrROd0WrbLYGLFRO2Nk6/jUQPaPHS3ThDggbXz3RwIDAQAB"
) ; 

и будет сказано что ключ лежит где-то здесь:

The private key has been generated on the server at
/var/lib/grommunio-admin-api/myorg.net.dkim.key

Этот путь до файла ключа будет нужен потом при настройке сервера по ssh.

Настройка сервера по ssh и настройки конфигов на сервере

Для доступа по ssh под root лучше сразу получить на сервер открытую часть ssh ключа и настроить конфиг ssh для быстрого подключения. Под root на сервер по паролю подключиться не получится. А через виртуальный дисплей виртуальной машины нельзя будет скопировать текст на сервер.

Поэтому самый верный вариант - зайти на сервер через виртуальный дисплей виртуальной машины, скопировать через scp открытую часть ключа с другой машины по сети, затем перейти на другую машину и подключаться по ключу с ней на сервер.


Войдя по ssh на сервер необходимо сделать ряд настроек:

Если ваш домен:

myorg.net

а селектор

mailserver 

1. Создаём каталог

mkdir -p /var/lib/grommunio-antispam/dkim/myorg.net

2. Копируем тот самый файл, путь к которому нам сообщили в веб интерфейсе в предыдущем пункте в файл с названием селектора в только что созданный каталог

cp /var/lib/grommunio-admin-api/myorg.net.dkim.key /var/lib/grommunio-antispam/dkim/myorg.net.mailserver.key

3. Создайм файл

cat << 'EOF' > /etc/grommunio-antispam/override.d/dkim_signing.conf
enabled = true;
sign_authenticated = true;
sign_local = true;

use_domain = "header";
use_domain_sign_local = "full";

domain {
    "myorg.net" {
        path = "/var/lib/grommunio-antispam/dkim/myorg.net.mailserver.key";
        selector = "mailserver";
    }
}

EOF

4. Устанавливаем права доступа на каталог и файл

chown -R groas:grommunio /var/lib/grommunio-antispam/dkim/
chmod 600 /var/lib/grommunio-antispam/dkim/myorg.net.mailserver.key

5. Оставляем в доверенных сетях только локальную петлю и устанавливаем параметр non_smtpd_milters в файл конфигурации postfix

echo 'mynetworks = 127.0.0.0/8' >> /etc/postfix/main.cf
echo 'non_smtpd_milters = inet:localhost:11332' >> /etc/postfix/main.cf

6. Перезапустить сервисы

systemctl restart grommunio-antispam
systemctl restart postfix

Перезапуска этих сервисов для полноценной работы механизма подписи емэйлов может быть недостаточно. Тогда перезагружаем сервер полностью

reboot

А если и это не помогло, то смотрим что в логах

tail -f /var/log/grommunio-antispam/rspamd.log

Важный момент при настройке почтового сервера на доменах третьего уровня

Общая суть в том, что понадобятся параметры

use_esld = false;
use_effective_domain = false;

в файле

/etc/grommunio-antispam/override.d/dkim_signing.conf

Иначе сервер будет схлопывать домен до первого уровня и подставлять это в тело письма вот сюда (это часть письма в сыром виде):

...DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=myorg.net; s=mailserver; t=1785189679;...

В случае с доменом третьего уровня, например, unit.myorg.net, значение должно быть

d=unit.myorg.net

, а без этих параметров оно:

d=myorg.net

Следовательно, принимающие сервера пытаются прочитать ключ для проверки подписи с домена myorg.net, а не с домена unit.myorg.net и не могут её проверить.


Пример рабочего конфига для домена третьего уровня

cat << 'EOF' > /etc/grommunio-antispam/override.d/dkim_signing.conf
enabled = true;
sign_authenticated = true;
sign_local = true;

use_domain = "header";

# КРИТИЧЕСКИ ВАЖНО: Выключаем нормализацию eSLD, которая схлопывает домен unit.myorg.net до myorg.net
use_esld = false;
use_effective_domain = false;

selector = "mailserver";
path = "/var/lib/grommunio-antispam/dkim/unit.myorg.net.mailserver.key";
EOF

Настройка TXT записи для домена

Эта настройка делается в веб панели управления доменом. В зависимости от того, где арендован домен. Например, reg.ru, beget.com и т.д...


Нюанс

Веб админка выдала на генерацию ключей что-то типа того:

mailserver._domainkey IN TXT ( "v=DKIM1; k=rsa; "
	"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/D2gdESmBR0TqqeZ42wXCsEjGpPOB7uxp4uY0De+297hKs3XV+1CehUu5BxXxKKeJJIKRzogvzzOj76AXWMpiF5LS4oOUd3I+I+H8aCAC69QuV/TKHuB0Qqv7c8S54dw4210fHOSMMwVYlzfRGeyPfrKBcfZi4v98j7jh2on5f5btiaeppkUgUxMOPjTRepl3VzxTkID9cLrIEDc"
	"iA3nVMXbkj4wULMNSL3IjfuW4MRIJ6a4zZZqOECjrhBiSgAies+l0yh/+mn3LQmVcjba0RTuxa7x5xuf21AnKZvv0uZeQrROd0WrbLYGLFRO2Nk6/jUQPaPHS3ThDggbXz3RwIDAQAB"
) ; 

Это на самом деле означает, что значение TXT записи

mailserver._domainkey

в веб админке провайдера днс должно быть таким:

v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/D2gdESmBR0TqqeZ42wXCsEjGpPOB7uxp4uY0De+297hKs3XV+1CehUu5BxXxKKeJJIKRzogvzzOj76AXWMpiF5LS4oOUd3I+I+H8aCAC69QuV/TKHuB0Qqv7c8S54dw4210fHOSMMwVYlzfRGeyPfrKBcfZi4v98j7jh2on5f5btiaeppkUgUxMOPjTRepl3VzxTkID9cLrIEDciA3nVMXbkj4wULMNSL3IjfuW4MRIJ6a4zZZqOECjrhBiSgAies+l0yh/+mn3LQmVcjba0RTuxa7x5xuf21AnKZvv0uZeQrROd0WrbLYGLFRO2Nk6/jUQPaPHS3ThDggbXz3RwIDAQAB

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

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

/var/lib/grommunio-antispam/dkim/myorg.net/dkim.key

base64 строка соответствует значению ключа

p=

из значения для TXT записи, приведённой выше.

Получить этот ключ можно следующим образом:

openssl rsa -in /var/lib/grommunio-antispam/dkim/myorg.net/dkim.key -pubout -outform DER 2>/dev/null | base64 -w 0

В ответ придёт что-то вроде:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/D2gdESmBR0TqqeZ42wXCsEjGpPOB7uxp4uY0De+297hKs3XV+1CehUu5BxXxKKeJJIKRzogvzzOj76AXWMpiF5LS4oOUd3I+I+H8aCAC69QuV/TKHuB0Qqv7c8S54dw4210fHOSMMwVYlzfRGeyPfrKBcfZi4v98j7jh2on5f5btiaeppkUgUxMOPjTRepl3VzxTkID9cLrIEDciA3nVMXbkj4wULMNSL3IjfuW4MRIJ6a4zZZqOECjrhBiSgAies+l0yh/+mn3LQmVcjba0RTuxa7x5xuf21AnKZvv0uZeQrROd0WrbLYGLFRO2Nk6/jUQPaPHS3ThDggbXz3RwIDAQAB

В теории (в теории, но на практике было и по другому. Это надо проверить ещй раз!) эти ключи должны совпадать. Т.е. ключ, который с переносами и кавычками был выдан веб интеряейсом, и ключ, который выдала команда выше -- должны быть одинаковы. Ведь файл ключа одинаковый (мы его просто скопировали для донастройки grommunio-antispam)!

Но на практике было и так что команда

openssl rsa -in...

выдавала ключ, который отличается. Если ключи будут отличаться, то в значение

p=

для DNS записи необходимо заменить на значение, полученное от команды:

openssl rsa -in...


Далее в веб-админке DNS сервера добавляем запись типа

TXT

, поддомен которой должен быть такой (полный домен имеется в виду такой: mailserver._domainkey.myorg.net)

mailserver._domainkey

, а его значением должно быть

v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/D2gdESmBR0TqqeZ42wXCsEjGpPOB7uxp4uY0De+297hKs3XV+1CehUu5BxXxKKeJJIKRzogvzzOj76AXWMpiF5LS4oOUd3I+I+H8aCAC69QuV/TKHuB0Qqv7c8S54dw4210fHOSMMwVYlzfRGeyPfrKBcfZi4v98j7jh2on5f5btiaeppkUgUxMOPjTRepl3VzxTkID9cLrIEDciA3nVMXbkj4wULMNSL3IjfuW4MRIJ6a4zZZqOECjrhBiSgAies+l0yh/+mn3LQmVcjba0RTuxa7x5xuf21AnKZvv0uZeQrROd0WrbLYGLFRO2Nk6/jUQPaPHS3ThDggbXz3RwIDAQAB

т.е. в общем виде так:

v=DKIM1; k=rsa; p=<результат, полученный от команды openssl rsa -in...>

Проверка TXT записи

После того, как TXT запись домена проделегируется проверить это можно командой:

dig TXT mailserver._domainkey.myorg.net +short

в ответ должно прийти что-то типа

"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/D2gdESmBR0TqqeZ42wXCsEjGpPOB7uxp4uY0De+297hKs3XV+1CehUu5BxXxKKeJJIKRzogvzzOj76AXWMpiF5LS4oOUd3I+I+H8aCAC69QuV/TKHuB0Qqv7c8S54dw4210fHOSMMwVYlzfRGeyPfrKBcfZi4v98j7jh2on5f5btiaeppkUgUxMOPjTRepl3VzxTkID9cLrIEDciA3nVMXbkj4wULMNSL3IjfuW4MRIJ6a4zZZqOECjrhBiSgAies+l0yh/+mn3LQmVcjba0RTuxa7x5xuf21AnKZvv0uZeQrROd0WrbLYGLFRO2Nk6/jUQPaPHS3ThDggbXz3RwIDAQAB"

здесь важно проверить что значение

p=

соответствует той самой base64 строке, которая выше была получена через

openssl rsa -in...


Проверка работы механизма подписи емэйлов

Войти в веб интерфейс почты (не в админку по https://mail.myorg.net:8443, а просто на https://mail.myorg.net) любым ящиком, который есть в домене и отправить с него письмо куда-нибудь себе на ящик (gmail, yandex и.т.д...).

У себя в аккаунте (gmail, yandex и.т.д...) ищем исходники письма (это обычно PlainText) и там должны быть такие строки:

Раз! Само наличие этой строки говорит о том, что сервер стал подписывать письма!

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=myorg.net; s=mailserver; t=1785189679; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type; bh=ckTMbuexoDGBdcu/luc2eNS+Ap0k9i9yjhltmXYdzGo=; b=BOe7sXr+EW6iTfZdDVCxA8gZ2gXBw3KcWC2PFALvbi6inSXlO/u3cZCLtEcBCJ9/accyFP Hn1W2qHSKEeY1B8Ri5SqqUoXBO7bEc5IPOr0dYyrivABFsT+Zr7XNeDUHlC6Lbt7YvaI4z vatOvc5T+qzDv/bqu685KqqK33PRQiY=

Но это ещё не всё... Необходимо проверить проверку этой подписи. Здесь как раз важно, чтобы TXT запись у домена была корректна.


Два!

Для этого также ищем среди исходинка письма следующую строку:

Authentication-Results: mx.google.com; dkim=pass header.i=@myorg.net header.s=mailserver header.b=BOe7sXr+; spf=pass (google.com: domain of admin@myorg.net designates 37.112.130.81 as permitted sender) smtp.mailfrom=admin@myorg.net; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=myorg.net

Здесь важно увидеть

dkim=pass

и

spf=pass

После этого можно проверить работу сервера через внешний сервис

https://mail-tester.com/

При корректной настройке почтового сервера этот сервис должен показать 10 из 10 возможных "попугаев".

Важно! У сервиса этого есть ограничения на кол-во проверок в сутки. Потом он просит денюжку или ждать следующего дня. Поэтому сначала проверяем текст в исходниках письма, как описано выше, и только потом - через сервис.

Скрипты и автоматизация

Все скрипты, представленные здесь, запускать необходимо с сервера Grommunio!


Скрипт создания одного ящика

cat << 'EOF' > create_mailbox.sh 
#!/bin/bash
USER_GROMMUNIO="$1"
PASS_GROMMUNIO="$2"

# Проверка входных параметров
if [ -z "$USER_GROMMUNIO" ] || [ -z "$PASS_GROMMUNIO" ]; then
    echo "Использование: $0 <user_grommunio> <pass_grommunio>"
    echo "Пример: $0 user@domain.ru 'ThePass'"
    exit 1
fi

echo "=== 1. Проверка / Создание ящика $USER_GROMMUNIO в Grommunio ==="

# Проверяем наличие ящика
if grommunio-admin user show "$USER_GROMMUNIO" >/dev/null 2>&1; then
    echo "i Ящик $USER_GROMMUNIO уже существует. Пропускаем создание."
else
    echo "Пользователь не найден. Создаём ящик $USER_GROMMUNIO..."

    DISPLAY_NAME=$(echo "$USER_GROMMUNIO" | cut -d'@' -f1)

    # Создаем юзера
    grommunio-admin user create "$USER_GROMMUNIO"
    if [ $? -ne 0 ]; then
        echo "Ошибка создания пользователя $USER_GROMMUNIO!"
        exit 1
    fi

    # Устанавливаем свойства пользователю
    grommunio-admin user modify \
		--property givenname="$DISPLAY_NAME" \
		--property surname="$DISPLAY_NAME" \
		--property displayname="$DISPLAY_NAME" \
		--property country="Russian Federation" \
		"$USER_GROMMUNIO"
    if [ $? -ne 0 ]; then
        echo "Ошибка установки свойств пользователя $USER_GROMMUNIO!"
        exit 1
    fi

    # Задаем пароль
    grommunio-admin passwd --password "$PASS_GROMMUNIO" "$USER_GROMMUNIO"
    if [ $? -ne 0 ]; then
        echo "Ошибка установки пароля для $USER_GROMMUNIO!"
        exit 1
    fi

    echo "✓ Ящик $USER_GROMMUNIO успешно создан и пароль установлен."
fi
EOF

chmod +x create_mailbox.sh


Скрипт переноса одного ящика из Zimbra в Grommunio

Для работы скрипта необходимо наличие рядом предыдущего скрипта (create_mailbox.sh)

А также внутри скрипта необходимо задать адрес сервера Zimbra и логин и пароль мастер-аккаунта Zimbra


cat << 'EOF' > migrate_account_from_zimbra_to_grommunio.sh
#!/bin/bash

# Настройки серверов
ZIMBRA_SERVER="192.168.0.1"
GROMMUNIO_SERVER="127.0.0.1"

# Мастер-аккаунт с Zimbra с паролем
ZIMBRA_ADMIN_USER="admin@myorg.net"
ZIMBRA_ADMIN_PASS="SuperAdminPassword"

# Определяем директорию, в которой расположен текущий скрипт
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CREATE_SCRIPT="$SCRIPT_DIR/create_mailbox.sh"

# Чтение аргументов
USER_ZIMBRA="$1"
USER_GROMMUNIO="$2"
PASS_GROMMUNIO="$3"

# Проверка входных параметров
if [ -z "$USER_ZIMBRA" ] || [ -z "$USER_GROMMUNIO" ] || [ -z "$PASS_GROMMUNIO" ]; then
    echo "Использование: $0 <mailbox_zimbra> <mailbox_grommunio> <pass_grommunio>"
    echo "Пример: $0 old_user@domain.ru new_user@domain.ru 'GromPass'"
    exit 1
fi

# Проверка наличия и прав на выполнение вспомогательного скрипта
if [ ! -f "$CREATE_SCRIPT" ]; then
    echo "Ошибка: Вспомогательный скрипт '$CREATE_SCRIPT' не найден!"
    exit 1
fi

if [ ! -x "$CREATE_SCRIPT" ]; then
    echo "Ошибка: Скрипт '$CREATE_SCRIPT' найден, но не имеет прав на выполнение (chmod +x)!"
    exit 1
fi

# Вызов внешнего скрипта для создания ящика
"$CREATE_SCRIPT" "$USER_GROMMUNIO" "$PASS_GROMMUNIO"
if [ $? -ne 0 ]; then
    echo "Ошибка при подготовке ящика $USER_GROMMUNIO через $CREATE_SCRIPT"
    exit 1
fi

echo "=== 2. Запуск переноса почты из Zimbra в Grommunio ==="

set -x

# Создаем временные защищенные файлы для паролей
PASSFILE1=$(mktemp)
PASSFILE2=$(mktemp)

printf '%s' "$ZIMBRA_ADMIN_PASS" > "$PASSFILE1"
printf '%s' "$PASS_GROMMUNIO" > "$PASSFILE2"

imapsync \
  --host1 "$ZIMBRA_SERVER" \
  --user1 "$USER_ZIMBRA" \
  --authuser1 "$ZIMBRA_ADMIN_USER" \
  --passfile1 "$PASSFILE1" \
  --authmech1 PLAIN \
  --ssl1 \
  --sslargs1 SSL_verify_mode=0 \
  --host2 "$GROMMUNIO_SERVER" \
  --user2 "$USER_GROMMUNIO" \
  --passfile2 "$PASSFILE2" \
  --authmech2 LOGIN \
  --ssl2 \
  --sslargs2 SSL_verify_mode=0 \
  --useheader "Message-ID" \
  --automap \
  --f1f2 "Sent=&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-" \
  --f1f2 "Trash=&BCMENAQwBDsENQQ9BD0ESwQ1-" \
  --f1f2 "Junk=&BB0ENQQ2BDUEOwQwBEIENQQ7BEwEPQQwBE8- &BD8EPgRHBEIEMA-" \
  --f1f2 "Drafts=&BCcENQRABD0EPgQyBDgEOgQ4-" \
  --noflags \
  --errorsmax 500000 \
  --noauthmd5

# если не надо синхронизировать спам и корзину
#  --exclude "Junk" \
#  --exclude "Trash" \

# Удаляем временные файлы с паролями
rm -f "$PASSFILE1" "$PASSFILE2"
EOF

chmod +x migrate_account_from_zimbra_to_grommunio.sh

Скрипт переноса всех ящиков из Zimbra в Grommunio

Для работы скрипта необходимо наличие рядом предыдущего скрипта (migrate_account_from_zimbra_to_grommunio.sh, для которого необходимо наличие предпредыдущего скрипта (create_mailbox.sh))

Чтобы скрипт знал какие ящики переносить необходимо создать файл mailboxes.txt, в котором одна строчка - один ящик. Например так:

cat << 'EOF' > mailboxes.txt
admin@myorg.net
manager@myorg.net
EOF


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

Файл с данными новых ящиков будет называться:

migrated_passwords.csv

При повторном запуске скрипта предварительно будет создаваться резервная копия этого файла, при его наличии.

Это сделано чтобы не потерять данные случайно сгенерированных паролей при прошлом запуске скрипта.


Сам скрипт:

cat << 'EOF' > migrate_all_zimbra_accounts.sh 
#!/bin/bash

TARGET_SCRIPT="./migrate_account_from_zimbra_to_grommunio.sh"
MAILBOXES_FILE="mailboxes.txt"
OUTPUT_CSV="migrated_passwords.csv"

if [ ! -f "$TARGET_SCRIPT" ]; then
  echo "Ошибка: Исполняемый файл '$TARGET_SCRIPT' не найден!" >&2
  exit 1
fi

if [ ! -x "$TARGET_SCRIPT" ]; then
  echo "Ошибка: Файл '$TARGET_SCRIPT' существует, но у него нет прав на выполнение (chmod +x)!" >&2
  exit 1
fi

if [ ! -f "$MAILBOXES_FILE" ]; then
  echo "Ошибка: Файл со списком ящиков '$MAILBOXES_FILE' не найден!" >&2
  exit 1
fi

generate_password() {
  # 16 символов: буквы (разного регистра), цифры и безопасные спецсимволы
  LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16
}


OUTPUT_CSV_BACKUP="${OUTPUT_CSV%.csv}_$(date +%Y-%m-%d-%H-%M-%S).csv"

if [ -f "$OUTPUT_CSV" ]; then
  cp "$OUTPUT_CSV" "$OUTPUT_CSV_BACKUP"
  echo "Создан бэкап предыдущего CSV: $OUTPUT_CSV_BACKUP"
fi

# Инициализация / очистка CSV файла
echo "Email;Password" > "$OUTPUT_CSV"

echo "Все проверки пройдены. Начинаем обработку списков..."
echo "------------------------------------------------"

while read ACCOUNT; do
  # Пропускаем пустые строки
  [ -z "$ACCOUNT" ] && continue

  NEW_PASS=$(generate_password)

  echo "Обработка ящика: $ACCOUNT"

  echo "Сохраняем данные аккаунта ${ACCOUNT} в файл $OUTPUT_CSV"
  echo "${ACCOUNT};${NEW_PASS}" >> "$OUTPUT_CSV"

  echo "Начата синхронизация аккаунта ${ACCOUNT}..."
  if "$TARGET_SCRIPT" "$ACCOUNT" "$ACCOUNT" "$NEW_PASS"; then
    echo "Успешно: $ACCOUNT"
  else
    echo "ОШИБКА: Сбой при обработке $ACCOUNT" >&2
  fi

  echo "------------------------------------------------"

done < "$MAILBOXES_FILE"

echo "Готово! Сгенерированные пароли сохранены в файл: $OUTPUT_CSV"
EOF

chmod +x migrate_all_zimbra_accounts.sh 

Автоматизация обновления ssl сертификатов, если веб-интерфейс стоит за реверс-прокси nginx, работающим на базе vestacp

Создаём скрипт

cat << 'EOF' > /usr/local/bin/sync-vesta-certs.sh
#!/usr/bin/env bash
set -euo pipefail

# === НАСТРОЙКИ ===
# предполагается что доступ без пароля на $VESTA_HOST настроен в файле /root/.ssh/config
VESTA_HOST="vesta"
VESTA_USER="root"

# Пути к сертификатам на VestaCP
REMOTE_CERT="/home/admin/conf/web/ssl.mail.myorg.net.pem"
REMOTE_KEY="/home/admin/conf/web/ssl.mail.myorg.net.key"

# Пути к сертификатам в Grommunio
# (При необходимости скорректируйте пути под вашу конфигурацию Nginx/Postfix/Grommunio)
LOCAL_CERT="/etc/grommunio-common/ssl/server-bundle.pem"
LOCAL_KEY="/etc/grommunio-common/ssl/server.key"

TMP_DIR="/tmp/vesta_ssl_sync"
UPDATED=0

# === ЛОГИКА ===
mkdir -p "$TMP_DIR"
chmod 700 "$TMP_DIR"

# Функция очистки при завершении
cleanup() {
    rm -rf "$TMP_DIR"
}
trap cleanup EXIT

# 1. Скачивание файлов с VestaCP
scp -q "$VESTA_USER@$VESTA_HOST:$REMOTE_CERT" "$TMP_DIR/remote.pem"
scp -q "$VESTA_USER@$VESTA_HOST:$REMOTE_KEY" "$TMP_DIR/remote.key"

# 2. Сравнение сертификата
if [ ! -f "$LOCAL_CERT" ] || ! cmp -s "$TMP_DIR/remote.pem" "$LOCAL_CERT"; then
    echo "$(date): Обнаружен новый SSL-сертификат. Обновляем..."
    cp "$TMP_DIR/remote.pem" "$LOCAL_CERT"
    chmod 644 "$LOCAL_CERT"
    UPDATED=1
fi

# 3. Сравнение приватного ключа
if [ ! -f "$LOCAL_KEY" ] || ! cmp -s "$TMP_DIR/remote.key" "$LOCAL_KEY"; then
    echo "$(date): Обнаружен новый приватный ключ. Обновляем..."
    cp "$TMP_DIR/remote.key" "$LOCAL_KEY"
    chmod 600 "$LOCAL_KEY"
    UPDATED=1
fi

# 4. Перезапуск сервисов Grommunio при обновлении
if [ "$UPDATED" -eq 1 ]; then
    echo "$(date): Сертификаты обновлены. Перезапуск почтовых сервисов..."
    
    # Перезапуск веб-сервера и системных служб Grommunio
    systemctl restart nginx || true
    systemctl restart postfix || true
    systemctl restart "gromox-*" || true
    systemctl restart grommunio-admin-api || true
    
    echo "$(date): Перезапуск успешно завершен."
else
    echo "$(date): Сертификаты не изменились."
fi
EOF


chmod +x /usr/local/bin/sync-vesta-certs.sh


Ставим его в планировщик

(crontab -l 2>/dev/null | grep -Fv "/usr/local/bin/sync-vesta-certs.sh"; echo "30 3 * * * /usr/local/bin/sync-vesta-certs.sh >> /var/log/vesta-cert-sync.log 2>&1") | crontab -


Если grommunio изначально был установлен в режиме получения сертификата LetsEncrypt...

, а потом появилась необходимость поставить перед ним реверс-прокси, который решает проблемы обновления сертификата, то:

Убираем из базы данных сертификатов certbot сертификат

certbot delete --non-interactive --cert-name mail.myorg.net

Посмотреть список сертификатов в базе данных можно так:

certbot certificates

Скрипт получения адресных книг из аккаунтов zimbra

cat << 'EOF' > export_all_contacts.sh
#!/bin/bash

# Проверяем, запущен ли скрипт от пользователя zimbra
if [ "$(whoami)" != "zimbra" ]; then
    echo "Ошибка: этот скрипт должен запускаться от имени пользователя zimbra."
    echo "Выполните: su - zimbra"
    exit 1
fi

OUTPUT_DIR="/tmp/zimbra_all_contacts"
FINAL_FILE="/tmp/zimbra_all_contacts_combined.csv"

echo "==> Создаем рабочую директорию: $OUTPUT_DIR"
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"

echo "==> Получаем список активных почтовых ящиков..."
ACCOUNTS=$(zmprov -l gaa)

if [ -z "$ACCOUNTS" ]; then
    echo "Ошибка: не найдено ни одного аккаунта."
    exit 1
fi

COUNT=0
SUCCESS=0

for account in $ACCOUNTS; do
    COUNT=$((COUNT + 1))
    # Убираем недопустимые символы для имени файла, если такие вдруг будут
    safe_name=$(echo "$account" | tr '/' '_')
    file_path="$OUTPUT_DIR/${safe_name}.csv"
    
    # Экспортируем контакты конкретного ящика
    zmmailbox -z -m "$account" getRestURL "/Contacts?fmt=csv" > "$file_path" 2>/dev/null
    
    # Проверяем, скачался ли непустой файл (минимальный CSV содержит хотя бы заголовки)
    if [ -s "$file_path" ]; then
        SUCCESS=$((SUCCESS + 1))
    else
        # Удаляем пустые файлы, чтобы они не мешали
        rm -f "$file_path"
    fi
done

echo "==> Экспорт завершен. Успешно выгружено ящиков с контактами: $SUCCESS из $COUNT"

# Объединяем файлы в один
if [ $SUCCESS -gt 0 ]; then
    echo "==> Объединяем файлы в один общий: $FINAL_FILE"
    
    # Берем заголовки из самого первого попавшегося файла
    FIRST_FILE=$(ls "$OUTPUT_DIR"/*.csv | head -n 1)
    head -n 1 "$FIRST_FILE" > "$FINAL_FILE"
    
    # Склеиваем содержимое всех файлов, пропуская первую строку (заголовки)
    for f in "$OUTPUT_DIR"/*.csv; do
        tail -n +2 "$f" >> "$FINAL_FILE"
    done
    
    echo "==> Готово! Общий файл сохранен как: $FINAL_FILE"
    echo "    Размер файла: $(du -h "$FINAL_FILE" | cut -f1)"
else
    echo "==> Внимание: ни у одного аккаунта не найдено контактов для экспорта."
fi
EOF
chmod +x export_all_contacts.sh

Особенности работы и способы починки

Ошибки входа в веб интерфейс

После нескольких итераций создания/удаления пользователя с одинаковым емэйлом веб интерфейс почтового ящика (не админский интерфейс на порту 8443, а обычный - на порту 443) показывает невменяемые ошибки прав доступа.

Как конкретно это решить без перезагрузки сервера до сих пор выяснить не удалось.

Но перезагрузка сервера - точно решает большинство таких проблем. Наверное, даже, все.

Кратко: ошибка где-то в кешах сессий или что-то в этом роде.

Перезагрузка сервера решает эту проблему. Поэтому это проблемой можно не считать

Комьюнити версия не разрешает создавать больше 5 учётных записей почты на домен

Чтобы создавать больше - нужно покупать лицензию. Или...

Открываем файл

/usr/share/grommunio-admin-api/orm/users.py

Ищем там строчки по

License user limit exceeded

Включаем скилл программиста и комментируем по вкусу. Затем перезагружаемся.

В этом разделе специально подробно не описан процесс решения этой проблемы - из-за солидарности с разработчиками продукта.

Переиндексация SQLite баз данных ящиков

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

Гипотеза пока не проверена до конца.

Индексы лежат здесь

/var/lib/grommunio-web/sqlite-index/

Внутри находятся каталоги, имена которых соответствуют именам ящиков. Внутри этих каталогов лежат файлы

index.sqlite3

Удаляем все индексы

rm -rf /var/lib/grommunio-web/sqlite-index/*

Перезапускаем сервис

systemctl restart grommunio-index.service

За работой сервиса можно проследить так

journalctl -f -u grommunio-index

, либо наблюдать за появлением в каталоге

/var/lib/grommunio-web/sqlite-index

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

Перенастройка таймера переиндексации

По умолчанию таймер настроен на запуск раз в 15 минут.

Перенастроить на запуск каждую ночь можно так:

systemctl edit grommunio-index.timer

В открывшемся файле внимательно читаем предостережения сверху и в нужное место вставляем новые параметры

[Timer]
OnCalendar=
OnCalendar=*-*-* 03:00:00

, затем сохраняем файл.

Проверяем

systemctl cat grommunio-index.timer