Показаны сообщения с ярлыком postfix. Показать все сообщения
Показаны сообщения с ярлыком postfix. Показать все сообщения

воскресенье, 23 апреля 2017 г.

Контроль длины очереди отложенных писем Postfix через Zabbix

В прошлом я писал статьи о настройке Postfix для пересылки сообщений для администратора через полноценный почтовый сервер:
Когда таких серверов становится много, а некоторые из них рассылают большое количество писем, становится трудно своевременно обнаруживать проблемы с отправкой писем. Помочь в таком случае может система мониторинга. При проблемах с отправкой писем почтовый сервер откладывает проблемные письма в отдельную очередь, чтобы через некоторое время попробовать отправить эти письма снова. В Postfix эта очередь отложенных писем называется deferred. Для контроля количества писем в этой очереди нам понадобится настроенный Zabbix-агент и установленный пакет sudo. Настройку Zabbix-агента и установку sudo не описываю в виду их тривиальности.

При помощи команды visudo разрешим пользователям из группы zabbix выполнять от имени пользователя root команду поиска файлов в очереди Postfix:
%zabbix    ALL=(ALL) NOPASSWD:/usr/bin/find /var/spool/postfix/deferred -type f
В файл конфигурации Zabbix-агента /etc/zabbix/zabbix_agentd.conf (или /usr/local/etc/zabbix24/zabbix_agentd.conf для случая FreeBSD с Zabbix-агентом версии 2.4) пропишем пользовательский параметр, который будет выполнять команду, подсчитывающую количество файлов, найденных в очереди отложенных писем Postfix:
UserParameter=postfix.deferred.length,sudo /usr/bin/find /var/spool/postfix/deferred -type f | wc -l
После внесения изменений в конфигурацию Zabbix-агента, не забудьте его перезапустить:
# /etc/init.d/zabbix-agent restart
В шаблоне есть:
  • один элемент данных с ключом postfix.deferred.length, с помощью которого раз в 5 минут контролируется количество писем в очереди отложенных,
  • один макрос {$POSTFIX_DEFERRED_LIMIT} со значением 200, который задаёт порог срабатывания триггера,
  • один триггер "Проблемы с отправкой почты", который срабатывает, если в очереди скопилось больше писем, чем указано в макросе.
Вот как выглядят соответствующие вкладки шаблона:



Вот наиболее интересный фрагмент графика с одного из серверов:

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

воскресенье, 19 марта 2017 г.

Смена темы письма в Postfix

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

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

Чтобы решить эту проблему и облегчить себе работу, я решил настроить все свои серверы так, чтобы Postfix добавлял в тему отправляемого письма имя сервера.

Сделать это просто. Для начала нужно установить пакет postfix-pcre:
# apt-get install postfix-pcre
Далее, добавим в файл /etc/postfix/main.cf одну строчку:
header_checks = pcre:/etc/postfix/rewrite_subject
Теперь нужно создать файл /etc/postfix/rewrite_subject и поместить в него правило, которое будет добавлять в тему письма дополнительный текст с именем сервера. Например, вот так:
/^Subject: (.*)$/ REPLACE Subject: $1 (from server.domain.tld)
Текст "/^Subject: (.*)$/" является регулярным выражением в стиле Perl. Это регулярное выражение будет совпадать со строкой заголовка, начинающейся с текста "Subject: ". Текст "(.*)" совпадает с любой темой письма и будет запомнен в переменной $1.

Действие "REPLACE" говорит о том, что нужно произвести замену текста.

Строка "Subject: $1 (from server.domain.tld)" содержит в текст, который заменит совпавшую строчку. При этом вместо переменной $1 будет подставлено её значение. Если имя переменной не отделено от остального текста пробельными символами, тогда её имя следует записывать в виде ${1}

Итак, осталось перезапустить почтовый сервер:
# /etc/init.d/postfix restart
Проверить, добавляется ли текст к теме письма можно следующим образом:
# mail -s test box@mailserver.tld
test
.
При этом в почтовом ящике должно появиться письмо с темой "test (from server.domain.tld)".

воскресенье, 6 марта 2016 г.

Postfix как локальный SMTP-ретранслятор во FreeBSD

Ранее я уже настраивал Postfix в Debian: Postfix как локальный SMTP-ретранслятор. Здесь я выложу ту же заметку, адаптированную применительно к FreeBSD.

Установим Postfix из портов:
# cd /usr/ports/mail/postfix
В процессе установки этого порта оставим отмеченными только опции SASL2 и TLS. Как зависимость опции SASL2 будет установлен также порт security/cyrus-sasl2. У этого порта я оставил отмеченными только опции CRAM, DIGEST, LOGIN и PLAIN.

Настроим Postfix для отправки почты с подменой отправителя и аутентификацией на сервере провайдера. Настроим в файле /usr/local/etc/postfix/main.cf следующую конфигурацию:
# Имя сервера и его почтового домена
myhostname = server.domain.tld
mydomain = domain.tld

# Откуда и для кого принимать почту к доставке
inet_protocols = ipv4
inet_interfaces = 127.0.0.1
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8

# К адресам локальных отправителей добавлять этот домен
masquerade_domains = domain.tld

# Карта соответствия локальных получателей адресам на почтовом сервере ISP  
alias_maps = hash:/etc/mail/aliases
alias_database = hash:/etc/mail/aliases

# Карта соответствия локальных отправителей ящикам на почтовом сервере ISP  
sender_canonical_maps = hash:/usr/local/etc/postfix/sender_maps

# Включаем использование аутентификации на сервере провайдера
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/usr/local/etc/postfix/passwords

# Включаем выбор учётных данных на сервере провайдера в зависимости от отправителя
smtp_sender_dependent_authentication = yes
sender_dependent_relayhost_maps = hash:/usr/local/etc/postfix/sender_relays

# Если в таблице отправителей нет соответствия, использовать этот почтовый сервер ISP
relayhost = [mailserver.domain.tld]:587

# Устанавливаем не более одного исходящего подключения на каждый домен
default_destination_concurrency_limit = 1

# Разрешаем использовать механизмы аутентификации PLAIN и LOGIN
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_mechanism_filter = plain, login
Теперь создадим необходимые карты. Укажем в файле /etc/mail/aliases, на какие ящики на сервере провайдера перенаправлять почту для локальных пользователей:
postmaster root
root recipient@domain.tld
Почта для пользователя root будет перенаправляться в ящик recipient@domain.tld.

Укажем в файле /usr/local/etc/postfix/sender_maps, какой ящик на сервере провайдера использовать для отправки почты от локального отправителя:
root sender@domain.tld
Когда пользователь root попытается отправить письмо, то на сервер провайдера оно уйдёт от отправителя sender@domain.tld.

Укажем в файле /usr/local/etc/postfix/sender_relays, какой сервер следует использовать для отправки писем от определённого отправителя:
sender@domain.tld [mailserver.domain.tld]:587
Когда пользователь root попытается отправить почту, письмо, в соответствии с настройками в файле /usr/local/etc/postfix/sender_maps, будет отправлено с адреса sender@domain.tld. Письмо от этого отправителя нужно отправить через порт 587 сервера mailserver.domain.tld.

Укажем в файле /usr/local/etc/postfix/passwords учётные данные каждого из ящиков на сервере провайдера:
sender@domain.tld sender:password
Когда пользователь root попытается отправить почту, письмо, в соответствии с настройками в файле /usr/local/etc/postfix/sender_maps, будет отправлено с адреса sender@domain.tld. Письмо от этого отправителя, в соответствии с настройками в файле /usr/local/etc/postfix/sender_relays, нужно отправить через порт 587 сервера mailserver.domain.tld. В соответствии с настройками в этом файле для аутентификации на сервере провайдера нужно будет использовать имя пользователя sender и пароль password.

Для файла /usr/local/etc/postfix/passwords стоит задать разрешения, ограничивающие возможность подсмотреть пароли локальными пользователями системы:
# chown root:root /usr/local/etc/postfix/passwords
# chmod u=rw,g=r,o= /usr/local/etc/postfix/passwords
При каждом обновлении файлов карт нужно не забывать обновлять их двоичные копии одной из следующих команд:
# postalias /etc/mail/aliases
# postmap /usr/local/etc/postfix/sender_maps
# postmap /usr/local/etc/postfix/sender_relays
# postmap /usr/local/etc/postfix/passwords
Двоичные копиии имеют то же имя, но с расширением .db. Права доступа к оригинальному файлу полностью переносятся и на его двоичную копию.

Теперь нужно отключить использование sendmail и включить postfix. Для этого отредатируем файл /etc/rc.conf, вписав в него следующие опции:
postfix_enable="YES"
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
Для отключения служебных периодически запускаемых скриптов sendmail впишем в файл /etc/periodic.conf следующие опции:
daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"
В процессе установки порта postfix предлагается заменить содержимое файла /etc/mail/mailer.conf. Если вы отказались, можно исправить упущение, вписав в него следующие настройки:
#
# Execute the Postfix sendmail program, named /usr/local/sbin/sendmail
#
sendmail        /usr/local/sbin/sendmail
send-mail       /usr/local/sbin/sendmail
mailq           /usr/local/sbin/sendmail
newaliases      /usr/local/sbin/sendmail
Теперь, когда с настройкой покончено, настало время остановить sendmail и запустить вместо него postfix. Для этого выполним следующие команды:
# /etc/rc.d/sendmail stop
# /usr/local/etc/rc.d/postfix start
Осталось проверить правильность работы системы. Попробуем отправить тестовое письмо от имени пользователя root пользователю root:
# mail -s test root
test
.
На ящик recipient@domain.tld должно прийти письмо от ящика sender@domain.tld.

воскресенье, 8 июня 2014 г.

Postfix как локальный SMTP-ретранслятор

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

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

Для начала установим необходимые пакеты:
# apt-get install postfix libsasl2-modules bsd-mailx
Для отправки почты с подменой отправителя и аутентификацией на сервере провайдера можно настроить Postfix. Настроим в файле /etc/postfix/main.cf следующую конфигурацию:
# Имя сервера и его почтового домена
myhostname = server.domain.tld
mydomain = domain.tld

# Откуда и для кого принимать почту к доставке
inet_protocols = ipv4
inet_interfaces = 127.0.0.1
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8

# К адресам локальных отправителей добавлять этот домен
masquerade_domains = domain.tld

# Карта соответствия локальных получателей адресам на почтовом сервере ISP  
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

# Карта соответствия локальных отправителей ящикам на почтовом сервере ISP  
sender_canonical_maps = hash:/etc/postfix/sender_maps

# Включаем использование аутентификации на сервере провайдера
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/passwords

