Table of Content


I use a very good and detailed guide created by Thomas Leister.
Since I have a Gentoo Linux running I want to explain within this article how you can use this guide for Gentoo Linux.
Therefore, please read this guide first to get an overview of all the stuff.

Requirements

You have to had a are running a Gentoo Linux with working internet connection and you must be root.

Goal

At the end we will have a running Gentoo Server with:

  • Dovecot
  • Postfix
  • MySQL
  • Rspamd
  • Postfix-Admin

Mail-Server

Certifications

  emerge -av app-crypt/certbot
  emerge -av app-crypt/certbot-nginx

Database

We will use MariaDB as Database (https://wiki.gentoo.org/wiki/MariaDB):
If you do not have mariadb installed already you have to configure it, therefore, we can use a predefined configuration helper:

  emerge -av --config dev-db/mariadb

Start database and add them to your autostart:

  rc-update add mysql default
  rc-service mysql start

Open MySQL Root Shell to perform SQL commands:

  mysql -u root -p -h localhost

The first step is to create a database for our mail-service, called vmail:

  create database vmail CHARACTER SET 'utf8';

Create a user who can access the database using vmaildbpass as password (please use a secure one):

  grant select on vmail.* to 'vmail'@'localhost' identified by 'vmaildbpass';

Next step we have to use our database vmail:

  use vmail;

** CHECK IF YOU NEED THIS … SINCE WE WILL USE POSTFIX-ADMIN **

Our Mail-setup will use 4 different tables. Therefore, we have to create them:

Domain Table

  CREATE TABLE `domains` (
    `id` int unsigned NOT NULL AUTO_INCREMENT,
    `domain` varchar(255) NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY (`domain`)
  );

Account Table

  CREATE TABLE `accounts` (
    `id` int unsigned NOT NULL AUTO_INCREMENT,
    `username` varchar(64) NOT NULL,
    `domain` varchar(255) NOT NULL,
    `password` varchar(255) NOT NULL,
    `quota` int unsigned DEFAULT '0',
    `enabled` boolean DEFAULT '0',
    `sendonly` boolean DEFAULT '0',
    PRIMARY KEY (id),
    UNIQUE KEY (`username`, `domain`),
    FOREIGN KEY (`domain`) REFERENCES `domains` (`domain`)
  );

Alias Table

  CREATE TABLE `aliases` (
    `id` int unsigned NOT NULL AUTO_INCREMENT,
    `source_username` varchar(64) NOT NULL,
    `source_domain` varchar(255) NOT NULL,
    `destination_username` varchar(64) NOT NULL,
    `destination_domain` varchar(255) NOT NULL,
    `enabled` boolean DEFAULT '0',
    PRIMARY KEY (`id`),
    UNIQUE KEY (`source_username`, `source_domain`, `destination_username`, `destination_domain`),
    FOREIGN KEY (`source_domain`) REFERENCES `domains` (`domain`)
  );

TLS Policy-Tabelle

	CREATE TABLE `tlspolicies` (
    `id` int unsigned NOT NULL AUTO_INCREMENT,
    `domain` varchar(255) NOT NULL,
    `policy` enum('none', 'may', 'encrypt', 'dane', 'dane-only', 'fingerprint', 'verify', 'secure') NOT NULL,
    `params` varchar(255),
    PRIMARY KEY (`id`),
    UNIQUE KEY (`domain`)
	);

Vmail User

We will use /home/vmail as mail-server file storage directory (mailboxes, filter-scripts, …):

  mkdir /home/vmail

vmail-Systembenutzer erstellen:

  useradd --shell /usr/sbin/nologin -d /home/vmail vmail

vmail Unterverzeichnisse erstellen:

  mkdir /home/vmail/mailboxes
  mkdir -p /home/vmail/sieve/global

/var/vmail an vmail-User übereignen und Verzeichnisrechte passend setzen:

  chown -R vmail:vmail /home/vmail
  chmod -R 770 /home/vmail

Dovecot

  emerge -av net-mail/dovecot

Please ensure that at least the following USE-flags are enabled.

  net-mail/dovecot USE="bzip2 ipv6 managesieve mysql pam sieve ssl tcpd zlib"

Configuration

All configuration files are located at /etc/dovecot/.

/etc/dovecot/dovecot.conf

protocols = imap lmtp sieve

/etc/dovecot/conf.d/10-ssl.conf

# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
ssl = required

# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before
# dropping root privileges, so keep the key file unreadable by anyone but
# root. Included doc/mkcert.sh can be used to easily generate self-signed
# certificate, just make sure to update the domains in dovecot-openssl.cnf
ssl_cert = /etc/letsencrypt/live/YOUR.MAIL.DOMAIN/fullchain.pem
ssl_key  = /etc/letsencrypt/live/YOUR.MAIL.DOMAIN/privkey.pem

# SSL ciphers to use
# ###############
# Added by Gentoo
# You are encouraged to change the cipher list to
#ssl_cipher_list = DEFAULT:!EXPORT:!LOW:!MEDIUM:!MD5
# if you are not required to support legacy mail clients.
# ###############
ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL

/etc/dovecot/conf.d/10-mail.conf

If you want maildirs to use hierarchical directories, such as:

  • Maildir/folder/
  • Maildir/folder/subfolder/
    Therefore, we have to add LAYOUT=fs:
mail_home = /home/vmail/%d/%n
mail_location = maildir:~/Maildir:LAYOUT=fs

/etc/dovecot/conf.d/10-master.conf

service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    #port = 993
    #ssl = yes
  }

  # Number of connections to handle before starting a new process. Typically
  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0
  # is faster. <doc/wiki/LoginProcess.txt>
  #service_count = 1

  # Number of processes to always keep waiting for more connections.
  #process_min_avail = 0

  # If you set service_count=0, you probably need to grow this.
  #vsz_limit = $default_vsz_limit
}

