Postfix + Dovecot + AD

prepare ssl certs
certbot certonly -a webroot --webroot-path=/usr/share/nginx/html/ -d mail.your.domain
fetch latest rainloop
wget https://www.rainloop.net/repository/webmail/rainloop-community-latest.zip
/etc/nginx/nginx.conf
worker_processes auto;
events {
    use                 epoll;
    worker_connections  1024;
    multi_accept        on;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log                  off;
    error_log                   off;
    sendfile                    on;
    sendfile_max_chunk          128k;
    send_timeout                3;
    tcp_nopush                  on;
    tcp_nodelay                 on;
    reset_timedout_connection   on;
    client_header_timeout       3;
    client_body_timeout         5;
    client_header_buffer_size   2k;
    client_body_buffer_size     256k;
    client_max_body_size        100m;
    keepalive_timeout           65;
    ssl_session_cache           shared:SSL:10m;
    ssl_session_timeout         10m;
    gzip                        on;
    gzip_disable                "msie6";
    gzip_types                  text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
    open_file_cache max=200000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
    include /etc/nginx/conf.d/*.conf;
}
/etc/nginx/conf.d/default.conf
server {
    listen 80;
    server_name _;
    keepalive_timeout 0;
    location = /status {
        allow 127.0.0.1;
        deny all;
        stub_status on;
    }
    location ~ ^/(fpm-status|fpm-ping)$ {
        allow 127.0.0.1;
        deny all;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
        include fastcgi_params;
    }
    location / {
        return 301 https://mail.your.domain;
    }
    access_log off;
    error_log off;
}
server {
    listen 80;
    listen 443 ssl http2;
    server_name mail.your.domain;
    ssl_certificate     /etc/letsencrypt/live/mail.your.domai/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mail.your.domai/privkey.pem;
    access_log          /var/log/nginx/rainloop-access.log main;
    error_log           /var/log/nginx/rainloop-error.log;
    root /data/web;
    index index.php index.html index.htm;
    client_max_body_size 0;
    location /.well-known/ {
        alias /usr/share/nginx/html/.well-known/;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ {
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(.*)$;
        fastcgi_keep_conn on;
        fastcgi_pass  127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
    }
    location ~ /\.ht {
        deny all;
    }
    location ^~ /data {
      deny all;
    }
    location ~* \.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        expires max;
    }
    location = /robots.txt {
        access_log off;
        log_not_found off;
    }
    location = /favicon.ico {
        access_log off;
        log_not_found off;
    }
    error_page 404 /index.php;
}
/etc/php-fpm.d/www.conf
[www]
user = nobody
group = nobody
listen = 127.0.0.1:9000
listen.owner = nobody
listen.group = nobody
listen.mode = 0664
listen.acl_users = apache,nginx
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 20
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 5
pm.process_idle_timeout = 3s;
pm.max_requests = 500

pm.status_path=/fpm-status
ping.path=/fpm-ping

;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
slowlog = /var/log/php-fpm/www-slow.log
;request_slowlog_timeout = 0
request_terminate_timeout = 0
;rlimit_files = 1024
;rlimit_core = 0

php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M

php_value[session.save_handler] = files
php_value[session.save_path]    = /var/lib/php/session
php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache
;php_value[opcache.file_cache]  = /var/lib/php/opcache
/etc/postfix/main.cf
inet_interfaces = all

smtpd_banner = $myhostname ESMTP ready
biff = no
append_dot_mydomain = no

smtpd_tls_cert_file=/etc/letsencrypt/live/mail.your.domain/cert.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.your.domain/privkey.pem
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${queue_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${queue_directory}/smtp_scache

myhostname = mail-host-1
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = your.domain
mydomain = your.domain
mydestination = $myhostname, localhost
relayhost =
mynetworks = 127.0.0.0/8, 192.168.0.0/16
recipient_delimiter = +
unknown_local_recipient_reject_code = 550
debug_peer_level = 2
debug_peer_list = 127.0.0.1, mail.your.domain
message_size_limit = 50000000

# ---------------------- VIRTUAL DOMAINS START ----------------------
virtual_transport = dovecot
virtual_mailbox_base            = /data/mail
virtual_mailbox_domains         = $mydomain

# Указываем на конфиг, в котором описано как получить данные о почтовом яшике
virtual_mailbox_maps            = ldap:$config_directory/virtual_mbox.conf

# Указываем на конфиги, в которых описан способ получения данных о псевдонимах почтовых ящиков и о списках рассылки
virtual_alias_maps              = ldap:$config_directory/virtual_alias.conf, ldap:$config_directory/virtual_mlist.conf
virtual_minimum_uid             = 5

# Идентификатор пользователя vmail
virtual_uid_maps                = static:mail
virtual_gid_maps                = static:mail
virtual_transport               = dovecot
#dovecot_destination_recipient_limit = 1
# ---------------------- VIRTUAL DOMAINS END ----------------------
smtpd_recipient_restrictions = check_recipient_access hash:$config_directory/recipient_access, reject
smtpd_sender_restrictions = check_sender_access hash:$config_directory/recipient_access, reject
/etc/postfix/update.rcpt.database.sh
#!/bin/bash

echo "mail.your.domain OK" > /etc/postfix/recipient_access
postmap /etc/postfix/recipient_access
systemctl restart dovecot
systemctl restart postfix
/etc/postfix/master.cf
# append to end:
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=mail:mail argv=/usr/libexec/dovecot/dovecot-lda -d ${recipient}
/etc/postfix/virtual_alias.conf
version = 3
server_host = ldap://dc.your.domain
search_base = OU=Пользователи,DC=your,DC=domain
timeout = 3

query_filter = (mail=%s)

result_filter = %s
result_attribute = userPrincipalName
special_result_attribute = member
scope = sub
bind = yes
bind_dn = [email protected]
bind_pw = [email protected]
/etc/postfix/virtual_mbox.conf
server_host = ldap://dc.your.domain
search_base = OU=Пользователи,DC=your,DC=domain
version = 3

# По какому полю идентифицируем пользователей
query_filter = (&(objectclass=person)(userPrincipalName=%s))
result_attribute = userPrincipalName

result_format = %u/
bind_dn = [email protected]
bind_pw = [email protected]
cache = no
/etc/postfix/virtual_mlist.conf
#debuglevel = 0
version = 3
server_host = ldap://dc.your.domain
search_base = OU=Пользователи,DC=your,DC=domain
timeout = 3

query_filter = (&(mail=%s)(sAMAccountType=268435457))

result_filter = %s
result_attribute = userPrincipalName
special_result_attribute = member
scope = sub
bind = yes
bind_dn = [email protected]
bind_pw = [email protected]
/etc/dovecot/dovecot-ldap.conf.ext
uris = ldap://dc.your.domain
dn = CN=service-mail-auth,CN=Users,DC=your,DC=domain
dnpass = [email protected]
tls = no
auth_bind = yes
auth_bind_userdn = %[email protected]
ldap_version = 3
base = OU=Пользователи,DC=your,DC=domain
deref = searching
scope = subtree
user_filter = (&(objectClass=person)(userPrincipalName=%u))
pass_filter = (&(ObjectClass=person)(userPrincipalName=%u))
/etc/dovecot/dovecot.conf
mail_location = maildir:/data/mail/%d/%n:UTF-8
mail_uid = mail
mail_gid = mail
#mail_nfs_index = yes
#mail_nfs_storage = yes
#mmap_disable = yes
#mail_fsync = always
#lock_method = fcntl
first_valid_uid = 5
first_valid_gid = 5
mbox_read_locks = fcntl
mbox_write_locks = fcntl
disable_plaintext_auth = no
auth_username_chars = abc[email protected]
auth_mechanisms = plain
ssl = yes
ssl_cert =  was automatically rejected:%n%r
lda_mailbox_autocreate = yes
lda_mailbox_autosubscribe = yes
passdb {
  driver = ldap
  args = /etc/dovecot/dovecot-ldap.conf.ext
}
userdb {
  driver = ldap
  args = /etc/dovecot/dovecot-ldap.conf.ext
  default_fields = home=/data/mail/%d/%n uid=mail gid=mail
}
service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}
service pop3-login {
  inet_listener pop3 {
    port = 110
  }
  inet_listener pop3s {
    port = 995
    ssl = yes
  }
}
service lmtp {
  unix_listener lmtp {
    mode = 0666
  }
}
service imap {
  #vsz_limit = $default_vsz_limit
  #process_limit = 1024
}
service pop3 {
  #process_limit = 1024
}
service auth {
  unix_listener /var/spool/prosody/private/auth {
    mode = 0660
    user = prosody
    group = prosody
  }
  unix_listener auth-userdb {
    mode = 0777
    user = mail
    group = mail
  }
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
  }
}
protocol lda {
  hostname = mail.your.domain
  sendmail_path = /usr/sbin/sendmail
}
protocol imap {
  #mail_plugins = $mail_plugins
  #mail_max_userip_connections = 10
}
protocol lmtp {
  # Space separated list of plugins to load (default is global mail_plugins).
  #mail_plugins = $mail_plugins
}
protocol pop3 {
  #mail_plugins = $mail_plugins
  #mail_max_userip_connections = 10
}
namespace inbox {
  inbox = yes
  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox "Черновики" {
    special_use = \Drafts
  }
  mailbox Junk {
#    autoexpunge = 7d
#    autoexpunge_max_mails = 100
    special_use = \Junk
  }
  mailbox Spam {
    special_use = \Junk
  }
  mailbox "Спам" {
    special_use = \Junk
  }
  mailbox junkmail {
    special_use = \Junk
  }
  mailbox "&BB0ENQQ2BDUEOwQwBEIENQQ7BEwEPQQwBE8- &BD8EPgRHBEIEMA-" {
    special_use = \Junk
  }
  mailbox "Нежелательная почта" {
    special_use = \Junk
  }
  mailbox Trash {
#    autoexpunge = 7d
#    autoexpunge_max_mails = 100
    special_use = \Trash
  }
  mailbox "Deleted Messages" {
    special_use = \Trash
  }
  mailbox "&BCMENAQwBDsENQQ9BD0ESwQ1-" {
    special_use = \Trash
  }
  mailbox "Удаленные" {
    special_use = \Trash
  }
  mailbox Sent {
    special_use = \Sent
    auto=subscribe
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox "&BCcENQRABD0EPgQyBDgEOgQ4-" {
    special_use = \Sent
  }
  mailbox "Отправленные" {
    special_use = \Sent
  }
  prefix =
  separator = /
  type = private
}
/etc/crontab
# update cerificates
1 1 * * sat root certbot renew --quiet --no-self-upgrade

# sync procedures
*/5 * * * * root /data/scripts/sync.all.sh

# trash cleaning procedures
1 1 * * * root /data/scripts/mailboxes.cleanup.sh

# archive blocked users mailboxes
1 2 * * * root /data/scripts/mailboxes.archive.blocked.sh
/data/scripts/sync.all.sh
#!/bin/bash

[[ $(ip a | grep -c .0.254) > 0 ]] && echo "cluster master" || exit

# sync mail data
rsync -avzru --delete-excluded /data/mail/ 192.168.0.252:/data/mail/

# sync web data
rsync -avzru --delete-excluded /data/web/ 192.168.0.252:/data/web/

# sync certificates
rsync -avzru --delete-excluded /etc/letsencrypt/ 192.168.0.252:/etc/letsencrypt/