Javad's mail server guide

Javad's mail server guide

Setting up and maintaining a mail server requires a considerable amount of effort, even for a small personal mail server. Any mail system comprises multiple components that communicate through various protocols. DNS is essential for email authentication and routing, and users expect you to provide them with user-friendly web interfaces. Unless you have only a handful of users, you also need an efficient way to store their data and credentials. Additionally, combating spam can turn into a nightmare if not planned for in advance. All these factors make setting up a mail server more than just installing and configuring a single piece of software or two. Part of this complexity stems from email being an old protocol, predating the web by almost a decade. When the SMTP protocol was designed, the Internet was still a safe and peaceful place, and there was hardly a need to prioritize security too tightly.

Of course, setting up and maintaining a mail server is still within the realm of possibility. The goal of this guide is to assist you in that endeavor. After reading this guide, you should be able to set up a small mail server for personal use or a small organization. I cover many essential concepts, sparing you hours of trial and error. Upon completing this guide, you should find it easy to navigate the extensive documentation of the tools discussed here.

In this guide, I place emphasis on security, minimalism, and simplicity. We will not compromise security for convenience, and we won't introduce a new tool unless absolutely necessary.

I don't want to limit this guide to a particular operating system because while it benefits some people, it renders this guide useless for others. With some exceptions, I avoid discussing platform-specific details, assuming you already know how to install software using your package manager or where to find config files. Personally, I prefer FreeBSD because the system is clean, and its ports system allows you to customize your software. However, you can use any operating system of your choice. All the tools covered here run equally well on Linux distributions or other UNIX systems.

So, let's get started.

Terminology

Mail User Agent

An MUA is a tool that lets you compose new emails, send, and receive them. The mail(1) utility, commonly found on most UNIX systems, is an example of an MUA. Some organizations offer fancy web interfaces that essentially perform the same job. Each user can use whatever MUA suits them.

Mail Transfer Agent

The MTA is the heart of any mail system and is responsible for delivering an email from its source to the destination. MTAs use the SMTP protocol, which we will cover shortly. Common examples of MTAs include Exim, which, as of January 2024, is the most popular MTA out there, and Postfix, which we will cover in this guide.

Mail Delivery Agent

MDAs are used to manage emails and mailboxes. These tools receive emails from MTAs and store them in a mailbox. Then, users can connect to their mailboxes and read or delete their messages. MDAs commonly use the POP and IMAP protocols. Dovecot is a popular MDA that we will cover in this guide.

Part 1: The Big Picture

Even a small mail server is a complicated service as multiple protocols and tools are involved. At a high level, a mail service is comprised of two major parts: an SMTP server and a POP/IMAP server. The SMTP server is responsible for receiving emails from mail clients and/or other mail servers. It then routes these emails to their destination, where they are stored in a mailbox. Users can use the POP/IMAP server to fetch their emails from their mailboxes. The first part, the SMTP server, is handled by Postfix, and the POP/IMAP part is handled by Dovecot.

The following diagram shows what we are going to set up in this guide. In addition to Postfix/Dovecot, which are our SMTP and POP/IMAP servers, we use OpenDKIM to sign outgoing messages and verify incoming messages. DNS is used for email routing and authentication. Postfix and Dovecot communicate through two different channels. Postfix hands over new mails through an LMTP channel to Dovecot, which stores them in the user's mailbox. Usernames/Passwords are verified through a SASL channel.

Basic Email Flow

Before we go any further, we need to understand the basic email flow. Let's say your email address is you@sender.tld, and you want to send this message to yourfriend@recipient.tld. How does your email reach its destination? Look at the following diagram.

  1. In step 1, you compose your email using your favorite email client (aka MUA) and send it. The mail server at sender.tld will receive your mail from your email client. Its task is to route the email to its destination.
  2. Your mail server at sender.com is responsible for routing the email. It looks at the recipient and finds out that the email is destined for yourfriend@recipient.tld. To route the email, your mail server needs to find out the IP address of the machine responsible for receiving emails for recipient.tld. This is done through DNS and MX records. So, your mail server at sender.tld queries for MX records of recipient.tld.
  3. In step 3, after a number of DNS queries, the DNS server responds with the IP address of the machine responsible for receiving email for recipient.tld. DNS plays a crucial role in email routing. As already mentioned, this is done through MX records; we will configure MX records later on.
  4. Now that your mail server at sender.tld knows where to deliver the email, it starts a TCP connection on port 25 to the mail server at recipient.tld and delivers the email through the SMTP protocol. We will get to the details of the SMTP protocol later on.
  5. In step 5, the mail server at recipient.tld looks at the destination of the email and finds out the email is destined for one of its users, namely yourfriend@recipient.tld. It must add the email to your friend's mailbox. It can either directly add the mail to the recipient's mailbox, or it can assign this task to an MDA (like Dovecot). We assume an MDA is involved here, so the mail server at recipient.tld hands your email over to the MDA for local delivery. The protocol used here is LMTP, which is very similar to SMTP.
  6. In step 6, the MDA stores the email in the recipient's mailbox.
  7. Your friend fetches the email using the POP/IMAP protocol.

The SMTP Protocol

Postfix is an SMTP server, and understanding SMTP is essential for configuring Postfix effectively. SMTP is used to submit emails and utilizes TCP port 25. When you compose an email in your email client and submit it to your mail server, a conversation like this will occur:

  1. % nc smtp.example.com 25
  2. 220 smtp.example.com ESMTP Postfix
  3. HELO relay.example.org
  4. 250 Hello relay.example.org, I am glad to meet you
  5. MAIL FROM:<bob@example.org>
  6. 250 Ok
  7. RCPT TO:<alice@example.com>
  8. 250 Ok
  9. DATA
  10. 354 End data with <CR><LF>.<CR><LF>
  11. From: "Bob" <bob@example.org>
  12. To: "Alice" <alice@example.com>
  13. Date: Sat, 11 Jun 2022 20:28:10 +0930
  14. Subject: Test message
  15. Hello,
  16. This is a test message.
  17. Your friend,
  18. Bob
  19. .
  20. 250 Ok: queued as 12345
  21. QUIT
  22. 221 Bye

In the above example, blue lines belong to the client, and black lines belong to the server. On line 1, we initiate a TCP connection on port 25 of the server smtp.example.com. The Postfix mail server is listening on that port, so it receives our connection and introduces itself. Each response from the server starts with a status code. Any 3-digit code starting with 2 (i.e., 2xx) means successful completion of the client's command.

On line 3, the client introduces itself to the server with the HELO command. On line 4, the server greets the client and accepts the connection.