# Включаем выбор учётных данных на сервере провайдера в зависимости от отправителя
smtp_sender_dependent_authentication = yes
sender_dependent_relayhost_maps = hash:/etc/postfix/sender_relays

# Если в таблице отправителей нет соответствия, использовать этот почтовый сервер ISP
relayhost = [mailserver.domain.tld]:587

# Устанавливаем не более одного исходящего подключения на каждый домен
default_destination_concurrency_limit = 1

# Разрешаем использовать механизмы аутентификации PLAIN и LOGIN
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_mechanism_filter = plain, login
После настройки самого Postfix, его можно перезапустить, чтобы настройки вступили в силу:
# /etc/init.d/postfix restart
Теперь создадим необходимые карты. Укажем в файле /etc/aliases, на какие ящики на сервере провайдера перенаправлять почту для локальных пользователей:
postmaster root
root recipient@domain.tld
Почта для пользователя root будет перенаправляться в ящик recipient@domain.tld.

Укажем в файле /etc/postfix/sender_maps, какой ящик на сервере провайдера использовать для отправки почты от локального отправителя:
root sender@domain.tld
Когда пользователь root попытается отправить письмо, то на сервер провайдера оно уйдёт от отправителя sender@domain.tld.

Укажем в файле /etc/postfix/sender_relays, какой сервер следует использовать для отправки писем от определённого отправителя:
sender@domain.tld [mailserver.domain.tld]:587
Когда пользователь root попытается отправить почту, письмо, в соответствии с настройками в файле /etc/postfix/sender_maps, будет отправлено с адреса sender@domain.tld. Письмо от этого отправителя нужно отправить через порт 587 сервера mailserver.domain.tld.

Укажем в файле /etc/postfix/passwords учётные данные каждого из ящиков на сервере провайдера:
sender@domain.tld sender:password
Когда пользователь root попытается отправить почту, письмо, в соответствии с настройками в файле /etc/postfix/sender_maps, будет отправлено с адреса sender@domain.tld. Письмо от этого отправителя, в соответствии с настройками в файле /etc/postfix/sender_relays, нужно отправить через порт 587 сервера mailserver.domain.tld. В соответствии с настройками в этом файле для аутентификации на сервере провайдера нужно будет использовать имя пользователя sender и пароль password.

Для файла /etc/postfix/passwords стоит задать разрешения, ограничивающие возможность подсмотреть пароли локальными пользователями системы:
# chown root:root /etc/postfix/passwords
# chmod ug=rw,o= /etc/postfix/passwords
При каждом обновлении файлов карт нужно не забывать обновлять их двоичные копии одной из следующих команд:
# postalias /etc/aliases
# postmap /etc/postfix/sender_maps
# postmap /etc/postfix/sender_relays
# postmap /etc/postfix/passwords
Двоичные копиии имеют то же имя, но с расширением .db. Права доступа к оригинальному файлу полностью переносятся и на его двоичную копию.

Осталось проверить правильность работы системы. Попробуем отправить тестовое письмо от имени пользователя root пользователю root:
# mail -s test root
test
.
На ящик recipient@domain.tld должно прийти письмо от ящика sender@domain.tld.

воскресенье, 9 февраля 2014 г.

Настройка SSL/TLS в Postfix, Dovecot, Lighttpd

Настройка SSL и TLS уже была вскользь упомянута в предыдущих заметках Установка и настройка Postfixadmin, Установка и настройка Dovecot и Установка и настройка Postfix, OpenDKIM, ClamAV-Milter, Milter-Greylist. В этой заметке я собираюсь раскрыть тему поподробнее. Учтите, что некоторые из описанных настроек я не тестировал, они приведены лишь для того, чтобы обозначить направление, в котором следует двигаться, если такие настройки понадобятся.

1. Подготовка сертификатов SSL/TLS

Сертификаты могут быть самоподписанными или подписанными удостоверяющим центром. Самоподписанные сертификаты обычно используются лишь для тестирования поддержки SSL/TLS в различных серверах и отладки настроек. На публичных серверах стоит использовать сертификаты, подписанные удостоверяющими центрами.

Услуги удостоверяющих центров, по моим меркам, обходятся довольно дорого, но некоторые удостоверяющие центры предоставляют и бесплатные ограниченные услуги для частных лиц. Мне известен только один такой удостоверяющий центр - StartSSL. Подробнее процедура получения такого сертификата описана, например, в статье Получаем бесплатный SSL сертификат или в заметке Бесплатный валидный (подписанный) SSL-сертификат через StartSSL. Перед получением сертификата нужно иметь настроенный почтовый сервер, чтобы иметь возможность подтвердить владение доменом.

2. Создание CA-сертификата (для самоподписания)

CA-сертификат - это сертификат центра сертификации (как бы тавтологично это ни звучало). Этим сертификатом подписываются все остальные сертификаты, используемые различными сервисами.

Воспользуемся скриптом, идущим в комплекте с пакетом openssl:
# /usr/lib/ssl/misc/CA.pl -newca
Перед созданием сертификата удостоверяющего центра скрипт задаст вам несколько вопросов. Нужно приготовиться предоставить следующую информацию:
  • Страна (Country)
  • Область (State or province)
  • Город (City or other municipal area)
  • Организация (Organization)
  • Подразделение (Organization unit)
  • Общепринятое имя (Common name)
  • Электронный адрес (Email address)
В процессе создания сертификата также нужно будет указать секретную фразу, которая будет использоваться каждый раз при подписании или отзыве удостоверенных сертификатов.

В поле «общепринятое имя» в данном случае нужно вписать название организации (или торговую марку, под которой она работает) или имя владельца. В случае создания сертификата сервера нужно указать его DNS-имя.

В текущем каталоге будет создан подкаталог demoCA, в котором будут созданы необходимые файлы.

После этого я дополнительно отредактировал файл конфигурации OpenSSL /etc/ssl/openssl.cnf, указав в нём полный путь к каталогу с сертификатами удостоверяющего центра (в двух местах) и настройки срока годности вновь создаваемых сертификатов:
dir = /etc/ssl/demoCA
и
default_days = 3650
3. Создание сертификата сервера

Хотя удостоверяющий центр может сгенерировать и сертификат сервера, его можно сгенерировать самостоятельно:
# openssl req -new -nodes -keyout domain1.net.pem -out domain1.net.pem -days 3650
Generating a 2048 bit RSA private key
...........................................................................................+++
...........................................................................................................................................  ....................+++
writing new private key to 'domain1.net.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:RU
State or Province Name (full name) [Some-State]:Bashkortostan Republic
Locality Name (eg, city) []:Ufa
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Vladimir Stupin
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:domain1.net
Email Address []:wheel69@yandex.ru

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
В этом случае при создании сертификата секретную фразу указывать не нужно, а в поле «Общеизвестное имя» нужно указать DNS-имя сервера.

В текущем каталоге будет создан файл domain1.net.pem с секретной частью сертификата.

4.1. Подписание сертификата сервера (самоподписание)

Для создания самоподписанного сертификата можно воспользоваться следующей командой:
# openssl ca -policy policy_anything -out domain1.net.pubilc.pem -infiles domain1.net.pem
Using configuration from /usr/lib/ssl/openssl.cnf
Enter pass phrase for /etc/ssl/ca/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 14036232404122642274 (0xc2cab701208ea762)
        Validity
            Not Before: Feb  5 15:31:10 2014 GMT
            Not After : Feb  3 15:31:10 2024 GMT
        Subject:
            countryName               = RU
            stateOrProvinceName       = Bashkortostan Republic
            localityName              = Ufa
            organizationName          = Vladimir Stupin
            commonName                = domain1.net
            emailAddress              = wheel69@yandex.ru
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                0B:DB:32:31:76:C8:F1:80:8E:2F:2E:70:8B:05:91:2A:91:69:AF:31
            X509v3 Authority Key Identifier: 
                keyid:B5:CB:D1:89:6A:E8:82:D2:D6:7C:A5:6C:13:88:EA:EE:D9:5F:8F:6E

Certificate is to be certified until Feb  3 15:31:10 2024 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

# mv domain1.net.pem domain1.net.private.pem
При подписании сертификата нужно будет указать секретную фразу из сертификата удостоверяющего центра (см. выше).

4.2. Подписание сертификата сервера (внешнее подписание)

В случае использования официального центра сертификации для подписания сертификата нужно следовать его инструкциям. Обычно для этого нужно скопировать из файла сертификата секцию CERTIFICATE REQUEST вместе с начальной и конечной строчками. Центр сертификации в ответ на запрос сгенерирует секцию CERTIFICATE, которую нужно вставить в private-файл после секции CERTIFICATE REQUEST.

После этого можно сгенерировать публичную часть сертификата:

# openssl x509 -in mail.stupin.su.pem -text > mail.stupin.su.public.pem
# mv domain1.net.pem domain1.net.private.pem
5. Подготовка сертификатов сервера к использованию

Расположим сертификаты так, чтобы они автоматически подхватились Dovecot'ом и настроим права доступа.

Чтобы локальные пользователи системы не получили доступ на чтение к приватной части сертификата и не смогли подменить публичную часть сертификата, нужно соответствующим образом выставить права доступа к файлам:
# chown root:root /etc/ssl/domain1.net.public.pem
# chmod u=rw,go=r /etc/ssl/domain1.net.public.pem

# chown root:root /etc/ssl/domain1.net.private.pem
# chmod u=rw,go= /etc/ssl/domain1.net.private.pem
6. Postfix

Postfix может выступать в роли SMTP-клиента, когда доставляет почту на другой почтовый сервер, и может выступать в роли SMTP-сервера, когда получает почту от другого сервера.

6.1. Настройка TLS в клиенте

Настроим шифрование и аутентификацию почтового сервера получателя, когда наш сервер будет пытаться отправить письмо по защищённому протоколу SMTP.

Сконвертируем pem-сертификат нашего удостоверяющего центра в формат crt и поместим в каталог для сертификатов доверенных центров авторизации:
# apt-get install ca-certificates
# mkdir /usr/share/ca-certificates/stupin.su
# openssl x509 -in /etc/ssl/demoCA/cacert.pem -out /usr/share/ca-certificates/stupin.su/cacert.crt
Теперь сгенерируем новый список сертификатов, которым будет доверять наша система:
# dpkg-reconfigure ca-certificates
В открывшемся окне нужно ответить «спрашивать» и отметить в списке наш сертификат. После этого будет сформирован новый файл /etc/ca-certificates.conf (нужно проверить, что в списке есть наш сертификат и в начале строчки нет восклицательного знака). pem-сертификаты будут помещены в каталог /etc/ssl/certs.