/etc/dovecot/conf.d/20-managesieve.conf

service managesieve-login {
    inet_listener sieve {
        port = 4190
    }
}

/etc/dovecot/conf.d/20-imap.conf

protocol imap {
  # Space separated list of plugins to load (default is global mail_plugins).
  mail_plugins = $mail_plugins quota imap_quota imap_sieve

  # Maximum number of IMAP connections allowed for a user from each IP address.
  # NOTE: The username is compared case-sensitively.
  mail_max_userip_connections = 20
  imap_idle_notify_interval = 29 mins
} 

/etc/dovecot/conf.d/20-lmtp.conf

protocol lmtp {
  # Space separated list of plugins to load (default is global mail_plugins).
  postmaster_address = postmaster@YOUR.MAIL.DOMAIN
  mail_plugins = $mail_plugins sieve
}

/etc/dovecot/conf.d/auth-sql.conf

# Authentication for SQL users. Included from 10-auth.conf.
# 
# <doc/wiki/AuthDatabase.SQL.txt>

passdb {
  driver = sql

  # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
  args = /etc/dovecot/dovecot-sql.conf
}

# "prefetch" user database means that the passdb already provided the
# needed information and there's no need to do a separate userdb lookup.
# <doc/wiki/UserDatabase.Prefetch.txt>
#userdb {
#  driver = prefetch
#}

userdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf
}

/etc/dovecot/conf.d/15-mailboxes.conf

namespace inbox {
  inbox = yes

  # These mailboxes are widely used and could perhaps be created automatically:
  mailbox Drafts {
    auto = subscribe
    special_use = \Drafts
  }
  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }
  mailbox Trash {
    auto = subscribe
    special_use = \Trash
  }

  # For \Sent mailboxes there are two widely used names. We'll mark both of
  # them as \Sent. User typically deletes one of them if duplicates are created.
  mailbox Sent {
    auto = subscribe
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
}

/etc/dovecot/conf.d/90-sieve.conf

  ###
  ### Spam learning
  ###
  # From elsewhere to Spam folder
  imapsieve_mailbox1_name = Spam
  imapsieve_mailbox1_causes = COPY
  imapsieve_mailbox1_before = file:/var/vmail/sieve/global/learn-spam.sieve

  # From Spam folder to elsewhere
  imapsieve_mailbox2_name = *
  imapsieve_mailbox2_from = Spam
  imapsieve_mailbox2_causes = COPY
  imapsieve_mailbox2_before = file:/var/vmail/sieve/global/learn-ham.sieve

  sieve_pipe_bin_dir = /usr/bin
  sieve_global_extensions = +vnd.dovecot.pipe

  quota = maildir:User quota
  quota_exceeded_message = Benutzer %u hat das Speichervolumen überschritten. / User %u has exhausted allowed storage space.

/etc/dovecot/dovecot-sql.conf

driver=mysql
connect = "host=127.0.0.1 dbname=vmail user=vmail password=vmaildbpass"
default_pass_scheme = SHA512-CRYPT

password_query = SELECT username AS user, domain, password FROM accounts WHERE username = '%n' AND domain = '%d' and enabled = true;
user_query = SELECT concat('*:storage=', quota, 'M') AS quota_rule FROM accounts WHERE username = '%n' AND domain = '%d' AND sendonly = false;
iterate_query = SELECT username, domain FROM accounts where sendonly = false;

Since dovecot-sql.confcontains sensitive data we secure the file:

chmod 440 dovecot-sql.conf

Global Sieve Filter Script for Spam Detection

Create the file /home/vmail/sieve/global/spam-global.sieve:

require "fileinto";

if header :contains "X-Spam-Flag" "YES" {
    fileinto "Spam";
}

if header :is "X-Spam" "Yes" {
    fileinto "Spam";
}

This script will move all emails taged with Spam-Flag header into Spam the folder.

Scripts for Spam Improvement (Rspamd)