On line 5, the client specifies the sender of the email with the MAIL FROM SMTP command. The server responds with code 250. On line 7, the client specifies the recipient of the email with the RCPT TO command. This command can be used multiple times if the email should be delivered to multiple recipients.

On line 9, the client invokes the DATA command, signifying the start of the body of the email. On line 10, the server instructs the client to signify the end of the email body with <CR><LF>.<CR><LF>, which basically is a line containing only a single period (line 21). Lines 11 to 20 are email content. The email content consists of two parts: email headers (lines 11-14) and the message body (lines 16-20), separated by an empty line (line 15). The email content may also contain one or more attachments. On line 22, the server accepts the email from the client and queues it for delivery. The client issues the command QUIT on line 23, and then the server closes the connection.

This is not unlike the real-world mail you receive from a postman. A real-world mail is usually a letter sent in an envelope. Think of the SMTP session as the envelope, and the letter inside the envelope is the body of the message you send using SMTP DATA command.

On the envelope, the sender and recipient addresses are written. These are similar to SMTP MAIL FROM and RCPT TO commands. The letter inside the envelope contains headers, like subject, time and date, the sender's name, the recipient's name, etc. These are comparable to the email headers. A real-world mail may contain attachments inside the envelope. This analogy can help you understand the SMTP protocol.

Understanding the SMTP protocol is important for a postmaster because you will use it to troubleshoot your mail system. In summary, the SMTP protocol consists of these steps:

TCP Connection(where does the client come from)
HELO(who the client claims to be)
MAIL FROM(who is the sender)
RCPT TO(who is the recipient)
DATA(the email content, includes email headers, message body, and attachments)

All these can be subject to some restrictions. We may not want to accept emails from certain clients or messages that contain certain words. We can put various restrictions on any of these steps and control who can send emails from where to whom.

The POP3 Protocol

Users can use either POP3 or IMAP protocols to connect to their mailboxes, fetch their emails, read or delete them, categorize them, and perform similar actions. Here is an example of the POP3 protocol. The IMAP protocol offers somewhat similar functionality.

  1. % nc pop3.example.com 25
  2. +OK POP3 server ready <1896.697170952@pop.example.com>
  3. APOP mrose c4c9334bac560ecc979e58001b3e22fb
  4. +OK mrose's maildrop has 2 messages (320 octets)
  5. STAT
  6. +OK 2 320
  7. LIST
  8. +OK 2 messages (320 octets)
  9. 1 120
  10. 2 200
  11. .
  12. RETR 1
  13. +OK 120 octets
  14. <the POP3 server sends message 1>
  15. .
  16. DELE 1
  17. +OK message 1 deleted
  18. RETR 2
  19. +OK 200 octets
  20. <the POP3 server sends message 2>
  21. .
  22. DELE 2
  23. +OK message 2 deleted
  24. QUIT
  25. +OK POP3 server signing off (maildrop empty)
  26. <close connection>

A connection to TCP port 110 is established on line 1. The server introduces itself to the client on line 2 and says it's ready to receive the client's requests. The client logs into the system using the APOP command on line 3. This command takes two parameters: a username and a hashed password.

On line 5, the user issues the STAT command, which returns the total number of messages and total size (lines 8 to 10). Here there are two messages, numbered 1 and 2. The first message is 120 bytes, and the second message is 200 bytes (hence the total size is 320 bytes).

On line 12, the user issues the RETR command to retrieve the message numbered 1. The server proceeds to send the requested message to the user (lines 14-15).

On line 16, the user issues the DELE command to delete message numbered 1. Finally, on line 24, the user issues the QUIT command to terminate the connection.

Mailbox formats

All messages are stored in a mailbox so that the receiver can read them. Each user has its own mailbox. There are two common formats for the mailbox: mbox and Maildir.

mbox

In this format, all emails are stored in a plaintext file. New messages are simply appended to the end of the file. Since all messages are stored in a single file, we need a way to differentiate the messages. This can be done by simply placing a special mark at the end of a message, where the next message begins. This special mark is commonly known as From_ line. This line means the current message is finished, and the next message begins from here. Any line that starts with the keyword From is considered a message separator.

What if a message contains a line that starts with the word From? That may be confusing because it can be misunderstood as a legitimate From_ line. To fix this, we can simply start that line with another character, say <.

Here is an example of a mailbox in mbox format:

From MAILER-DAEMON Fri Jul  8 12:08:34 2011
From: Author 
To: Recipient 
Subject: Sample message 1
 
This is the body.
>From (should be escaped).
There are 3 lines.
 
From MAILER-DAEMON Fri Jul  8 12:08:34 2011
From: Author 
To: Recipient 
Subject: Sample message 2

This is the second body.

Maildir

In Maildir format, each user has a dedicated directory, and each message is stored in a separated file somewhere in that directory. This directory usually consists of 3 more subdirectories, named tmp, new, and cur.

The tmp directory is used to store any kind of temporary files, like the messages that are in the process of delivery. New and unread messages are stored in the new subdirectory, and when they are marked as read, they are moved into the cur subdirectory.

Email Headers

Each email message consists of two parts: Headers and the Body.

Headers are metadata about the actual message (the Body). They provide important information about the message, such as the sender's address, the receiver's address, the subject of the message, and the timestamp of the message. Headers and the Body are separated by an empty line.

It is important to understand that the SMTP protocol does not handle email headers. Emails are not routed based on the sender/receiver information present in the headers. The SMTP commands, MAIL FROM and RCPT TO, specify the actual sender/receiver of the message.

The role of DNS

DNS plays an important role in email delivery. As we have seen, DNS is used to find the mail server responsible for a particular domain. This is done through MX records. But the role of DNS does not end here.

DNS can prevent unauthorized parties from sending emails on behalf of domains they do not own. This is called email authentication, which the original SMTP protocol does not cover.

There are 3 types of email authentication methods we are going to cover in this guide, namely SPF, DKIM, and DMARC.

Sender Policy Framework (SPF)

How can you ensure that you have received the email from a legitimate mail server at foocompany.com and nobody is spoofing their domain? Unfortunately, domains can be spoofed, so we need a way to ensure that emails come from legitimate mail servers belonging to that domain.

We have SPF for that. SPF is used to ensure that the sending MTA is allowed to use the domain they claim to own. This way you can ensure that the email comes from the legitimate mail server.

SPF allows you to define a list of IP addresses that are allowed to use your domain. Only emails that come from these IP addresses are considered to be legitimately originated from your domain. Receiving mail servers can check if the actual IP address used to send the email exists in this list.