Добавим в файл /etc/postfix/main.cf следующие настройки:
# Настройка SMTP-клиента, отправляющего почту
smtp_tls_loglevel = 2
smtp_tls_CApath = /etc/ssl/certs
Осталось перезапустить сервер, чтобы настройки вступили в силу:
# /etc/init.d/postfix restart
6.2. Настройка TLS в сервере

Настраиваем шифрование с использованием ранее подготовленных SSL-сертификатов. Для этого добавим в файл /etc/postfix/main.cf следующие настройки:
# Настройка SMTP-сервера, принимающего почту
smtpd_use_tls = yes
smtpd_tls_key_file = /etc/ssl/domain1.net.private.pem
smtpd_tls_cert_file = /etc/ssl/domain1.net.public.pem
smtpd_tls_CApath = /etc/ssl/certs
smtpd_tls_loglevel = 2
smtpd_tls_received_header = yes
Осталось перезапустить сервер, чтобы настройки вступили в силу:
# /etc/init.d/postfix restart
6.3. Несколько сертификатов TLS на сервере

Postfix не имеет поддержки расширения SNI, потому что в SMTP клиент не может указать, к серверу с каким доменным именем он подключался. Соответственно, пока клиент не может этого сообщить, SMTP-сервер не может выбрать правильный сертификат. Для обслуживания почты из других доменов придётся обойтись настройкой MX-записи, указывающей на доменное имя сервера, которое он сообщает в команде HELO/EHLO.

Postfix также не имеет явных настроек для работы с разными сертификатами на разных IP-адресах. Однако, можно запустить несколько экземпляров сервера smtpd с разными настройками прослушиваемого IP-адреса, имени сервера и используемых сертификатов. Для этого можно попытаться задать в файле /etc/postfix/master.cf следующие настройки:
192.168.0.1:smtp inet  n  -        n        -        -        smtpd
  -o smtpd_tls_key_file=/etc/ssl/domain1.net.private.pem
  -o smtpd_tls_cert_file=/etc/ssl/domain1.net.public.pem
  -o myhostname=domain1.net
  -o mydomain=domain1.net
  -o myorigin=domain1.net

172.16.0.1:smtp inet  n  -        n        -        -        smtpd
  -o smtpd_tls_key_file=/etc/ssl/domain2.ru.private.pem
  -o smtpd_tls_cert_file=/etc/ssl/domain2.ru.public.pem
  -o myhostname=domain2.ru
  -o mydomain=domain2.ru
  -o myorigin=domain2.ru
Или можно воспользоваться новой возможностью Postfix - создать несколько экземпляров сервера. Подробнее об этом можно почитать на странице http://www.postfix.org/MULTI_INSTANCE_README.html

6.4. Оптимизация производительности

Настройка менеджера TLS-сессий описана в заметке Установка и настройка Postfix, OpenDKIM, ClamAV-Milter, Milter-Greylist в разделе «6. Оптимизация скорости работы Postfix».

7. Dovecot

При включении поддержки SSL нужно задать сертификат для использования по умолчанию. Включим поддержку SSL в файле /etc/dovecot/conf.d/10-ssl.conf:
ssl = yes
ssl_cert = </etc/ssl/domain1.net.public.pem
ssl_key = </etc/ssl/domain1.net.private.pem
И перезапустить Dovecot, чтобы новые настройки вступили в силу:
# /etc/init.d/dovecot restart
Dovecot может использовать как один сертификат, так и несколько. В случае использования нескольких сертификатов можно назначать отдельные сертификаты как на отдельные IP-адреса, на которых Dovecot будет ожидать подключения, так и на отдельные сервисы. Например, можно задать отдельные сертификаты для POP3 и для IMAP (хотя не ясно, какой в этом может быть смысл). Например, вот так:
local 192.168.0.1 {
  protocol imap {
    ssl_cert = </etc/ssl/dovecot/imap.domain1.net.public.pem
    ssl_key  = </etc/ssl/dovecot/imap.domain1.net.private.pem
  }

  protocol pop3 {
    ssl_cert = </etc/ssl/dovecot/pop.domain1.net.pubilc.pem
    ssl_key  = </etc/ssl/dovecot/pop.domain1.net.private.pem
  }
}

local 172.16.0.1 {
    ssl_cert = </etc/ssl/dovecot/domain2.ru.public.pem
    ssl_key  = </etc/ssl/dovecot/domain2.ru.private.pem
}
В примере выше указаны раздельные сертификаты для сервисов на первом IP-адресе и общий сертификат для сервисов на втором IP-адресе. Вместо IP-адресов можно указывать доменные имена, однако не впадайте в заблуждение - эти доменные имена не могут указывать на один и тот же IP-адрес. Перед использованием доменные имена решаются в IP-адреса, так что два домена, указывающих на один и тот же IP-адрес, породят две секции с одинаковым IP-адресом.

Использование раздельных сертификатов для отдельных IP-адресов и сервисов не избавляет от необходимости указывать сертификаты по умолчанию. Будьте внимательны!

Также Dovecot поддерживает расширение протокола SSL, которое называется Server Name Indication или, сокращённо, SNI. Если клиент поддерживает это расширение, то можно использовать разные сертификаты для разных доменных имён. Настройки, которые можно прописать при использовании SNI:
local_name domain1.net {
  ssl_cert = </etc/ssl/domain1.net.pubilc.pem
  ssl_key = </etc/ssl/domain1.net.private.pem
}

local_name domain2.ru {
  ssl_cert = </etc/ssl/domain2.ru.public.pem
  ssl_key = </etc/ssl/domain2.ru.private.pem
}
Более подробную информацию о настройке SSL в Dovecot можно найти на официальной wiki-странице: Dovecot SSL configuration. SNI поддерживается не всеми клиентами. Одним из клиентов с подтверждённой поддержкой SNI является Thunderbird в Linux.

8. Lighttpd

Для настройки SSL в веб-сервере Lighttpd достаточно включить модуль ssl:
# lighty-enable-mod ssl
B прописать используемый сертификат в файл /etc/lighttpd/conf-enabled/05-ssl.conf:
$SERVER["socket"] == "0.0.0.0:443" {
  ssl.engine  = "enable"
  ssl.pemfile = "/etc/ssl/vladimir.stupin.su.pem"

  ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM"
  ssl.honor-cipher-order = "enable"
}
И перезапустить веб-сервер, чтобы его новые настройки вступили в силу:
# /etc/init.d/lighttpd restart
Как видно, в настройках проверяется совпадение с прослушиваемым IP-адресом и TCP-портом 443. Очевидно, что таким образом можно задать несколько SSL-сертификатов, каждый из которых будет использоваться при обращении клиента к определённому IP-адресу веб-сервера. Например, вот так:
$SERVER["socket"] == "192.168.0.1:443" {
  ssl.engine  = "enable"
  ssl.pemfile = "/etc/ssl/domain1.net.pem"

  ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM"
  ssl.honor-cipher-order = "enable"
}

$SERVER["socket"] == "172.16.0.1:443" {
  ssl.engine  = "enable"
  ssl.pemfile = "/etc/ssl/domain2.ru.pem"

  ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM"
  ssl.honor-cipher-order = "enable"
}
Однако, таким образом можно задать только один сертификат для одного IP-адреса. Если же на одном IP-адресе обслуживается несколько доменов, то можно либо прописать в этот сертификат доменные имена всех необходимых сайтов, либо воспользоваться поддержкой SNI.

Веб-сервер Lighttpd поддерживает SNI начиная с версии 1.4.24, в случае если установлен OpenSSL версии 0.9.8f или выше. Для задания отдельных сертификатов для отдельных доменов можно воспользоваться условными секциями, меняя внутри них используемый сертификат:
$HTTP["host"] == "domain1.net" {
  ssl.pemfile = "/etc/ssl/domain1.net.pem"
}

$HTTP["host"] == "domain2.ru" {
  ssl.pemfile = "/etc/ssl/domain2.ru.pem"
}
Для того, чтобы принудительно использовать SSL на определённых сайтах, можно вписать в файл /etc/lighttpd/conf-enabled/05-ssl.conf следующую условную секцию:
$HTTP["host"] =~ "^domain(1\.net|2\.ru)$" {
  url.redirect = (".*" => "https://%0$0")
}
Стоит отметить, что сайты должны быть готовы к подобной переадресации. Например, во всех внутренних ссылках на сайте стоит убрать спецификацию протокола http. Все внутренние ссылки сайта должны быть либо относительными, либо начинаться с двух косых черт.

Продолжение следует...

воскресенье, 2 февраля 2014 г.

Установка и настройка Postfix, OpenDKIM, ClamAV-Milter, Milter-Greylist

Продолжение заметок Установка и настройка Postfixadmin и Установка и настройка Dovecot. В этой заметке мы настроим MTA и его обвязку. В качестве альтернативы Postfix можно воспользоваться Exim, настройка которого описана тут: Настройка Exim

1. Установка Postfix

Для работы Postfix понадобится поставить следующие пакеты:
# apt-get install postfix postfix-mysql
2. Базовая настройка Postfix

Выполним базовую настройку Postfix, вписав в файл /etc/postfix/main.cf следующее содержимое:
# Настраиваем имя почтового сервера и его домен по умолчанию
myhostname = mail.domain.tld
mydomain = domain.tld
myorigin = domain.tld
mydestination = $myhostname, localhost.$mydomain, localhost
 
# Ожидаем подключения на всех интерфейсах и только по IPv4
inet_protocols = ipv4
inet_interfaces = all
 
# Я не доверяю даже локальным отправителям, поэтому любой,
# кто захочет отправить письмо наружу, должен будет пройти аутентификацию
mynetworks = 
 
# Настройка уведомлений администратора об ошибках
notify_classes = resource, software, protocol, policy
 
# Настройка ограничений прав пользователя для транспорта virtual
virtual_minimum_uid = 120
virtual_uid_maps = static:120
virtual_gid_maps = static:120
 
# Настройка использования файла пересылок
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
 
local_recipient_maps = $alias_maps
 
# Максимальный размер принимаемого письма
message_size_limit = 31457280
 
# Ограничения на этапе установке подключения
smtpd_client_restrictions =
  # Разрешить клиентов из доверенных сетей
  permit_mynetworks,
  # Разрешить клиентов, прошедших аутентификацию
  permit_sasl_authenticated,
  # Отклонять клиентов, у которых доменное имя из PTR-записи
  # не решается в тот же IP по A-записи
  reject_unknown_client_hostname
 
# Ограничения на этапе команды HELO/EHLO
smtpd_helo_restrictions =
  permit_mynetworks,
  permit_sasl_authenticated,
  # Отклонять клиентов, использующих неправильный синтаксис доменного имени в HELO
  reject_invalid_helo_hostname,
  # Отклонять клиентов, указывающих в HELO не полное доменное имя
  reject_non_fqdn_helo_hostname,
  # Отклонять клиентов, DNS-имя из HELO которых не имеет A- или MX-записи
  reject_unknown_helo_hostname
 
