Перейти к основному контенту

SMTP-форвардинг из CommuniGate Pro

{{ВИДЕО}}

Раздел: Интеграции (внутри книги «Установка и настройка»). Связанные разделы: Сервер SMTP, Источники писем, Правила хранения.

Назначение

Перенаправление копии SMTP-трафика из CommuniGate Pro в Клавдий для архивирования. Клавдий выступает приёмником SMTP (роль «SMTP-сервер»); CommuniGate Pro направляет ему копии писем с помощью скрипта resubmitter.pl и правила обработки почты.

Если Клавдий должен сам забирать почту из CommuniGate Pro по IMAP — используйте источник «IMAP» (Источники писем). Эта страница описывает push-вариант (CommuniGate → Клавдий).

Документация производителя

Как это работает

  1. Письмо попадает в очередь CommuniGate Pro и подпадает под правило resubmitter.
  2. Правило вызывает Helper resubmitter.pl. Скрипт переписывает получателя user@domain → user%domain@<адрес-Клавдия> и кладёт файл в каталог Submitted/.
  3. Движок доставки CommuniGate Pro берёт файл и отправляет письмо по маршруту из Router для адреса <адрес-Клавдия>.
  4. Клавдий принимает письмо по SMTP, задача smtp_collection архивирует его по правилам хранения.

Скрипт resubmitter.pl не отправляет ничего по сети — он только переписывает получателя и кладёт файл в очередь. По сети письмо отправляет движок доставки CommuniGate Pro по маршруту из Router. Поэтому порт доставки задаётся в Router, а не в скрипте.

Сохраняются ли отправитель и получатель?

Да. Скрипт переписывает только конверт SMTP (RCPT TO: → user%domain@<адрес-Клавдия>), а заголовки тела письма (From:, To:, Cc:) оставляет нетронутыми. Клавдий определяет отправителя и получателя для архива именно по заголовкам тела, поэтому в архиве письмо попадает с корректными исходными адресами (напр. user1@cgp.lab → user2@cgp.lab), а не с подменённым конвертным адресом. Оригинальные адреса также сохраняются в дописанных скриптом заголовках Return-Path: и Envelope-To:.

Правила хранения, фильтрующие по отправителю/получателю, работают как обычно — они опираются на адреса из заголовков, а не на конверт.

Предварительные требования

  • CommuniGate Pro установлен и обрабатывает почту (есть домен, ящики, ходят письма).
  • Клавдий установлен, настроен минимум по Быстрому старту: есть группа хранилищ, правило хранения, узел.
  • Сетевая доступность: CommuniGate Pro может установить TCP-соединение к Клавдию на порт SMTP-слушателя.

Шаг 1. Настроить приёмник SMTP в Клавдии

  1. В веб-панели Клавдия откройте [Сбор почты → SMTP-сервер] (см. Сервер SMTP).
  2. В блоке [Глобальные настройки SMTP]:
    • Порт — порт, на котором Клавдий будет принимать трафик от CommuniGate Pro. Если Клавдий и CommuniGate Pro на одном хосте (например, оба в network_mode: host) — подойдёт 8025 (стандартный 25 может быть занят). Если порты не конфликтуют — 25.
    • Шифрование — Без TLS (LAN/MX) для простейшего лаб-варианта; STARTTLS для прод.
    • Имя сервера (hostname) — напр. klvd.local.
  3. Нажмите [Сохранить настройки].
  4. Разместите слушатель на узле: [Кластер → Узлы] → узел → роль SMTP, поле [Размещение слушателей]:
    • SMTP IP — адрес, на котором Клавдий примет трафик от CommuniGate Pro (0.0.0.0 — все интерфейсы, или конкретный IP).
    • Сети SMTP (trusted networks) — сеть CommuniGate Pro в CIDR (напр. 127.0.0.1/32 если CGP на том же хосте, или 192.168.1.0/24). Письма из этих сетей Клавдий принимает без SMTP-AUTH — это и нужно для форвардинга.
  5. Убедитесь, что роль SMTP включена на узле (вкладка [Роли]).

Запомните: адрес Клавдия (IP или hostname, видимый из CommuniGate Pro) и порт SMTP-слушателя. Они понадобятся в шагах 3–4.

Шаг 2. Создать правило хранения для SMTP-писем