Here is an example SPF record. It says, any machine from 192.0.2.0/24 subnet as well as 198.51.100.123 are permitted to use domain example.com to send email messages. All other servers are not permitted (-all)
example.com.        IN      TXT     "v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.123 a -all"

DomainKeys Identified Mail (DKIM)

How do you know if an email was not altered by an unauthorized party in transit? In the real world, you sign your mails so that the receiver, who can recognize your signature, can ensure that the message is indeed written by you. A similar concept is used in email systems.

DKIM allows you to sign the emails originated from your domain. This way, the receiver can ensure that the email is sent from a valid address in your domain and is authorized by you. This signature is very similar to signatures in the real world; it is just mathematically calculated and added to the email. This process is fully performed automatically by some tools and the end user need not be involved.

The emails are signed through a pair of keys: A public key and a private key.

The public key is stored in the DNS system and anybody can get it, whereas the private key should be kept secret and only you should have it. You sign all authorized outgoing emails using the private key. Then the receiver can verify this signature by using the associated public key, which is available to them through DNS.

Later, we will use OpenDKIM to generate a DKIM record for our domain.

Domain-based Message Authentication, Reporting and Conformance (DMARC)

DMARC allows you to define policies for emails originated from your domain given the results of SPF and DKIM checks. You can provide instructions to receiving mail servers on how to handle emails that fail to pass SPF or DKIM checks. You may ask receiving mail servers to ignore failed SPF and DKIM checks and deliver the email anyway, or you may ask them to put these kinds of messages into the Spam folder or reject them altogether. This example DMARC record asks receiving mail servers to ignore failed DKIM and SPF checks. Set p=quarantine to mark them as spam, and p=reject to ask for rejection. adkim=r; aspf=r; means DKIM and SPF checks are "relaxed". Use s to indicate a strict mode.

_dmarc.example.com.    IN      TXT     "v=DMARC1; p=none; adkim=r; aspf=r;"

Part 2: Preparing

Check your system's date and time

Incorrect date and time can confuse mail programs. Messages may be stored as files, and their timestamp is important for applications that work with those files. Many email headers also contain time and date, not to mention that you need to have the correct time and date when you are looking at a particular message in log files. Use any NTPD client that suits you. I use OpenNTPD, but many Linux distributions now use Chrony. Whatever you use, just make sure your NTP service is configured correctly and actually works.

Check if your system can resolve DNS queries

Postfix locates the destination mail servers through DNS queries. So not having a working DNS service means you can't deliver any email to their destination. Slow DNS queries can greatly slow down your overall mail server performance, so you are advised to use a reliable DNS server. On a UNIX system, add your DNS servers to /etc/resolv.conf.

nameserver 198.51.100.1
nameserver 198.51.100.2

Syslog configuration

I prefer to have all mail-related logs, regardless of the application that generated them, in a single file. Make sure mail logs are organized in whatever format and place that you prefer. On systems that still haven't deprecated syslog, you can manage your mail logs through /etc/syslog.conf.

*.err;kern.warning;auth.notice;mail.none		/dev/console
mail.*					-/var/log/maillog

Check if you have working A, MX, and PTR records

Other mail servers use your MX records to discover your mail server, and it should actually resolve to an IP address. So make sure you have configured MX and A records for your domain. The DNS resolution should also work in reverse order as well: the IP address of your mail server should point to your domain. This is done via PTR records. As PTR records use a different namespace, you may need to ask your ISP to define the PTR record for you.

For A and MX records, simply add these lines to your zone file. Each MX record has a priority (10 in the example below). The lower this number, the higher the priority of that server.

example.com.              IN      MX      10      mx.example.com.
mx.example.com.         IN      A                  198.51.100.100

You can use dig(1) or drill(1) to check your DNS records. Here's an example:

% drill example.com MX
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 28521
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 
;; QUESTION SECTION:
;; example.com.   IN      MX

;; ANSWER SECTION:
example.com.      300     IN      MX      20 mx.example.com.

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 377 msec
;; SERVER: 198.51.100.1
;; WHEN: Sun Jan 14 15:30:19 2024
;; MSG SIZE  rcvd: 46

% drill mx.example.com A
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 51555
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 
;; QUESTION SECTION:
;; mx.example.com. IN      A

;; ANSWER SECTION:
mx.example.com.    1147    IN      A       198.51.100.100

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 88 msec
;; SERVER: 198.51.100.1
;; WHEN: Sun Jan 14 15:32:51 2024
;; MSG SIZE  rcvd: 45

% drill -x 198.51.100.100
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 17700
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 
;; QUESTION SECTION:
;; 100.100.51.198.in-addr.arpa. IN      PTR

;; ANSWER SECTION:
100.100.51.198.in-addr.arpa.    21600   IN      PTR     mx.example.com.

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 306 msec
;; SERVER: 198.51.100.1
;; WHEN: Sun Jan 14 15:35:06 2024
;; MSG SIZE  rcvd: 71


Firewall ports

The SMTP protocol uses TCP port 25 and Postfix and other mail servers use this port to send and deliver email messages. Make sure nothing blocks this port. Most ISPs block this port to prevent spamming from their network. Ask them to open it for you. POP and IMAP use TCP ports 110 and 143 respectively, so make sure nothing blocks these ports either. DNS uses port 53 on both TCP and UDP, ask your netwrok administrator to unblock these ports.

Part 3: Postfix

Installing Postfix