# Отклонять клиентов, не использовавших команду HELO/EHLO
smtpd_helo_required = yes
 
# Ограничения на этапе команды MAIL FROM
smtpd_sender_restrictions =
  # Отклонять почту от отправителей с неполным доменным именем
  reject_non_fqdn_sender,
  # Отклонять почту от отправителей из несуществующих доменов
  reject_unknown_sender_domain,
  permit_mynetworks,
  permit_sasl_authenticated

# Ограничения на этапе команды RCPT TO
smtpd_recipient_restrictions =
  # Отклонять почту для получателей с неполным доменным именем
  reject_non_fqdn_recipient,
  # Отклонять почту для получателей в несуществующем домене
  reject_unknown_recipient_domain,
  permit_mynetworks,
  permit_sasl_authenticated,
  # Запретить отправлять почту в домены, которые мы не обслуживаем
  reject_unauth_destination,
  # Отклонять почту для получателей, которых у нас нет
  reject_unlisted_recipient
3. Интеграция Postfix и Postfixadmin

Добавляем пользователя postfix для доступа к базе данных:
USE mysql;
 
INSERT INTO user(user, password, host) VALUES('postfix', PASSWORD('postfix_password'), 'localhost');
 
FLUSH PRIVILEGES;
Даём пользователю postfix доступ к таблице ящиков, доменов, псевдонимов, псевдонимов доменов и квот:
USE mysql;
 
INSERT INTO tables_priv(host, db, user, table_name, table_priv, column_priv) VALUES
('localhost', 'postfixadmin', 'postfix', 'alias', '', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'alias_domain', '', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'mailbox', '', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'domain', '', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'quota2', '', 'Select');
 
INSERT INTO columns_priv(host, db, user, table_name, column_name, column_priv) VALUES
('localhost', 'postfixadmin', 'postfix', 'alias', 'goto', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'alias', 'address', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'alias', 'active', 'Select');
 
INSERT INTO columns_priv(host, db, user, table_name, column_name, column_priv) VALUES
('localhost', 'postfixadmin', 'postfix', 'alias_domain', 'target_domain', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'alias_domain', 'alias_domain', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'alias_domain', 'active', 'Select');
 
INSERT INTO columns_priv(host, db, user, table_name, column_name, column_priv) VALUES
('localhost', 'postfixadmin', 'postfix', 'mailbox', 'maildir', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'mailbox', 'username', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'mailbox', 'active', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'mailbox', 'quota', 'Select');
 
INSERT INTO columns_priv(host, db, user, table_name, column_name, column_priv) VALUES
('localhost', 'postfixadmin', 'postfix', 'domain', 'domain', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'domain', 'backupmx', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'domain', 'active', 'Select');
 
INSERT INTO columns_priv(host, db, user, table_name, column_name, column_priv) VALUES
('localhost', 'postfixadmin', 'postfix', 'quota2', 'username', 'Select'),
('localhost', 'postfixadmin', 'postfix', 'quota2', 'bytes', 'Select');
 
FLUSH PRIVILEGES;
Подготовим файлы SQL-карт, которыми будет пользоваться Postfix для получения различной информации из базы данных. Все файлы карт создаются в каталоге /etc/postfix/sql. Перед созданием файлов создаём каталог:
# mkdir /etc/postfix/sql
Создаём файл /etc/postfix/sql/domains.cf, в котором содержится SQL-запрос для получения списка активных доменов, для которых осуществляется приём почты:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT domain
        FROM domain
        WHERE domain = '%s'
          AND backupmx = 0
          AND active = 1
Создаём файл /etc/postfix/sql/backup_domains.cf, в который поместим SQL-запрос, извлекающий список активных доменов, для которых наш почтовый сервер является резервным:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT domain
        FROM domain
        WHERE domain = '%s'
          AND backupmx = 1
          AND active = 1
Почтовый сервер будет принимать почту для этих доменов, не складывая в какой-либо почтовый ящик, а размещая письма в очереди на отправку. При первой возможности он перешлёт её почтовому серверу, имеющему в DNS запись MX с меньшим числом, то есть - с большим приоритетом. Сервер с самым высоким приоритетом поместит письма в почтовые ящики получателей.

Создаём файл /etc/postfix/sql/alias.cf, содержащим SQL-запрос для извлечения списка активных пересылок:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT goto
        FROM alias
        WHERE address = '%s'
          AND active = 1
Когда почта направляется на адрес пересылки, Postfix подменит адрес доставки на адрес, указанный в этой таблице. Postfix предпримет несколько попыток замены, до тех пор, пока SQL-запрос не перестанет возвращать результаты или результат ничем не будет отличаться от исходного адреса. Таким образом можно создать цепочку пересылок, однако это не приветствуется, поскольку при этом Postfix будет выполнять больше SQL-запросов к базе данных и таким образом можно случайно зациклить цепочку. Postfix имеет ограничение на максимальную длину цепочки, так что слишком длинные или зацикленные цепочки работать не будут.

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

Создаём файл /etc/postfix/sql/alias_domain.cf, в который поместим SQL-запрос для извлечения активных пересылок доменов:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT alias.goto
        FROM alias_domain
        JOIN alias ON alias.address = CONCAT('%u', '@', alias_domain.target_domain)
          AND alias.active = 1
        WHERE alias_domain.alias_domain = '%d'
          AND alias_domain.active = 1
Пересылки для доменов действуют следующим образом: Если в домене получателя есть почтовый ящик или пересылка, локальная часть адреса которого совпадает с локальной частью адреса получателя, то доменная часть адреса получателя подменяется на указанную в пересылке.

Например, если есть доменная пересылка domain.tld -> domain.com и на почтовом сервере есть адрес box@domain.com, то при отправке письма на адрес box@domain.tld, его получателем будет адрес box@domain.com. К этому адресу применимы обычные пересылки, то есть реальным получателем этого письма может быть и другой ящик, для которого пересылка создана явным образом, например box@domain.tld -> boxer@domain.ru.

Создаём файл /etc/postfix/sql/alias_domain_catchall.cf, который содержит SQL-запрос для пересылок доменов, захватывающих письма для любого ящика в домене:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT alias.goto
        FROM alias_domain
        JOIN alias ON alias.address = CONCAT('@', alias_domain.target_domain)
          AND alias.active = 1
        WHERE alias_domain.alias_domain = '%d'
          AND alias_domain.active = 1
Если для получателя не найдена явная пересылка с адреса на адрес и не найдена пересылка из домена в другой домен с такой же локальной частью адреса, то это правило перехватит почту для любого другого ящика в домене и перешлёт её на указанный ящик.

Например, почта направляется в ящик office@domain.tld, есть пересылка из домена domain.tld в домен domain.com, но ящика или пересылки с именем office@domain.com нет, но есть пересылка из домена domain.tld на ящик info@domain.com, то почта для office@domain.tld попадёт в ящик info@domain.com. Довольно сложное объяснение. Проще - такие пересылки захватывают почту для не существующих в домене адресов и перенаправляют её на указанный адрес.

Создаём файл /etc/postfix/sql/mailbox.cf, в который поместим запрос, возвращающий путь к каталогу активного почтового ящика:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT maildir
        FROM mailbox
        WHERE username = '%s'
          AND active = 1
Создаём файл /etc/postfix/sql/alias_domain_mailbox.cf, в который поместим SQL-запрос для извлечения каталога почтового ящика с пересылкой из другого домена:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT mailbox.maildir
        FROM alias_domain
        JOIN mailbox ON mailbox.username = CONCAT('%u', '@', alias_domain.target_domain)
          AND mailbox.active = 1
        WHERE alias_domain.alias_domain = '%d'
          AND alias_domain.active = 1
Смысл этого файла совпадает со смыслом файла /etc/postfix/sql/alias_domain.cf и мне не совсем понятно, для чего нужен этот файл. Возможно для того, чтобы можно было отправлять почту с ящика в другом домене, успешно авторизуясь на SMTP-сервере.

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

Если вы настраивали Dovecot с поддержкой квот, то понадобится также создать файл /etc/postfix/sql/mailbox_limit.cf для проверки превышения квоты получателем:
user = postfix
password = postfix_password
hosts = 127.0.0.1
dbname = postfixadmin

query = SELECT '452 Mailbox is over quota'
        FROM mailbox
        JOIN quota2 ON quota2.username = mailbox.username
          AND mailbox.quota - quota2.bytes <= 0
        WHERE mailbox.username = '%s'
          AND mailbox.active = 1
Этого SQL-запроса в Postfixadmin нет, я добавил его сам. Насколько я знаю, существует некий патч к транспорту virtual из Postfix, который добавляет в него поддержку квот. Мы, однако, будем использовать транспорт dovecot, поэтому патчи нам не нужны. Однако, если не добавить в Postfix правила, использующего приведённый выше запрос, то при превышении квоты почтового ящика происходит неприятная ситуация: Postfix письмо принимает, но не может передать его Dovecot, поскольку тот отказывается его принять, ссылаясь на превышение квоты. В результате письмо продолжает болтаться в почтовой очереди Postfix, чего нам не нужно, т.к. потенциальные злоумышленники могут забить сначала квоту почтового ящика, а затем и очередь Postfix. Вместо этого мы добавим в Postfix правило, которое будет сообщать о временной ошибке доставки почты, если соответствующий ящик переполнен. Отправитель же может поступить по собственному усмотрению - повторять периодические попытки отправить письмо.

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

Выставим права доступа к файлам SQL-карт, чтобы потенциальный злоумышленник, получивший локальный доступ к системе, не смог получить список почтовых ящиков:
# cd /etc/postfix
# chown -R postfix:postfix sql
# chmod u=rwx,g=rx,o= sql
# cd sql
# chmod u=rw,g=r,o= *
Теперь осталось добавить использование SQL-карт в Postfix. Для этого вписываем в файл /etc/postfix/main.cf следующие строки:
virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/domains.cf
relay_domains = proxy:mysql:/etc/postfix/sql/backup_domains.cf
virtual_alias_maps =
   mysql:/etc/postfix/sql/alias.cf,
   mysql:/etc/postfix/sql/alias_domain.cf,
   mysql:/etc/postfix/sql/alias_domain_catchall.cf
virtual_mailbox_maps =
   mysql:/etc/postfix/sql/mailbox.cf,
   mysql:/etc/postfix/sql/alias_domain_mailbox.cf