Клавдий хранит письма только по явным правилам (строгий режим). Без правила письмо, принятое по SMTP, будет пропущено (и зафиксировано в журнале).

  1. Откройте [Хранение → Правила хранения] (см. Правила хранения).
  2. Создайте правило, условие которого выделяет SMTP-источник:
    • Источник = «Сбор из потока SMTP» (источник создаётся автоматически после первого принятого SMTP-письма; можно создать вручную в разделе [Источники]).
  3. Задайте действие (напр. «Хранить бессрочно» или срок) и категорию при необходимости.

Шаг 3. Разместить скрипт resubmitter.pl на сервере CommuniGate Pro

Скрипт размещается в каталоге данных CommuniGate Pro (обычно /var/CommuniGate).

  1. Создайте файл /var/CommuniGate/resubmitter.pl со содержимым из раздела «Скрипт resubmitter.pl» ниже.

  2. Откройте файл и в блоке BEGIN CONFIG укажите [адрес Клавдия] (тот, что запомнили в шаге 1):

    my @ScannerAddresses = (
    '127.0.0.1',
    );
    
    • Для нескольких Клавдиев (round-robin балансировка) — перечислите через запятую.
    • Адрес — это IP/hostname Клавдия, видимый с сервера CommuniGate Pro. Если CGP и Клавдий на одном хосте — 127.0.0.1. Если в одной сети — IP Клавдия в этой сети.
  3. Сделайте скрипт исполняемым:

    chmod +x /var/CommuniGate/resubmitter.pl
    
  4. Убедитесь, что в системе есть интерпретатор Perl (обычно есть по умолчанию): perl -v.

Если CommuniGate Pro работает в Docker — скрипт можно положить в образ (через Dockerfile COPY resubmitter.pl /opt/CommuniGate/resubmitter.pl) или примонтировать через volume в /var/CommuniGate/. Адрес Клавдия при этом подставляется на этапе сборки образа.

