Почтовый шлюз: задание со звёздочкой (Усиливаем защиту внешними средствами)
Под "внешними средствами" я понимаю дополнительный софт, без которого впринципе можно и обойтись. Однако, если его применение облегчит нам жизнь, снизит количества спама или замедлит рост энтропии - почему бы и нет.
Отстрел ботов
Сейчас практически must-have для почтового сервера. Резко снижает содомию в логах, и загрузку всей остальной системы за счёт меньшей работы антимпама/антивируса/dns'а и т.д.
Для этой задачи вы можете использовать или "старый добрый" fail2ban или мою разработку -- f2b.
Настройку fail2ban вы и без меня осилите, т.к. оно достаточно индивидуально, привожу только failregex.
Всё что ниже 5й линии - это сообщения из соответствующих проверок acl_smtp_helo
и acl_smtp_rcpt
.
failregex = SMTP protocol synchronization error \(input sent without waiting for greeting\): rejected connection from .*\[<HOST>\]
SMTP protocol synchronization error \(next input sent too soon: pipelining was not advertised\): rejected .*\[<HOST>\]
rejected EHLO from \[<HOST>\]: syntactically invalid argument
rejected HELO from \[<HOST>\]: syntactically invalid argument
Connection from \[<HOST>\] refused: too many connections from that IP address
\[<HOST>\] .* host is listed in zen.spamhaus.org
\[<HOST>\] .* host is listed in bl.spamcop.net
\[<HOST>\] .* Bad rev hostname \(.*\)
\[<HOST>\] .* relay not permitted
\[<HOST>\] .* too many connections from that IP address
\[<HOST>\] .* IP address in HELO
\[<HOST>\] .* HELO should be FQDN
\[<HOST>\] .* localhost is a silly HELO
\[<HOST>\] .* Using my HELO is a bad idea
\[<HOST>\] .* SPF for sender domain not allows mail from your host
\[<HOST>\] .* You are blocked
\[<HOST>\] .* Dont like your hostname
Обратите внимание - используются не все проверки. Логика отбора такая: под раздачу должны попадать хосты, у которых есть какой-то серьёзный косяк, и которые продолжают настойчиво к нам ломиться.
[exim]
findtime = 300 # если в последние T секунд ...
maxretry = 5 # .. было N срабатываний для одного и того же ip ..
bantime = 3600 # .. отправить его отдохнуть на часик
logpath = /var/log/exim/mainlog
enabled = true
filter = exim-custom
f2b настраивается примерно так (только описание самого jail'а):
[jail:postfix]
enabled = yes
source = files:/var/log/mail.log
filter = preg:/etc/f2b/filters/postfix.preg
backend = exec-ipset:banned
bantime = 3600
Грейлистинг
В приведённом ранее конфиге были места, где устанавливался флаг, что этот хост нужно проверить грейлистингом, однако самой проверки не было.
Для реализации грейлистинга используется sqlgrey. Спроектирован он изначально под postfix, и его check_policy
, здесь это придётся костылять средствами exim'а.
В acl check_rcpt
, поближе к концу, но перед accept domains = +our_domains
:
# greylisting
defer message = Service temporarily unavailable, try again later
condition = ${if eq{$acl_m_greylist}{1} {yes}{no}}
set acl_m0 = request=smtpd_access_policy\nprotocol_state=RCPT\nprotocol_name=${uc:$received_protocol}\nhelo_name=$sender_helo_name\nclient_address=$sender_host_address\nclient_name=$sender_host_name\nsender=$sender_address\nrecipient=$local_part@$domain\ninstance=$sender_host_address/$sender_address/$local_part@$domain\n\n
set acl_m0 = ${sg{${readsocket{/var/run/sqlgrey.sock}{$acl_m0}{5s}{}{action=DUNNO}}}{action=}{}}
condition = ${if eq{${uc:${substr{0}{5}{$acl_m0}}}}{DEFER} {yes}{no}}
Выглядит оно достаточно страшно, но тем не менее работает.
Обратите внимание на используемые переменные вида acl_m*
-- они раскрываются для каждого сообщения, поскольку грейлистинг использует триплеты senderip-from-rcpt.
Это также означает, что мы можем использовать эту проверку только в пределах acl_smtp_rcpt
.
Справочно, конфиг самого sqlgrey:
unix = /var/run/sqlgrey.sock
reconnect_delay = 10
max_connect_age = 24
awl_age = 32
group_domain_level = 10
db_type = SQLite
db_name = /var/db/sqlgrey/stats.db
optmethod = optout
DKIM
В exim'е есть базовая поддержка dkim'а, надо только чтобы она была включена при компиляции.
Этот блок - дописывается в начало файла exim'а:
DKIM_DOMAIN = ${lc:${domain:$h_from:}}
DKIM_FILE = /etc/exim/${lc:${domain:$h_from:}}.key
DKIM_PRIVATE_KEY = ${if exists{DKIM_FILE}{DKIM_FILE}{0}}
...и добавляем три строчки к транспорту remote_smtp
:
remote_smtp:
driver = smtp
interface = 4.3.2.1
no_delay_after_cutoff
# ага, вот эти ребята
dkim_domain = DKIM_DOMAIN
dkim_selector = dkim
dkim_private_key = DKIM_PRIVATE_KEY
Антиспам
В нашем варианте в качестве антиспама используется rspamd.
spamassassin прикручивается почти так же, надо поменять variant=
и подправить проверку переменной $spam_action
Про донастройку rspamd я отдельно расскажу, здесь только как его подключить к exim'у.
Вот этот блок - в начало конфига.
spamd_address = 127.0.0.1 11333 variant=rspamd
А этот - дописать в acl check_data
, чтобы он принял примерно такой вид:
check_data:
# не проверять сообщения с наших собственных серверов
accept hosts = +our_networks : +our_hosts : +relay_from_hosts
# пропускать всё с "наших" доменов
# с проверкой, что письмо от "нашего" домена идёт с "наших" хостов -
# надо было разбираться раньше
accept sender_domains = +our_domains
# не проверять сообщения, если используется submission
accept condition = ${if eq {$interface_port}{587} {yes}{no}}
# не проверять юзеров, которые авторизовались в системе
accept authenticated = *
# это правило как раз и отправляет письмо на проверку антиспамом
# nobody - это "профиль пользователя" в антиспаме
warn spam = nobody:true
# тут - проверка антивирусником, смотри далее
# если письмо набрало больше порога - отлуп
deny condition = ${if eq{$spam_action}{reject}}
message = Message discarded as high-probability spam
# если антиспам считает письмо "хорошим" - добавить хидер
# потом это поможет в разбирательствах, попало письмо в проверку или нет
accept condition = ${if eq{$spam_action}{no action} {yes}{no}}
add_header = X-Spam-Status: No
# если письмо набрало сколько-то баллов, но недобрало до отлупа -
# добавляем дополнительную информаию для дальнейших разбирательств
#
# я дальше покажу как отловить такие письма и сложить отдельно
accept condition = ${if eq{$spam_action}{add header} {yes}{no}}
add_header = X-Spam-Status: Yes
add_header = X-Spam-Score: $spam_score ($spam_bar)
add_header = X-Spam-Report: $spam_report
# ну ладно, так уж и быть - пущу
accept
Антивирус
Антивирусов под линуксом немного, запутаться сложно. По дефолту показываю на примере clamav, если у вас что-то другое - исправляйте под свои реалии.
В начало конфига, к общим настройкам:
av_scanner = clamd:/var/run/clamav/clamd.sock
В секцию acl'ов, дописать в середину check_data
:
check_data:
<...>
# discard viruses and malware
deny malware = */defer_ok
message = This message was detected as possible malware
defer_ok
- говорит серверу при недоступности антивирусника или его кривой настройкой,
отвечать 4XX ошибками - "сервис временно недоступен" (вместо 5XX). Почта не пропадёт.
Немного про эффективность clamd: официальные базы ловят немного, и обновляются весьма неспешно, по моим наблюдениям - примерно пятую часть от новья и половину от старых. Разгребая спецящик "под спам" в день находишь 2-3 бинарника/архива. Смотришь логи clamav, но уже за неделю - те же 2-3 шт. Хотя надо учитывать ещё две вещи - наличие DNSBL/XBL (думаю, без них до clamav доходило бы намного больше) и сам характер непойманных "вирусов".
Примеры из непойманного:
- zip-архив, внутри - js-скрипт с eval'ом. Не знаю на кого это рассчитано и как оно намеревалось запускаться.
- запароленные архивы, пароль указан в самом письме. Часть из них clamav всё же ловит, но не всё.
- rar, переименованный в маргинальщину типа .ace. Это рассчитано на winrar, который жрёт всё. Такое clamav должен ловить.
Чтобы победить эту напасть и повысить уловы, рекомендуют заюзать неофициальные базы: тыц Часть из них платные, часть - полностью свободные. При подключении части этих баз, отсекается порядка 80% случаев, типа описанных выше.
Потребление памяти в процессе работы (до 20 писем/мин): ~350 метров.
Если есть желание пополнить базу, вот скриптик в помощь:
#!/bin/sh
# исправьте на свои
USER="Alex Z"
EMAIL="ad_user@runbox.com"
# нет прокси - убрать или закомментировать
PROXY="http://192.168.49.1:8080"
export http_proxy="$PROXY"
export https_proxy="$PROXY"
ls *.zip 2> /dev/null | while read ZIP; do
unzip "$ZIP" && rm -vf "$ZIP"
done
ls *.exe *.ace *.scr *.jar *.doc *.vbs 2> /dev/null | while read FILE; do
echo "Processing $FILE"
clamsubmit -N "$USER" -e "$EMAIL" -n "$FILE" > /dev/null && rm -vf "$FILE"
done
Выделяем где-нибудь отдельную директорию, кладём туда этот скриптик, накидываем "улов" и запускаем. Занимает времени - полминуты, но повышает ЧСВ на 100500.
Опять же по опыту, добавляют в базу примерно 1/30 образцов.