Также найдём в файле /etc/postfix/main.cf правило smtpd_recipient_restrictions и добавим проверку квот перед разрешением отправлять почту для наших сетей и аутентифицированных пользователей:
smtpd_recipient_restrictions =
  reject_non_fqdn_recipient,
  reject_unknown_recipient_domain,
  check_recipient_access proxy:mysql:/etc/postfix/sql/mailbox_limit.cf,
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_unauth_destination,
  reject_unlisted_recipient
Уже в таком виде мы получим Postfix, который сможет принимать почту для получателей в наших доменах. Правда, в таком виде он не умеет пересчитывать использование квот почтовых ящиков в таблице quota2 при получении нового письма. Отправить письмо наружу тоже не получится - он не примет письмо без аутентификации клиента, а механизмы аутентификации ещё не были настроены.

4. Интеграция Postfix и Dovecot

Сейчас мы добавим пересчёт квот при получении нового письма и добавим механизмы аутентификации. В файл /etc/postfix/main.cf нужно добавим настройки для использования Dovecot как LDA и SASL-провайдера:
# Dovecot LDA
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1
virtual_mailbox_base = /home/vmail

# Dovecot SASL
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
Если Postfix получит письмо для получателя, настроенного через параметры virtual_*, он попытается воспользоваться транспортом virtual, для того чтобы поместить письмо в ящик получателя. Вместо LDA virtual, имеющимся в составе Postfix, мы будем использоваться LDA, имеющимся в составе Dovecot. Он умеет пересчитывать квоты в таблице quota2.

Для SMTP-аутентификации клиентов будет использоваться SASL-сервис из состава Dovecot. Если вы пропустили настройку этого сервиса Dovecot, стоит заглянуть в описание настройки Dovecot и настроить его сейчас.

Осталось вписать в файл /etc/postfix/master.cf агент локальной доставки (LDA) Dovecot:
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}
Теперь можно запустить MTA:
# /etc/init.d/postfix restart
Если есть подозрение, что учтённое использование квоты не соответствует действительности, можно пересчитать использование квот для всех ящиков при помощи следующей команды:
# doveadm quota recalc -A
5. Настройка SSL/TLS

Настраиваем шифрование с использованием ранее подготовленных SSL-сертификатов. Для этого добавим в файл /etc/postfix/main.cf следующие настройки:
# Настройка SMTP-сервера, принимающего почту
smtpd_use_tls = yes
smtpd_tls_key_file = /etc/ssl/mail_private.pem
smtpd_tls_cert_file = /etc/ssl/mail_public.pem
smtpd_tls_CApath = /etc/ssl/certs
smtpd_tls_loglevel = 2
smtpd_tls_received_header = yes
 
# Настройка SMTP-клиента, отправляющего почту
smtp_tls_loglevel = 2
smtp_tls_CApath = /etc/ssl/certs
6. Оптимизация скорости работы Postfix

Демоны, входящие в состав Postfix, периодически перезапускаются. Это касается и демонов smtp и smtpd. Пока они работают, каждый из них поддерживает в своей памяти сеансовые ключи для всех серверов или клиентов, с которыми были установлены защищённые соединения.

Вычисление сеансового ключа - ресурсоёмкая операция и она будет выполняться при перезапуске каждого демона для каждого сервера или клиента, с которым взаимодействует Postifx. Это не страшно для небольших почтовых систем, но для систем, взаимодействующих с большим количеством других серверов и клиентов, это может оказаться критичным.

Чтобы ключи хранились в кэше и не пересчитывались каждый раз, можно включить использование менеджера сеансовых ключей tlsmgr, входящего в состав Postfix. Для этого впишем в файл /etc/postfix/main.cf файл, в котором размещаются сеансовые ключи и зададим время кэширования:
smtpd_tls_session_cache_database = sdbm:/var/lib/postfix/tlsmgr_smtpd.db
smtpd_tls_session_cache_timeout = 3600s
 
smtp_tls_session_cache_database = sdbm:/var/lib/postfix/tlsmgr_smtp.db
smtp_tls_session_cache_timeout = 3600s
Стоит отметить, что каталог с сеансовыми ключами стоит размещать вне chroot-окружения Postfix, поскольку потенциальный злоумышленник, взломавший демона, работающего в chroot-окружении, сможет подменить файлы кэша, получив таким образом возможность, например, принять почту от не аутентифицированного отправителя.

Как уже было сказано, процессы Postfix периодически перезапускаются. При каждом перезапуске каждый процесс устанавливает собственное подключение к базе данных. Чтобы уменьшить одновременное количество подключений к базе данных и снизить частоту переподключений, можно настроить специальный прокси-сервер. Он будет принимать соединения от демонов Postfix и выполнять запросы, пользуясь небольшим количество постоянных подключений к базе данных.

Для настройки использования прокси нужно в файле /etc/postfix/main.cf перед каждой используемой картой mysql указать через двоеточие текст proxy:
virtual_mailbox_domains = proxy:proxy:mysql:/etc/postfix/sql/domains.cf
relay_domains = proxy:proxy:mysql:/etc/postfix/sql/backup_domains.cf
virtual_alias_maps =
   proxy:mysql:/etc/postfix/sql/alias.cf,
   proxy:mysql:/etc/postfix/sql/alias_domain.cf,
   proxy:mysql:/etc/postfix/sql/alias_domain_catchall.cf
virtual_mailbox_maps =
   proxy:mysql:/etc/postfix/sql/mailbox.cf,
   proxy:mysql:/etc/postfix/sql/alias_domain_mailbox.cf
 
...
 
smtpd_recipient_restrictions =
  check_recipient_access proxy:mysql:/etc/postfix/sql/mailbox_limit.cf,
Также следует указать демону-прокси список обслуживаемых им карт, чтобы он прочитал их до перехода в chroot-окружение. Для этого добавим в файл /etc/postfix/main.cf следующие опции:
proxy_read_maps =
  proxy:mysql:/etc/postfix/sql/domains.cf,
  proxy:mysql:/etc/postfix/sql/backup_domains.cf,
  proxy:mysql:/etc/postfix/sql/alias_domain.cf,
  proxy:mysql:/etc/postfix/sql/alias_domain_mailbox.cf,
  proxy:mysql:/etc/postfix/sql/alias_domain_catchall.cf,
  proxy:mysql:/etc/postfix/sql/alias.cf,
  proxy:mysql:/etc/postfix/sql/mailbox.cf,
  proxy:mysql:/etc/postfix/sql/mailbox_limit.cf
Осталось перезапустить Postfix, чтобы настройки вступили в силу:
# /etc/init.d/postfix restart
7. Настройка ClamAV-Milter

На мой взгляд, защита от вирусов является обязательным компонентом почтовой системы. Отсутствие проверки на вирусы говорит о низкой квалификации системного администратора. Наиболее доступным, простым в настройке и ставшим стандартом де-факто для проверки почты в мире юникс-систем, считается антивирус ClamAV, который мы и настроим.

Устанавливаем демон ClamAV для проверки файлов на вирусы и демон ClamAV-Milter, работающий по протоколу Milter и предназначенный специально для проверки на наличие вирусов писем:
# apt-get install clamav-milter clamav-daemon
Сразу же обновляем антивирусную базу:
# freshclam
Настройка для демона ClamAV не требуется. Настроим ClamAV-Milter - нужно указать такое расположение Milter-сокета, которое было бы доступно для почтовой системы внутри chroot-окружения в каталоге /var/spool/postfix. Я указал в файле /etc/clamav/clamav-milter.conf такую строчку:
MilterSocket /var/spool/postfix/clamav-milter
Теперь настроим Postfix на использование этого сокет-файла. Для этого в файле /etc/postfix/main.cf укажем в списке Milter-фильтров настроенный сокет:
milter_default_action = accept
milter_protocol = 2
 
smtpd_milters = unix:/clamav-milter
non_smtpd_milters = unix:/clamav-milter
Перезапустим ClamAV-Milter и Postfix, чтобы настройки вступили в силу:
# /etc/init.d/clamav-milter restart
# /etc/init.d/postfix restart
Осталось проверить, что антивирусная система используется. Для этого создадим специально предназначенный для таких целей тестовый файл EICAR:
$ echo -n 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > eicar.txt
И попробуем его отправить во вложении с какого-нибудь почтового ящика почтовой системы на тот же ящик. Если письмо не пришло, значит антивирусная система работает. Для полной уверенности можно ещё заглянуть в журнал почтовой системы /var/log/mail.log, где должна появиться строчка вида:
Dec 29 17:04:46 mail postfix/cleanup[2035]: 770B685287: milter-hold: END-OF-MESSAGE from localhost[127.0.0.1]: milter triggers HOLD action; from=<box@domain.tld> to=<box@domain.tld> proto=ESMTP helo=<computer.domain.tld>
Чтобы выяснить, какой конкретно вирус был обнаружен, можно обратиться к журналу демона ClamAV в файле /var/log/clamav/clamav.log, где должна быть запись, относящаяся примерно к тому же времени и содержащая текст следующего вида:
Sun Dec 29 17:04:46 2013 -> fd[10]: Eicar-Test-Signature(4017e0393a0297fc376f7be69ad6a1b0:881) FOUND
8. Настройка OpenDKIM

DKIM - DomainKeys Identified Mail - это цифровая подпись, которую почтовый сервер добавляет к письмам от отправителей из обслуживаемых им доменов. Для этого сервер использует секретную часть ключа, которая известна только ему. Получатель может проверить подпись письма при помощи публичной части ключа, доступной через DNS-запись домена отправителя.

Нужно понимать, что DKIM не защищает от подделки отправителя и не может являться гарантией того, что письмо является полезным и безопасным, т.е. не содержит спама и вирусов. DKIM лишь удостоверяет, что почтовый сервер уполномочен владельцем домена на обслуживание почты, исходящей из этого домена. Спамеры вполне могут приобрести домен, настроить DNS и почтовый сервер для отправки писем с DKIM-подписью. Однако это требует финансовых затрат, а источник спама при этом легко идентифицировать и заблокировать. Спамеры же стараются зарабатывать деньги, оставаясь анонимными. Поэтому письмо, имеющее правильную DKIM-подпись, можно с высокой степенью достоверности считать полезным и безопасным.