Шаг 4. Зарегистрировать Helper в CommuniGate Pro

  1. Войдите в веб-интерфейс администратора CommuniGate Pro (по умолчанию http://<сервер-CGP>:8010) как postmaster.

  2. Перейдите в [Установки → Общее → Помощники (Helpers)].

  3. В последней пустой строке таблицы [Фильтрация данных] заполните поля:

    Поле Значение
    Имя программы resubmitter
    Уровень журнала Подробности (для отладки), затем Проблемы
    Путь к программе /var/CommuniGate/resubmitter.pl
    Тайм-аут 60 сек
    Авторестарт 1 сек
  4. Установите флажок рядом с именем resubmitter и нажмите [Модифицировать].

Если скрипт запустился — в журнале появится строка resubmitter.pl started. Если путь неверен или нет прав — отобразится ошибка.

Раздел официальной документации: Внешние фильтры (Helpers).

Шаг 5. Настроить маршрутизацию доставки на Клавдий (Router)

По умолчанию CommuniGate Pro доставляет почту на стандартный порт 25. Если SMTP-слушатель Клавдия listens на другом порте (напр. 8025), добавьте запись в Router, указывающую порт.

  1. В WebAdmin CommuniGate Pro откройте [Установки → Почта → Маршрутизатор].

  2. Добавьте запись (синтаксис адрес = хост.порт._relay — порт через точку перед _relay):

    127.0.0.1 = 127.0.0.1.8025._relay
    
    • Левая часть — адрес, который скрипт подставляет получателю (<адрес-Клавдия> из шага 3).
    • Правая часть — куда доставлять: хост.порт._relay. хост — адрес Клавдия, порт — порт SMTP-слушателя Клавдия из шага 1.
    • Если Клавдий слушает стандартный 25 — запись не нужна (доставка идёт на 25 по умолчанию), но можно задать явно: 127.0.0.1 = 127.0.0.1._relay.
  3. Нажмите [Обновить конфигурацию] (или [Модифицировать]).

Документация по синтаксису: SMTP / Router. Запись host.port._relay отключает MX-разрешение и доставляет напрямую на host:port.

Шаг 6. Создать правило обработки почты (Rule)

Правило вызывает Helper resubmitter для писем, которые нужно архивировать.

  1. В WebAdmin CommuniGate Pro откройте [Установки → Почта → Правила] (общесерверные правила).

  2. Создайте новое правило с именем resubmitter и нажмите [Изменить].

  3. Задайте условия (чтобы правило применялось ко всем локальным письмам, но не зацикливалось на обратной доставке в Клавдий):

    Данные Операция Параметр
    Любой Маршрут is LOCAL*
    Submit Address not in SMTP[<адрес-Клавдия>*]
    Submit Address is not PIPE*
    • LOCAL* — письма, адресованные локальным ящикам CommuniGate Pro.
    • not in SMTP[<адрес-Клавдия>*] — исключает письма, которые уже идут от/к Клавдию (предотвращает цикл).
    • <адрес-Клавдия> — тот же адрес, что в шаге 3 (напр. 127.0.0.1).
  4. Задайте действие:

    Действие Параметр
    Внешний Фильтр resubmitter
  5. Сохраните правило и убедитесь, что оно включено (стоит первым или выше правил, останавливающих обработку).

Раздел документации: Правила обработки почты. Действие «Внешний Фильтр» доступно только в общесерверных правилах.

Шаг 7. Проверить

  1. Через веб-почту CommuniGate Pro (или почтовый клиент) отправьте письмо user1@<домен-CGP> → user2@<домен-CGP> с темой cgp-klvd-push-001.
  2. В CommuniGate Pro проверьте журнал Helper:
    • Файл SystemLogs/<год>-<месяц>-<день>_resubmitter.log в каталоге данных CGP.
    • Должна быть строка done ... transfered для отправленного письма.
  3. В Клавдии:
    • [Клавдий → Обзор] → блок [Системные метрики] → строки «SMTP-сервер» / «Приём SMTP»: обработано +1.
    • Задачи — задача smtp_collection выполнилась без ошибок.
    • Архив — письмо cgp-klvd-push-001 появилось (если правило хранения из шага 2 подходит; иначе — в журнале запись о пропуске письма, и нужно скорректировать правило).
  4. При проблемах — см. Обслуживание → Диагностика и Коды ошибок (коды #ERROR_KLVD_SMTP_*).

Диагностика типовых проблем

Симптом Вероятная причина Действие
В журнале CGP resubmitter.log пусто Правило не срабатывает Проверить условия правила (особенно LOCAL* и фильтр по Submit Address); поднять уровень журнала CGP «Установка в Очередь» до «Подробности»
resubmitter.log: REJECTED can't create ...tmp Нет прав на запись в Submitted/ Проверить права на /var/CommuniGate/Submitted/ (владелец — процесс CGP)
Письмо доходит до .sub, но не доставляется в Клавдий Неверный Router / порт / сеть Проверить запись Router (шаг 5); убедиться, что CGP может открыть TCP к <адрес-Клавдия>:<порт>
Клавдий принимает SMTP, но письмо не в архиве Нет правила хранения (шаг 2) Создать правило для SMTP-источника; проверить журнал Клавдия на skip
Цикл: письмо гоняется CGP↔Клавдий Не настроен фильтр not in SMTP[<адрес-Клавдия>*] Добавить/скорректировать условие в шаге 6
bind() failed в Клавдии на порту 25 Порт 25 занят другим MTA Перенести SMTP-слушатель Клавдия на другой порт (шаг 1) и обновить Router (шаг 5)

Скрипт resubmitter.pl

Ниже — полный текст скрипта. Скопируйте в /var/CommuniGate/resubmitter.pl, подставьте адрес Клавдия в @ScannerAddresses (шаг 3), сделайте исполняемым.

#!/usr/bin/perl -w
# script: resubmitter.pl
#
# howto:
#   Invoke from CommuniGate Pro Rule, such as:
#   Data:
#     [Any Route] [is] 'LOCAL*'
#     [Submit Address] [not in] 'SMTP [80.69.98.241*,SMTP [80.69.98.242*'
#     [Submit Address] [is not] 'PIPE*'
#   Action:
#     [ExternalFilter] [resubmitter]

use strict;
use POSIX qw(strftime);

## BEGIN CONFIG

# Here you specify the scanner IPs or hostnames
# You can specify one or multiple names for round-robin load balancing
my @ScannerAddresses = (
'127.0.0.1',          # ← адрес Клавдия (видимый с сервера CommuniGate Pro)
);


my $verbose = 1;    # debug option
my $quitCounter = 0;    # if >0, feed this number then quit
my $SubmittedDir= "Submitted";

## END CONFIG

$| = 1;

myLog("* resubmitter.pl started");
print "* resubmitter.pl started.\n";

my $msgCounter=0;
my $scannerCounter=0;

while(<STDIN>) {
chomp;
my ($prefix,$command,@args) = split(/ /);
myLog("Cmd: $_") if $verbose;
if($command eq 'INTF') {
  print "$prefix INTF 3\n";

} elsif($command eq 'QUIT') {
  myLog("QUIT command received");
  print "$prefix OK\n";
  last;
} elsif($command eq 'KEY') {
  myLog("KEY command received") if $verbose;
  print "$prefix OK\n";
} elsif($command eq 'FILE') {
  processFILE($prefix,$args[0]);
} else {
  print "$prefix ERROR unexpected command: $command\n";
  myLog("Error: unexpected command: $command") if $verbose;

}
if($quitCounter>0) {
  if(--$quitCounter==0) {
    print "* time to quit and to be restarted\n";
    myLog("* time to quit and to be restarted") if $verbose;
    last;
  }
}
}

print "* stopping the resubmitter plugin\n";
myLog("* stopping the plugin");
exit(0);

# Subs

sub processFILE {
my ($prefix,$fileName) = @_;

myLog("processing: $prefix FILE $fileName");
unless( open (FILE,"$fileName")) {
  print qq/$prefix REJECTED can't open $fileName: $!\n/;
  myLog("ERROR: REJECTED: can't open $fileName: $!");
  return undef;
}


my $returnPath="";
my @recipients;
while(<FILE>) {
  chomp;
  last if($_ eq '');
  my $address=(/\<(.*?)\>/)[0];
  if(/^P/) {
    $returnPath=$address;
    myLog("returnPath: $returnPath") if $verbose;
  }
  elsif(/^R/) {
    $address =~ s/\@/\%/g;
    $address.='@'.$ScannerAddresses[$scannerCounter];
    myLog("recipient: $address") if $verbose;
    push(@recipients,$address);
  }
  
}
$scannerCounter=0 if(++$scannerCounter>=scalar(@ScannerAddresses));
$fileName=~/(\d+)\./;
my $subFileName=$SubmittedDir.'/'.$1;
myLog("Submitted file name: $subFileName") if $verbose;

unless( open(WFILE,">$subFileName.tmp")) {
  print qq/$prefix REJECTED can't create $subFileName.tmp: $!\n/;
  myLog("ERROR: REJECTED: can't create $subFileName.tmp: $!");
  close(FILE);
  return undef;
}

print WFILE "Return-Path: <$returnPath>\n";

#print WFILE "Envelope-To: ".join(",",@recipients)."\n";
  my $line='Envelope-To: ';
  for(my $idx=0;$idx<@recipients;$idx++) {
    $line.=$recipients[$idx];
    $line.="," if($idx<@recipients-1);
    if(length($line)>900) {
      print WFILE "$line\n";
      $line=' ';
    }
  }
  if(length($line)>2) {
    print WFILE "$line\n";
  }


while(<FILE>) {
  next if(/^Envelope-to:/i);
  print WFILE $_;
}

close(WFILE);
close(FILE);

unless( rename("$subFileName.tmp","$subFileName.sub") ) {
  print qq/$prefix REJECTED can't rename open $subFileName.tmp to $subFileName.sub: $!\n/;
  myLog("ERROR: REJECTED: can't rename open $subFileName.tmp to $subFileName.sub: $!");
  unlink("$subFileName.tmp");
  return undef;
}

myLog("done $prefix $fileName, transfered");
print qq/$prefix OK\n/;

undef;
}

sub myLog {
   my ($data)=@_;

   $data =~ s/[\015\012]//g;
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
   my $fName=sprintf("SystemLogs/%04d-%02d-%02d_resubmitter.log",$year+1900,$mon+1,$mday);
   my $tmstr=sprintf("%02d:%02d:%02d",$hour,$min,$sec);

   unless (open LOG,">>$fName") {
       print "* can't append to $fName: $!\n";
       return;
   }

#    LOG->autoflush(1);

   print LOG "$tmstr $data\n";
   close LOG;
}

__END__

См. также

Навигация по книге