/home/vmail/sieve/global/learn-spam.sieve

require ["vnd.dovecot.pipe", "copy", "imapsieve"];
pipe :copy "rspamc" ["learn_spam"];

/home/vmail/sieve/global/learn-ham.sieve

require ["vnd.dovecot.pipe", "copy", "imapsieve"];
pipe :copy "rspamc" ["learn_ham"];

Compile Sieve files: will generate *.svbin files

sievec FILE.sieve

Postfix - Installation and Configuration

emerge -av mail-mta/postfix

Ensure you have the use flag mysql enabled.

Mail-Queue Settings

maximal_queue_lifetime = 1h
bounce_queue_lifetime = 1h
maximal_backoff_time = 15m
minimal_backoff_time = 5m
queue_run_delay = 5m

TLS Settings

tls_preempt_cipherlist = yes
tls_ssl_options = NO_COMPRESSION
tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA

Outgoing SMTP-Connections (Postfix as sender)

smtp_tls_security_level = dane
smtp_dns_support_level = dnssec
smtp_tls_policy_maps = mysql:/etc/postfix/sql/tls-policy.cf
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_tls_protocols = !SSLv2, !SSLv3
smtp_tls_ciphers = high
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

Incoming SMTP-Connections

smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2, !SSLv3
smtpd_tls_ciphers = high
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache

smtpd_tls_cert_file=/etc/letsencrypt/live/mail.mysystems.tld/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.mysystems.tld/privkey.pem

Local mail delivery using Dovecot

virtual_transport = lmtp:unix:private/dovecot-lmtp

Spam-filter and DKIM-Sig via Rspamd

smtpd_milters = inet:localhost:11332
non_smtpd_milters = inet:localhost:11332
milter_protocol = 6
milter_mail_macros =  i {mail_addr} {client_addr} {client_name} {auth_authen}
milter_default_action = accept

Server Restrictions for clients, receiver and relaying

Settings, Postfix as Relay (for clients)

smtpd_relay_restrictions =      reject_non_fqdn_recipient
                                reject_unknown_recipient_domain
                                permit_mynetworks
                                reject_unauth_destination

Bedingungen, damit Postfix ankommende E-Mails als Empfängerserver entgegennimmt (zusätzlich zu relay-Bedingungen)

check_recipient_access prüft, ob ein account sendonly ist

smtpd_recipient_restrictions = check_recipient_access mysql:/etc/postfix/sql/recipient-access.cf

Bedingungen, die SMTP-Clients erfüllen müssen (sendende Server)

smtpd_client_restrictions =     permit_mynetworks
                                check_client_access hash:/etc/postfix/without_ptr
                                reject_unknown_client_hostname

Wenn fremde Server eine Verbindung herstellen, müssen sie einen gültigen Hostnamen im HELO haben.

smtpd_helo_required = yes
smtpd_helo_restrictions =   permit_mynetworks
                            reject_invalid_helo_hostname
                            reject_non_fqdn_helo_hostname
                            reject_unknown_helo_hostname

Clients blockieren, wenn sie versuchen zu früh zu senden

smtpd_data_restrictions = reject_unauth_pipelining

Restrictions für MUAs (Mail user agents)

mua_relay_restrictions = reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_mynetworks,permit_sasl_authenticated,reject
mua_sender_restrictions = permit_mynetworks,reject_non_fqdn_sender,reject_sender_login_mismatch,permit_sasl_authenticated,reject
mua_client_restrictions = permit_mynetworks,permit_sasl_authenticated,reject

Postscreen Filter

Postscreen Whitelist / Blocklist

postscreen_access_list =        permit_mynetworks
                                cidr:/etc/postfix/postscreen_access
postscreen_blacklist_action = drop

Verbindungen beenden, wenn der fremde Server es zu eilig hat

postscreen_greet_action = drop

DNS blocklists

postscreen_dnsbl_threshold = 2
postscreen_dnsbl_sites =    ix.dnsbl.manitu.net*2
                            zen.spamhaus.org*2
postscreen_dnsbl_action = drop

MySQL Abfragen

virtual_alias_maps = mysql:/etc/postfix/sql/aliases.cf
virtual_mailbox_maps = mysql:/etc/postfix/sql/accounts.cf
virtual_mailbox_domains = mysql:/etc/postfix/sql/domains.cf
local_recipient_maps = $virtual_mailbox_maps

Sonstiges

Maximale Größe der gesamten Mailbox (soll von Dovecot festgelegt werden, 0 = unbegrenzt)

mailbox_size_limit = 0

Maximale Größe eingehender E-Mails in Bytes (50 MB)

message_size_limit = 52428800

Keine System-Benachrichtigung für Benutzer bei neuer E-Mail

biff = no

Nutzer müssen immer volle E-Mail Adresse angeben - nicht nur Hostname

append_dot_mydomain = no

Trenn-Zeichen für “Address Tagging”

recipient_delimiter = +