Добавление DKIM-подписей к письмам позволяет повысить доверие других почтовых систем к нашим письмам. Проверка DKIM-подписей - это отдельная тема, связанная с фильтрацией писем после их приёма. Перед настройкой DKIM стоит воспользоваться другими средства для повышения доверия к почте от нашего сервера, которые проще в настройке и чаще используются, - это настройка PTR- и SPF-записей:
  1. PTR-запись для IP-адреса сервера должна указывать на его имя, которое он сообщает в команде HELO/EHLO. A-запись для этого имени должна указывать на этот же IP-адрес. Наличие правильной PTR-записи по стандарту не обязательно, но большинство почтовых систем его проверяют, поэтому выполнение этого требования весьма желательно.
  2. SFP-запись - это TXT-запись следующего вида:
    domain.tld. IN TXT "v=spf1 +mx ~all"
    Если указанный домен обслуживается одним почтовым сервером, этой записи достаточно. В более сложных случаях следует обратиться к документу проекта Sender Policy Framework, описывающему синтаксис SFP-записи - SPF Record Syntax. Стоит также прочесть о наиболее частых ошибках, допускаемых при создании SFP-записи - Common mistakes.

Приступим к настройке OpenDKIM для добавления подписей к почте из наших доменов. Настройка производится в соответствии со статьёй OpenDKIM + Postfix = просто с небольшими отличиями - я использую сокет-файл.

Устанавливаем OpenDKIM:
# apt-get install opendkim opendkim-tools
Готовим каталог для DKIM-ключей:
# mkdir /etc/postfix/dkim/
# cd /etc/postfix/dkim/
Генерируем ключи для домена domain.tld:
# opendkim-genkey -D /etc/postfix/dkim/ -d domain.tld -s mail
# mv mail.private mail.domain.tld.private
# mv mail.txt mail.domain.tld.txt
Далее можно сгенерировать ключи для других доменов, обслуживаемых нашей почтовой системой.

Создаём таблицы ключей для каждого из доменов в файле /etc/postfix/dkim/keytable:
mail._domainkey.domain.tld domain.tld:mail:/etc/postfix/dkim/mail.domain.tld.private
Создаём в файле /etc/postfix/dkim/signingtable таблицу, задающую ключи для доменов. В конце файла можно указать ключ для подписи писем из тех доменов, для которых ключ не указан явным образом:
domain.tld mail._domainkey.domain.tld
* mail._domainkey.stupin.su
Меняем группу и права доступа к файлам:
# cd /etc/postfix/dkim/
# chown root:opendkim *
# chmod u=rw,g=r,o= *
В файле /etc/opendkim.conf прописываем следующую конфигурацию:
# Настройки журналирования и отладки
Syslog                yes
SyslogSuccess         yes
LogWhy                yes
X-Header              yes
 
# Меняем для того, чтобы Postfix мог писать в сокет
UMask                 000
Canonicalization      relaxed/relaxed
 
# Sign и Verify - подписывать и проверять подпись
Mode                  sv
KeyTable              file:/etc/postfix/dkim/keytable
SigningTable          file:/etc/postfix/dkim/signingtable
OversignHeaders       From
Сокет, создаваемый OpenDKIM, должен быть доступен внутри chroot-окружения Postfix, внутри /var/spool/postfix/. Однако этот каталог принадлежит Postfix и поэтому пользователь opendkim не сможет создать в нём сокет-файл. Чтобы обойти это ограничение мы самостоятельно создадим внутри chroot-окружения каталог, к которому будет иметь доступ пользователь opendkim:
# mkdir /var/spool/postfix/opendkim
# chown opendkim:opendkim /var/spool/postfix/opendkim
В файле /etc/defaults/opendkim прописываем настройки сокета, который будет использоваться Postfix'ом:
SOCKET="local:/var/spool/postfix/opendkim/socket"
Содержимое файла /etc/postfix/dkim/mail.domain.tld.txt и файлов для других доменов в соответствующие зоны DNS.

В файл конфигурации Postfix /etc/postfix/main.cf, в настройки smtpd_milters и non_smtpd_milters добавляем новый сокет-файл:
milter_default_action = accept
milter_protocol = 2
 
smtpd_milters = unix:/clamav-milter, unix:/opendkim/socket
non_smtpd_milters = unix:/clamav-milter, unix:/opendkim/socket
Этот сокет-файл должен быть указан в конце списка, чтобы OpenDKIM подписывал уже готовое письмо, в которое больше не будет добавляться дополнительных заголовков.

Курсивом отмечен Unix-сокет ClamAV-Milter, который был настроен в предыдущем разделе. Если вы не настраивали его, здесь их тоже не будет. Для настройки OpenDKIM достаточно лишь добавить Unix-сокеты, отмеченные жирным шрифтом.

Осталось перезапустить демонов OpenDKIM и Postfix, чтобы настройки вступили в силу:
# /etc/init.d/opendkim restart
# /etc/init.d/postfix restart
Всё готово, можно проверять. Попробовал отправить тестовое письмо на почтовый ящик в Yandex-почте, в почтовом ящике при открытии этого письма увидел следующую картинку:


9. Настройка Milter-Greylist

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

Частично мы уже настроили проактивную фильтрацию на этапе настройки Postfix. Он проверяет правильность имени почтовой системы, которым она представляется в команде HELO/EHLO, проверяет существование домена отправителя и домена получателя. Рассмотрим другие средства:
  1. RDNSBL - Reverse DNS Black Lists - чёрные списки адресов. Почтовый сервер формирует DNS-запрос, используя доменное имя чёрного списка и IP-адрес отправителя, который хочет передать нам почту. Если DNS-запрос завершится успешно, значит IP-адрес имеется в этом списке. Почтовый сервер может отклонить почту, если IP-адрес отправителя есть в чёрном списке. На мой взгляд, этот способ в чистом виде использовать не стоит, потому что у каждого проекта, ведущего чёрные списки, могут быть разные политики добавления и удаления. В некоторые чёрные списки легко попасть, но практически невозможно из них выбраться.
  2. Тарпиттинг - задержка, которую выдерживает почтовый сервер, перед тем как ответить клиенту. Большинство рассыльщиков спама стараются разослать как можно больше писем, поэтому обычно им некогда ждать и они отключаются от сервера получателя, не дождавшись ответа. Обычно задержка составляет 5-20 секунд.
  3. Встречная проверка отправителя. Почтовый сервер получателя пытается инициировать отправку письма на адрес отправителя. Результат проверки становится ясным до отправки тела письма, в этот момент почтовый сервер завершает сеанс и использует результат проверки для того, чтобы принять или отклонить письмо. Для проверки используется пустой адрес отправителя.

    Программы для рассылки спама обычно не готовы к прохождению такой проверки, поэтому такой способ проверки может помочь защититься от спама. Однако такая проверка может вызывать проблемы, если два сервера проверяют наличие отправителей друг на друге, но при этом один из них откажется принять почту на пустой адрес. Также могут возникнуть проблемы при использовании этого метода, когда на другом сервере настроен грейлистинг. Сервер с грейлистингом может сообщить о временной ошибке, так что сервер со встречной проверкой не сможет проверить существование адреса отправителя.
  4. Грейлистинг или серые списки - сервер получателя запоминает триплет с IP-адресом отправителя, адресом отправителя и адресом получателя, а затем сообщает о временной ошибке. Почтовый сервер, соответствующий стандартам, попытается повторить отправку сообщения через некоторое время. Если обе попытки не следуют друг за другом, а происходят спустя некоторый минимальный интервал времени, и если совпал весь триплет, то письмо принимается.

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

    Однако, есть ещё одна сложность - некоторые большие почтовые системы могут каждый раз пытаться отправить письмо с другого IP-адреса. Для обхода подобных ситуаций вместо IP-адреса запоминаются только первых три октета IP-адреса, так что если IP-адрес отправителя находится в той же сети, то письма могут дойти. Более крупные сети обычно добавляются вручную в белый список IP-адресов, почта с которых принимается сразу.

Обычная фильтрация спама происходит уже после приёма письма. Обычно для оценки степени доверия к письму применяются Байесовские фильтры. Они выставляют оценку, основываясь на большом количестве различных признаков, характерных для спама или полезной почты. Например, соответствие заголовков письма SPF-записи в домене отправителя и наличие правильной DKIM-подписи свидетельствует о полезности письма. Их отсутствие, наличие в письме ключевых слов, приёмов оформления HTML-писем, характерных для спама, свидетельствует о том, что письмо бесполезное.

Мой опыт показывает, что обычная фильтрация писем оказывается очень ресурсоёмкой, поэтому я избегаю её использования. Тарпиттинг и встречная проверка отправителя могут вызывать трудно диагностируемые проблемы. Из средств проактивной фильтрации я предпочитаю использовать SPF, чёрные и белые списки совместно грейлистингом. Эти средства предоставляют достаточно хорошую защиту от спама и используют минимум ресурсов, а возникающие с ними проблемы легко поддаются диагностике.

Лучшим из найденных мной в дистрибутиве Debian демонов для грейлистинга оказался демон milter-greylist. Перечислю несколько причин, почему он показался мне лучшим:
  • Написан на C, а стало быть - не требователен к ресурсам.
  • Работает по протоколу Milter, а стало быть почтовую систему можно настроить так, что при его поломке письма будут продолжать приходить.
  • Обладает исключительно гибкими настройками. Например, можно отключить фильтрацию для определённого домена-отправителя, для сети IP-адресов отправителя, для IP-адресов в чёрном списке, для определённого получателя или использовать произвольное сочетание этих критериев, настраивая для каждого из правил время грейлистинга и время действия разрешения.
  • Поддерживает онлайн-синхронизацию данных между несколькими экземплярами демона, работающими на разных компьютерах. Это позволяет использовать общие данные грейлистинга на ферме из SMTP-серверов, занимающихся приёмом почты. Даже если клиент будет каждый раз попадать на разный SMTP-сервер, грейлистинг будет продолжать нормально работать.
  • Не требует для своей работы баз данных SQL.

Устанавливаем пакет с демоном грейлистинга:
# apt-get install milter-greylist
Открываем файл конфигурации /etc/milter-greylist/greylist.conf и заменяем строчки, настраивающие сокет:
socket "/var/spool/postfix/milter-greylist" 666
Процедуру фильтрации грейлистинга будем применять только в одном из двух случаев:
  • IP-адрес отправителя находится чёрном списке zen.spamhaus.org.
  • Письмо адресовано пользователю postmaster, hostmaster, webmaster, abuse или info в нашем домене.
Встречаются администраторы, которые бездумно вписывают все найденные чёрные списки в конфигурацию своего почтового сервера. Я не хочу создавать непробиваемую стену для тех, кто готов работать в соответствии со стандартами и приложить немного дополнительных усилий для отправки почты.

Во втором случае я исхожу из следующей простой логики: это общепринятые адреса, поэтому спамеры могут слать на них письма «вслепую», точно не зная, существует ли вообще такой ящик.

