Exim+Dovecot2+PgSQL - добиваем связку
Добавлено: 03 май 2012, 14:17
В общей картине что у нас есть? У нас есть
- RedHat Enterprise Linux 6.2
- Настроеный кластер PostgreSQL
- Настроеный MDA - Dovecot2
- Установленый Clamav
- Установленый Spamassassin
- Установленый Exim 4.77
Правим /etc/exim/exim.conf
Код: Выделить всё
DOMAIN_QUERY = SELECT domain FROM domain WHERE domain='${domain}' AND active=true
# БД+Хосты
hide pgsql_servers = pgpool.domain.tld/mail/mail/пароль_пользователя_БД
domainlist local_domains = ${lookup pgsql{SELECT domain FROM domain WHERE domain='${domain}' AND active=true}}
domainlist relay_to_domains = ${lookup pgsql{DOMAIN_QUERY}}
domainlist dummy_domains =
hostlist relay_from_hosts = localhost:127.0.0.1/8:<ext_if_ip>:mail.domain.tld:domain.tld
hostlist spamers = ${lookup pgsql{SELECT host FROM blacklist WHERE host='${sender_host_address}'}}
host_lookup = *
rfc1413_hosts = *
rfc1413_query_timeout = 0s
allow_domain_literals = false
# TLS/SSL
tls_advertise_hosts = *
tls_certificate = /etc/pki/tls/private/exim.pem
tls_privatekey = /etc/pki/tls/private/exim.pem
daemon_smtp_ports = 25 : 465 : 587
tls_on_connect_ports = 465
# Цепляем антивирь и антиспам
spamd_address = 127.0.0.1 783
av_scanner = clamd:/tmp/clamd.sock
smtp_banner = mx.domain.tld ESMTP Postfix 2.9.1/2.9.1 (CentOS 5.3); $tod_full
local_interfaces = 127.0.0.1 : <ext_ip_if>
qualify_domain = mx.domain.tld
qualify_recipient = mx.domain.tld
primary_hostname = mx.domain.tld
never_users = root
exim_user = exim
exim_group = exim
trusted_users = lighttpd
dsn_from = Mail Delivery System <Mailer-Daemon@$qualify_domain>
# Логи, тайминги
ignore_bounce_errors_after = 1d
timeout_frozen_after = 1d
delay_warning = 4h:8h:24h:48h
log_selector = \
+all_parents \
+connection_reject \
+incoming_interface \
+lost_incoming_connection \
+received_sender \
+received_recipients \
+smtp_confirmation \
+smtp_syntax_error \
+smtp_protocol_error \
-queue_run
# Тюним параметры SMTP
smtp_accept_max = 500
smtp_accept_max_per_connection = 250
smtp_accept_max_per_host = 20
smtp_connect_backlog = 50
smtp_accept_queue_per_connection = 30
split_spool_directory = true
remote_max_parallel = 15
message_size_limit = 64M
smtp_enforce_sync = true
allow_mx_to_ip
disable_ipv6 = true
auto_thaw = 1h
# Небольшой фейк с заголовком
received_header_text = Received: \
${if def:sender_rcvhost {from $sender_rcvhost\n\t}\
{${if def:sender_ident \
{from ${quote_local_part:$sender_ident} }}\
${if def:sender_helo_name {(helo=$sender_helo_name)\n\t}}}}\
by $qualify_domain \
${if def:received_protocol {with $received_protocol}} \
${if def:tls_cipher {($tls_cipher)\n\t}}\
(Postfix 2.9.1)\n\t\
${if def:sender_address \
{(envelope-from <$sender_address>)\n\t}}\
id $message_exim_id\
${if def:received_for {\n\tfor $received_for}}
# Списки доступа
acl_smtp_mail = acl_check_mail
acl_smtp_dkim = acl_check_dkim
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
begin acl
acl_check_mail:
deny condition = ${if eq{$sender_helo_name}{} {1}}
message = Nice boys say HELO first
warn condition = ${if eq{$sender_host_name}{} {1}}
set acl_m_greylistreasons = Host $sender_host_address lacks reverse DNS\n$acl_m_greylistreasons
accept
# Проверяем соответствие DKIM домену отправителя
acl_check_dkim:
deny message = Mail from DKIM-domain without DKIM-signature! Wassup?!
sender_domains = ${lookup pgsql{SELECT array_to_string(array_agg(domains), ':') AS domains FROM dkim_domains WHERE active=true}} # Запись в базе в виде | gmail.com | gmail.com |
dkim_signers = ${lookup pgsql{SELECT array_to_string(array_agg(signers), ':') AS signers FROM dkim_domains WHERE active=true}}
dkim_status = none:invalid:fail
accept
acl_check_rcpt:
accept hosts = :
control = dkim_disable_verify
deny message = Restricted characters in address
domains = +local_domains
local_parts = ^[.] : ^.*[@%!/|]
deny message = Restricted characters in address
domains = !+local_domains
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
accept local_parts = postmaster
domains = +local_domains
require verify = sender
accept hosts = +relay_from_hosts
control = submission
control = dkim_disable_verify
# Ratelimit - ограничиваем частоту отправляемых писем в час. Данные выбираем... конечно из базы
warn authenticated = *
ratelimit = ${lookup pgsql{SELECT rate FROM mailbox WHERE username='${quote_pgsql:$authenticated_id}' LIMIT 1}} / 1h / strict / $authenticated_id
log_message = Authenticated sender rate $sender_rate / $sender_rate_period
deny authenticated = *
ratelimit = ${lookup pgsql{SELECT rate FROM mailbox WHERE username='${quote_pgsql:$authenticated_id}' LIMIT 1}} / 1h / strict / $authenticated_id
log_message = Authenticated sender rate $sender_rate / $sender_rate_period
# Авторизованый пользователь минует проверку DKIM
accept authenticated = *
control = submission
control = dkim_disable_verify
# Наш собственный блеклист
deny message = "Your address in banlist!"
senders = ${lookup pgsql{SELECT sender FROM blacklist WHERE sender='${quote_pgsql:$sender_address}' OR sender='*@${quote_pgsql:$sender_address_domain}' LIMIT 1}}
deny hosts = +spamers
message = "Host rejected by spam list on rbl.domain.tld!"
# Проверка корректности
require message = relay not permitted
domains = +local_domains : +relay_to_domains
require verify = recipient
deny message = "HELO/EHLO required by SMTP RFC"
condition = ${if eq{$sender_helo_name}{}{yes}{no}}
deny condition = ${if match{$sender_helo_name}{\N^\d+$\N}{yes}{no}}
hosts = !127.0.0.1:!localhost:*
message = "There can not be only numbers in HELO!"
deny condition = ${if eq{$sender_address}{}{yes}{no}}
hosts = +relay_from_hosts
message = "Your message have not return address"
deny message = "The use of IP is forbidden in HELO!"
hosts = *:!+relay_from_hosts
condition = ${if eq{$sender_helo_name}\
{$sender_host_address}{true}{false}}
deny condition = ${if eq{$sender_helo_name}\
{$interface_address}{yes}{no}}
hosts = !127.0.0.1 : !localhost : *
message = "The use of my IP is forbidden!"
# хосты на динамике нам тоже ни к чему
deny message = "Dynamic hosts is forbidden!"
condition = ${if match{$sender_host_name}\
{dsl|dial|pool|peer|dhcp|cable} {yes}{no}}
# Если домен отправителя имеет SPF-запись смотрим соответствует ли она IP-адресу с которого пришло письмо (требуется пакет libspf2)
deny message = [SPF] $sender_host_address is not allowed to send mail \
from $sender_address_domain.
log_message = SPF check failed.
set acl_m9 = -ipv4=$sender_host_address \
-sender=$sender_address \
-helo=$sender_helo_name
set acl_m9 = ${run{/usr/bin/spfquery $acl_m9}}
condition = ${if eq {$runrc}{1}{true}{false}}
# Смотрим нет ли отправителя в блеклистах
deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
hosts = !+relay_from_hosts
!authenticated = *
log_message = found in $dnslist_domain
dnslists = ${lookup pgsql{SELECT array_to_string(array_agg(dnsblists), ':') AS test FROM dnsbl WHERE bl=true}}
accept
# Проверка тела письма
acl_check_data:
warn condition = ${if !def:h_Message-ID: {1}}
set acl_m_greylistreasons = Message lacks Message-Id: header. Consult RFC2822.\n$acl_m_greylistreasons
deny malware = *
message = This message contains a virus ($malware_name).
accept condition = ${if >={$message_size}{100000} {1}}
add_header = X-Spam-Note: SpamAssassin run bypassed due to message size
warn spam = nobody/defer_ok
add_header = X-Spam-Flag: YES
accept condition = ${if !def:spam_score_int {1}}
add_header = X-Spam-Note: SpamAssassin invocation failed
warn add_header = X-Spam-Score: $spam_score ($spam_bar)\n\
X-Spam-Report: $spam_report
deny condition = ${if >{$spam_score_int}{100} {1}}
message = Your message scored $spam_score SpamAssassin point. Report follows:\n\
$spam_report
accept
# Роутеры
begin routers
# Резольв
dnslookup:
driver = dnslookup
domains = !+local_domains
transport = remote_smtp
ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
no_more
# Проверяем не отключен-ли домен пользователя
disabled_domains:
driver = redirect
condition = ${lookup pgsql{SELECT domain FROM domain WHERE domain='${domain}' AND active=false}}
allow_fail = yes
data = :fail: Domain disabled
no_more
# Проверяем не отключен-ли пользователь
disabled_users:
driver = redirect
condition = ${lookup pgsql{SELECT username FROM mailbox WHERE username='${quote_pgsql:$local_part@$domain}' AND active=false}}
allow_fail = yes
data = :fail: User disabled
no_more
# Проверим не является ли пользователь алиасом
system_aliases:
driver = redirect
allow_fail
allow_defer
data = ${lookup pgsql{SELECT goto FROM alias WHERE address='${quote_pgsql:$local_part@$domain}' OR address='${quote_pgsql:@$domain}'}}
# Проверим существует-ли пользователь, если да письмо проходит дальше в транспортировщик к MDA
dovecot_user:
driver = accept
condition = ${if eq{} {${lookup pgsql{SELECT username FROM mailbox WHERE local_part='${local_part}' AND domain='${domain}' AND active=true}}}{no}{yes}}
transport = dovecot_delivery
# Транспорты
begin transports
# Определим какой домен юзать для подписи DKIM
DKIM_DOMAIN = ${lc:${domain:$h_from:}}
remote_smtp:
driver = smtp
interface = <ext_if_ip>
# Подписываем исходящую почту DKIM-ключом
dkim_domain = DKIM_DOMAIN
dkim_selector = delta
dkim_private_key = ${lookup pgsql{SELECT rsa_key FROM dkim WHERE domain='${quote_pgsql:$dkim_domain}' LIMIT 1}{$value}}
dkim_canon = relaxed
# Доставка в dovecot посредством LDA
dovecot_delivery:
driver = pipe
command = /usr/libexec/dovecot/dovecot-lda -d $local_part@$domain -f $sender_address
message_prefix =
message_suffix =
delivery_date_add
envelope_to_add
return_path_add
log_output
user = mail
address_pipe:
driver = pipe
return_output
address_reply:
driver = autoreply
begin retry
* * F,2h,15m; G,16h,1h,1.5; F,4d,6h
begin rewrite
# Авторизация пользователей. Мы юзаем Dovecot
begin authenticators
auth_plain:
driver = dovecot
public_name = PLAIN
server_socket = /var/run/dovecot/auth-client
server_set_id = $auth1
auth_login:
driver = dovecot
public_name = LOGIN
server_socket = /var/run/dovecot/auth-client
server_set_id = $auth1
auth_cram_md5:
driver = dovecot
public_name = CRAM-MD5
server_socket = /var/run/dovecot/auth-client
server_set_id = $auth1
Код: Выделить всё
# service clamd start
# service spamassassin start
# service exim start
Ах, да... совсем забыл - DKIM!!!
Сгенерим пару ключей:
Код: Выделить всё
#openssl genrsa -out dkim.private.key 768
#openssl rsa -in dkim.private.key -out dkim.public.key -pubout -outform PEM
Содержимое dkim.public.key (только строчки с данными в одну строчку, коментарии нам не нужны) внесем в конфигу нашей зоны dns в виде текстовой записи
Код: Выделить всё
delta._domainkey.domain.tld. TXT v=DKIM1; t=y; k=rsa; p=<ключ>