Настройка почтового сервера
В наше время этим никого не удивишь, но возможно кому-то статья будет полезна
К почтовой системе предъявлялись следующие требования:
- почта на linux, пользователи авторизуются в существующий AD
- доступ прием и отправка писем с телефона и с ПК, с интернета
- файловая система где размещена почтовая база, логи и конфиги должны быть размещены на шифрованном разделе.
Начнем:
ось: Ubuntu 20.04.1 LTS
Шифрация файловой системы /data — Veracrypt
MTA: Postfix + postscreen + postgrey + opendkim
IMAP: Dovecot
SSL сертификаты: certbot
Доступ web: Nginx + Roundcube
Ограничения от китайцев и других "друзей" жаждущих ssh: GeoIP iptables
За перебором паролей присматривает: fail2ban
С интернета с внешнего IP адреса пробрасываются несколько TCP портов:
TCP 80 nginx для обеспечения работы certbot
TCP 443 nginx для roundcube и web доступа к почте
TCP 465 postfix для отправки почты с почтового агента на телефоне
TCP 993 dovecot для доступа почтового агента на телефоне
Информации много, свел все в разделы, в окончании раздела эта штука [END] подскажет что читаемый раздел закончился.
Базовая настройка выполняется довольно просто
root@mail:~# apt update
root@mail:~# apt upgrade
root@mail:~# apt autoremove
root@mail:~# timedatectl list-timezones | grep Asia/Vl
Asia/Vladivostok
root@mail:~# timedatectl set-timezone Asia/Vladivostok
root@mail:~#
root@mail:~# systemctl disable multipath-tools
Synchronizing state of multipath-tools.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable multipath-tools
root@mail:~#
[END]
В проекте использовался veracrypt, установить его на базовую ось несложно, останавливаться на этом не буду. Почтовый сервер работает на esx, при перезагрузке почтового сервера или просто при включении, базовая операционная система на почтовом сервере должна подняться, все модули ОС должны запустится, но почтовый функционал должен быть недоступен, до входа оператора (или администратора) и монтирование файловой системы с обязательным вводом пароля от файловой системы.
Два диска, один sda для системы, второй sdb для veracrypt
root@mail:~# ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sda4 /dev/sda5 /dev/sdb /dev/sdb1
Создаем fdisk на sdb партицию sdb1
root@mail:~# veracrypt -t --create /dev/sdb1 --hash sha512 --encryption AES --filesystem ext4 --volume-type normal
Enter password:
Re-enter password:
Enter PIM:
Enter keyfile path [none]:
Please type at least 320 randomly chosen characters and then press Enter:
fx2b
Done: 5,289% Speed: 151 MiB/s Left: 32 minutes
The VeraCrypt volume has been successfully created.
root@mail:~#
Файл /etc/default/veracrypt
root@mail:~# mcedit /etc/default/veracrypt
VERACRYPT_SHUTDOWN_UNMOUNT="yes"
VERACRYPT_SUSPEND_UNMOUNT="yes"
Создаем каталог /usr/local/veracrypt в нем создаем несколько файлов
root@mail:~# mcedit /usr/local/veracrypt/config
encrypted_partition="/dev/sdb1"
mounting_point="/data"
root@mail:~# mcedit /usr/local/veracrypt/mount
#!/bin/bash
script_folder="/usr/local/veracrypt"; # "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
script_name=$( basename "$0" )
function msg () {
echo "$1"
logger "$script_name - $1"
}
# Load the variables that we will use for Veracrypt
if [[ -f "$script_folder/config" ]]; then
source "$script_folder/config"
else
msg "ERROR - Unable to find the file $script_folder/config"
exit 1
fi
# Make sure that we have found the variables that veracrypt will need
if [[ "$encrypted_partition" == "" || "$mounting_point" == "" ]]; then
msg "ERROR - The values of encrypted_partition ($encrypted_partition) and/or mounting_point ($mounting_point) does not seems to be valid"
exit 1
fi
# First, make sure that we can see the encrypted partition
if [[ ! $( fdisk -l | grep "$encrypted_partition" ) ]]; then
msg "ERROR - Unable to find the partition $encrypted_partition. If this parition is on an external disk, please verify that the disk is connected."
exit 1
fi
# Then check if the partition is already mounted or not And it's not yet mounted in Veracrypt, run Veracrypt to mount it
if [[ $( lsblk "$encrypted_partition" | grep "veracrypt" | grep "$mounting_point" ) ]]; then
msg "INFO - The encrypted partition $encrypted_partition is already mounted to $mounting_point"
else
# Make sure that if the mounting point folder exists, it's empty
if [[ -d "$mounting_point" && $( ls "$mounting_point" ) != "" ]]; then
msg "ERROR - The mounting point $mounting_point does not seems to be empty. Please verify the content of this folder."
exit 1
fi
# If the mounting point does not exist, create it
if [[ ! -d "$mounting_point" ]]; then
msg "INFO - Creating the mounting point folder $mounting_point ..."
mkdir "$mounting_point"
fi
# Try to mount the partition
msg "INFO - Mounting the encrypted partition $encrypted_partition to the mounting point $mounting_point ..."
veracrypt -t -k "" --pim=0 --protect-hidden=no "$encrypted_partition" "$mounting_point"
errorlevel=$?
# Verify the exit value of the veracrypt command
if [[ $errorlevel -ne 0 ]]; then
msg "ERROR - The veracrypt command returned an error (errorlevel: $errorlevel)"
exit 1
fi
# Check if the encrypted partition has been successfully mounted or not
if [[ $( lsblk "$encrypted_partition" | grep "veracrypt" | grep "$mounting_point" ) ]]; then
msg "OK - The encrypted partition $encrypted_partition has been mounted to the mounting point $mounting_point"
else
msg "ERROR - Unable to mount the encrypted partition $encrypted_partition to the mounting point $mouting_point"
exit 1
fi
fi
root@mail:~# mcedit /usr/local/veracrypt/unmount
#!/bin/bash
script_folder="/usr/local/veracrypt"; # "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
script_name=$( basename "$0" )
function msg () {
echo "$1"
logger "$script_name - $1"
}
# Load the variables that we will use for Veracrypt
if [[ -f "$script_folder/config" ]]; then
source "$script_folder/config"
else
msg "Error - Unable to find the file $script_folder/config"
exit 1
fi
# Make sure that we have found the variables that veracrypt will need
if [[ "$encrypted_partition" == "" || "$mounting_point" == "" ]]; then
msg "Error - The values of encrypted_partition ($encrypted_partition) and/or mounting_point ($mounting_point) does not seems to be valid"
exit 1
fi
# Check if the mounting point is mounted or not
if [[ ! $( lsblk "$encrypted_partition" | grep "veracrypt" | grep "$mounting_point" ) ]]; then
msg "OK - The encrypted partition $encrypted_partition is already unmounted from the mounting point $mounting_point"
exit 0
fi
# Check if there are some open files in use
if [[ $( lsof | grep "$mounting_point" ) != "" ]]; then
msg "Warning - It seems that some files are still in use in the folder $mounting_point. Please close them before unmounting."
exit 1
fi
# Try to unmount the encrypted partition from the mounting point
#/usr/bin/veracrypt -d "$mounting_point"
sudo /usr/bin/veracrypt -d -f;
#/data
errorlevel=$?
# Verify the result
if [[ $errorlevel -ne 0 ]]; then
msg "Error - Unable to properly unmount the encrypted partition $encrypted_partition from the mounting point $mounting_point (errorlevel: $errorlevel)"
exit 1
else
msg "OK - Successfully unmounted the encrypted partition $encrypted_partition from the mounting point $mounting_point"
fi
root@mail:~# mcedit /usr/local/veracrypt/verify
#!/bin/bash
script_folder="/usr/local/veracrypt"; # "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
script_name=$( basename "$0" )
function msg () {
echo "$1"
logger "$script_name - $1"
}
# Load the variables that we will use for Veracrypt
if [[ -f "$script_folder/config" ]]; then
source "$script_folder/config"
else
msg "Error - Unable to find the file $script_folder/config"
exit 1
fi
# Make sure that we have found the variables that veracrypt will need
if [[ "$encrypted_partition" == "" || "$mounting_point" == "" ]]; then
msg "Error - The values of encrypted_partition ($encrypted_partition) and/or mounting_point ($mounting_point) does not seems to be valid"
exit 1
fi
# Check if it's mounted or not
if [[ $( lsblk "$encrypted_partition" | grep "veracrypt" | grep "$mounting_point" ) ]]; then
msg "OK - The encrypted partition $encrypted_partition is currently mounted to the mounting point $mounting_point."
else
msg "Error - The encrypted partition $encrypted_partition is currently NOT mounted to the mounting point $mounting_point."
exit 1
fi
Создаем пользователя и отключаем ему доступ через SSH, для обеспечения безопастности
root@mail:~# useradd -s /bin/bash -d /home/data/ -m -G sudo data
root@mail:~# passwd data
root@mail:~# groupadd no-ssh
root@mail:~# usermod -a -G no-ssh data
Добавляем в файл sshd_config
root@mail:~# mcedit /etc/ssh/sshd_config
DenyGroups no-ssh
root@mail:~# /etc/init.d/ssh restart
Создаем сценарии монтирования раздела, перезапуска прикладной части:
root@mail:~# mcedit /home/data/data_mount
#!/bin/bash
RES=`sudo /usr/local/veracrypt/verify`
if [[ ${RES} =~ OK ]]; then
echo "MOUNT OK"
sleep 2
read -p "Рестарт процессов будем делать? (y/n): " -n 1 item
case "$item" in
y|Y)
#
echo " ok"
sudo /etc/init.d/postfix restart
sleep 2
sudo /etc/init.d/nginx restart
sleep 2
sudo /etc/init.d/dovecot restart
sleep 2
sudo /etc/init.d/fail2ban restart
;;
n|N)
exit 0;
;;
*)
exit 0;
;;
esac
exit 1;
fi
if [[ ${RES} =~ Error ]]; then
echo "Mount data .... "
sudo /usr/local/veracrypt/mount
sleep 8
fi
RES=`sudo /usr/local/veracrypt/verify`
if [[ ${RES} =~ OK ]]; then
echo ""
echo "MOUNT OK !!! RUN MAIL SYSTEM"
echo ""
sleep 1
sudo /etc/init.d/postfix restart
sudo /etc/init.d/nginx restart
sudo /etc/init.d/dovecot restart
sudo /etc/init.d/fail2ban restart
echo "---"
fi
if [[ ${RES} =~ Error ]]; then
echo ""
echo "WARNING !!! MOUNT ERROR!!! MAIL SYSTEM NOT RUN!!! EXIT"
echo ""
fi
sleep 8
root@mail:~#
root@mail:~# mcedit /home/data/data_umount
#!/bin/bash
echo "Umount data .... "
RES=`sudo /usr/local/veracrypt/verify`
if [[ ${RES} =~ OK ]]; then
echo ""
echo "MOUNT !!! STOP MAIL SYSTEM"
sudo /etc/init.d/postfix stop
sudo /etc/init.d/nginx stop
sudo /etc/init.d/dovecot stop
echo ""
sleep 3
echo "---"
sudo /usr/local/veracrypt/unmount
sleep 3
fi
RES=`sudo /usr/local/veracrypt/verify`
if [[ ${RES} =~ Error ]]; then
echo ""
echo "TEST UMOUNT OK"
echo ""
fi
if [[ ${RES} =~ OK ]]; then
echo ""
echo "TEST UMOUNT ERROR !!!"
echo ""
fi
sleep 15
root@mail:~#
Добавим в sudoers, чтоб пользователь data мог после входа, монтировать раздел и перезапускать службы
root@mail:~# mcedit /etc/sudoers
data ALL=(ALL) NOPASSWD: /usr/local/veracrypt/mount
data ALL=(ALL) NOPASSWD: /usr/local/veracrypt/unmount
data ALL=(ALL) NOPASSWD: /usr/local/veracrypt/verify
data ALL=(ALL) NOPASSWD: /etc/init.d/postfix
data ALL=(ALL) NOPASSWD: /etc/init.d/nginx
data ALL=(ALL) NOPASSWD: /etc/init.d/dovecot
data ALL=(ALL) NOPASSWD: /etc/init.d/fail2ban
data ALL=(ALL) NOPASSWD: /usr/bin/veracrypt
На этом настройка veracrypt вроде окончана, для примера листинг монитирования:
root@mail:~# ./data_mount
Mount data ....
INFO - Mounting the encrypted partition /dev/sdb1 to the mounting point /data ...
Enter password for /dev/sdb1:
OK - The encrypted partition /dev/sdb1 has been mounted to the mounting point /data
MOUNT OK !!! RUN MAIL SYSTEM
Restarting postfix (via systemctl): postfix.service.
Restarting nginx (via systemctl): nginx.service.
Restarting dovecot (via systemctl): dovecot.service.
Restarting fail2ban (via systemctl): fail2ban.service.
Для продолжения нажмите любую клавишу...
[END]
Как в наше время без SSL, а если быть точнее без TLS 1.1 или 1.2
Сертификат будет один, на один адрес и им будут пользоватся несколько служб:
- nginx https через roundcube доступ к почте с ПК
- postfix smtps отправка почты с почтового агента на мобильном телефоне
- dovecot imaps доступ к ящику из почтового агента на мобильном телефоне
Установим certbot
root@mail:~# apt install certbot
root@mail:~# apt install python3-certbot-nginx nginx
root@mail:~#
root@mail:~# mcedit /etc/letsencrypt/cli.ini
max-log-backups = 0
authenticator = webroot
webroot-path = /var/www/html/
text = True
root@mail:~#
Настроим nginx для certbot
root@mail:~# mcedit /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
error_log /dev/null;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log off;
error_log /dev/null;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
root@mail:~#
root@mail:~# mcedit /etc/nginx/sites-available/certbot
server {
listen 80;
server_name mail.хххх.ru;
server_tokens off;
location / {
return 301 https://$host$request_uri;
}
location /.well-known {
root /var/www/html;
}
}
root@mail:~#
root@mail:~# ln -s /etc/nginx/sites-available/certbot /etc/nginx/sites-enabled/certbot
root@mail:~# /etc/init.d/nginx restart
Создать сертификат
root@mail:~# certbot certonly -d mail.хххх.ru
Отозвать сертификат
root@mail:~# certbot revoke --cert-path /etc/letsencrypt/live/mail.хххх.ru/cert.pem
Сертификат на 3 месяца, для обновления сертификата добавим в планировщик:
root@mail:~# crontab -e
0 1 * * * /etc/letsencrypt/certbot-renew.sh
root@mail:~#
Создадим исполняемый файл /etc/letsencrypt/certbot-renew.sh
root@mail:~# mcedit /etc/letsencrypt/certbot-renew.sh
#!/bin/sh
LOG="/var/log/letsencrypt.log"
echo "= START `date` ==================================" >> $LOG
/bin/certbot renew --renew-hook "service postfix reload" --renew-hook "service nginx reload" --renew-hook "service dovecot restart" >> $LOG
echo "= END =============================" >> $LOG
root@mail:~#
Должны появится три файла:
/etc/letsencrypt/live/mail.хххх.ru/fullchain.pem
/etc/letsencrypt/live/mail.хххх.ru/privkey.pem
/etc/letsencrypt/live/mail.хххх.ru/fullchain.pem
На этом думаю настройка certbot заканчивается
[END]
Нравится он мне в настройке, безопастный, несложный в настройке, гибкий
root@mail:~# apt-get install postfix mailutils postgrey postfix-policyd-spf-perl postfix-ldap postfix-pcre
root@mail:~# mkdir -p /data/etc/postfix/
root@mail:~# mv /etc/postfix/main.cf /data/etc/postfix/main.cf
root@mail:~# mv /etc/postfix/master.cf /data/etc/postfix/master.cf
root@mail:~# ln -s /data/etc/postfix/main.cf /etc/postfix/main.cf
root@mail:~# ln -s /data/etc/postfix/master.cf /etc/postfix/master.cf
root@mail:~# mcedit /data/etc/postfix/main.cf
compatibility_level = 2
soft_bounce = no
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix/sbin
data_directory = /var/lib/postfix
mail_owner = postfix
default_privs = nobody
myhostname = mail.хххх.ru
mydomain = хххх.ru
myorigin = $mydomain
inet_interfaces = all
inet_protocols = ipv4
mydestination = localhost.$mydomain, localhost, $mydomain, $myhostname
mynetworks = 127.0.0.0/8, 10.3.17.10, 10.3.17.11, 10.3.17.12, 10.3.17.15, 10.3.17.21
# 127.0.0.0 - Webmail
# 10.3.17.10 - IDRAC
# 10.3.17.11 - ESX
# 10.3.17.12 - PowerChute
# 10.3.17.15 - DC
# 10.3.17.21 - NAS
alias_maps = hash:/data/etc/postfix/cnf_aliases
local_recipient_maps = $alias_maps
unknown_local_recipient_reject_code = 550
mynetworks_style = host
# ФИЛЬТРЫ ЗАГОЛОВКОВ
# Переписываем заголовки если нужно отправителя
sender_canonical_maps = hash:/data/etc/postfix/cnf_sender_canonical
# Подчищает лишние заголовки в письмах убирая лишнее, действует на входящею почту
header_checks = pcre:/data/etc/postfix/cnf_header_checks.pcre
mime_header_checks = pcre:/data/etc/postfix/cnf_header_checks_mime.pcre
# Подчищает лишние заголовки в письмах убирая лишнее, действует на исходящею почту
smtp_header_checks = pcre:/data/etc/postfix/cnf_smtp_header_checks.pcre
# POSTSCREEN
# postscreen_watchdog_timeout = 10 (default: 10s)
# postscreen_cache_cleanup_interval (default: 12h)
# postscreen_cache_retention_time (default: 7d)
postscreen_cache_retention_time = 90d
# Список разрешенных / запрещенных IP клиентов
postscreen_access_list = permit_mynetworks, cidr:/data/etc/postfix/postscreen_access.cidr
# postscreen_reject_footer = Postscreen Test
# список dnsbl-серверов с весами
postscreen_dnsbl_sites = b.barracudacentral.org=127.0.0.[2..11]*3,
zen.spamhaus.org=127.0.0.[2..11]*3,
sbl.spamhaus.org=127.0.0.[2..11]*3,
sbl-xbl.spamhaus.org=127.0.0.[2..11]*2,
bl.spamcop.net=127.0.0.[2..11]*2,
zombie.dnsbl.sorbs.net=127.0.0.[2..11]*2,
dul.dnsbl.sorbs.net=127.0.0.[2..11]*2,
dnsbl.sorbs.net=127.0.0.[2..11]*1,
# порог веса, выше которого соединения будут блокироваться
postscreen_dnsbl_threshold = 3
# Размещение базы
postscreen_cache_map = btree:$data_directory/postscreen_cache
# настройка паузы перед началом SMTP-диалога
postscreen_greet_banner = Hello from $mydomain !
postscreen_greet_wait = 10s
# reject 550 and logging:
postscreen_greet_action = enforce
# Если вы не знаете, что произошло в глубоких тестах протокола, не включайте его
postscreen_pipelining_enable = no
postscreen_non_smtp_command_enable = no
postscreen_bare_newline_enable = no
# reject 550 and logging:
postscreen_pipelining_action = enforce
postscreen_forbidden_commands = CONNECT, GET, POST
# reject 550 and logging:
postscreen_non_smtp_command_action = enforce
# reject 550 and logging:
postscreen_bare_newline_action = enforce
# реакция postscreen на адрес, помещенный в черный список
postscreen_blacklist_action = enforce
# реакция postscreen на адрес, получивший вес больше порога
postscreen_dnsbl_action = enforce
# размер очередей
postscreen_post_queue_limit = 300
postscreen_pre_queue_limit = 300
# После паузы POSTSCREEN выдается честное приведствие
smtpd_banner = $myhostname ESMTP $mail_name
# RESTRICTIONS
# запретить использование адресов, отличных от определенных в документе RFC-821
strict_rfc821_envelopes = yes
# требовать обязательное использование команды HELO/EHLO
smtpd_helo_required = yes
# проводить все проверки а потом уже давать отлуп
smtpd_delay_reject = yes
#запретить использование команды VRFY
disable_vrfy_command = yes
smtpd_client_restrictions = permit_mynetworks,
permit_sasl_authenticated,
# белый / черный список доменов, добавляем руками
check_client_access hash:/data/etc/postfix/cnf_access_client,
# Регулярка с именами типа .dsl.
check_client_access pcre:/data/etc/postfix/cnf_access_client.pcre,
# запретить доступ клиентам, не зарегистрированным в DNS
reject_unknown_client_hostname,
reject_unknown_reverse_client_hostname,
# запретить некорректное использование команд конвейерной обработки
reject_unauth_pipelining,
smtpd_helo_restrictions = permit_mynetworks,
# Список сетей и адресов которые используют спамеры
check_helo_access hash:/data/etc/postfix/cnf_access_helo,
# имени хоста, в выданном клиентом приветствии, имеет некорректный синтаксис
reject_invalid_helo_hostname,
# имени хоста, в выданном клиентом приветствии, не является FQDN
reject_non_fqdn_helo_hostname,
# имени хоста, в выданном клиентом приветствии, не существует A или MX запись в DNS
reject_unknown_helo_hostname,
smtpd_sender_restrictions = permit_mynetworks,
# белый / черный список доменов, добавляем руками
check_sender_access hash:/data/etc/postfix/cnf_access_sender,
# спамеры представляющиеся private network
check_sender_mx_access hash:/data/etc/postfix/cnf_sender_mx_access,
# имени домена адреса отправителя не существует A или MX запись в DNS
reject_unknown_sender_domain,
# адрес отправителя сообщения имеет некорректный формат
reject_non_fqdn_sender,
smtpd_data_restrictions = permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_pipelining,
# Отказ в приеме сообщениям с пустым именем отправителя, нескольким получателям
reject_multi_recipient_bounce,
smtpd_recipient_restrictions = permit_mynetworks,
check_policy_service unix:private/policy-spf,
# если для имени домена адреса получателя не существует A или MX запись в DNS
reject_unknown_recipient_domain,
# адрес получателя сообщения имеет некорректный формат
reject_non_fqdn_recipient,
reject_multi_recipient_bounce,
# запретить Postfix быть открытым релеем
reject_unauth_destination,
reject_unlisted_recipient,
permit_sasl_authenticated,
# запретить Postfix быть открытым релеем
reject_unauth_destination,
permit_auth_destination,
check_policy_service inet:127.0.0.1:10023,
reject_invalid_hostname
policy-spf_time_limit = 3600s
# SMTP Authentication
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
# TLS
tls_high_cipherlist = ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA 384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
tls_disable_workarounds = CVE-2010-4180, LEGACY_SERVER_CONNECT, 0xFFFFFFFF
tls_ssl_options = NO_TICKET, NO_COMPRESSION, LEGACY_SERVER_CONNECT
tls_daemon_random_bytes = 32
tls_random_bytes = 32
# Генератор псевдослучайных чисел
tls_random_source = dev:/dev/urandom
tls_random_reseed_period = 3600s
tls_random_prng_update_period = 3600s
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.хххх.ru/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.хххх.ru/privkey.pem
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
# время, в течение которого данные в кэше TLS-сессии считаются актуальными
smtpd_tls_session_cache_timeout = 3600s
# сообщать клиентам о поддержке TLS
smtpd_use_tls = yes
smtpd_tls_security_level = may
smtpd_tls_ask_ccert = yes
# детальность сообщений о TLS-активности, выводимых в лог
smtpd_tls_loglevel = 1
smtpd_tls_protocols = TLSv1.1, TLSv1.2
smtpd_tls_ciphers = high
# запрашивать заголовки сообщений с информацией о версии протокола и алгоритме шифрования
smtpd_tls_received_header = yes
smtpd_tls_mandatory_ciphers = high
smtpd_tls_mandatory_exclude_ciphers = aNULL, DES, 3DES, MD5, DES+MD5, RC4
smtpd_tls_mandatory_protocols = TLSv1.1, TLSv1.2
# использовать TLS, если удаленный сервер сообщает о поддержке TLS
smtp_use_tls = yes
# использовать аутентификацию SMTP только для TLS-соединений
smtpd_tls_auth_only = yes
smtp_tls_loglevel = 1
smtp_tls_protocols = TLSv1.1, TLSv1.2
smtp_tls_ciphers = high
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_tls_mandatory_ciphers = high
smtp_tls_mandatory_protocols = TLSv1.1, TLSv1.2
# LIMITS
message_size_limit = 51200000
smtpd_soft_error_limit = 10
smtpd_hard_error_limit = 15
smtpd_error_sleep_time = 20
anvil_rate_time_unit = 60s
smtpd_client_connection_count_limit = 20
smtpd_client_connection_rate_limit = 30
smtpd_client_message_rate_limit = 30
# QUEUE
maximal_queue_lifetime = 1d
bounce_queue_lifetime = 1d
milter_default_action=accept
smtpd_milters = unix:opendkim/opendkim.sock
non_smtpd_milters = unix:opendkim/opendkim.sock
# VIRTUAL
# Postfix ищет получателя, отправителя, или группу в AD, если не находит reject
smtpd_sender_login_maps = ldap:/data/etc/postfix/ad_sender_login.cf
virtual_mailbox_maps = ldap:/data/etc/postfix/ad_local_recipients.cf
virtual_alias_maps = ldap:/data/etc/postfix/ad_mail_groups.cf, ldap:/data/etc/postfix/ad_virtual_alias_maps.cf
# DOVECOT
virtual_transport = lmtp:unix:private/dovecot
mailbox_transport = lmtp:unix:private/dovecot
Проверьте у себя если нужно подправьте
root@mail:~# mcedit /data/etc/postfix/master.cf
# для postscreen
# smtp inet n - y - - smtpd
smtp inet n - y - 1 postscreen
smtpd pass - - y - - smtpd
dnsblog unix - - y - 0 dnsblog
tlsproxy unix - - y - 0 tlsproxy
# 587 port authentication with dovecot
#submission inet n - y - - smtpd
# -o syslog_name=postfix/submission
# -o smtpd_tls_security_level=encrypt
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_sasl_type=dovecot
# -o smtpd_sasl_path=private/auth
# -o smtpd_sasl_security_options=noanonymous
# -o smtpd_sasl_local_domain=$myhostname
# -o smtpd_reject_unlisted_recipient=no
# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
# -o smtpd_recipient_restrictions=reject_unverified_recipient,reject_unknown_recipient_domain,reject_non_fqdn_recipient,reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_security_level=encrypt
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
-o smtpd_sasl_security_options=noanonymous
-o smtpd_sasl_local_domain=$myhostname
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=reject_unverified_recipient,reject_unknown_recipient_domain,reject_non_fqdn_recipient,reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
# 127.0.0.1:1025 inet n - y - - smtpd
# Perl SPF
policy-spf unix - n n - - spawn
user=nobody argv=/usr/sbin/postfix-policyd-spf-perl
root@mail:~#
smtpd_sender_login_maps = ldap:/data/etc/postfix/ad_sender_login.cf
Почтовая система при отправке письма, ищет в AD объект objectClass=user, после чего проверяет добавлен ли он в группу безопастности в AD CN=MailUserGroup,OU=Groups,OU=хххх,DC=хххх,DC=ru, далее ищет либо по доменному имени sAMAccountName=%u либо по почте mail=%s
Если находит пользователя по почте или по его доменному имени, письмо принимается к доставке, если не находит — футболит
root@mail:~# mcedit /data/etc/postfix/ad_sender_login.cf
version = 3
server_host = 10.3.17.15:389
search_base = DC=хххх,DC=ru
scope = sub
bind = yes
bind_dn = mail_user@хххх.ru
bind_pw = yyyyyyy
query_filter = (&(objectClass=user)(memberof=CN=MailUserGroup,OU=Groups,OU=хххх,DC=хххх,DC=ru)(|(sAMAccountName=%u)(mail=%s)))
result_attribute = mail
Тест по имени пользователя в AD
Например пользователь user1 ( почта у пользователя user_email1@хххх.ru )
root@mail:~# postmap -q user_email1@хххх.ru ldap:/data/etc/postfix/ad_sender_login.cf
user_email1@хххх.ru
root@mail:~#
Тест по электронке пользователя в AD
Например пользователь user1 ( почта у пользователя user_email1@хххх.ru )
root@mail:~# postmap -q user_email1@хххх.ru ldap:/data/etc/postfix/ad_sender_login.cf
user_email1@хххх.ru
root@mail:~#
virtual_mailbox_maps = ldap:/data/etc/postfix/ad_local_recipients.cf
Почтовая система производит поиск электронной почты в AD по атрибутам mail=%s или otherMailbox=%u@%d при этом учетка пользователя не должена быть заблокирована sAMAccountType=805306368
root@mail:~# mcedit /data/etc/postfix/ad_local_recipients.cf
version = 3
server_host = 10.3.17.15:389
search_base = DC=хххх,DC=ru
scope = sub
bind = yes
bind_dn = mail_user@хххх.ru
bind_pw = yyyyyyy
query_filter = (&(|(mail=%s)(otherMailbox=%u@%d))(sAMAccountType=805306368))
result_filter = %s
result_attribute = mail
special_result_attribute = member
root@mail:~#
Например пользователь user1 ( почта у пользователя user_email1@хххх.ru )
Если существует:
root@mail:~# postmap -q user_email1@хххх.ru ldap:/data/etc/postfix/ad_local_recipients.cf
user_email1@хххх.ru
root@mail:~#
Если не существует почты
root@mail:~# postmap -q user_email122@хххх.ru ldap:/data/etc/postfix/ad_local_recipients.cf
root@mail:~#
virtual_alias_maps = ldap:/data/etc/postfix/ad_mail_groups.cf, ldap:/data/etc/postfix/ad_virtual_alias_maps.cf
Почта которая должна доставлятся локально либо через наследования через AD группу ad_mail_groups.cf либо напрямую пользователю ad_virtual_alias_maps.cf
root@mail:~# mcedit /data/etc/postfix/ad_mail_groups.cf
version = 3
server_host = 10.3.17.15:389
search_base = DC=хххх,DC=ru
scope = sub
bind = yes
bind_dn = mail_user@хххх.ru
bind_pw = yyyyyyy
query_filter = (&(objectclass=group)(mail=%s))
result_filter = %s
leaf_result_attribute = mail
special_result_attribute = member
root@mail:~#
Например группа в AD veeam_backup в атрибутах добавлена почта veeam_backup@хххх.ru и добавлены два пользователя user1 и user2
user1 ( почта у пользователя user_email1@хххх.ru )
user2 ( почта у пользователя user_email2@хххх.ru )
root@mail:~# postmap -q veeam_backup@хххх.ru ldap:/data/etc/postfix/ad_mail_groups.cf
user_email1@хххх.ru,user_email2@хххх.ru
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/ad_virtual_alias_maps.cf
version = 3
server_host = 10.3.17.15:389
search_base = DC=хххх,DC=ru
scope = sub
bind = yes
bind_dn = mail_user@хххх.ru
bind_pw = yyyyyyy
query_filter = (&(objectClass=user)(memberof=CN=MailUserGroup,OU=Groups,OU=хххх,DC=хххх,DC=ru)(|(sAMAccountName=%u)(mail=%s)))
result_attribute = sAMAccountName
root@mail:~#
root@mail:~# postmap -q user1@хххх.ru ldap:/data/etc/postfix/ad_virtual_alias_maps.cf
user1
root@mail:~# postmap -q user_email1@хххх.ru ldap:/data/etc/postfix/ad_virtual_alias_maps.cf
user1
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_sender_canonical
# у некоторых исходящих сообщений будут переписываться заголовки отправителей
#bgl@gt.localdomain buh@rambler.ru
#drt@gt.localdomain dir@rambler.ru
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_header_checks.pcre
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.acm/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.ax/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.bat/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.bin/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.bpl/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.cat/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.chm/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.cmd/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.com/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.cpl/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.drv/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.exe/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.inf/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.ini/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.msc/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.nls/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.ocx/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.olb/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.pif/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.rom/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.scr/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.sys/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.tlb/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.vbs/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.vxd/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.669/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.aac/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.aif/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.aiff/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.amf/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.au/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.far/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.it/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.itz/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.kar/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.m3u/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.m4a/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mdz/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mid/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.midi/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.miz/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mmf/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mod/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mp1/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mp2/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mp4/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mtm/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.nsa/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.nst/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.ogg/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.okt/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.pls/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.ptm/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.rmi/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.s3m/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.s3z/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.snd/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.stm/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.stz/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.ult/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.voc/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.wma/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.xm/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.xmz/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.asf/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.m2v/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.mov/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.nsv/ REJECT Attached file(s) rejected
/^Content-(Type|Disposition):.*name[[:space:]]*=.*\.qt/ REJECT Attached file(s) rejected
/^Bel-Tracking: .*/ REJECT
/^Hel-Tracking: .*/ REJECT
/^Kel-Tracking: .*/ REJECT
/^BIC-Tracking: .*/ REJECT
/^Lid-Tracking: .*/ REJECT
/^X-Mailer: Avalanche/ REJECT
/^Subject:.*viagra/ DISCARD
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_header_checks_mime.pcre
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)acm)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)ax)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)bat)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)bin)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)bpl)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)cat)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)chm)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)cmd)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)com)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)cpl)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)drv)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)exe)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)inf)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)ini)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)msc)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)nls)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)ocx)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)olb)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)pif)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)rom)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)scr)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)sys)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)tlb)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)vbs)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)vxd)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)669)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)aac)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)aif)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)aiff)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)amf)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)au)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)far)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)it)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)itz)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)kar)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)m3u)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)m4a)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mdz)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mid)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)midi)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)miz)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mmf)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mod)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mp1)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mp2)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mp4)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mtm)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)nsa)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)nst)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)ogg)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)okt)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)pls)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)ptm)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)rmi)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)s3m)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)s3z)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)snd)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)stm)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)stz)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)ult)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)voc)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)vaw)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)wma)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)xm)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)xmz)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)asf)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)m2v)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)mov)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)nsv)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
/^\s*Content-(Disposition|Type).*name\s*=\s*"?(.+(\.|2E)qt)(\?=)?"?\s*$/ REJECT Attached file(s) rejected
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_smtp_header_checks.pcre
# Подчищает заголовки из письма, для того чтобы при отправки письма на ружу небыло видно многих других параметров
/^Delivered-To:/ IGNORE
/^Received:/ IGNORE
/^User-Agent:/ IGNORE
/^X-Mailer:/ IGNORE
/^X-Originating-IP:/ IGNORE
/^X-PHP-Originating-Script/ IGNORE
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/postscreen_access.cidr
# Postscreen, указывает список постоянно разрешенных / запреженных IP клиентов
# Очередность строк в CIDR-файле важна при обработке, т.к. поиск идет до первого совпадения.
# Просмотреть информацию об IP в черном/белом списке
# root@mail:~# postmap -q xx.xx.xx.xx cidr:/etc/postfix/postscreen_access.cidr
# root@mail:~# postmap -q 192.168.0.1 cidr:/etc/postfix/postscreen_access.cidr
# permit
# root@mail:~#
127.0.0.1 permit
# IPMI
10.3.17.10 permit
# ESX
10.3.17.11 permit
# Powerchute
10.3.17.12 permit
# DC
10.3.17.15 permit
# NAS
10.3.17.21 permit
192.168.0.0/16 reject
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_access_client
# Файл cnf_access_client предназначен для разрешения доступа с хостов, которые не зарегистрированы в DNS или странные настройки
# host OK - разрешить хосту отправлять нам письма
# host REJECT - запретить доступ
# например
mail.ru OK
2k.ru OK
awerynos.ru REJECT
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_access_client.pcre
# Файл cnf_access_client.pcre предназначен для блокировки Dial-Up, кабельных и xDSL сетей,
# которые не зарегистрировали нормальное имя PTR
# Если все же нужно принимать сообщения от одного из таких, добавьте хост или IP-адрес cnf_access_client
/[ax]dsl.*\..*\..*/i REJECT Your message looks like01 SPAM
/\.dsl.*\..*\..*/i REJECT Your message looks like02 SPAM
/cable.*\..*\..*/i REJECT Your message looks like03 SPAM
/client.*\..*\..*/i REJECT Your message looks like04 SPAM
/dhcp.*\..*\..*/i REJECT Your message looks like05 SPAM
/dial.*\..*\..*/i REJECT Your message looks like06 SPAM
/dialup.*\..*\..*/i REJECT Your message looks like07 SPAM
/dslam.*\..*\..*/i REJECT Your message looks like08 SPAM
/node.*\..*\..*/i REJECT Your message looks like09 SPAM
/pool.*\..*\..*/i REJECT Your message looks like10 SPAM
/ppp.*\..*\..*/i REJECT Your message looks like11 SPAM
/user.*\..*\..*/i REJECT Your message looks like12 SPAM
/user.*\..*\..*/i REJECT Your message looks like13 SPAM
/dynip.*\..*\..*/i REJECT Your message looks like14 SPAM
/kabel.*\..*\..*/i REJECT Your message looks like15 SPAM
/unknown.*\..*\..*/i REJECT Your message looks like16 SPAM
/host.*\..*\..*/i REJECT Your message looks like17 SPAM
/static.*\..*\..*/i REJECT Your message looks like18 SPAM
/dsl.*\..*\..*/i REJECT Your message looks like19 SPAM
/dynamic.*\..*\..*/i REJECT Your message looks like20 SPAM
/wimax.*\..*\..*/i REJECT Your message looks like20 SPAM
/gprs.*\..*\..*/i REJECT Your message looks like20 SPAM
/cdma.*\..*\..*/i REJECT Your message looks like20 SPAM
/wifi.*\..*\..*/i REJECT Your message looks like20 SPAM
/broadband.*\..*\..*/i REJECT Your message looks like20 SPAM
/vpn.*\..*\..*/i REJECT Your message looks like20 SPAM
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_access_helo
# Файл cnf_access_helo предназначен для блокировки абонентов, выдающих в приветствии имена, связанные с localhost, или одно из FQDN нашего сервера
# Это СПАМЕРЫ
127.0.0.1 REJECT Your message looks like21 SPAM
localhost REJECT Your message looks like22 SPAM
localhost.localdomain REJECT Your message looks like23 SPAM
localhost.domain.com REJECT Your message looks like24 SPAM
localhost.хххх.ru REJECT Your message looks like25 SPAM
хххх.ru REJECT Your message looks like26 SPAM
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_access_sender
# Файл cnf_access_sender предназначен для разрешения доступа отправителей, адреса или имена доменов которых попали в какой-либо блэклист или удаленный сервер запрещает проверку их существования. Для разрешения доступа нужного отправителя или всех отправителей нужного домена необходимо добавить правила вида
# <Почтовый адрес отправителя> OK
# @<Имя домена отправителя> OK
alana.khv@mail.ru OK
allafedorova@coperfilus.ru REJECT Your server configured incorrectly
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_sender_mx_access
# Файл sender_mx_access предназначен для отклонения соединения от клиентов, когда mx отправителя резолвится в адрес из частной сети
# Это СПАМЕРЫ
255.255.255.255 550 MX in world broadcast address
0.0.0.0/8 550 MX in block that refer to source hosts on "this" network
10.0.0.0/8 550 MX in RFC 1918 private network
127.0.0.0/8 550 MX in loopback network
169.254.0.0/16 550 MX in link local network
172.16.0.0/12 550 MX in RFC 1918 private network
192.0.2.0/24 550 MX in TEST-NET network
192.168.0.0/16 550 MX in RFC 1918 private network
198.18.0.0/15 550 MX in allocated for use in benchmark tests. RFC 2554
224.0.0.0/4 550 MX in class D multicast network
240.0.0.0/5 550 MX in class E reserved network
248.0.0.0/5 550 MX in reserved network
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/cnf_aliases
postmaster: root
mailer-daemon: postmaster
virusalert: root
administrator: root
daemon: root
lp: root
news: root
uucp: root
games: root
man: root
at: root
postgres: root
mdom: root
amanda: root
ftp: root
wwwrun: root
squid: root
msql: root
gnats: root
nobody: root
bin: root
newsadm: news
newsadmin: news
usenet: news
ftpadm: ftp
ftpadmin: ftp
ftp-adm: ftp
ftp-admin: ftp
hostmaster: root
mail: root
postman: postmaster
post_office: postmaster
abuse: postmaster
spam: postmaster
faxadm: root
faxmaster: root
webmaster: root
gnats-admin: root
mailman: root
mailman-owner: mailman
root: user1
root@mail:~#
root@mail:~# mcedit /data/etc/postfix/_postfix-db
#!/bin/sh
POSTMAP="/usr/sbin/postmap"
POSTALIAS="/usr/sbin/postalias"
CONF="/data/etc/postfix"
$POSTALIAS "$CONF/cnf_aliases"
$POSTMAP "$CONF/cnf_access_client"
$POSTMAP "$CONF/cnf_access_helo"
$POSTMAP "$CONF/cnf_access_sender"
$POSTMAP "$CONF/cnf_sender_mx_access"
$POSTMAP "$CONF/cnf_sender_canonical"
$POSTMAP "$CONF/cnf_transport"
/etc/init.d/postfix restart
root@mail:~#
Sender Policy Framework, SPF (инфраструктура политики отправителя) — расширение для протокола отправки электронной почты через SMTP. SPF определен в RFC 7208. Благодаря SPF можно проверить, не подделан ли домен отправителя.
В файле /data/etc/postfix/main.cf строка
check_policy_service unix:private/policy-spf
В файле mcedit /data/etc/postfix/master.cf
policy-spf unix - n n - - spawn
user=nobody argv=/usr/sbin/postfix-policyd-spf-perl
Нужно убедится что файл /usr/sbin/postfix-policyd-spf-perl запускается и никаких ошибок не выдает, например так:
root@mail:~# /usr/sbin/postfix-policyd-spf-perl
^C
root@mail:~#
этого достаточно чтоб spf заработал
Postgrey хорошая штука
В файле /data/etc/postfix/main.cf строка
check_policy_service inet:127.0.0.1:10023
Postgrey настраивается очень просто:
root@mail:~# mcedit /etc/default/postgrey
POSTGREY_OPTS="--inet=10023 --delay=250 --max-age=40 --auto-whitelist-clients=3"
POSTGREY_TEXT="Your customized rejection message here"
Смотрим со стороны открытых сетевых портов:
root@mail:~# /etc/init.d/postgrey restart
root@mail:~# netstat -lpn | grep 10023
tcp 0 0 127.0.0.1:10023 0.0.0.0:* LISTEN 1084/postgrey --pid
root@mail:~#
root@mail:~# /etc/init.d/postgrey status
● postgrey.service - LSB: Start/stop the postgrey daemon
Loaded: loaded (/etc/init.d/postgrey; generated)
Active: active (running) since Sun 2020-11-15 22:48:25 +10; 54min ago
Docs: man:systemd-sysv-generator(8)
Process: 919 ExecStart=/etc/init.d/postgrey start (code=exited, status=0/SUCCESS)
Tasks: 1 (limit: 9450)
Memory: 36.1M
CGroup: /system.slice/postgrey.service
└─1084 postgrey --pidfile=/var/run/postgrey.pid --daemonize --inet=10023 --delay=250 --max-age=40 --auto-whitelist-clients=3 --greylist-text=Your customized rejectio…
ноя 15 22:48:25 mail postgrey[986]: whitelisted: хх.хх.хх.хх/х
ноя 15 22:48:25 mail postgrey[986]: whitelisted: хх.хх.хх.хх/хх
ноя 15 22:48:25 mail postgrey[1084]: Process Backgrounded
ноя 15 22:48:25 mail postgrey[1084]: 2020/11/15-22:48:25 postgrey (type Net::Server::Multiplex) starting! pid(1084)
ноя 15 22:48:25 mail postgrey[1084]: Resolved [localhost]:10023 to [127.0.0.1]:10023, IPv4
ноя 15 22:48:25 mail postgrey[919]: ...done.
ноя 15 22:48:25 mail systemd[1]: Started LSB: Start/stop the postgrey daemon.
ноя 15 22:48:25 mail postgrey[1084]: Binding to TCP port 10023 on host 127.0.0.1 with IPv4
ноя 15 22:48:25 mail postgrey[1084]: Setting gid to "125 125"
ноя 15 22:48:25 mail postgrey[1084]: Setting uid to "118"
root@mail:~#
Ну хорошо, если служба работает и порт открыт, значит postgrey будет применятся при попытке отправить сообщения на почтовый сервер.
DomainKeys Identified Mail — метод E-mail аутентификации, разработанный для обнаружения подделывания сообщений, пересылаемых по email. Метод дает возможность получателю проверить, что письмо действительно было отправлено с заявленного домена. DKIM упрощает борьбу с поддельными адресами отправителей, которые часто используются в фишинговых письмах и в почтовом спаме.
root@mail:~#
root@mail:~# apt-get install opendkim opendkim-tools
root@mail:~#
root@mail:~# mcedit /etc/default/opendkim
RUNDIR=/var/spool/postfix/opendkim/
SOCKET=local:$RUNDIR/opendkim.sock
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=
root@mail:~#
root@mail:~# mkdir -p /var/spool/postfix/opendkim
root@mail:~# chown opendkim:opendkim /var/spool/postfix/opendkim
root@mail:~# chmod 777 /var/spool/postfix/opendkim
root@mail:~# mkdir -p /etc/opendkim
root@mail:~# chown opendkim:root /etc/opendkim
root@mail:~#
root@mail:~# mcedit /etc/opendkim.conf
Syslog yes
#Режим подписи и проверка подписей
Mode sv
#Указываем список ключей
KeyTable file:/etc/postfix/dkim/keytable
#Соответствие ключей и доменов
SigningTable file:/etc/postfix/dkim/signingtable
root@mail:~#
root@mail:~# /etc/init.d/opendkim restart
root@mail:~#
# создание ключей для домена хххх.ru
root@mail:~# opendkim-genkey -D /etc/opendkim -d хххх.ru -s mail
root@mail:~#
root@mail:~# mcedit /etc/opendkim/keytable
# Указывается информация о приватном ключе:
# имя_ключа домен:селектор:/путь/до/ключа
mail._domainkey.хххх.ru хххх.ru:mail:/etc/opendkim/mail.private
root@mail:~#
root@mail:~# mcedit /etc/opendkim/signingtable
# указываются домены, которые необходимо подписывать
# домен имя_ключа
хххх.ru mail._domainkey.хххх.ru
root@mail:~#
root@mail:~# mcedit /etc/opendkim/mail.private
-----BEGIN RSA PRIVATE KEY-----
MII ......
....... ySl
-----END RSA PRIVATE KEY-----
root@mail:~#
root@mail:~# chmod 500 /etc/opendkim
root@mail:~# chown -R opendkim:root /etc/opendkim/*
root@mail:~# chmod -R 400 /etc/opendkim/*
root@mail:~#
#Получим публичный ключ из секретного
root@mail:~# openssl rsa -pubout -in /etc/opendkim/mail.private
writing RSA key
-----BEGIN PUBLIC KEY-----
M.......
.....AB
-----END PUBLIC KEY-----
root@mail:~#
root@mail:~#
Необходимо создать TXT-записи следующего вида в DNS зоне:
mail._domainkey.хххх.ru TXT v=DKIM1; k=rsa; t=s; p=<публичный ключ в одну строку>
root@mail:~# adduser postfix opendkim
root@mail:~# /etc/init.d/opendkim restart
root@mail:~# /etc/init.d/postfix restart
Проверка работоспособности
root@mail:~# opendkim-testkey -d хххх.ru -s mail -vvv -k /etc/opendkim/mail.private
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: key loaded from /etc/opendkim/mail.private
opendkim-testkey: checking key 'mail._domainkey.хххх.ru'
opendkim-testkey: key not secure
opendkim-testkey: key OK
Добавляем в /etc/postfix/main.cf если не добавлен:
root@mail:~# mcedit /etc/postfix/main.cf
milter_default_action=accept
smtpd_milters = unix:opendkim/opendkim.sock
non_smtpd_milters = unix:opendkim/opendkim.sock
root@mail:~#
перезапускаем postfix и opendkim:
root@mail:~# /etc/init.d/opendkim restart
root@mail:~# /etc/init.d/postfix restart
root@mail:~#
root@mail:~# echo "Test mapping" | sendmail user_email1@хххх.ru
В конфигурации main.cf, есть упоминания об
root@mail:~# mcedit /etc/postfix/main.cf
virtual_transport = lmtp:unix:private/dovecot
mailbox_transport = lmtp:unix:private/dovecot
root@mail:~#
Это есть ничто иное как передача полученных и проверенных сообщений в dovecot для их сохранение в mailbox пользователя
[END]
Dovecot мы будем использовать для хранения почты пользователей и доступа по IMAPS
Конфигурации в каталоге /data/etc/dovecot/
Почтовые ящики пользователей в каталоге /data/mail/
Логи лежат тут /data/var/log/
root@mail:~# apt-get install postfix dovecot-core dovecot-imapd dovecot-lmtpd
root@mail:~# groupadd -g 5000 vmail
root@mail:~# useradd -u 5000 -g vmail -s /usr/sbin/nologin -d /data/mail -m vmail
root@mail:~# usermod -s /usr/sbin/nologin vmail
root@mail:~# mkdir /data/mail
root@mail:~# chown vmail:vmail /data/mail
root@mail:~# chown -R vmail:vmail /data/mail/
Добавим
root@mail:~# mcedit /etc/dovecot/dovecot.conf
....
!include /data/etc/dovecot/*.conf
root@mail:~#
Скопом привожу все конфигурации, для удобства
root@mail:~# cat /data/etc/dovecot/10-logging.conf
log_path = /data/var/log/dovecot.log
root@mail:~#
root@mail:~# cat /data/etc/dovecot/10-master.conf
service imap-login {
inet_listener imap {
port =143
}
inet_listener imaps {
port = 993
ssl = yes
}
}
service pop3-login {
inet_listener pop3 {
port = 0
}
inet_listener pop3s {
port = 0
}
}
service submission-login {
inet_listener submission {
port = 0
}
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot {
mode = 0600
user = postfix
group = postfix
}
}
service auth {
unix_listener /var/spool/postfix/private/auth {
group = postfix
mode = 0660
user = postfix
}
user = root
}
root@mail:~#
root@mail:~# cat /data/etc/dovecot/11-mail.conf
mail_location = maildir:/data/mail/%u
mail_uid = vmail
mail_gid = vmail
root@mail:~#
root@mail:~# cat /data/etc/dovecot/11-ssl.conf
disable_plaintext_auth = yes
auth_cache_size = 0
auth_cache_ttl = 1 hour
auth_cache_negative_ttl = 1 hour
ssl = required
ssl_cert = </etc/letsencrypt/live/mail.хххх.ru/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.хххх.ru/privkey.pem
ssl_client_ca_dir = /etc/ssl/certs
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes
ssl_cipher_list = ALL:HIGH:!SSLv2:!MEDIUM:!LOW:!EXP:!RC4:!MD5:!aNULL:!RSA:!CAMELLIA:!DH:!kRSA:!SRP:!kDHd:!DSS:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!SHA1:!SHA256:!SHA384:!LOW@STRENGTH
root@mail:~#
root@mail:~# cat /data/etc/dovecot/15-lda.conf
protocol lda {
# Space separated list of plugins to load (default is global mail_plugins).
mail_plugins = $mail_plugins sieve
}
root@mail:~#
root@mail:~# cat /data/etc/dovecot/15-mailboxes.conf
namespace inbox {
mailbox Drafts {
special_use = \Drafts
}
mailbox Trash {
special_use = \Trash
}
mailbox Sent {
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
mailbox "Archive" {
special_use = \Archive
}
}
root@mail:~#
root@mail:~# cat /data/etc/dovecot/20-imap.conf
protocol imap {
mail_plugins = $mail_plugins imap_sieve
}
namespace inbox {
inbox = yes
mailbox Drafts {
auto = subscribe
special_use = \Drafts
}
mailbox Trash {
auto = subscribe
special_use = \Trash
}
mailbox Sent {
auto = subscribe
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
mailbox "Archive" {
special_use = \Archive
}
}
root@mail:~#
root@mail:~# cat /data/etc/dovecot/20-managesieve.conf
protocols = $protocols sieve
service managesieve-login {
inet_listener sieve {
port = 4190
}
}
protocol sieve {
plugin {
sieve = /data/mail/_sieve2/%u/sieve
sieve_storage = /data/mail/_sieve2/%u/sieve_storage
}
}
root@mail:~# cat /data/etc/dovecot/91-plugin.conf
plugin {
mail_home = /data/mail/%u
home=/data/mail/%u
}
root@mail:~#
root@mail:~# cat /data/etc/dovecot/auth-ldap.conf
# Добавляемое к имени пользователю имя домена по умочанию (если пользователь введет имя user, то для dovecot он будет user@test.alt)
auth_default_realm = хххх.ru
passdb {
driver = ldap
args = /data/etc/dovecot/auth-ldap.conf.ext
}
userdb {
driver = ldap
args = /data/etc/dovecot/auth-ldap.conf.ext
}
root@mail:~#
root@mail:~# cat /data/etc/dovecot/auth-ldap.conf.ext
# %u - username user
# %n - username user@domain -> user
# %d - domain user@domain -> domain
# %h - home directory
hosts = 10.3.17.15
ldap_version = 3
dn = CN=mail_user,CN=Users,DC=хххх,DC=ru
dnpass = yyyyyyy
base = DC=хххх,DC=ru
scope = subtree
tls = no
auth_bind = yes
debug_level = 0
default_pass_scheme = CRYPT
pass_filter = (&(ObjectClass=user)(memberof=CN=MailUserGroup,OU=Groups,OU=хххх,DC=хххх,DC=ru)(|(mail=%u)(sAMAccountName=%n)))
pass_attrs = =uid=%{ldap:sAMAccountName},userPassword=password
user_filter = (&(ObjectClass=user)(memberof=CN=MailUserGroup,OU=Groups,OU=хххх,DC=хххх,DC=ru)(|(mail=%u)(sAMAccountName=%n)))
user_attrs = =user=%{ldap:sAMAccountName}
root@mail:~# ls -l /data/
drwxrwx--- 10 vmail vmail 4096 окт 23 16:52 mail
root@mail:~#
root@mail:~# ls -l /data/mail
drwx------ 9 vmail vmail 4096 ноя 16 08:31 user1
drwx------ 8 vmail vmail 4096 ноя 16 15:29 user2
drwx------ 5 vmail vmail 4096 ноя 16 12:26 user3
root@mail:~#
Тестирование порта IMAPS
root@mail:~# openssl s_client -crlf -connect 127.0.0.1:993
CONNECTED(00000003)
Can't use SSL_get_servername
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = mail.хххх.ru
verify return:1
Certificate chain
0 s:CN = mail.хххх.ru
i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
i:O = Digital Signature Trust Co., CN = DST Root CA X3
Server certificate
-----BEGIN CERTIFICATE-----
MII....A==
-----END CERTIFICATE-----
subject=CN = mail.хххх.ru
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
SSL handshake has read 3113 bytes and written 363 bytes
Verification: OK
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID:
Session-ID-ctx:
Resumption PSK:
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
.......
Start Time: ........
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID:
Session-ID-ctx:
Resumption PSK:
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
......
Start Time: ......
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
Max Early Data: 0
---
read R BLOCK
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN] Dovecot (Ubuntu) ready.
Просмотр активных подключений
root@mail:~# doveadm who
username # proto (pids) (ips)
user1 1 imap (6951) (хх.хх.хх.хх)
root@mail:~#
Тест подключения
root@mail:~# doveadm auth test -x service=imap -x rip=10.3.18.14 user_email1@хххх.ru
Password:
passdb: user_email1@хххх.ru auth succeeded
extra fields:
user=user_email1@хххх.ru
uid=user1
root@mail:~#
root@mail:~#
[END]
Очень удобно
Можно разрешить или запретить доступ к примеру к SSH на основании привязке к стране
root@mail:~# apt-get install xtables-addons-common
root@mail:~# apt-get install libtext-csv-xs-perl
root@mail:~# mkdir /usr/share/xt_geoip
Регистрируемся на https://www.maxmind.com
Узнаем или создаем License key
https://www.maxmind.com/en/accounts/420372/license-key
Скачиваем https://github.com/mschmitt/GeoLite2xtables
Архив /data/_install/geoip/GeoLite2xtables.zip разорхивируем
Вносим свой ключ в файл
/data/_install/geoip/GeoLite2xtables-master/geolite2.license.example
Переименовываем файл geolite2.license.example в geolite2.license
Выполняем:
root@mail:~# /data/_install/geoip/GeoLite2xtables-master/00_download_geolite2
Результат, два файла:
/tmp/GeoLite2-Country-Blocks-IPv4.csv
/tmp/GeoLite2-Country-Blocks-IPv6.csv
Выполняем:
root@mail:~# /data/_install/geoip/GeoLite2xtables-master/10_download_countryinfo
Результат, файл:
/tmp/CountryInfo.txt
Выполняем
root@mail:~# cat /tmp/GeoLite2-Country-Blocks-IPv{4,6}.csv | /data/_install/geoip/GeoLite2xtables-master/20_convert_geolite2 /tmp/CountryInfo.txt > /usr/share/xt_geoip/dbip-country-lite.csv
Результат, файл:
/usr/share/xt_geoip/dbip-country-lite.csv
root@mail:~# head /usr/share/xt_geoip/dbip-country-lite.csv
"1.0.0.0","1.0.0.255","16777216","16777471","AU","Australia"
"1.0.1.0","1.0.1.255","16777472","16777727","CN","China"
"1.0.2.0","1.0.3.255","16777728","16778239","CN","China"
"1.0.4.0","1.0.7.255","16778240","16779263","AU","Australia"
"1.0.8.0","1.0.15.255","16779264","16781311","CN","China"
"1.0.16.0","1.0.31.255","16781312","16785407","JP","Japan"
"1.0.32.0","1.0.63.255","16785408","16793599","CN","China"
"1.0.64.0","1.0.127.255","16793600","16809983","JP","Japan"
"1.0.128.0","1.0.255.255","16809984","16842751","TH","Thailand"
"1.1.0.0","1.1.0.255","16842752","16843007","CN","China"
root@mail:~#
root@mail:~# /usr/lib/xtables-addons/xt_geoip_build -D /usr/share/xt_geoip/ -i /usr/share/xt_geoip/dbip-country-lite.csv
root@mail:~#
root@mail:~# ls /usr/share/xt_geoip/
A1.iv4 AT.iv4 BJ.iv4 CA.iv4 CU.iv4 EC.iv6 FR.iv6 GQ.iv6 ID.iv6 JP.iv6 LB.iv6 ME.iv6 MT.iv6 NL.iv6 PL.iv6 RW.iv6 SN.iv6 TH.iv6 UG.iv6 WS.iv6
A1.iv6 AT.iv6 BJ.iv6 CA.iv6 CU.iv6 EE.iv4 GA.iv4 GR.iv4 IE.iv4 KE.iv4 LC.iv4 MF.iv4 MU.iv4 NO.iv4 PM.iv4 SA.iv4 SO.iv4 TJ.iv4 UM.iv4 YE.iv4
A2.iv4 AU.iv4 BL.iv4 CC.iv4 CV.iv4 EE.iv6 GA.iv6 GR.iv6 IE.iv6 KE.iv6 LC.iv6 MF.iv6 MU.iv6 NO.iv6 PM.iv6 SA.iv6 SO.iv6 TJ.iv6 UM.iv6 YE.iv6
A2.iv6 AU.iv6 BL.iv6 CC.iv6 CV.iv6 EG.iv4 GB.iv4 GS.iv4 IL.iv4 KG.iv4 LI.iv4 MG.iv4
..........
..........
root@mail:~#
Правило должно заработать:
root@mail:~# iptables -I INPUT ! -i lo -m geoip ! --src-cc UA,RU -j DROP
root@mail:~# iptables -I INPUT ! -i lo -m geoip --src-cc CN -j DROP
Если что то не срослось, значит что то сделано не верно при конвертации, у меня тоже не спервого раза получилось, читал гуглил, работал напильником и наждачкой:
root@mail:~# iptables -I INPUT ! -i lo -m geoip ! --src-cc UA,RU -j DROP
Could not open /usr/share/xt_geoip/LE/UA.iv4: No such file or directory
iptables v1.4.21: Could not read geoip database
root@mail:~#
root@mail:~# iptables -I INPUT ! -i lo -m geoip --src-cc CN -j DROP
Could not open /usr/share/xt_geoip/CN.iv4: No such file or directory
iptables v1.8.4 (legacy): Could not read geoip database
root@mail:~#
root@mail:~#
root@mail:~# iptables -I INPUT ! -i lo -m geoip ! --src-cc UA,RU -j DROP
iptables: No chain/target/match by that name.
Вероятно:
root@mail:~# modprobe xt_geoip
root@mail:~#
[END]
Конфиг nginx.conf приводил ранее, пришло время привести конфигурацию roundcube
root@mail:~# cat /etc/nginx/sites-available/roundcube
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
# в зоне one размером 10 мегабайт, и средняя скорость обработки запросов для этой зоны не может превышать 10 запроса в секунду
server {
listen 443 ssl;
server_name mail.хххх.ru;
access_log /data/var/log/nginx/roundcube.access.log;
error_log /data/var/log/nginx/roundcube.error.log warn;
root /data/var/www/roundcube;
index index.php;
ssl_session_timeout 24h;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2;
ssl_session_cache shared:SSL:20m;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000;";
ssl_certificate /etc/letsencrypt/live/mail.хххх.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mail.хххх.ru/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/mail.хххх.ru/fullchain.pem;
add_header Strict-Transport-Security "max-age=31536000";
# Убрать версию
server_tokens off;
# Перечитать все настройки
satisfy any;
# Разрешаем без пароля заходить с определенных сетей, по остальным запрос авторизации
allow 10.3.17.0/24;
allow 10.3.16.0/24;
allow 10.3.1.0/24;
auth_basic "webmail";
auth_basic_user_file /data/etc/nginx/webmail_htpasswd;
# create passwd !
# htpasswd -c /data/etc/nginx/webmail_htpasswd web_user_mail ( ххххххх )
# re-create passwd !
# htpasswd /data/etc/nginx/webmail_htpasswd web_user_mail
gzip on;
gzip_static on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_proxied any;
gzip_vary on;
gzip_types text/plain text/xml application/xml application/x-javascript text/javascript text/css text/json font/ttf font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_disable "msie6";
location / {
try_files $uri $uri/ /index.php?q=$uri&$args;
}
location ~ ^/(README.md|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ {
deny all;
}
location ~ ^/(config|temp|logs)/ {
deny all;
}
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|pdf|ppt|txt|bmp|rtf|js)$ {
root /data/var/www/roundcube;
access_log off; # не пишем логи
expires 3d; # кешируем у клиента на 3 дня
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Метод отправки данных sendfile более эффективен, чем стандартный метод read+write
sendfile on;
# Определяет максимальное количество файлов, информация о которых будет в кеше
open_file_cache max=200000 inactive=20s;
# Определяет через какое время информация будет удалена из кеша
open_file_cache_valid 30s;
# Будет кешировать информацию о тех файлах, которые были использованы хотя бы 2 раза
open_file_cache_min_uses 2;
# Будет кешировать информацию об отсутствующих файлах
open_file_cache_errors on;
# Будет ждать 70 секунд перед закрытием keepalive соединения.
keepalive_timeout 70;
# Максимальное количество keepalive запросов от одного клиента.
keepalive_requests 50;
# Если клиент перестал читать отвечать, Nginx будет сбрасывать соединение с ним
reset_timedout_connection on;
# Будет ждать 10 секунд тело запроса от клиента, после чего сбросит соединение.
client_body_timeout 10;
# Если клиент прекратит чтение ответа, Nginx подождет 2 секунды и сбросит соединение
send_timeout 2;
# В этом случае сервер не будет принимать запросы размером более 50Мб
client_max_body_size 50m;
client_body_buffer_size 50m;
}
root@mail:~#
Конструкция вида:
allow…
auth_basic "web";
auth_basic_user_file /data/etc/nginx/webmail_htpasswd;
allow… обеспечивает доступ к web roundcube внутри компании, конечно почтовая авторизация внутри roundcube никуда не делась и это нормально.
Если обратится с публичной сети интернет на web сервер, то, до стандартного приглашения от roundcube, nginx выдаст вначале запрос с требованием auth_basic авторизации.
Если тот, кто пришел знает логин/пароль от auth_basic, он его вводит и nginx открывает доступ к roundcube, и почтовой авторизации.
Такая двойная авторизация позволяет уберечься от атак на возможные уязвимости в roundcube с интернета и задержать недоброжилателей еще на стадии auth_basic.
В файле /data/etc/nginx/webmail_htpasswd хранится одна пара логина и пароля, и наверно, как Вы дагадались эту пару знают все работающие коллеги кто обратился с вопросом, а как войти в почту со своего ноутбука или домашнего ПК. Небезопастно что один логин/пароль для auth_basic, возможно, но в тоже время авторизация auth_basic это простая учетка и простой пароль из нескольких цифр, и больше формальность, которая все же эффективно работает, а за динамикой неверного ввода логина и пароля следит fail2ban и блокирует нерадивых хацкеров кто пытается подобрать.
В тоже время никто не мешает разослать заранее всем работающим коллегам новый пароль от этой учетки и затем сменить одним движением, в период скажем раз в полгода или год.
Настроить roundcube не сложно, описывать тут как скачать архив, разложить его и настроить один конфиг не вижу смысла.
А как же пользователи заходят с телефона, просто, на Play Маркете понравился почтовый клиент TypeApp, довольно просто настроить, нет назойливой рекламы, нет неожиданностей вроде отправки почты или логинов/паролей на левые узлы.
TypeApp для входящей почты:
Почта user_email1@хххх.ru
Пароль <пароль пользователя в AD>
Сервер IMAP mail.хххх.ru
Безопастность SSL/TLS (проверка сертификата)
Аутентификация PLAIN
Порт 993
TypeApp для исходящей почты:
SMTP сервер mail.хххх.ru
Безопастность SSL/TLS (проверка сертификата)
Порт 465
Требуется авторизация — крыжик стоит
Аутентификация AUTOMATIC
Почта user_email1@хххх.ru
Пароль <пароль пользователя в AD>
Пользуемся TypeApp уже полгода вроде нормально
[END]
root@mail:~# mcedit
root@mail:~#
root@mail:~# mcedit /etc/fail2ban/jail.conf
[INCLUDES]
before = paths-debian.conf
[DEFAULT]
bantime.increment = true
ignoreip = 127.0.0.1/8 ::1
ignorecommand =
bantime = 10m
findtime = 10m
maxretry = 5
maxmatches = %(maxretry)s
backend = auto
usedns = warn
logencoding = auto
enabled = false
mode = normal
filter = %(__name__)s[mode=%(mode)s]
# ACTIONS
destemail = root@localhost
sendername = Fail2ban
sender = root@localhost
mta = mail
protocol = tcp
chain = <known/chain>
port = 0:65535
fail2ban_agent = Fail2Ban/%(fail2ban_version)s
banaction = iptables-multiport
banaction_allports = iptables-allports
action = %(action_mw)s
# JAILS
# в течении 1 часа
# findtime = 3600
# 15 неудачных попыток
# maxretry = 15
# банить IP на час
# bantime = 3600
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
findtime = 3600
maxretry = 3
# 3600 * 24 * 10 = 864 000 (10 дней)
bantime = 864000
[nginx-http-auth]
enabled = true
port = http,https
logpath = %(nginx_error_log)s
findtime = 3600
maxretry = 15
bantime = 3600
[nginx-limit-req]
port = http,https
logpath = %(nginx_error_log)s
[nginx-botsearch]
port = http,https
logpath = %(nginx_error_log)s
maxretry = 2
[postfix]
enabled = true
mode = more
port = smtp,465,submission
logpath = %(postfix_log)s
backend = %(postfix_backend)s
findtime = 3600
maxretry = 15
bantime = 3600
[postfix-sasl]
enabled = true
filter = postfix[mode=auth]
port = smtp,465,submission,imap,imaps,pop3,pop3s
logpath = %(postfix_log)s
backend = %(postfix_backend)s
findtime = 3600
maxretry = 15
bantime = 3600
[postfix-rbl]
enabled = true
filter = postfix[mode=rbl]
port = smtp,465,submission
logpath = %(postfix_log)s
backend = %(postfix_backend)s
findtime = 3600
maxretry = 15
bantime = 3600
[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps,submission,465,sieve
logpath = %(dovecot_log)s
backend = %(dovecod_backend)s
findtime = 3600
maxretry = 15
bantime = 3600
root@mail:~#
root@mail:~# mcedit /etc/fail2ban/paths-common.conf
[DEFAULT]
nginx_error_log = /data/var/log/nginx/*error.log
nginx_access_log = /data/var/log/nginx/*access.log
postfix_log = /data/var/log/mail.log
postfix_backend = %(default_backend)s
dovecot_log = /data/var/log/dovecot.log
dovecod_backend = %(default_backend)s
root@mail:~#
Testing Fail2ban
root@mail:~#systemctl restart fail2ban
root@mail:~#
root@mail:~# fail2ban-client status
Status
|- Number of jail: 6
`- Jail list: dovecot, nginx-http-auth, postfix, postfix-rbl, postfix-sasl, sshd
root@mail:~#
root@mail:~# fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 1
| |- Total failed: 24
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 68
|- Total banned: 72
`- Banned IP list: 106.13.65.207 109.111.164.63 ..... 92.53.103.158
root@mail:~#
root@mail:~# fail2ban-client status dovecot
Status for the jail: dovecot
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /data/var/log/dovecot.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
root@mail:~#
root@mail:~# fail2ban-client status postfix
Status for the jail: postfix
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /data/var/log/mail.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
root@mail:~#
root@mail:~# fail2ban-client status nginx-http-auth
Status for the jail: nginx-http-auth
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /data/var/log/nginx/roundcube.error.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
root@mail:~#
root@mail:~# fail2ban-client status nginx-http-auth
Status for the jail: nginx-http-auth
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /data/var/log/nginx/roundcube.error.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: хх.хх.хх.хх
root@mail:~#
Вывести IP из бана
root@mail:~# fail2ban-client set nginx-http-auth unbanip хх.хх.хх.хх
1
root@mail:~#
[END]
Не плохой получился инстанс
Работает шустро, перебор паролей к службам dovecot, nginx, postfix и ssh — fail2ban вовремя присекает + GeoIP iptables хорошее подспорье блокирует все левых желающих халявки