Перевод выполнен Алексеем Паутовым в рамках некоммерческого проекта RussianLDP (http://www.rldp.ru/). Именно на этом сайте и надлежит искать новые версии, если таковые будут.

40. Контентное сканирование во время ACL

Расширение exim для включения контентного сканирования во время ACL, раньше известное как exiscan, изначально было осуществлено как патч Tom Kistner. Код был интегрирован в главный исходный код для 4.50 релиза exim, и Том продолжает его поддерживать. Большинство этой части взято из спецификации Тома.

Также возможно просматривать содержимое сообщений в другой момент времени. Функция local_scan() (смотрите раздел 41) позволяет контентное сканирование после выполнения всех ACL. Транспортный фильтр может использоваться для сканирования сообщений во время доставки (смотрите опцию transport_filter, описанную в части 24).

Если Вы хотите включить контентное сканирование во время ACL, при сборке exim Вам необходимо принять меры для задания WITH_CONTENT_SCAN в Вашем Local/Makefile. Когда Вы это сделаете, exim cобирается с:

  • Двумя дополнительными ACL (acl_smtp_mime и acl_not_smtp_mime), которые работают для всех частей MIME для SMTP и не-SMTP сообщений соответственно.
  • Дополнительные условия и модификаторы ACL: decode, malware, mime_regex, regex и spam. Они могут использоваться в ACL, которая запускается в конце приёма сообщения (ACL acl_smtp_data).
  • Дополнительные функции управления (no_mbox_unspool), которые сохраняют копии помещённых в очередь сообщений или частей сообщений в целях отладки.
  • Дополнительные переменные раскрытия, которые устанавливаются в новых ACL с новыми условиями.
  • Две новые опции главной конфигурации: av_scanner и spamd_address.

    Есть другая опция контентного сканирования для Local/Makefile, называемая WITH_OLD_DEMIME. Если она установлена, компилируется старое, не одобряемое условие ACL demime в дополнение ко всем другим возможностям контентного сканирования.

    Контентное сканирование непрерывно развивается и продолжают добавляться новые возможности. В то время, когда такие возможности остаются нестабильными и склонны к несовместимым изменениям, они делаются доступными в exim путём установки опций в Local/Makefile, чьи имена начинаются с EXPERIMENTAL_. Такие возможности не документированы в этом руководстве. Вы можете узнать о них путём прочтения файла с именем doc/experimental.txt.

    Все средства контентного сканирования работают с MBOX-копией сообщения временно создаваемой в названном файле:
    spool_directory/scan/message_id/message_id.eml
    

    Расширение .eml дружелюбная подсказка вирусным сканерам, что они могут ожидать внутри файла структуру, подобную MBOX. Файл создаётся при первом вызове средства контентного сканирования. Последующие вызовы условий контентного сканирования снова открывают тот же самый файл. Каталог рекурсивно удаляется, когда ACL acl_smtp_data завершает работу, если не установлена опция:
    control = no_mbox_unspool
    

    Когда ACL MIME декодирует файлы, они по умолчанию кладутся в тот же самый каталог.

    40.1. Сканирование на вирусы

    Условие ACL malware позволяет подключить программу вирусного сканера к exim. Оно поддерживает общий (generic) интерфейс к сканерам, вызываемым через шелл, и специализированные интерфейсы для вирусных сканеров типа daemon, которые постоянно находятся в памяти и поэтому значительно быстрее.

    Вы можете установить опцию av_scanner в первой части конфигурационного файла exim для задания используемого сканера вместе с любыми необходимыми дополнительными опциями. Базовый синтаксис следующий:
    av_scanner = scanner-type:option1:option2:[...]:optionN
    

    Если Вы не установите av_scanner, по умолчанию она будет:
    av_scanner = sophie:/var/run/sophie
    

    Если значение av_scanner начинается с символа доллара, оно раскрывается до использования. В этом релизе поддерживаются следующие типы сканеров:

    Когда av_scanner коректно установлена, Вы можете использовать условие malware в DATA ACL. Отметьте: Вы не можете использовать условие malware в MIME ACL.

    Опция av_scanner раскрывается при каждом вызове malware. Это позволяет использовать различные сканеры. Смотрите ниже примеры использования. Условие malware кэширует свои результаты, таким образом, Вы можете использовать его неоднократно для одного и того же сообщения, фактически, процесс сканирования выполняется лишь один раз. Однако, использование раскрываемых элементов в av_scanner отключает кэширование, и каждое использование условия malware вызывает новое сканирование сообщения.

    Условие malware берёт правый параметр, который раскрывается до использования. Он может быть одним из:

    Вы можете добавить /defer_ok к условию malware, чтобы принимать сообщения даже если есть какие-то проблемы с вирусным сканером. Когда вирус найден, условие устанавливает переменную раскрытия с именем $malware_name, которая содержит имя вируса. Вы можете использовать её в модификаторе message, который определяет ошибку, возвращаемую отправителю и/или протоколируемые данные. Если вирусный сканер не может самостоятельно распаковать MIME и TNEF-контейнеры, Вы должны использовать условие demime (смотрите секцию 40.6) до условия malware. Вот очень простой пример сканирования:
    deny message = This message contains malware ($malware_name)
         demime = *
         malware = *
    

    Следующий пример принимает сообщения когда у сканера проблемы:
    deny message = This message contains malware ($malware_name)
         demime = *
         malware = */defer_ok
    

    Следующий пример показывает как использовать переменную ACL для сканирования обоими sophie и aveserver. Он предполагает, что Вы установили:
    av_scanner = $acl_m0
    
    в главной конфигурации exim
    deny message = This message contains malware ($malware_name)
         set acl_m0 = sophie
         malware = *
    
    deny message = This message contains malware ($malware_name)
         set acl_m0 = aveserver
         malware = *
    

    40.2. Сканирование SpamAssassin

    Условие ACL spam вызывает демона spamd SpamAssassin для получения очков за спам и отчёта для сообщения. Вы можете получить SpamAssassin по адресу http://www.spamassassin.org, или, если у Вас есть рабочая инсталляция Perl, можете использовать CPAN путём запуска:
    perl -MCPAN -e 'install Mail::SpamAssassin'
    

    SpamAssassin обладает собственным набором конфигурационных файлов. Пожалуйста, посмотрите его документацию, чтобы узнать, как Вы можете его настроить. Однако, инсталляция по умолчанию работает хорошо.

    Установив и настроив SpamAssassin, запустите демон spamd. По умолчанию он слушает 127.0.0.1, TCP-порт 783. Если Вы используете иной хост или порт для spamd, Вы должны установить опцию spamd_address в глобальной части конфигурации exim следующим образом (например):
    spamd_address = 192.168.99.45 387
    

    Вам нет нужды устанавливать эту опцию, если Вы используете значение по умолчанию. Для версии 2.60 spamd также поддерживает коммуникацию через UNIX-сокеты. Если Вы хотите их использовать, установите spamd_address в абсолютный путь до сокета вместо пары адрес/порт:
    spamd_address = /var/run/spamd_socket
    

    У Вас может быть несколько серверов spamd, для улучшения масштабируемости. Они могут находиться на других машинах, доступных по сети. Для задания нескольких серверов spamd, установите в опцию spamd_address несколько пар адрес/порт, разделённых двоеточиями:
    spamd_address = 192.168.2.10 783 : \
                    192.168.2.11 783 : \
                    192.168.2.12 783
    

    Поддерживается вплоть до 32 серверов spamd. Сервера запрашиваются случайным образом. Когда сервер не в состоянии ответить на попытку подключения, пробуются все другие сервера, пока какой-либо не будет успешным. Если ни один сервер не ответил, условие spamd задерживается.

    Предупреждение: невозможно использовать соединение через UNIX-сокет с несколькими серверами spamd.

    40.3. Вызов SpamAssassin из ACL exim

    Вот простой пример использования условия spam в DATA ACL:
    deny message = This message was classified as SPAM
         spam = joe
    

    Правая сторона условия spam определяет имя пользователя, для которого SpamAssassin сканирует. Если Вы не хотите использовать сканирование конкретного пользователя, и использовать профиль SpamAssassin как системный профиль системы, Вы можете сканировать для неизвестного пользователя или просто использовать nobody. Однако, Вы должны что-то поместить в правую строну.

    Имя пользователя позволяет Вам использовать антиспамовый профиль на домен или на пользователя. Правая сторона раскрывается до использования, таким образом, в неё Вы можете поместить поиск или условие. Когда правая строна вычисляется в 0 или false, сканирования не происходит, и условие немедленно неуспешно.

    Сканирование с помощью SpamAssassin использует много ресурсов. Если Вы сканируете каждое сообщение, большие сообщения могут вызывать существенное ухудшение производительности. Поскольку большинство спама представляют собой маленькие сообщения, рекомендуется, чтобы Вы не просматривали большие. Например:
    deny message = This message was classified as SPAM
         condition = ${if < {$message_size}{10K}}
         spam = nobody
    

    Условие spam возвращает истину, если происходит соответсвие или превышение заданному в пользовательском профиле SpamAssassin. Если Вы хотите использовать условие spam для его сторонних эффектов (смотрите ниже переменные), можете заставить его всегда возвращать true путём добавления к имени пользователя :true. Когда выполняется условие spam, оно устанавливает множество переменных раскрытия. За исключением $spam_score_int, они доступны лишь внутри ACL; их значения не сохраняются с сообщением, и, таким образом, не могут быть использованы во время доставки.

    Это вызывает приём сообщения даже если существуют со spamd. Вот более длинный и прокомментированный пример использования условия spam:
    # put headers in all messages (no matter if spam or not)
    warn  message = X-Spam-Score: $spam_score ($spam_bar)
          spam = nobody:true
    warn  message = X-Spam-Report: $spam_report
          spam = nobody:true
    
    # add second subject line with *SPAM* marker when message
    # is over threshold
    warn  message = Subject: *SPAM* $h_Subject:
          spam = nobody
    
    # reject spam at high scores (> 12)
    deny  message = This message scored $spam_score spam points.
          spam = nobody:true
          condition = ${if >{$spam_score_int}{120}{1}{0}}
    

    40.4. Сканирование частей MIME

    Глобальная опция acl_smtp_mime определяет ACL, которая вызывается для каждой MIME-части SMTP-сообщения, включая типы, состоящие из нескольких частей (multipart), в последовательности их позиций в сообщении. Точно так же, опция acl_not_smtp_mime определяет ACL, которая используется для MIME-частей не-SMTP сообщений. Эти опции могут обе относиться к одной и той же ACL, если Вы хотите одну и ту же обработку в обоих случаях.

    Эти ACL вызываются (возможно, несколько раз) лишь до ACL acl_smtp_data в случае сообщения SMTP или лишь до приёма не-SMTP сообщений. Однако, MIME ACL вызывается, лишь если сообщение содержит строку заголовка MIME-Version:. Когда вызов MIME ACL не приводит к accept, обработка ACL прерывается, а клиенту посылается соответствующий код результата. В случае SMTP-сообщения ACL acl_smtp_data не вызывается, когда это происходит.

    Вы не можете использовать условия malware или spam в MIME ACL, они могут использоваться лишь в DATA или не-SMTP ACL. Однако, Вы можете использовать условие mime_regex для сравнения с декодированной MIME-частью (смотрите секцию 40.5).

    В начале MIME ACL множество переменных устанавливаются из информации заголовков для релевантной части MIME. Это описано ниже. По умолчанию содержимое части MIME не декодируется в файл на диске, исключая части MIME, чей тип содержимого message/rfc822. Если Вы хотите декодировать часть MIME в файл на диске, можете использовать модификатор decode. Общий синтаксис таков:
    decode = [/path/]filename
    

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

    Однако, Вы должны иметь в виду, что $mime_filename может содержать что угодно. Если Вы помещаете файлы вне пути по умолчанию, они не удаляются автоматически.

    Для вложений RFC 822 сообщения, вложенные в сообщения с типом содержимого message/rfc822 ACL вызывается снова таким же самым образом, как для первичного сообщения, лишь если установлена переменная раскрытия $mime_is_rfc822 (смотрите ниже). Приложенные сообщения всегда декодируются на диск до проверки, файлы удаляются после завершения проверки. ACL MIME поддерживает условия regex и mime_regex. Они могут использоваться для сравнения регулярного выражения с сырыми и декодированными частями MIME, соответственно. Они описаны в секции 40.5. Следующий список описывает все переменные раскрытия, которые доступны в ACL MIME:

    40.5. Сканирование с регулярными выражениями

    Вы можете задать Ваши собственные регулярные сообщения, совпадающие с полным телом сообщения или индивидуальными частями MIME. Условие regex получает одно или более регулярное выражение как аргумент и сравнивает его с полным сообщением (при вызове в DATA ACL) или сырой частью MIME (при вызове в MIME ACL). Условие regex сравнивается построчно с максимальной длиной строки в 32k символов. Это означает, что Вы не можете получить многострочные сравнения с условием regex.

    Условие mime_regex может быть вызвано лишь в ACL MIME. Оно сравнивается вплоть до 32k декодированного содержимого (всё содержимое сразу, не построчно). Если часть не была декодирована с модификатором decode ранее в ACL, она автоматически декодируется при выполнении mime_regex (используя путь по умолчанию и значение имени файла). Если декодированные данные более 32k, проверяются лишь первые 32k.

    Регулярные выражения передаются как список, разделённый двоеточиями. Для включения символа двоеточия Вы должны его удвоить. Так как правая строка раскрыватся до использования, Вы также должны экранировать символ доллара и обратные слэши обратными слэшами или используя средство \N для отключения раскрытия. Вот простой пример, который содержит два регулярных выражения:
    deny message = contains blacklisted regex ($regex_match_string)
         regex = [Mm]ortgage : URGENT BUSINESS PROPOSAL
    

    Условие возвращает истину, если совпадает любое регулярное выражение. Тогда переменная раскрытия $regex_match_string устанавливается и содержит соответствующее регулярное выражение.

    Предупреждение: с большими сообщениями это условие может быть довольно ресурсоёмким.

    40.6. Условие demime

    Условие ACL demime предоставляет распаковку MIME, проверяя корректность и блокируемые расширения файлов. Оно может использоваться лишь в DATA и не-SMTP ACL. Условие demime использует более простой интерфейс к декодированию MIME, чем функциональность MIME ACL, но не предоставляет никаких дополнительных средств. Пожалуйста, отметьте, что это условие устарело и оставлено лишь для обратной совместимости.

    Вы должны установить опцию WITH_OLD_DEMIME в Local/Makefile во время сборки для возможности использовать условие demime. Условие demime распаковывает в сообщении контейнеры MIME. Оно детектирует ошибки в MIME-контейнерах и может сравнивать расширения файлов, найденные в сообщении со списком. Использование этого средства приводит к файлам, содержащим распакованные части MIME-сообщения во временном каталоге сканирования. Если Вы производите сканирование антивирусом, рекомендуется использовать условие demime до условия антивируса (malware).

    В правой стороне условия demime Вы можете поместить список, разделённый двоеточиями, расширений файлов с которыми оно будет сравниваться. Например:
    deny message = Found blacklisted file attachment
         demime  = vbs:com:bat:pif:prf:lnk
    

    Если найдено одно из расширений файлов, условие истинно, иначе ложно. Если при демимизации происходит временная ошибка (например, disk full), условие задержано, и сообщение временно отклоняется (если только в условии не стоит команда warn). Правая сторона раскрывается до использования как списка, таким образом, Вы можете использовать в ней поиск. Если раскрытие приводит к пустой строке, false или нулю (0), демаймизации не происходит и условие неудачно. Условие demime устанавливает следующие переменные:

    Обе, $demime_errorlevel и $demime_reason, устанавливаются при первом вызове условия demime и не изменяются при последующих вызовах.

    Если Вы хотите не проверять расширения файлов, а использовать условие demime для распаковки или с целью проверки ошибок, поместите * в правую сторону. Вот более сложный пример использования этого средства:
    # Reject messages with serious MIME container errors
    deny  message = Found MIME error ($demime_reason).
          demime = *
          condition = ${if >{$demime_errorlevel}{2}{1}{0}}
    # Reject known virus spreading file extensions.
    # Accepting these is pretty much braindead.
    deny  message = contains $found_extension file (blacklisted).
          demime  = com:vbs:bat:pif:scr
    # Freeze .exe and .doc files. Postmaster can
    # examine them and eventually thaw them.
    deny  log_message = Another $found_extension file.
          demime = exe:doc
          control = freeze