Изменим список своих сетей:
list "my network" addr { 127.0.0.1/8 }
Для примера, создадим список IP-адресов своего провайдера. Несмотря на то, что эти IP-адреса могут числиться в списке PBL на spamhaus.org, мы их поместим в белый список:
list "ufanet network" addr { \
        192.168.0.0/16 \
        172.16.0.0/12 \
        10.0.0.0/8 \
        46.191.128.0/17 \
        81.30.176.0/20 \
        81.30.192.0/19 \
        89.189.128.0/19 \
        77.79.128.0/18 \
        79.140.16.0/20 \
        92.50.128.0/18 \
        94.41.0.0/16 \
        95.105.0.0/17 \
        136.169.128.0/17 \
        145.255.0.0/19 \
}
Стоит отметить, что в многострочных правилах после каждой строки, имеющей продолжение, должен стоять символ обратной косой черты.

Заменим получателей, для которых всегда будет применяться грейлистинг:
list "grey users" rcpt { \
        postmaster@domain.tld \
        webmaster@domain.tld \
        hostmaster@domain.tld \
        abuse@domain.tld \
        info@domain.tld \
}
Добавляем правило для чёрного списка zen.spamhaus.org:
dnsrbl "ZEN" zen.spamhaus.org 127.0.0.0/28
И, наконец, сводим все настройки в конечный список контроля доступа (ACL):
racl whitelist list "my network"
racl whitelist list "my friends"
racl whitelist list "broken mta"
racl whitelist list "ufanet network"
racl greylist dnsrbl "ZEN" delay 10m autowhite 3d
racl greylist list "grey users" delay 10m autowhite 3d
racl whitelist default
В файле /etc/default/milter-greylist включаем демон milter-greylist и опять настраиваем сокет:
ENABLED=1
SOCKET="/var/spool/postfix/milter-greylist"
В файле /etc/postfix/main.cf добавляем использование milter-greylist в начало настройки smtpd_milters:
smtpd_milters = unix:/milter-greylist, unix:/clamav-milter, unix:/opendkim/socket
Осталось перезапустить демоны:
# /etc/init.d/milter-greylist start
# /etc/init.d/postfix restart
Фильтрация спама - очень щепетильная тема. Многие администраторы люто негодуют, когда слышат слова "грейлистинг" и "спамхаус". Спорным можно считать и то, что грейлистингу в описанной конфигурации всегда подвергается адрес postmaster, почта которому должна доходить всегда, в том числе для решения проблем с её непрохождением. Я считаю, что показанная здесь конфигурация грейлистинга достаточно либеральна, в том числе потому, что пресловутый спамхаус не используется для безоговорочного отклонения всех писем подряд. Однако, я буду рад услышать ваши доводы.

Примечание от 13 марта 2013 года: Немного поменял порядок проверок в правилах smtpd_*_restrictions.

Продолжение следует...

воскресенье, 22 сентября 2013 г.

Простейший почтовый релей на основе Postfix

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

Однако, для этого можно воспользоваться и почтовым сервером Postfix. Он обладает модульной структурой, а потому хорошо масштабируется и может использоваться как для построения крупных многодоменных почтовых систем с вирутальными учётными записями, поддержкой шифрования, фильтрации вирусов, защитой от спама, так и для создания простейших почтовых релеев. То что нам нужно.

Установим пакет с Postfix:
# apt-get install postfix
И отредактируем файл конфигурации Postfix /etc/postfix/main.cf, задав в нём следующие минимальные настройки:
myhostname = server.example.net
mydomain = example.net
inet_protocols = ipv4
inet_interfaces = 127.0.0.1
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8
relayhost = [mail.example.net]
masquerade_domains = example.net

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
Этот файл задаёт имя сервера (myhostname), с которого будет отправляться почта, его почтовый домен (mydomain), почтовый сервер, принимающий почту (relayhost), и домен, которым будут дополняться локальные учётные записи (masquerade_domains).

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

Перезапустим почтовый сервер:
# /etc/init.d/postfix restart
Для использования в собственных скриптах можно использовать как команду sendmail из состава Postfix, так и почтовый клиент, запускаемый из командной строки. Например, команду mail из пакета bsd-mailx. Установить её можно как обычно:
# apt-get install bsd-mailx

воскресенье, 23 декабря 2012 г.

Postadmin 2

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

При написании веб-интерфейса я пошёл по пути наименьшего сопротивления. Поскольку на тот момент я обладал довольно слабым знанием SQL, то решил не разрабатывать структуру таблиц с нуля, а воспользоваться имеющейся структурой, при необходимости добавляя новые поля и таблицы. Также, поскольку у меня в прошлом уже был опыт написания приложений на PHP, я решил воспользоваться именно им. Эти два решения позволяли мне увеличить шансы на успешное достижение целей.

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

В начале года я стал осваивать Perl и начал пользоваться первым попавшимся шаблонизатором HTML::Template. Для работы с базами данных я освоил практически не имеющий альтернатив модуль DBI, что, впрочем, не так уж и плохо - DBI весьма хорош. Из трёх найденных мной веб-фреймворков, Catalyst, Mojolicious и Dancer, я решил остановиться на Dancer, как на наиболее простом из всех них. Это, конечно, не самый лучший набор инструментов, но для многих задач он вполне достаточен, а по сравнению с моим самописным недофреймворком на PHP он бесспорно лучше.

Итак, для закрепления навыков владения перечисленными инструментами (Perl, HTML::Template, DBI, Dancer), я решил портировать на них Postadmin. Функционал по синхронизации информации с порталом SharePoint я решил не воспроизводить, т.к. в настоящее время мне негде его отлаживать. Впрочем, почтового сервера у меня теперь тоже нет, поэтому в программе могут присутствовать некоторые скрытые недоработки. Если (вдруг!) кто-то решит воспользоваться этой программой и обнаружит недоработку, я готов её исправить.

Снимков экрана не привожу, так как шаблоны страниц фактически те же самые. Получившийся архив можно взять здесь. Вместе с самим веб-интерфейсом в архиве есть примеры настройки базы данных, веб-сервера, postfix и dovecot и небольшая инструкция.

суббота, 24 марта 2012 г.

Веб-интерфейс Postadmin для управления почтовым сервером

Обещал выложить свой веб-интерфейс для управления почтовым сервером, так вот - он тут: postadmin.tbz

Вы можете спросить: "Чем оно лучше PostfixAdmin?" У этих программ разное назначение. Если вам нужно средство для управления почтовым хостингом, то PostfixAdmin для вас. Если же вам нужно средство управления корпоративным почтовым сервером, то я считаю свою программу более подходящей для этого.

Чего нет в моей программе, в отличие от PostfixAdmin:
  • Разделения привилегий различных пользователей. Пользователь имеет только одни привилегии - администратора, а ограничение доступа делается средствами веб-сервера.
  • Нет квот на количество почтовых ящиков и нет квот на количество псевдонимов.
  • Нет настроек для сбора почты со внешних почтовых ящиков (fetchmail).
  • Нет настроек отсылки уведомлений об отсутствии пользователя (vacation).
  • Нет псевдонимов для доменов.
Что есть в моей программе такого, чего нет в PostfixAdmin:
  • Управление списками рассылок/псевдонимами/подписками (bcc) - в Postfixadmin есть только управление псевдонимами. Все эти функции совмещены в пределах одной таблицы и их удобно редактировать прямо со страницы почтового ящика.
  • Управление "чёрным списком" отправителей, почта от которых не принимается.
  • Управление "белым списком" адресатов, отправлять почту которым может пользователь с ограничениями на отправку. Отдельная категория доступа на отправку - это возможность отправлять на любые внешние адреса.
  • Синхронизация справочной информации с MS SharePoint Services 3.0.
  • Управление произвольными списками ограничений (при изменении конфигурации самого Postfix).
Ну и кроме того:
  • В моей программе шаблоны HTML-страниц лежат отдельно, поэтому дизайн программы не прибит к ней гвоздями и его можно менять.
  • По идее, в мою программу проще добавить новую функциональность, т.к. для создания новых виджетов есть типовые функции.
Скриншоты, чтобы дать представление об интерфейсе.

Список доменов и транспортов:

Редактирование домена и транспорта:

Список пользователей-почтовых ящиков:

Редактирование пользователя-почтового ящика:


Добавление нового пользователя-почтового ящика:

Таблица списков рассылок/псевдонимов/подписок:

Редактирование списков управления доступом SMTP-сервера:

Редактирование отдельного правила:

Синхронизация информации о пользователях с порталом MS SharePoint Services 3.0:

Более подробные (но не пошаговые и "на пальцах") инструкции по настройке находятся в самом архиве, в файле README.

Как я уже говорил здесь: http://morbow.blogspot.com/2012/03/blog-post.html, в дальнейшей разработке этого веб-интерфейса я пока не заинтересован. Если кого-то заинтересует этот веб-интерфейс, я готов ответить на вопросы по нему по почте wheelof@gmail.com. Если кто-то напишет для программы какую-то дополнительную функциональность - прошу поделиться, если не жалко :)

P.S. Переписал программу на Perl с использованием шаблонизатора HTML::Template и фреймворка Dancer. Почитать о ней можно тут: Postadmin 2.

пятница, 24 февраля 2012 г.

Совмещение таблиц bcc и alias в Postfix

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

Месяца два назад аккуратно поэкспериментировал с таблицей virtual_alias_maps и обнаружил, что в случае, если запрос для одного псевдонима возвращает два адреса, Postfix отправляет письмо на оба адреса.

Таким образом можно делать рассылки штатными средствами самого Postfix. Можно добавить в таблицу псевдонимов несколько записей с одинаковым псевдонимом, но с разными получателями и получить простейший список рассылки.

Потом я подумал о том, что между такой рассылкой и копированием входящей почты нескольким адресатам фактически нет никакой разницы. Разница только лишь в том, куда подставлена эта карта - в recipient_bcc_maps или в virtual_alias_maps. Потом я подумал о том, что обе функции можно объединить в одной таблице, если учитывать существование адреса в таблице пользователей и статус этого пользователя - включен или отключен. Если пользователь включен, то он должен получить своё письмо в свой ящик, а вместе с ним все те, кто подписан на его входящую почту - это поведение для recipient_bcc_maps. Если пользователь отключен или он не существует, то почту должны получить лишь те, кто подписан на входящую почту этого адреса - это поведение virtual_alias_maps. Добавим в таблицу ещё дополнительную колонку, которая позволит отличать подписку на исходящую почту и входящую почту и получим совмещённую таблицу алиасов, подписок, пересылок и скрытых копий входящей и исходящей почты.

