### Contents

1. [Introduction](#art-1)
2. [Installing](#art-2)
3. [Postfix SMTP server](#art-3)
    1. [main.cf](#art-3-1)
    2. [master.cf](#art-3-2)
    3. [User aliases](#art-3-3)
    4. [Starting Postfix](#art-3-4)
4. [Dovecot POP3/IMAP server with Sieve mail filter](#art-4)
    1. [Configuration files](#art-4-1)
        1. [dovecot.conf](#art-4-1-1)
        2. [10-auth.conf](#art-4-1-2)
        3. [10-logging.conf](#art-4-1-3)
        4. [10-mail.conf](#art-4-1-4)
        5. [10-master.conf](#art-4-1-5)
        6. [10-ssl.conf](#art-4-1-6)
        7. [15-lda.conf](#art-4-1-7)
        8. [15-mailboxes.conf](#art-4-1-8)
        9. [20-managesieve.conf](#art-4-1-9)
        10. [90-sieve.conf](#art-4-1-10)
        11. [auth-system.conf](#art-4-1-11)
5. [SpamAssassin spam filter](#art-5)
    1. [Updating built-in rule set](#art-5-1)
    2. [Bayesian classifier training](#art-5-2)
6. [OpenDKIM signing and verifying filter](#art-6)
    1. [opendkim.conf](#art-6-1)
    2. [Generating keys](#art-6-2)
    3. [Populating KeyTable and SigningTable](#art-6-3)
    4. [internal-hosts file](#art-6-4)
    5. [Starting OpenDKIM](#art-6-5)
7. [OpenDMARC email policy filter](#art-7)
8. [DNS records](#art-8)
    1. [MX and A/AAAA](#art-8-1)
    2. [PTR](#art-8-2)
    3. [SPF](#art-8-3)
    4. [DMARC](#art-8-4)
    5. [DKIM](#art-8-5)
9. [Setting up a ClamAV antivirus](#art-9)
10. [Greylisting with postgrey](#art-10)

### 1. Introduction {#art-1}

E-mail itself is a system consisting of several parts. A main E-mail protocol — SMTP is used for exchanging of mail between servers. For a user to manage his mail IMAP and POP3 protocols are used. Difference between IMAP and POP3 is that IMAP manages mail stored on a server, while POP3, once fetched mail, deletes it from server.

As SMTP server I use Postfix. For IMAP (I don't use POP3 because I need access from multiple devices) I use Dovecot. Dovecot is also being used for SASL authentication in Postfix. And Dovecot supports Sieve protocol, through its implementation called Pigeonhole, that allows users to write their own message filtering rules.

I decided to make use of greylisting. Even though I don't have a problem with spam after training of SpamAssassin. Luckily, it is very easy.

Server is configured in a simple way using PAM (real system users) with user's passwords and with mail stored in ~/Maildir.

But, as you know, there is a major problem called spam. And in order to withstand this problem there was mechanisms created. So that no one could impersonate you DKIM, DMARC and SPF were created. To ensure encrypted connection a MTA-STS (RFC8461) standard was made. To resist incoming spam anti-spam systems like SpamAssassin or spamd are used. Also it is good to check incoming mail for viruses, there are many antiviruses and I use one called ClamAV.

I have mentioned DKIM (DomainKeys Identified Mail), it is used to sign mail going out that it is indeed comes from your server, and verify incoming mail. And for that I use OpenDKIM implementation.

DMARC (Domain-based Message Authentication, Reporting and Conformance) is a policy that tells the server what to do if received mail failed some checks. To perform DMARC policy checks I use OpenDMARC implementation. OpenDMARC also performs SPF policy checks.

SPF (Sender Policy Framework) allows the server to check that an incoming mail came from an IP address set in SPF DNS record for domain it originates from.

Many servers will reject mail from a dynamic IP-address, so a static address is a must. But, from my experience, GMail, Yandex and Rambler will at least place my message to Junk directory. On Gmail I get to Inbox most of the time btw. There is such thing used against spam called block lists, and Spamhaus is the biggest player here. So, if a server uses such lists I get rejected at the stage of establishing a connection with no chance to get through anti-spam system.

If you have a static address then in Spamhaus you can submit your address for deletion.

Also, while not stated by RFCs, some servers will still reject your mail if your IP-address doesn't have a PTR DNS record set, and some will expect a PTR record to be a hostname of your server, like mail.example.org, or like my the.arav.su.

There is another problem known as not trusted top level domain, like mine .top domain. :) Because of how cheap it is, it is popular among spammers.

### 2. Installing {#art-2}

You need to install following packages: `postfix`, `dovecot`, `pigeonhole` (or could be named as `dovecot-sieve`), `clamav`, `opendkim`, `opendmarc`, `spamassassin`, and `postgrey`.

### 3. Postfix SMTP server {#art-3}

Its configuration files are in directory `/etc/postfix`. There are two configuration files we'll work with. The first one is a `main.cf` file. Then we configure services in `master.cf`. Also I'll show you how to make aliases for users.

#### 3.1. main.cf {#art-3-1}

So, you have `main.cf` opened, first we need to make changes in an existing configuration:

    myhostname = mail.example.org
    mydomain = example.org
    myorigin = $mydomain

`myhostname` should be a subdomain that points to an IP of a mail server. This hostname is usually an A DNS record even if you have only one IP-address it is discuraged making it a CNAME.

`mydomain` is your domain. In `myorigin` we set a domain name from what sent mail appear. Yes, you can use other options as a variable to reduce copy-pasting.

    inet_interfaces = all

In `inet_interfaces` we list interfaces we listen on. In this case we listen on all available interfaces.

    mydestination = $myhostname, localhost, $mydomain, mail.$mydomain

`mydestination` is a list of domains our server deliver mail for.

    local_recipient_maps = unix:passwd.byname $alias_maps

Here we tell Postfix where to look for names of local recipients.

    mynetworks = localhost, 192.168.0.0/24

It is a list of trusted remote clients allowed to relay mail through our server.

    alias_maps = hash:/etc/postfix/aliases
    alias_database = $alias_maps

For database of aliases we use `/etc/postfix/aliases` file in a special format we learn about later.

    recipient_delimiter = +

A symbol used to separate username and an extension.

    home_mailbox = Maildir/

Here we use a Maildir/ mailbox style when every message is stored in a separated file.

    mailbox_transport = lmtp:unix:private/dovecot-lmtp

Here we tell Postfix what LMTP (Local Mail Transport Protocol) server to use.

    inet_protocols = ipv4

Here we leave only IPv4 support, if you have an IPv6 address then you may want to add `ipv6`.

So here we are done with an existing configuration and ready to make our additions to add encryption, milters, configure SASL authentication, restrictions and make some tweaks.

And we start with configuring milters. Milter stands for mail filter, a special protocol originated in Sendmail SMTP server.

    milter_default_action = accept
    milter_protocol = 6
    smtpd_milters =
        unix:/var/spool/opendmarc/opendmarc.sock
        unix:/var/spool/opendkim/opendkim.sock
        inet 192.168.0.54:7357
    non_smtpd_milters = $smtpd_milters

`milter_default_action` specifies default action of a milter to accept messages.

`milter_protocol` specifies protocol version used by milters, current is 6.

`smtpd_milters` is a list of milters the messages will go through. You can connect to milter with internet protocol with `inet:`, with a unix socket `unix:`. The last one with port 7357 is a ClamAV.

Now lets add some tweaks:

    biff = no
    strict_rfc821_envelopes = yes
    disable_vrfy_command = yes
    smtpd_helo_required = yes
    smtpd_delay_reject = yes
    
    mailbox_size_limit = 0
    message_size_limit = 52428800

`biff` set to no to disable local service for new mail notifications. It is a server, we don't need them here.

`strict_rfc821_envelopes` set to yes require addresses to be enclosed with <>.

Disabling VRFY command with `disable_vrfy_command` prevents some email addresses harvesting techniques.

`smtpd_helo_required` requires remote client to send HELO or EHLO command. This may stop some poorly written spam bots.

`smtpd_delay_reject` makes Postfix wait for RCPT TO command before evaluating some restrictions.

`mailbox_size_limit` and `message_size_limit` sets maximum size of a whole mailbox and of each email. Here I set no limit for a mailbox, and max of 50MiB for an email message.

And now it's time for SASL configuration:

    smtpd_sasl_type = dovecot
    smtps_sasl_path = private/auth
    smtps_sasl_auth_enable = yes
    smtpd_sasl_security_options = noanonymous
    smtpd_sasl_local_domain = $mydomain
    broken_sasl_auth_clients = no

It's pretty clear. We chose dovecot as our SASL server, set path to it that lies within /var/spool directory, and then enabled it. Then we prohibit anonymous connections. Add our domain for SASL authentication, and deny access for the clients with obsolete version of AUTH command.

It's time for encryption, lets specify a list of high (strong) ciphers to use:

    tls_high_cipherlist = ECDHE:DHE:kGOST:!aNULL:!eNULL:!RC4:!MD5:!3DES:!AES128:!CAMELLIA128:!ECDHE-RSA-AES256-SHA:!ECDHE-ECDSA-AES256-SHA

I took this ciphers set from [https://pub.nethence.com/security/ciphers](https://pub.nethence.com/security/ciphers).

Now lets configure encryption for outgoing connections.

    smtp_use_tls = yes
    smtp_tls_security_level = encrypt
    smtp_tls_note_starttls_offer = yes
    smtp_tls_connection_reuse = yes
    smtp_tls_key_file = /etc/letsencrypt/live/example.org/privkey.pem
    smtp_tls_cert_file = /etc/letsencrypt/live/example.org/fullchain.pem
    smtp_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1
    smtp_tls_protocols = $smtp_tls_mandatory_protocols
    smtp_tls_mandatory_ciphers = high

First we enable it with a `smtp_use_tls`. Enforce use of encryption with (`smtp_tls_security_level`) set to encrypt. Noting servers of our ability to use encryption with option `smtp_tls_note_starttls_offer`. Reuse connection instead of opening the new one each time.

`smtp_tls_key_file` and `smtp_tls_cert_file` are paths to our TLS key and certificate.

In `smtp_tls_mandatory_protocols` and `smtp_tls_protocols` we disallow old vulnerable protocols to use. Leave only TLS version 1.2 and 1.3 available.

In `smtp_tls_mandatory_ciphers` we declare to use only good secure cyphers we previously set.

For incoming connections everything is the same, just a few more options added:

    smtpd_use_tls = yes
    smtpd_tls_security_level = encrypt
    smtpd_tls_auth_only = yes
    smtpd_tls_key_file = /etc/letsencrypt/live/example.org/privkey.pem
    smtpd_tls_cert_file = /etc/letsencrypt/live/example.org/fullchain.pem
    smtpd_tls_mandatory_protocols = $smtp_tls_mandatory_protocols
    smtpd_tls_protocols = $smtpd_tls_mandatory_protocols
    smtpd_tls_mandatory_ciphers = $smtp_tls_mandatory_ciphers
    smtpd_tls_loglevel = 1
    smtpd_tls_received_header = yes
    smtpd_tls_session_cache_timeout = 3600s
    tls_random_source = dev:/dev/urandom

`smtpd_tls_auth_only` allows authorisation only on encrypted connections.

`smtpd_tls_loglevel` set to 1 to log a summary of a TLS handshake.

`smtpd_tls_received_header` makes Postfix include information about the protocol and cypher used to a `Received:` header.

`smtpd_tls_session_cache_timeout` is for how long to store session. `tls_random_source` is for setting an entropy source.

The final part is my "favorite". :) The restrictions! There is a set of them for each stage the message falls through. Here are the ones I configured:`smtpd_helo_restrictions`, `smtpd_relay_restrictions`, `smtpd_data_restrictions`, `smtpd_sender_restrictions`, and `smtpd_recipient_restrictions`.

So lets roll. This my working restrictions setup:

    smtpd_helo_restrictions =
       reject_unknown_helo_hostname,
       reject_invalid_helo_hostname,
       reject_non_fqdn_helo_hostname

    smtpd_data_restrictions =
       reject_multi_recipient_bounce,
       reject_unauth_pipelining

    smtpd_sender_restrictions =
       permit_sasl_authenticated,
       reject_non_fqdn_sender,
       reject_unknown_sender_domain

    smtpd_recipient_restrictions =
       reject_unknown_recipient_domain,
       reject_non_fqdn_recipient

In order to explain what every restriction does I'd have to copy-paste from `man 5 postconf`. :)

The first set of restrictions are for HELO or EHLO command, that we force the client to send with priorly set option `smtpd_helo_required` to yes. Here `reject_unknown_helo_hostname` rejects hostnames that doesn't have DNS A or MX records. `reject_invalid_helo_hostname` rejects malformed hostnames, and `reject_non_fqdn_helo_hostname` ensures that the hostname is a fully-qualified domain name.

The second one are for DATA command. And here man page is better than me at explaining it. Here is the link for [reject_multi_recipient_bounce](http://www.postfix.org/postconf.5.html#reject_multi_recipient_bounce) and [reject_unauth_pipelining](http://www.postfix.org/postconf.5.html#reject_unauth_pipelining). Or just look them up in `man 5 postconf`. All I can say is that it is better to have them than not to. :)

The rest is simpler, `permit_sasl_authenticated` in `smtpd_sender_restrictions` accepts the senders that were authenticated by SASL (e.g. Dovecot or Cyrus). And all the `reject_unknown_*` and `reject_non_fqdn_*` has the same meaning as for theirs *_helo_* counterparts, just used in theirs specific places.

#### 3.2. master.cf {#art-3-2}

The following services are needed: `smtp`, `submission`, `smtps`, and we add `spamassassin` service. The rest in this file left untouchable.

    smtp       inet  n       -       n       -       -       smtpd
     -o content_filter=spamassassin
    submission inet  n       -       n       -       -       smtpd
     -o syslog_name=postfix/submission
     -o smtpd_tls_security_level=encrypt
     -o smtpd_sasl_auth_enable=yes
     -o smtpd_tls_auth_only=yes
     -o milter_macro_daemon_name=ORIGINATING
     -o content_filter=spamassassin
    smtps      inet  n       -       n       -       -       smtpd
     -o content_filter=spamassassin
     -o syslog_name=postfix/smtps
     -o smtpd_tls_wrappermode=yes
     -o smtpd_sasl_auth_enable=yes
    
    spamassassin unix -      n       n       -       -       pipe
    user=spamd argv=/bin/vendor_perl/spamc
     -e /sbin/sendmail -oi -f ${sender} ${recipient}

`smtp` is listening on port 25, and `smtps` on port 465. `submission` is listening on port 587 and is used by mail client to send mail.

#### 3.3. User aliases {#art-3-3}

User aliases are in `aliases` file. They has a form &quot;`<alias>: <username>`&quot;, e.g. `me: arav`. Where `username` may be other alias. After editing you need to run `newaliases` program to update `aliases.db` file.

#### 3.4. Starting Postfix {#art-3-4}

To start a Postfix service on systemd-based Linux distro run `systemctl start postfix`. To make Postfix run on every boot run `systemctl enable postfix`.

### 4. Dovecot POP3/IMAP server with Sieve mail filter {#art-4}

As I stated in the Introduction I use only IMAP, since it keeps mail on a server.

First we need to create a `/etc/dovecot` directory and copy example configuration there, but we don't need everything. Example configuration lies in a `/usr/share/doc/dovecot/example-config` directory.

Copy `dovecot.conf` file and `conf.d` directory.

We will need `dovecot.conf` and files from a `conf.d` directory: 10-auth.conf, 10-mail.conf, 10-ssl.conf, 15-mailboxes.conf, 20-lmtp.conf, 90-sieve.conf, 10-logging.conf, 10-master.conf, 15-lda.conf, 20-managesieve.conf, auth-system.conf.ext.

Here I will leave a brief necessary explanation of options, since you will see theirs documentation when will be editing configs.

#### 4.1. Configuration files {#art-4-1}

##### 4.1.1. dovecot.conf {#art-4-1-1}


    protocols = imap lmtp sieve

Here we activate IMAP for access to mailbox, LMTP for local message delivery from Postfix and Sieve protocol for user-defined custom filters.

    listen = *

Here we simply tell Dovecot to listen on all available interfaces.

    base_dir = /var/run/dovecot

Uncomment setting to set a base directory for runtime files.

    instance_name = dovecot

A new for this instance. In case you run multiple you can set a different names for them.

    login_greeting = Dovecot ready.

A greeting message for the clients.

    login_trusted_networks = 192.168.0.0/24 192.168.1.0/24

A list of trusted network ranges.

    shutdown_clients = yes

I chose to force closing client connections on master process shutdown.

Here we are done with `dovecot.conf` file and let's go to `conf.d` directory.

##### 4.1.2. 10-auth.conf {#art-4-1-2}


    disable_plaintext_auth = yes

It disables login without encryption.

    auth_realms = example.org

A list of realms for SASL authentication. Just leave here your domain.

    auth_username_format = %Ln

I chose to drop domain.

    auth_mechanisms = plain

I use a plain mechanism.

And, finally, at the bottom leave `auth-system.conf.ext` include directive.

##### 4.1.3. 10-logging.conf {#art-4-1-3}

Simply uncomment `log_path = syslog` there.

##### 4.1.4. 10-mail.conf {#art-4-1-4}

In `10-mail.conf` we set following:

    mail_location = maildir:~/Maildir

We use Maildir/ scheme and store it in user's home directory.

Next set `mail_server_admin = admin@%d`, or whatever name you want. Here `%d` will expand to a domain.

##### 4.1.5. 10-master.conf {#art-4-1-5}

In section `service imap-login` uncomment everything in `inet_listener` sub-sections for IMAP and IMAPS.

If you want to use POP3 protocol, then do the same for `service pop3-login` section.

Go to `service lmtp` section and add following sub-section to enable LMTP service on a UNIX socket: 

    unix_listener /var/spool/postfix/private/dovecot-lmtp {
      group = postfix
      user = postfix
      mode = 0666
    }

Then go to `service auth` section and add following UNIX listener:

    unix_listener /var/spool/postfix/private/auth {
      user = postfix
      mode = 0666
    }

##### 4.1.6. 10-ssl.conf {#art-4-1-6}

Set `ssl = required` to enable and enforce encryption.

In `ssl_cert` and `ssl_key` set paths to your TLS certificate and key.

Next you will see `ssl_key_password` where you type in your certificate's password if it is set.

And if you run your own PKI then you can specify your CA cert in `ssl_ca`.

Next good thing to do is to generate Diffie-Hellmann parameters file and set it in `ssl_dh` option. How to do it is written in a hel
comment for this option.

Next set a cipher list `ssl_cipher_list` to a list you previously specified in Postfix's `main.cf` file.

And finally `ssl_prefer_server_ciphers = yes`.

##### 4.1.7. 15-lda.conf {#art-4-1-7}

Here we simply uncomment and set `mail_plugins = $mail_plugins sieve` in `protocol lda` section.

##### 4.1.8. 15-mailboxes.conf {#art-4-1-8}

Here in a namespace inbox leave Drafts, Junk, Trash, and Sent mailboxes. Additionally, for every mailbox I added `auto = subscribe` options so they will appear in a mail client.

##### 4.1.9. 20-managesieve.conf {#art-4-1-9}

Uncomment `protocols` option, `service managesieve-login`, and `service managesieve` sections.

Within `service managesieve-login` section uncomment fully `inet_listener sieve` sub-section. And next you can tweak `service_count`, `process_min_avail` and `vsz_limit` options to your taste.

##### 4.1.10. 90-sieve.conf {#art-4-1-10}

Here I just have `sieve_before = /var/lib/dovecot/sieve.d/` option uncommented, the rest is default.

##### 4.1.11. auth-system.conf.ext {#art-4-1-11}

Here is default except for in passdb I added `failure_show_msg=yes` in `args`.


### 5. SpamAssassin spam filter {#art-5}

First, we'll work with a `local.cf` file to configure SpamAssassin.

I didn't any fancy tweaking here, didn't make any custom rules. I just changed options presented in file.

I left `rewrite_header` option commented since I don't want to append anything to a Subject header of spam messages.

Option `report_safe` I set to 2 to save spam messages as a text/plain attachment instead of modifying original message.

`trusted_networks` sets networks and hosts that are considered trusted, i.e. not spammers.

`lock_method` left to be flock, since I don't use NFS.

`required_score` left to be its default value of 5.0.

I use Bayesian classifier, so options `use_bayes` and `bayes_auto_learn` are set to 1.

I chose to normalise charset to UTF-8 with option `normalize_charset 1`.

Next we move to `*.pre` files.

In `init.pre` I commented out URIDNSBL plugin, since I don't use them.

In `v342.pre` I enabled `FromNameSpoof` and `Phishing` plugins.

#### 5.1. Updating built-in rule set {#art-5-1}

I sometimes run `sa-update` util to update built-in rules and pre-compile them with `sa-compile` util. After that restart SpamAssassin.

And so SpamAssassin make use of compiled rules ensure that a plugin Rule2XSBody in a `v320.pre` is uncommented.

#### 5.2. Bayesian classifier training {#art-5-2}

After you set up SpamAssassin for the first time you have to train Bayesian classifier. It will start to work after 200 messages will be examined.

For training use `sa-learn` utility and use `--ham` and `--spam` to mark messages as normal mail and spam. I additionaly have to specify a path to database with `--dbpath /var/lib/spamassassin/.spamassassin`, otherwise it will complain, so try first without it.


### 6. OpenDKIM signing and verifying filter {#art-6}

On ArchLinux I got a problem that OpenDKIM is unable to write in `/run`, so I created `/var/spool/opendkim` directory for it.

After configuring it, you need to add DNS record with your public key, it is covered in [8.5](#art-8-5) section.

#### 6.1. opendkim.conf {#art-6-1}

Well, that's main config file:

    KeyTable           refile:/etc/opendkim/keytable
    SigningTable       refile:/etc/opendkim/signingtable
    InternalHosts      refile:/etc/opendkim/internal-hosts
    
    Socket local:/var/spool/opendkim/opendkim.sock
    PidFile /var/spool/opendkim/opendkim.pid
    UMask  000
    UserID opendkim:opendkim
    
    Mode sv
    SubDomains yes
    
    Canonicalization relaxed/simple
    
    Syslog yes
    SyslogSuccess yes
    LogWhy yes
    
    SoftwareHeader yes

I myself set up a multi-domain variant just in case. So, here we have two main tables: `KeyTable` and `SigningTable`. Those files tells OpenDKIM where to find keys and what domains to sign. You may use one key for all domains or generate keys for each domain.

`InternalHosts` tells OpenDKIM what hosts should be signed rather than verified.

`Socket` tells where to listen to connections, in this case we use UNIX sockets.

`Mode` selects operating mode(s). In our case we have two modes: (s)igner and (v)erifier.

`SubDomains` set to yes tells that we allow subdomains of our domains to be signed and verified.

`Canonicalization` selects the canonicalization method(s) to be used with signing. We set relaxed for header and simple for body. I don't fully understand it and just use what suggested.

Below are logging options that tells to write in syslog.

With `SoftwareHeader` set to yes OpenDKIM will be always adding &quot;DKIM-Filter&quot; header field.

#### 6.2. Generating keys {#art-6-2}

    opendkim-genkey -r -s myselector -b 2048 -d example.com

This command will generate a key pair stored in files &quot;myselector.private&quot; and &quot;myselector.txt&quot; for a given domain.

`-r` restricts the key to emails use only. `-s` is a name of selector.`-b` is the size of the key in bits. `-d` is our domain.

Name of a selector is usually a `mail`, but that's just what I use, you can choose whatever you want.


#### 6.3. Populating KeyTable and SigningTable {#art-6-3}

KeyTable has following structure (a line per domain):

    myselector._domainkey.example.com example.com:myselector:/etc/opendkim/myselector.private

And SigningTable this one:

    *@example.com myselector._domainkey.example.com

#### 6.4. internal-hosts file {#art-6-4}

As stated above in this file we put hosts whose mail should be signed rather than verified. And its structure is the following:

    127.0.0.1
    192.168.0.0/24

`127.0.0.1` is necessary to be there according to a manual.


#### 6.5. Starting OpenDKIM {#art-6-5}

`systemctl start opendkim` and `systemctl enable opendkim` to start and enable OpenDKIM service to run on OS start up if you got Poetteringed just like me. :)

### 7. OpenDMARC email policy filter {#art-7}

Its configuration lies in `/etc/opendmarc/opendmarc.conf` and is fully documented. Here are the options I changed:

    AuthservID OpenDMARC
    FailureReports true
    FailureReportsBcc admin@example.org
    FailureReportsSentBy admin@example.org
    IgnoreAuthenticatedClient yes
    RejectFailures true
    RequiredHeaders yes
    Socket unix:/var/spool/opendmarc/opendmarc.sock
    SoftwareHeader true
    SPFSelfValidate true
    Syslog true
    TrustedAuthservIDs mail.example.org,example.org
    UMask 002

What's in a `Socket` option should be added to Postfix's `smtpd_milters` and `non_smtpd_milters`.

Creating DMARC DNS record covered in [8.4](#art-8-4).

### 8. DNS records {#art-8}

#### 8.1. MX and A/AAAA {#art-8-1}

It's good to have a dedicated A (IPv4 address) or AAAA (IPv6 address) record for a mail server's hostname instead of a CNAME record so other servers won't need to do two DNS requests. Hostname is usually mail.example.org if there's just one server, you can call it whatever you want. Remind you that we set it in Postfix in `myhostname` option.

And A record looks like this:

    mail  IN  86400  A  203.0.113.4

Where `mail` is a hostname, 86400 is a TTL of a record in seconds.

Next we need to add a MX (mail exchanger) record that looks like this:

      MX 10 mail.example.org.

Here 10 is a priority of a record. The lower a number the higher a priority.

A period at the end of the hostnames is necessary in DNS records.

#### 8.2. PTR {#art-8-2}

PTR is a reverse DNS record that stands for pointer and is used to &ldquo;bind&rdquo; a hostname to IP-address. Mail servers looks for this record and check so this name equals to a hostname provided in EHLO. Most servers will reject your mail if your PTR looks something like 1.2.3.4.pppoe.someisp.net or not set at all.

There are three ways to set this record: ask your hosting or internet-provider, or get your own Autonomous System (:^)).

Example of this record:

    1  IN  PTR  mail.example.org.

#### 8.3. SPF {#art-8-3}

SPF stands for Sender Policy Framework and in my case it looks exactly like this:

    v=spf1 +a +mx -all

So, `v` is a version of a protocol. `+a +mx` means that only servers specified in the A and MX DNS records could send email, and `-all` that no one else could do that.

#### 8.4. DMARC {#art-8-4}

DMARC stands for Domain-based Message Authentication Reporting and Conformance. And its DNS record could be like this one that I use:

    _dmarc    IN    TXT    "v=DMARC1; p=reject; rua=mailto:admin@example.org; ruf=mailto:admin@example.org; adkim=s; aspf=s"

`v` is a version of a protocol.

`p` is a default policy that could be set to `none`, `quarantine` and `reject`. I chose to `reject` mail that comes from &laquo;me&rdquo; if there's something wrong with a origin of a message. If you could get email from subdomains then you need to set `sp` as well.

`rua` is an address for the reports and `ruf` is for the forensic reports. `aspf` verifies that an address in the MAIL FROM command and `From:` header matches example.org in strict (s) mode, and in relaxed (default, r) mode matches domain or its subdomains. For `adkim` is the same except in this case sender domain name should match a domain in `d=domain` in a `DKIM-Signature` header.

#### 8.5. DKIM {#art-8-5}

In 5.2 we generated a key pair for our domain and now we'll take what's inside a `myselector.txt` file and add it to our DNS.

DKIM DNS record looks like this:

    myselector._domainkey    IN    TXT    ( "v=DKIMv1; k=rsa; s=email; p=<public key goes here>"
                                            "<public key continues here>" )

Brackets are used in case a content of a record doesn't fit on one line, and it won't fit.

### 9. Setting up a ClamAV antivirus {#art-9}

All you need to make it work together with Postfix is to add `/run/clamav/milter.sock` to `smtpd_milters` and `non_smtpd_milters` options in Postfix, also make some changes in configs of ClamAV.

In `clamav-milter.conf` you need the following:

    MilterSocket unix:/run/clamav/milter.sock
    ClamdSocket unix:/run/clamav/clamd.ctl

Also, in case you need ClamAV to add headers also in case a message is free of viruses add `AddHeader Add` or `AddHeader Replace` option. The difference between them is detaily described in config file itself.

Before starting ClamAV you need to update its virus definitions with `freshclam` util. Also, enable and start `clamav-freshclam` systemd service to keep definitions recent.

I don't know how it is in other distros, but, for whatever reason, an Arch Linux's package doesn't come with a systemd service file for the ClamAV milter. So I just copy it here from <a href="https://wiki.archlinux.org/index.php/ClamAV#Using_the_milter" rel="noopener noreferrer" target="_blank">ArchWiki</a>:

    [Unit]
    Description='ClamAV Milter'
    After=clamav-daemon.service
    
    [Service]
    Type=forking
    ExecStart=/usr/bin/clamav-milter --config-file /etc/clamav/clamav-milter.conf
    
    [Install]
    WantedBy=multi-user.target

Save it as `/usr/lib/systemd/system/clamav-milter.service` and run `systemctl daemon-reload`.

Next you need to enable and start `clamav-daemon` and `clamav-milter`.

### 10. Greylisting with postgrey {#art-10}

`postgrey` is being configured primarily by passing arguments you configure in its service file. I go by defaults. So it is listening on `127.0.0.1:10030`.

So, to set a listen port pass this option: `--inet=127.0.0.1:10030`.

To configure greylisting duration use option `--delay=seconds`. `--max-age=N` to delete entries older than N days since the last time that they have been seen.

Look `perldoc postgrey` for help.

There are two configuaration files named `postgrey_whitelist_clients` and `postgrey_whitelist_recipients`. Dunno about other distros, but in ArchLinux they are in `/etc/postfix`. Could be in `/etc/postgrey` in other distros.

`postgrey_whitelist_clients` is list of trusted SMTP servers that will not be greylisted. Also there comes some problematic servers that will not attempt to send a mail again, or have a large pool of addresses. This file comes pre-filled with some known server such as `gmail.com` and `outlook.com`.

`postgrey_whitelist_recipients` contains a list of recipients for which mail won't be greylisted. By default there are `postmaster` and `abuse` recipients listed.

In postfix's `main.cf` you need just add this last line:

    smtpd_recipient_restrictions =
       reject_unknown_recipient_domain,
       reject_non_fqdn_recipient,
       #[b check_policy_service inet:127.0.0.1:10030]