In this section we're going to install Postfix. You can use your operating system's package manager and this is the recommended way because it allows you to easily update Postfix into the newer version without much headache. But as the postmaster, you need to know how to compile Postfix from the source code. This is why we are going to install Postfix from source code in this guide, however you are encouraged to use a package manager.

  • Download the Postfix distfile from postfix.org
  • Unpack the distfile
  • Generate makefiles
  • Build Postfix
  • Install the package
  • Our Postfix setup needs a few dependencies, make sure these dependencies are already installed on your system.

  • OpenSSL
  • BerkleyDB
  • LibPCRE2
  • We will install postfix into /usr/local/. We also enable compiler optimization and security features, like stack protection. The following command is long, but it's not complex. Many of the paramaters are for specifiying the installation path of different Postfix components like binary files, configuration files, man pages, and other components. We also enabled SASL and TLS, and told Postfix where it can find various libraries and header files it needs for compilation and linking. We have also disabled debugging flags and EAI support. Please read the official documentatios for a complete description of these parameters.

    % make makefiles DEBUG= pie=yes config_directory=/usr/local/etc/postfix daemon_directory=/usr/local/libexec/postfix meta_directory=/usr/local/libexec/postfix \
    command_directory=/usr/local/sbin sendmail_path=/usr/local/sbin/sendmail newaliases_path=/usr/local/bin/newaliases mailq_path=/usr/local/bin/mailq \
    manpage_directory=/usr/local/man readme_directory=no html_directory=no queue_directory=/var/spool/postfix data_directory=/var/db/postfix mail_owner=postfix \
    setgid_group=maildrop CC="cc" OPT="-O2 -pipe -march=core2 -fstack-protector-strong -fno-strict-aliasing" CCARGS="-Wmissing-prototypes -Wformat \
    -Wno-comment -DUSE_SASL_AUTH -DNO_EAI -DHAS_PCRE=2 -I/usr/local/include -DDEF_SERVER_SASL_TYPE=\\\"dovecot\\\" -DUSE_TLS -I/usr/include \
    -I/usr/local/include/db5" shared=yes shlib_directory=/usr/local/lib/postfix dynamicmaps=yes "AUXLIBS_PCRE=`/usr/local/bin/pcre2-config --libs8`" \
    AUXLIBS="-L/usr/lib -fstack-protector-strong -lssl -lcrypto -L/usr/local/lib/db5 -ldb-5.3"

    You need to add required user accounts and groups. Add the following line to /etc/passwd using vipw:

    postfix:*:125:125:Postfix Mail System:/var/spool/postfix:/usr/sbin/nologin
    And the following lines to /etc/group using vigr:
    postfix:*:125:
    maildrop:*:126:
    

    Now run make to start compilation, and then run make install to install Postfix onto your system. The installation process may ask you some questions, just accept the defaults.

    Postfix Architecture

    Postfix, as an MTA, receives incoming emails from various sources. For example, Postfix can receive emails from a remote SMTP client or from a local application. After that, Postfix needs to determine where and how to deliver the email to its destination. This is called email routing. If the destination is a local user, Postfix simply adds the email to their mailbox. On the other hand, if the destination is a user on a remote machine, Postfix needs to start an SMTP session to deliver the email. This process of receiving incoming emails from various sources, routing them, and delivering them to their destination is a long process, and it may not be a good idea to have a big monolithic application to handle this process.

    To properly handle this whole process, Postfix comprises many small programs, each one is responsible for doing a small part of this process. Let's have a quick look at some of these programs before we get into the configuration.

    Incoming emails originate either from a local application or from a remote SMTP client. smtpd is responsible for receiving emails from remote mail clients, while the pickup utility is responsible for receiving local emails. The pickup utility does not interact with remote users and does not need to.

    Both of these programs then feed the received message into the cleanup daemon. The cleanup utility inserts any required missing header into the message (like From:, To:, and Date:), rewrites addresses in the standard form (user@fully-qualified-domain) with the help of the trivial-rewrite utility, and places the message into the incoming queue. The qmgr daemon is then informed of the newly received message.

    Postfix maintains several queues to efficiently handle message transmission. Think of queues as regular filesystem directories. Messages are placed into these directories as files.

    When a message is ready to be delivered, the qmgr program moves the message from the incoming queue into the active queue. Then it dispatches a delivery agent to actually deliver the message. Delivery agents are small programs that can deliver a message to a destination. Messages for remote users are delivered through the smtp agent. Postfix can directly add the message to a local user's mailbox through local, virtual, or lmtp agents (depending on the type of delivery). Postfix can even feed the message directly into the standard input of another program through the pipe user agent.

    If Postfix fails to deliver the message due to a temporary reason, the qmgr daemon moves the message into the deferred queue and retries later.

    Postfix configuration

    You do not need to know about all of the Postfix configuration parameters. We will talk about the most important parameters, those that you actually need. Before delving into the configuration file, there's an important concept you need to understand: lookup tables

    Key Value
    1 Apple
    2 Orange
    3 Banana
    4 Grapes

    Think of a lookup table as a simple table with two columns: a key and a value. Each row of this table consists of a <key>,<value> pair. Lookup tables work in this way: you specify the key, and the value associated with that key is returned as a result. In the above table, each row represents a key-value pair. For instance, if you specify the key "2," the associated value is "Orange." This is the basic structure of a lookup table, where you can look up values based on their corresponding keys.

    Lookup tables come in many different types. Some of them can understand complex regular expressions (like pcre, regexp, cidr). These are read from top to bottom, and the first match wins. Some other types of lookup tables are binary indexed files built from simple text files (like hash and btree). These maps are fast, because they are indexed and the order of entries does not matter.

    All of the lookup tables have this simple <key>,<value> structure. postconf -m can tell you what type of lookup tables your postfix installation supports.

    The main configuration file of Postfix is main.cf. This file has a simple syntax. Comments start with # and continue to the end of the line. You can break a long line into multiple smaller lines by adding some whitespace (tabs, spaces) at the beginning of the subsequent lines. This would still be understood as a single logical line.

    Basic configuration parameters

    There are some basic configuration parameters that we should cover before going any further.

    Setting the hostname

    As we have seen, at the beginning of any SMTP connection, the mail server greets the mail client with a hostname. We must configure this hostname. By default, Postfix uses the hostname set on your server to introduce itself to mail clients. So, either set a Fully Qualified Domain Name as the hostname of your server, or explicitly tell postfix what domain to use when greeting mail clients. You are encouraged to use an FQDN.
    myhostname = mail.example.com
    When in delivery mode, Postfix acts as a mail client to other mail servers and should issue a domain name to introduce itself using the SMTP HELO or EHLO commands. The paramater that speicifes this domain is smtp_helo_name, which defaults to $myhostname. Some mail servers are configured to reject clients that do not use an FQDN. So make sure to use a Fully Qualified Domain Name.

    Setting the domain

    Postfix is smart enough to derive the domain name from the $myhostname. But you can explicitly let Postfix know which domain to use. The mydomain parameter should be set to the internet domain of your mail system.
    mydomain = example.com

    Setting the destination domain

    We must let Postfix know for which domain it is responsible to receive mail and should consider itself as the final destination. All emails sent to this domain are accepted by Postfix as the final destination. You can use other configuration parameters as variables to avoid hardcoding the values.
    mydestination = $mydomain, $myhostname

    Fixing incomplete domain names

    Many mail clients use incomplete email addresses like joe (as opposed to joe@example.com) while sending an email, specifically emails sent from cron jobs and other UNIX utilities. This becomes a problem only when the email is destined to a user on a remote host. Postfix can append the domain part to these incomplete mail addresses and fix them. The value of $myorigin is apeended to these incomplete addresses.
    myorigin = $mydomain

    Specifiying where to deliver emails destined to root

    Delivering email to the root user is a challenge as Postfix requires root privileges to do so. A common practice is to have another user receive emails destined to root. There is a file that maps email address to UNIX system accounts. You can define email addresses in this file, and emails destined to these addresses are delivered to the corresponding UNIX system account. All we need to do is to edit this file and specify an alternative user account for the root address. This file, called aliases file, is usually located beside main.cf in configuration directory. Edit this file and add this line to deliver all mails destined to root to another user, say admin
    root: joe
    
    This aliases file is an example of a lookup table we've seen earlier. This file uses the hash format, so we need to create an indexed version to speed up the lookup process. Add as many aliases as you need. Be aware that a UNIX system account must exist to receive the email. Then run this command to create an indexed version.
    postalias hash:/etc/postfix/aliases
    

    Starting Postfix and sending a test message

    Now with these basic parameters out of the way, we are ready to start Postfix and send a test message to see if it works.
    # postfix start
    
    Use your favourite mail client to send a test message. Here we use the mail(1) utility, you can also use nc(1) or telnet(1) as we have seen earlier.
    % mail joe@example.com
    Subject: Test message
    
    This is a test message to see if the mail system works.
    .
    
    Finish your message by entering a single period. Check the logs files:
    % tail -f /var/log/maillog
    

    SMTP Client Restrictions

    Earlier, we discussed the ability to impose restrictions on mail clients and control the senders, destinations, and contents of emails. Now it's time to talk about these restrictions as they are an important part of configuring Postfix. By using these restrictions, you can reject undesired emails and prevent possible spammers from misusing your mail server. Postfix has parameters to put various restrictions in each of the SMTP stages. The following table summarizes stages of an SMTP session and the restriction parameters you can apply at each stage.

    Stage Restriction parameter Description
    TCP Connection establishment smtpd_client_restrictions Restrictions to put on the client IP address or hostname
    HELO/EHLO smtpd_helo_restrictions Restrictions to put in the context of a client HELO/EHLO command
    MAIL FROM smtpd_sender_restrictions Restrictions to put in the context of a client MAIL FROM command
    RCPT TO smtpd_relay_restrictions Restrictions to put in the context of a client RCPT TO command, specifically used to define relay permission rules
    RCPT TO smtpd_recipient_restrictions Restrictions to put in the context of a client RCPT TO command
    DATA smtpd_data_restrictions Restrictions to put in the context of a client DATA command (email content)

    We simply put our restriction rules in those parameters listed in the second column. Now what are these restrictions? The restriction rules we are talking about range from simple reject or permit to advanced checks using lookup tables.

    The following table summarizes the restriction rules and the stage in which we can use them. By default, Postfix waits until the client issues its first RCPT TO command, then it applies the restrictions. It does not evaluate and apply restrictions earlier than the RCPT TO stage. This is useful as you can put all of your restrictions into smtpd_recipient_restrictions and smtpd_relay_restrictions. However, if for any reason you don't want to wait until the client issues its RCPT TO command, you can instruct Postfix to immediately reject the client by setting smtpd_delay_reject=no.



    Stage Available rules at this stage Description
    smtpd_client_restrictions check_client_access type:table Use a lookup table to check client access based on their address
    reject_unknown_client_hostname Reject the client if the client domain and IP address do not match by forward and reverse DNS lookups
    reject_unknown_reverse_client_hostname Reject the client when its IP address does not resolve to its domain
    reject_unauth_pipelining Used to prevent clients from sending bulk mail
    smtpd_helo_restrictions check_helo_access type:table Use a lookup table to check client access based on their HELO hostname
    reject_invalid_helo_hostname Reject the request when the HELO or EHLO hostname is malformed.
    reject_non_fqdn_helo_hostname Reject the request when the HELO hostname is not an FQDN
    reject_unknown_helo_hostname Reject the request when the HELO hostname has no MX or A record in DNS
    smtpd_sender_restrictions check_sender_access type:table Use a lookup table to check sender access
    reject_non_fqdn_sender Reject the request when the MAIL FROM address specifies a domain that is not in FQDN form
    reject_sender_login_mismatch An alias for reject_authenticated_sender_login_mismatch, reject_unauthenticated_sender_login_mismatch
    reject_unknown_sender_domain Reject the request when Postfix is not the final destination for the sender address, or if the sender domain does not exist
    reject_unverified_sender Reject the request when mail to the MAIL FROM address is known to bounce, or when the sender address destination is not reachable
    smtpd_relay_restrictions permit_sasl_authenticated Permit the request when the client is successfully authenticated
    permit_mynetworks Permit the request when the client IP address matches any network or network address listed in $mynetworks
    reject_unauth_destination This will prevent your mail server from becoming an open relay, very important
    smtpd_recipient_restrictions check_recipient_access type:table Use a lookup table to check recipient access
    reject_non_fqdn_recipient Reject the request when the RCPT TO address specifies a domain that is not in FQDN format
    reject_unknown_recipient_domain Reject the request when the recipient domain does not exist
    reject_rbl_client rbl_domain=d.d.d.d Reject the request if the client is listed in rbl_domain


    This is how our restriction rules look like. We require that all clients introduce themselves with a HELO command as it is required by RFC. We also disable SMTP VFRY command and hide Postfix version. We used warn_if_reject for rules that are considered too restrictive. This will not reject the request, but will just log the effect of restriction.

    smtpd_helo_required = yes
    
    disable_vrfy_command = yes
    
    # Hide Postfix version
    smtpd_banner = $myhostname ESMTP
    
    # Wait until the RCPT TO command before evaluating restriction rules
    smtpd_delay_reject = yes
    
    
    smtpd_relay_restrictions =
      reject_sender_login_mismatch,
      permit_sasl_authenticated,
      permit_mynetworks,
      reject_unauth_destination
    
    smtpd_recipient_restrictions =
      check_client_access hash:/etc/postfix/client_ip_access,
      check_sender_access hash:/etc/postfix/sender_access,
      check_recipient_access hash:/etc/postfix/recipient_access,
      reject_non_fqdn_recipient,
      reject_non_fqdn_sender,
      reject_unknown_sender_domain,
      reject_unknown_recipient_domain,
      reject_unauth_pipelining,
      permit_mynetworks,
      permit_sasl_authenticated,
      warn_if_reject reject_unknown_client_hostname, # too restrictive, you may reject legimiate email
      reject_unknown_reverse_client_hostname,
      reject_invalid_helo_hostname,
      reject_non_fqdn_helo_hostname,
      warn_if_reject reject_unknown_helo_hostname, # too restrictive, you may reject legimiate email
      reject_sender_login_mismatch,
      reject_unauth_destination,
      reject_rbl_client bl.spamcop.net,
      reject_rbl_client sbl-xbl.spamhaus.org,
      reject_rbl_client zen.spamhaus.org,
      reject_rbl_client db.wpbl.info,
      reject_rbl_client cbl.abuseat.org,
      reject_rbl_client proxies.blackholes.wirehub.net,
      reject_rbl_client query.bondedsender.org,
      check_policy_service unix:private/policyd-spf,
      permit
    

    Using SSL/TLS

    What good is a mail server when others can easily see what you are sending and receiving? We need to use SSL/TLS to ensure that nobody can see what we are sending/receiving, otherwise they can easily steal our data.

    Keep in mind that many mail clients still do not support SSL/TLS, and if we force it, we will lose some emails. So we will use it only when available. This is called Opportunistic TLS. Mandatory TLS uses TCP port 465, also known as SMTPS.

    TLS configuration involves two parts. When Postfix acts as server to mail clients (smtpd_*), and when Postfix acts as a mail client to other mail servers (smtp_*).

    In order to use TLS, you need a certificate and a private key in PEM format. You can buy them, get one for free using Let's Encrypt, or generate a self-signed certificate.

    In the below configuration, We have enabled Opportunistic TLS for both server-side and client-side sessions. We also disabled weak SSL/TLS versions, as well as ciphers that are known to be weak. Since TLS handshake is time-consuming and uses network bandwidth, we cache TLS sessions into a file.

    tls_high_cipherlist = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
    
    tls_random_source = dev:/dev/urandom
    
    tls_ssl_options = no_compression
    
    
    # Server-side TLS. How others will connect to Postfix
    # Opportunistic TLS: announce STARTTLS support to remote SMTP clients, but do not require that clients use TLS encryption
    smtpd_use_tls = yes
    smtpd_tls_security_level = may
    
    # When TLS encryption is optional in the Postfix SMTP server, do not announce or accept SASL authentication over unencrypted connections
    smtpd_tls_auth_only = yes
    
    # The minimum TLS cipher grade that the Postfix SMTP server will use with opportunistic TLS encryption
    smtpd_tls_ciphers = high
    
    # Path of keys
    smtpd_tls_key_file = /etc/postfix/certs/privkey.pem
    smtpd_tls_cert_file = /etc/postfix/certs/fullchain.pem
    
    # Log only a summary message on TLS handshake completion
    smtpd_tls_loglevel = 1
    
    # TLS protocols accepted by the Postfix SMTP server with opportunistic TLS encryption.
    smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, TLSv1.2
    
    # TLS protocols accepted by the Postfix SMTP server with mandatory TLS encryption.
    smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, TLSv1.2
    
    # The minimum TLS cipher grade that the Postfix SMTP server will use with mandatory TLS encryption.
    smtpd_tls_mandatory_ciphers=high
    
    # Cache TLS sessions
    smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_tls_cache
    
    
    # How postfix will connect to other mail servers as a client
    # Opportunistic mode: use TLS when a remote SMTP server announces STARTTLS support, otherwise send the mail in the clear.
    smtp_use_tls = yes
    smtp_tls_security_level = may
    
    # The minimum TLS cipher grade that the Postfix SMTP client will use with opportunistic TLS encryption.
    smtp_tls_ciphers = high
    
    # TLS protocols that the Postfix SMTP client will use with opportunistic TLS encryption.
    smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, TLSv1.2
    
    # Log the hostname of a remote SMTP server that offers STARTTLS
    smtp_tls_note_starttls_offer = yes
    
    # A file containing CA certificates of root CAs trusted to sign either remote SMTP server certificates or intermediate CA certificates.
    smtp_tls_CAfile=/etc/ssl/cert.pem
    
    # TLS protocols that the Postfix SMTP client will use with mandatory TLS encryption.
    smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, TLSv1.2
    
    # Cache TLS sessions when in client mode
    smtp_tls_session_cache_database  = btree:${data_directory}/smtp_tls_cache
    

    The OpenSSL package includes a s_client utility that you can use to simulate a TLS session:

    openssl s_client -starttls smtp -connect localhost:25
    

    After the EHLO command, the server should offer you with STARTTLS.

    Using SASL

    We can define username/passwords for our users and allow them to use our mail server. Postfix supports SASL authentication framework, described in RFC 2222. We use Dovecot as SASL plug-in in this guide.

    Clients need to use ESMTP in order to use SASL (they need to issue EHLO command as opposed to HELO). Postfix will then offer them with some authentication methods, like PLAIN or LOGIN. Then the client will choose their preferred authentication method and submit their username/password. Next, Postfix will hand the credentials over to dovecot, which uses an autherntication backend (like LDAP or a passwd file) to verify if the credentials are valid.

    # Enable SASL authentication in the Postfix SMTP server.
    smtpd_sasl_auth_enable = yes
    
    # Use dovecot as SASL plug-in for authentication
    smtpd_sasl_type = dovecot
    
    # Dovecot will create a socket on this path that postfix will use for authentication (relative to $data_directory)
    smtpd_sasl_path = private/auth
    
    # Disallow methods subject to passive (dictionary) attack and methods that allow anonymous authentication
    smtpd_sasl_security_options = noanonymous nodictionary
    
    # Plaintext authentication over TLS is fine
    smtpd_sasl_tls_security_options = noanonymous
    
    # Do not report the SASL authenticated user name in the smtpd(8) Received message header.
    smtpd_sasl_authenticated_header = no
    
    # append the domain to a SASL login name that does not have a domain part
    smtpd_sasl_local_domain = $myhostname
    
    # Enable interoperability with remote SMTP clients that implement an obsolete version of the AUTH command
    broken_sasl_auth_clients = yes
    

    Needless to say that we need to install and configure Dovecot. Use the package manager of your OS to install Dovecot. Dovecot should expose a socket for Postfix, so that the latter could ask for credentials verification. Dovecot also needs a backend to store/retrieve the usernames and passwords. In this example, we use a simple passwd-like file.

    auth_default_realm = example.com
    auth_mechanisms = plain login
    disable_plaintext_auth = no
    
    # expose a socket for postfix
    service auth {
      unix_listener /var/spool/postfix/private/auth {
        mode = 0660
        user = postfix
        group = postfix
      }
    }
    
    # use a passwd-like file as authentication backend
    # passwords are encrypted using CRYPT format
    userdb {
      args = username_format=%n /usr/local/etc/dovecot/users
      default_fields = uid=vmail gid=vmail
      driver = passwd-file
    }
    
    # the same file can store both usernames and passwords
    passdb {
      args = scheme=CRYPT username_format=%n /usr/local/etc/dovecot/users
      driver = passwd-file
    }
    
    # cache all authentication results for one hour
    auth_cache_size = 10M
    auth_cache_ttl = 1 hour
    auth_cache_negative_ttl = 1 hour
    

    Define your username and passwords in /usr/local/etc/dovecot/users. You can use doveadm(1) to generate crypted passwords:

    % doveadm pw
    Enter new password:
    Retype new password:
    {CRAM-MD5}913331d8782236a8ecba7764a63aa27b26437fd40ca878d887f11d81245c2c6b
    

    One last thing that remains is that, we must assign usernames to email addresses. In other words, usernames must own email addresses, otherwise any logged in user can send email on behalf of any other user. We use a lookup table for that. Add this line into your main.cf:

    smtpd_sender_login_maps = hash:/usr/local/etc/postfix/controlled_envelope_senders
    

    Where the file contains:

    # envelope sender             owners (SASL login names)
    bob@example.com            bob@example.com
    alice@example.com           alice@example.com
    
    Finally, create an indexed version to speed up the lookup:
    postmap hash:/usr/local/etc/postfix/controlled_envelope_senders
    

    Now you can place reject_sender_login_mismatch somewhere in your smtpd_recipient_restrictions

    smtpd_recipient_restrictions =
      ...
      reject_sender_login_mismatch,
      ...
    

    Virtual Mailboxes

    So far, our mailboxes were based on UNIX system accounts defined in /etc/passwd. You may want to give somebody a mailbox, but don't want to give him a UNIX system account.

    The solution to this is using virtual mailboxes, which are mailboxes that are not bound to any username in /etc/passwd. We also want to use an external delivery agent to add newly received mails into mailboxes. Even though Postfix is able to deliver mails into mailboxes, we will use Dovecot for that, because it gives us more features. We can use POP3 and IMAP protocols to access our mailboxes.

    # deliver email to the LMTP socket expose in this path by dovecot 
    virtual_transport = lmtp:unix:private/dovecot-lmtp
    
    # we accept mail for example.com
    virtual_mailbox_domains = example.com
    
    # valid address in this domain are defined here
    virtual_mailbox_maps = hash:/usr/local/etc/postfix/vmailbox
    
    # each email address can have one or more aliases, which are defined here
    virtual_alias_maps = hash:/usr/local/etc/postfix/virtual
    

    Where /usr/local/etc/postfix/vmailbox is:

    # email address                    mailbox path
    joe@example.com                example.com/joe/
    bob@example.com                example.com/bob/
    alice@example.com              example.com/alice/
    

    and /usr/local/etc/postfix/virtual is:

    # alias                                         real email
    root@example.com                    joe@example.com
    postmaster@example.com        bob@example.com
    hostmaster@example.com        alice@example.com
    webmaster@example.com        alice@example.com
    abuse@example.com                alice@example.com
    
    

    Here, we defined our addresses in a simple plaintext file. This is acceptable for small mail servers when new users are rarely added. But if you have hundreds or thousands of users, you might want to consider using more efficient solutions like LDAP, or a SQL database. We will cover that in another guide.

    Add these lines to your dovecot.conf to expose an LMTP socket for Postfix.

    # expose an LMTP socket for postfix to deliver mail
    service lmtp {
      unix_listener /var/spool/postfix/private/dovecot-lmtp {
        mode = 0600
        user = postfix
        group = postfix
      }
    }
    

    Part 4: Dovecot

    We already covered some Dovecot configuration in previous parts. Now it's time to study Dovecot more throughly. As you already know, Dovecot is a POP/IMAP server. It manages mailboxes and email messages. Users can fetch and organize their messages.

    Basic Dovecot configuration

    The first thing that we should do is setting our domain name. This is done via hostname parameter.

    hostname = example.com
    

    We can specify a message to greet clients:

    login_greeting = Example.com Mail Server Ready...
    

    Specify an email address here. This is used as the source address from which email rejection messages are sent.

    postmaster_address = postmaster@datxsoft.com
    

    Tell dovecot which protocols it should support.

    protocols = imap pop3 sieve
    

    The first thing we need to is to create IMAP mailbox folders. Each mailbox has a folder for newly received emails, which is commonly called Inbox. Spam and Junk messages are commonly stored in Spam folder. When you delete a message, it goes to Trash. And finally, drafts are stored in Drafts folder. We need to create these folders. Add these lines to your Dovecot configuration.

    namespace inbox {
      inbox = yes
      location =
      mailbox Drafts {
        auto = subscribe
        special_use = \Drafts
      }
      mailbox Junk {
        auto = subscribe
        special_use = \Junk
      }
      mailbox Sent {
        auto = subscribe
        special_use = \Sent
      }
      mailbox "Sent Messages" {
        special_use = \Sent
      }
      mailbox Trash {
        auto = subscribe
        special_use = \Trash
      }
      prefix =
      separator = /
      type = private
    }
    

    Next, we need to tell dovecot the format of mailboxes and where to store them. If you have multiple domains, you can keep their mailboxes separate by storing them in their own path.

    mail_location = maildir:/var/mail/virtual/%d/vmailbox/%n
    mail_attachment_dir = /var/mail/virtual/%d/attachments
    mail_attachment_min_size = 32k
    

    %d is replaced with the domain name, and %n is replaced with the name of mailbox (username). This way, mailboxes of each domain are kept separate. Attachments that are more than 32k are stored in a separate directory.

    Dovecot must have approperiate permissions on /var/mail/virtual in order to store messages there. Add this line to your /etc/passwd thorugh vipw:

    vmail:x:5000:5000::/var/mail/virtual:/usr/sbin/nologin
    

    And this one to /etc/group thorugh vigr:

    vmail:x:5000:dovecot
    

    Now create the directories:

    DOMAIN_NAME=example.com
    mkdir /var/mail/virtual/${DOMAIN_NAME}/{vmailbox,attachments}
    chown vmail:vmail /var/mail/virtual/${DOMAIN_NAME}/{vmailbox,attachments}
    chmod 770 /var/mail/virtual/${DOMAIN_NAME}/{vmailbox,attachments}
    

    Dovecot SSL configuration

    Clients should connect to dovecot through a secure channel. Username/Passwords should not be sent over network in plaintext format, and their email messages may contain sensitive data.

    # Using SSL is required
    ssl = required
    
    # path of public and private keys
    ssl_cert = </etc/dovecot/certs/fullchain.pem
    ssl_key = </etc/dovecot/certs/privkey.pem
    
    ssl_min_protocol = TLSv1.2
    
    ssl_cipher_list = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
    
    ssl_prefer_server_ciphers = yes
    

    Part 5: OpenDKIM

    OpenDKIM is an open source implementation of DKIM, released under a BSD-like license. We have already covered DKIM concepts. Now we get to the configuration. OpenDKIM uses a private key to sign the messages. The associated public key is stored in the DNS system so that others can get it and verify our signature. This is called "signer" mode. OpenDKIM can also verify incoming email sent from other mail servers. This is called "verifier" mode.

    First off, install OpenDKIM through your package manager. In OpenDKIM, private keys are assigned a name, and this name is called Selector. You can use any name you wish. But the convention is to use current date, or the hostname of your MTA. You can have multiple domains and sign emails originated from each domain using a different key (Selector).

    Generate a key by running this command:

    opendkim-genkey -s SELECTOR -d DOMAIN
    opendkim-genkey -s mta0 -d example.com
    

    The corresponding public key will be printed out on the terminal. Publish this public key on your DNS server using TXT records.

    SELECTOR._domainkey.DOMAIN
    

    We used mta0 as our selector and our domain is example.com, so the record would be:

    mta0._domainkey.example.com
    

    Move your private keys to /var/db/dkim/, change their ownership to opendkim:opendkim (assuming that you are running opendikm as this user:group, more on that in a bit), and their permission to 0600. The /var/db/dkim should have a permission of 0700.

    KEY_NAME=mykey.pm
    chmod 0700 /var/db/dkim
    chown opendkim:opendkim /var/db/dkim
    mv ${KEY_NAME} /var/db/dkim
    chown opendkim:opendkim  /var/db/dkim/${KEY_NAME}
    chmod 0700 /var/db/dkim/${KEY_NAME}
    

    The KeyTable and SigningTable

    OpenDKIM can do more than just blindly sign emails using a key. You can have fine-grained control over which keys should be used for which emails. For these advanced use-cases it's important to understand KeyTable and SigningTable files.

    The KeyTable file is a list of keys. Let's say you have two keys, exkey and specialkey. We sign all emails for example.com with exkey. But we have a special user for which we want to use the other key, namely specialkey. Additionally, for exkey, we want to use selector foo, and for specialkey we want to use selector bar (both arbitary names). Our key files would look like this:

    exkey               example.com:foo:/var/db/dkim/example.key
    specialkey        example.com:bar:/var/db/dkim/special.key
    

    We have specified the path of each key, named them using a selector (foo and bar), and assigned them a domain (example.com for both keys), and then grouped eveything under a name (exkey and specialkey). Now its time to write SigningTable, which would look like this:

    specialuser@example.com         specialkey
    *@example.com                         exkey
    

    This simply says use specialkey for specialuser@example.com, and sign everything else by exkey. You can add more keys for more domains similarly.

    OpenDKIM configuration file

    Now that our keys are ready, let's get to OpenDKIM's main configuration file, opendkim.conf, usually located under /etc.

    # Automatically restart OpenDKIM on failures
    AutoRestart             Yes
    
    # But no more than 10 restart per hour
    AutoRestartRate         10/1h
    
    # Our domain
    Domain                  example.com
    
    # Listen on this socket, Postfix will connect to this socket and pass the mails to OpenDKIM
    # to be signed or verified.
    Socket                  inet:10000@127.0.0.1
    
    # Save your PID here
    PidFile         /run/opendkim/opendkim.pid
    
    # Use syslog for logging
    Syslog                  Yes
    
    # Log successful signing
    SyslogSuccess           Yes
    
    # Run as opendkim user and group
    UserID          opendkim:opendkim
    
    # Run in "singer" and "verifier" mode, both sign outgoing mails
    # that originate from our own users, and also very incoming emails
    # originated from other mail servers, but are destined to our users
    Mode                    sv
    
    Canonicalization        relaxed/simple
    
    # Trusted hosts whose email should be signed rather than verified are defined
    # in this file
    InternalHosts           refile:/etc/opendkim/TrustedHosts
    
    # Path of KeyTable
    KeyTable                refile:/etc/opendkim/KeyTable
    
    # Path of SigningTable, since we used wildcards, we need to specify the format as "refile"
    SigningTable            refile:/etc/opendkim/SigningTable
    
    UMask                   022
    

    Now that our OpenDKIM is ready, start the service. The last step is to pass emails to OpenDKIM for signing. Add these lines to your main.cf:

    # OpenDKIM
    milter_protocol = 2
    milter_default_action = accept
    
    smtpd_milters = inet:127.0.0.1:10000
    non_smtpd_milters = inet:127.0.0.1:10000
    

    Part 6: policyd-spf

    Similar to DKIM, another helper program is needed for SPF. This part is optional; if you're not concerned about remote mail servers using domains they do not own, you can skip this section. Otherwise, read on.

    Start by installing policyd-spf using your package manager. Then, make it a Postfix helper program by adding the following line to master.cf (adjust the path if necessary):

    policyd-spf  unix  -       n       n       -       0       spawn
       user=nobody argv=/usr/local/bin/policyd-spf
    

    Then, place this line somewhere into your smtpd_recipient_restrictions, AFTER reject_unauth_destination:

    smtpd_recipient_restrictions =
      ...
      reject_unauth_destination,
      ...
      check_policy_service unix:private/policyd-spf
      ...
    
    policyd-spf_time_limit = 3600
    

    Part 7: Combating Spam

    If your users are receiving lots of spam messages, you can opt for a full-featured anti-spam solution like Rspamd. However, many mail servers may not need to go that far. Typically, spamming will not be a significant problem unless your users leave a notable fingerprint on the Internet. We have already taken measures to prevent spamming to some extent, such as enforcing mail clients to strictly adhere to the SMTP protocol, ensuring working PTR records, valid addresses, and domains, etc. These steps will help prevent spamming to a certain extent.

    Another option is to use DNSBL or DNS Blocklist. These are online blacklists that contain a list of known spammers. When a client attempts to send a message through our mail server, we can check whether it is listed as a spammer by performing a DNS query against one or more DNSBLs. This approach is highly efficient, but it's essential not to overdo it. Add a few of them to your configuration. DNSBLs use a wide array of criteria for listing and delisting addresses, so choose the ones that best suit your needs. Please refer to the "SMTP Client Restrictions" section for some examples.


    powered by FreeBSD logo powered by vi logo Valid HTML 4.0!


    This page was edited on Fri Jan 26 17:22:16 +0330 2024