memoru

How To: Install an E-Mail Server on Fedora 40

PostgreSQL will be used to store data about mailboxes and aliases.

OpenDKIM

sudo dnf install opendkim opendkim-tools
In file /etc/opendkim.conf set the domain and the mode to sign and verify :
Domain example.org
Mode sv

Generate the signing key for the domain:

cd /etc/opendkim/keys
sudo opendkim-genkey -d example.org
sudo chown opendkim:opendkim /etc/opendkim/keys/*

Start the OpenDKIM service and make it start with the system:

sudo systemctl enable --now opendkim

DNS

Create the following record, replacing GET_ME_FROM_DEFAULTTXT by what comes after p= in /etc/opendkim/keys/default.txt:
default._domainkey.example.org. 1 IN TXT "v=DKIM1;t=s;h=sha256;k=rsa;p=GET_ME_FROM_DEFAULTTXT"

SpamAssassin

sudo dnf install spamassassin
sudo systemctl enable --now spamassassin
sudo useradd -d /var/spool/spamassassin -s /bin/false spamassassin

Postfix

DNS

Create those records in your zone:

smtp.example.org. 1 IN CNAME server1.example.org.

example.org. 1 IN MX 100 smtp.example.org.

default._bimi.example.org. 1 IN TXT "v=BIMI1;l=https://www.example.org/images/logo.svg"

_dmarc.example.org. 1 IN TXT "v=DMARC1; p=reject; rua=mailto:postmaster@example.org;"

The BIMI record is used to display a brand logo in Gmail and other e-mail clients.

The v=spf1 mx -all SPF record will ensure that no server can send email from your domain except the ones in your domain's MX records:
example.org. 1 IN TXT "v=spf1 mx -all"

TLS Certificate

sudo certbot certonly --nginx -d smtp.example.org

Configuration

Install the postfix packages:

sudo dnf install postfix postfix-pgsql

Create the user and the directories which will contain the actual email data:

useradd -d /var/mail -U -u 5000 vmail
mkdir -p /var/mail/vmail/example.org
chown -R vmail:vmail /var/mail/vmail

Set up the database:

sudo -u postgres psql

CREATE DATABASE postfix;

USE postfix;

CREATE TABLE addresses (email VARCHAR(50) NOT NULL PRIMARY KEY, active BOOLEAN NOT NULL DEFAULT true, passwd
VARCHAR(106) NOT NULL);

CREATE TABLE aliases (source VARCHAR(50) NOT NULL PRIMARY KEY, target VARCHAR(50) NOT NULL);

CREATE USER postfix WITH ENCRYPTED PASSWORD '';
GRANT SELECT ON public.addresses TO postfix;
GRANT SELECT ON public.aliases TO postfix;

INSERT INTO public.addresses (email, active, passwd) VALUES ('postmaster@example.org', true, 'mygreatpassword');
Create file /etc/postfix/pgsql_mailboxes.cf:
user = postfix
password = 
hosts = 127.0.0.1
dbname = postfix
query = SELECT 1 FROM addresses WHERE email = '%s'
Create file /etc/postfix/pgsql_aliases.cf`:
user = postfix
password = 
hosts = 127.0.0.1
dbname = postfix
query = SELECT 1 FROM aliases WHERE source = '%s'
    
Edit file /etc/postfix/main.cf:
myhostname = server1.example.org
mydomain = example.org
mydestination = localhost.$mydomain, localhost
inet_interfaces = all
smtpd_tls_cert_file = /etc/letsencrypt/live/smtp.example.org/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/smtp.example.org/privkey.pem
virtual_mailbox_base = /var/mail/vmail
virtual_mailbox_domains = example.org
virtual_mailbox_maps = pgsql:/etc/postfix/pgsql_mailboxes.cf
virtual_alias_maps = pgsql:/etc/postfix/pgsql_aliases.cf
virtual_gid_maps = static:5000
virtual_uid_maps = static:5000
virtual_minimum_uid = 5000
virtual_transport = lmtp:unix:private/dovecot-lmtp
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
milter_default_action = accept
milter_protocol = 2
smtpd_milters = unix:/run/opendkim/opendkim.sock
non_smtpd_milters = unix:/run/opendkim/opendkim.sock

In file /etc/postfix/master.cnf, uncomment smtp, submission and submissions and their arguments (beware not to uncomment actual comments about the configuration!).

Then, add as an argument to service smtp:

  -o content_filter=spamassassin

Then, add a new service:

spamassassin unix -     n       n       -       -       pipe user=spamassassin argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}

Start the postfix systemd service and make it start with the system:

systemctl enable --now postfix

Dovecot

DNS

imap.example.org. 1 IN CNAME server1.example.org.

TLS Certificate

sudo certbot certonly --nginx -d imap.example.org

Configuration

sudo dnf install dovecot dovecot-pgsql dovecot-pigeonhole
openssl dhparam -out /etc/dovecot/dh.pem 4096 Edit file /etc/dovecot/dovecot.conf:
protocols = imap pop3 lmtp
Edit file /etc/dovecot/conf.d/10-mail.conf:
mail_location = maildir:/var/mail/vmail/%d/%n
mail_uid = 5000
mail_gid = 5000
mail_privileged_group = vmail
Edit file /etc/dovecot/conf.d/15-lda.conf:
postmaster_address = postmaster@example.org
protocol lda {
    # Space separated list of plugins to load (default is global mail_plugins).
    mail_plugins = $mail_plugins sieve
}
Edit file /etc/dovecot/conf.d/20-lmtp.conf:
protocol lmtp {
    # Space separated list of plugins to load (default is global mail_plugins).
    mail_plugins = $mail_plugins sieve
    postmaster_address = postmaster@example.org
}
    
Edit file /etc/dovecot/conf.d/10-master.conf:
service imap-login {
    inet_listener imap {
        port = 143
    }
    inet_listener imaps {
        port = 993
        ssl = yes
    }
}

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

service auth {
    #unix_listener auth-userdb {
    #  #mode = 0666
    #  #user =
    #  #group =
    #}

    # Postfix smtp-auth
    unix_listener /var/spool/postfix/private/auth {
        mode = 0666
    }
}

Create file /etc/dovecot/conf.d/10-ssl.conf with contents:

ssl_cert = </etc/letsencrypt/live/imap.example.org/fullchain.pem
ssl_key = </etc/letsencrypt/live/imap.example.org/privkey.pem
ssl_dh = </etc/dovecot/dh.pem

Create file /etc/dovecot/conf.d/10-auth.conf with contents:

#!include auth-system.conf.ext
!include auth-sql.conf.ext

Create file /etc/dovecot/dovecot-sql.conf.ext with contents:

driver = postgres
connect = host=127.0.0.1 dbname=postfix user=postfix password=
default_pass_scheme = PLAIN
password_query = \
    SELECT email as username, passwd AS password FROM addresses WHERE email = '%u'
user_query = \
    SELECT 5000 AS uid, 5000 as gid, email, '/var/mail/vmail/%d/%n' AS home \
    FROM addresses WHERE email = '%u'
iterate_query = SELECT email AS user FROM addresses

Start the dovecot systemd service and make it start with the system:

systemctl enable --now dovecot

Setting up Thunderbird Autoconfig

DNS

Create the following record in your DNS zone:

autoconfig.example.org.	1	IN	CNAME	server1.example.org.

Issue a new Let's Encrypt TLS certificate:

sudo certbot certonly --nginx -d autoconfig.example.org

Create file: /etc/nginx/conf.d/autoconfig.example.org.conf with the following contents:

server {
    listen                  443 ssl http2;
    listen                  [::]:443 ssl http2;
    server_name             autoconfig.example.org;

    # SSL
    ssl_certificate         /etc/letsencrypt/live/autoconfig.example.org/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/autoconfig.example.org/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/autoconfig.example.org/chain.pem;

    # logging
    access_log              /var/log/nginx/access.log combined buffer=512k flush=1m;
    error_log               /var/log/nginx/error.log warn;

    root /var/www/autoconfig.example.org;

    include sts;
}

# HTTP redirect
server {
    listen      80;
    listen      [::]:80;
    server_name autoconfig.example.org;

    location / {
        return 301 https://autoconfig.example.org/$request_uri;
    }
}

Create file: /var/www/autoconfig.example.org/mail/config-v1.1.xml:

<?xml version="1.0" encoding="UTF-8"?>
<clientConfig version="1.1">
    <emailProvider id="example.org">
    <domain>example.org</domain>
    <displayName>example.org mail service</displayName>
    <displayShortName>example.org mail service</displayShortName>
    <incomingServer type="imap">
        <hostname>imap.example.org</hostname>
        <port>993</port>
        <socketType>SSL</socketType>
        <authentication>password-cleartext</authentication>
        <username>%EMAILADDRESS%</username>
    </incomingServer>
    <outgoingServer type="smtp">
        <hostname>smtp.example.org</hostname>
        <port>465</port>
        <socketType>SSL</socketType>
        <authentication>password-cleartext</authentication>
        <username>%EMAILADDRESS%</username>
    </outgoingServer>
    </emailProvider>
</clientConfig>

Reload NGINX's configuration with: sudo systemctl reload nginx