Вот и запрос для создания соответствующей таблицы:
CREATE TABLE `subscription` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `direction` varchar(1) NOT NULL default 'I',
  `email` varchar(255) character set latin1 NOT NULL,
  `recipient` varchar(255) character set latin1 NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `subscription` (`direction`,`email`,`recipient`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Запрос для создания таблицы пользователей/ящиков у меня выглядит так:
CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `active` varchar(1) default 'Y',
  `email` varchar(255) character set latin1 NOT NULL,
  `password` varchar(255) character set latin1 NOT NULL default '',
  `surname` varchar(255) NOT NULL default '',
  `name` varchar(255) NOT NULL default '',
  `patronym` varchar(255) NOT NULL default '',
  `lastip` varchar(32) default NULL,
  `lasttime` datetime default NULL,
  `bytes` bigint(20) default NULL,
  `messages` int(11) default NULL,
  `max_bytes` bigint(20) unsigned NOT NULL default '1073741824',
  `max_messages` int(10) unsigned NOT NULL default '1000',
  `ad_login` varchar(255) character set latin1 NOT NULL default '',
  `phones` varchar(255) NOT NULL default '',
  `position` varchar(255) NOT NULL default '',
  `department` varchar(255) NOT NULL default '',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `email` (`email`),
  UNIQUE KEY `fullname` (`surname`,`name`,`patronym`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
И, наконец, запрос для создания таблицы доменов/транспортов выглядит так:
CREATE TABLE `domain` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `domain` varchar(255) character set latin1 NOT NULL,
  `transport` varchar(255) character set latin1 NOT NULL default 'dovecot:',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `domain` (`domain`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
В файле /etc/postfix/main.cf имеется 5 карт для работы с этими таблицами:
transport_maps = mysql:/etc/postfix/mysql/transport.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql/user.cf
virtual_alias_maps = mysql:/etc/postfix/mysql/subscription.cf
sender_bcc_maps = mysql:/etc/postfix/mysql/sender_bcc.cf
recipient_bcc_maps = mysql:/etc/postfix/mysql/recipient_bcc.cf
Ещё одна карта используется для отказа в приёме писем для тех адресатов, у которых переполнился ящик:
check_recipient_access mysql:/etc/postfix/mysql/quota.cf
Ещё одна карта используется для аутентификации POP-before-SMTP (и для IMAP-before-SMTP):
mysql:/etc/postfix/mysql/pop-before-smtp.cf
Вот сами эти карты. Первая - /etc/postfix/mysql/transport.cf:
user = postfix
password =  postfix_password
dbname = mail
hosts = 127.0.0.1
query = SELECT transport FROM domain WHERE domain='%s'
Вторая - /etc/postfix/mysql/user.cf:
user = postfix
password = postfix_password
dbname = mail
hosts = 127.0.0.1
query = SELECT CONCAT(SUBSTRING_INDEX(email, '@', -1), '/', SUBSTRING_INDEX(email, '@', 1), '/')
        FROM user
        WHERE email='%s'
Третья - /etc/postfix/mysql/subscription.cf:
user = postfix
password = postfix_password
dbname = mail
hosts = 127.0.0.1
query = SELECT recipient
        FROM subscription
        WHERE direction='I'
          AND email='%s'
          AND email NOT IN (SELECT email
                            FROM user
                            WHERE email='%s'
                              AND active='Y')
Четвёртая - /etc/postfix/mysql/sender_bcc.cf:
user = postfix
password = postfix_password
dbname = mail
hosts = 127.0.0.1
query = SELECT recipient
        FROM subscription
        WHERE direction='O'
          AND email='%s'
          AND email IN (SELECT email
                        FROM user
                        WHERE email='%s'
                          AND active='Y')
Пятая - /etc/postfix/mysql/recipient_bcc.cf:
user = postfix
password = postfix_password
dbname = mail
hosts = 127.0.0.1
query = SELECT recipient
        FROM subscription
        WHERE direction='I'
          AND email='%s'
          AND email IN (SELECT email
                        FROM user
                        WHERE email='%s'
                          AND active='Y')
Шестая - /etc/postfix/mysql/quota.cf:
user = postfix
password = postfix_password
dbname = mail
hosts = 127.0.0.1
query = SELECT '452 Mailbox is over quota'
        FROM user
        WHERE email = '%s'
          AND ((bytes >= max_bytes AND max_bytes > 0)
            OR (messages >= max_messages AND max_messages > 0))
Седьмая - /etc/postfix/mysql/pop-before-smtp.cf:
user = postfix
password = postfix_password
dbname = mail
hosts = 127.0.0.1
query = SELECT DISTINCT lastip
        FROM user
        WHERE lastip='%s'
          AND ADDTIME(lasttime, '0:1:0') > NOW()
Для реализации POP-before-SMTP и IMAP-before-SMTP сделаны два скрипта, которые вызываются сразу после аутентификации в Dovecot.

Первый - /etc/dovecot/pop-before-smtp.sh:
#!/bin/sh

echo "UPDATE user SET lasttime=NOW(), lastip='$IP' WHERE email='$USER';" | mysql -udovecot -pdovecot_password -h127.0.0.1 mail
exec /usr/lib/dovecot/pop3 "$@"
И второй - /etc/dovecot/imap-before-smtp.sh:
#!/bin/sh

echo "UPDATE user SET lasttime=NOW(), lastip='$IP' WHERE email='$USER';" | mysql -udovecot -pdovecot_password -h127.0.0.1 mail
exec /usr/lib/dovecot/imap "$@"
В Dovecot используются два файла, позволяющие брать информацию из БД MySQL.

Первый - /etc/dovecot/dovecot-mysql.conf:
driver = mysql
connect = host=127.0.0.1 dbname=mail user=dovecot password=dovecot_password
default_pass_scheme = CRYPT
password_query = SELECT password FROM user WHERE email='%u'
user_query = SELECT CONCAT(SUBSTRING_INDEX(email, '@', -1), '/', SUBSTRING_INDEX(email, '@', 1), '/'), \
                    999 AS uid, \
                    999 AS gid, \
                    CONCAT('*:bytes=', max_bytes, ':messages=', max_messages) AS quota_rule \
             FROM user \
             WHERE email = '%u'
Второй - /etc/dovecto/dovecot-dict-mysql.conf:
connect = host=127.0.0.1 dbname=mail user=dovecot password=dovecot_password
map {
  pattern = priv/quota/storage
  table = user
  username_field = email
  value_field = bytes
}
map {
  pattern = priv/quota/messages
  table = user
  username_field = email
  value_field = messages
}
Файл конфигурации самого Dovecot /etc/dovecot/dovecot.conf у меня выглядит вот так:
protocols = pop3 imap
disable_plaintext_auth = no
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_location = maildir:/var/mail/virtual/%Ld/%Ln
first_valid_uid = 999
first_valid_gid = 999

dict {
  quotadict = mysql:/etc/dovecot/dovecot-dict-mysql.conf
}
plugin {
  quota = dict:user::proxy::quotadict
  quota_rule = *:storage=1G:messages=1000
}
protocol pop3 {
  mail_plugins = quota
  mail_executable = /etc/dovecot/pop-update-lastlog.sh
}
protocol imap {
  mail_plugins = quota imap_quota
  mail_executable = /etc/dovecot/imap-update-lastlog.sh
}
protocol lda {
  postmaster_address = postmaster@mydomain.ru
  mail_plugins = quota
  quota_full_tempfail = yes
  rejection_reason = Your message to <%t> was automatically rejected:%n%r
  quota_exceeded_message = 422: No space to store your message
}

auth_default_realm = mydomain.ru

auth default {
  mechanisms = plain login
  passdb sql {
    args = /etc/dovecot/dovecot-mysql.conf
  }
  userdb sql {
    args = /etc/dovecot/dovecot-mysql.conf
  }
  socket listen {
    client {
      path = /var/spool/postfix/private/auth
      mode = 0660
      user = postfix
      group = postfix
    }
    master {
      path = /var/run/dovecot/auth-master
      mode = 0660
      user = vmail
      group = vmail
    }
  }
}
Для ограничения доступа к таблицам из postfix и dovecot были созданы два пользователя, которые имеют минимально необходимые им для работы права доступа к соответствующей БД MySQL:
insert into user(host, user, password)
values('localhost', 'postfix', PASSWORD('postfix_password'));
insert into tables_priv(host, db, user, table_name, table_priv, column_priv)
values('localhost', 'mail', 'postfix', 'subscription', '', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'subscription', 'direction', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'subscription', 'email', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'subscription', 'recipient', 'select');

insert into tables_priv(host, db, user, table_name, table_priv, column_priv)
values('localhost', 'mail', 'postfix', 'domain', '', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'domain', 'domain', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'domain', 'transport', 'select');

insert into tables_priv(host, db, user, table_name, table_priv, column_priv)
values('localhost', 'mail', 'postfix', 'user', '', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'active', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'email', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'password', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'bytes', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'max_bytes', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'messages', 'select'); 
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'max_messages', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'lasttime', 'select'); 
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'postfix', 'user', 'lastip', 'select');

insert into user(host, user, password)
values('localhost', 'dovecot', PASSWORD('dovecot_password'));
insert into tables_priv(host, db, user, table_name, table_priv, column_priv)
values('localhost', 'mail', 'dovecot', 'user', '', 'select,update');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'email', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'password', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'bytes', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'max_bytes', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'messages', 'select'); 
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'max_messages', 'select');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'bytes', 'update');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'messages', 'update'); 
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'lasttime', 'update');
insert into columns_priv(host, db, user, table_name, column_name, column_priv)
values('localhost', 'mail', 'dovecot', 'user', 'lastip', 'update');
flush privileges;
Естественно, во всём тексте статьи, для облегчения восприятия, реальные пароли заменены на их заменители - postfix_password и dovecot_password.

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

Управление доменами и транспортами:

Редактирование одного домена и его транспорта:

Управление пользователями/ящиками (совмещены две разные картинки):

Редактирование одного пользователя/ящика:

Заведение нового пользователя/ящика:

Управление всеми подписками на входящие (скоро переделаю для управления подписками на исходящие тоже):

Страница синхронизации информации почтовой системы с порталом MS SharePoint Services 3.0:

В процессе написания веб-приложения сам собой зародился простенький фреймворк в процедурном стиле, в который входит несколько функций для более удобного доступа к БД, функции для работы с шаблонами, виджеты таблиц и форм редактирования объектов БД, функции для ведения веб-сессий в БД (стандартный механизм сессий для PHP мне не подошёл, сессии сейчас в приложении никак не используются), и каркас приложения, который вызывает функции обработки форм и формирования HTML-блоков. Аутентификацию на базе модуля сессий делал, но пока что она не имеет смысла, т.к. нет механизмов авторизации, то есть программа может отличать пользователей друг от друга, но у всех пользователей, включая неаутентифицированных, пока что равные права. Сейчас ограничение доступа осуществляется средствами веб-сервера, в котором каталог с приложением просто запаролен.

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