Introduction
In order to set up a full simple mail server, this guide takes advantage of Postfix as an SMTP server, Dovecot to provide POP/IMAP functionality, and RoundCube as a webmail program or client so that users can check and receive email from their favorite web browsers.
Dovecot: Dovecot is an open-source IMAP and POP3 email server for Linux/UNIX-like systems, written with security primarily in mind.
Postfix: Postfix is a free and open-source mail transfer agent (MTA) that routes and delivers electronic mail from one server to another over the internet.
Roundcube: Once the mails have been delivered into a mailbox, most users would need an easy to use interface to read their mails. Roundcube does this pretty well. It is a browser-based multilingual IMAP client with an application-like user interface. It provides full functionality you expect from an email client, including MIME support, address book, folder manipulation, message searching and spell checking.
So, at first, as usual, we use fully updated system:
dnf update -y
Now, configure some prerequisites.
Before proceeding further, also ensure that no other MTAs such as Sendmail are existing as this will cause conflict with Postfix configuration. To remove Sendmail, for example, run the command:
dnf remove sendmail
Now set FQDN (Fully Qualifed Domain Name) and set hostname:
hostnamectl set-hostname mail.example.com
exec bash
vim /etc/hosts
192.0.2.1 mail.example.com
How to install mariadb server, apache web server and php version 7.3 (or 7.4) you can find at another post, like: https://www.gonscak.sk/?p=530
So, now, we can install out MTA Postfix, very simple, with mysql support (our users will be stored in mysql database):
dnf install postfix postfix-mysql -y
systemctl start postfix
systemctl enable postfix
To check postfix status, write this command:
systemctl status postfix
Now, we enable some ports of firewall. If you want use POP3, enable it. I prefer not to use. In order to send emails from your server, port 25 (outbound) must be open. To be able to send emails using a desktop email client (Thunderbird or Outlook), we need to enable the submission service in Postfix. And to encrypt our communications, we need a TLS certificate.
firewall-cmd --permanent --add-service={http,https,smtp-submission,smtps,imap,imaps}
systemctl reload firewalld
sudo firewall-cmd --permanent --add-service={pop3,pop3s}
systemctl reload firewalld
When we configure a desktop email client, enabling encryption is always a good idea. We can easily obtain a free TLS certificate from Let’s Encrypt. Issue the following commands to install Let’s Encrypt client (certbot) on CentOS 8/RHEL 8 from the EPEL repository. If you don’t have a web server running yet, I recommend you install one (Apache).
dnf install epel-release -y
dnf install certbot python3-certbot-apache
dnf install httpd
systemctl start httpd
systemctl enable httpd
We create and simple virtual host for Apache to obtain certificate. Like this:
vim /etc/httpd/conf.d/mail.gonscak.sk.conf
ServerName mail.gonscak.sk
DocumentRoot /var/www/html/
systemctl reload httpd
Now, if everything is ok (Apache is realoaded), we can obtain our TLS certificate for postfix/dovecot in future settings:
certbot --apache --email you@example.com -d mail.example.com
and the results:
Congratulations! You have successfully enabled https://mail.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/mail.example.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/mail.example.com/privkey.pem
Configuring Postfix
To send emails from a desktop email client, we need to enable the submission service of Postfix so that the email client can submit emails to Postfix SMTP server. Edit the master.cf
file.
vim /etc/postfix/master.cf
In submission
section, uncomment or add the following lines. Please allow at least one whitespace (tab or spacebar) before each -o
. In postfix configurations, a preceding whitespace character means that this line is continuation of the previous line.
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_tls_wrappermode=no
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
The above configuration enables the submission daemon of Postfix and requires TLS encryption. So later on our desktop email client can connect to the submission daemon in TLS encryption. The submission daemon listens on TCP port 587. STARTTLS is used to encrypt communications between email client and the submission daemon.
Microsoft Outlook only supports submission over port 465. If you are going to use Microsoft outlook mail client, then you also need to enable submission service on port 465 by adding the following lines in the file.
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
Save and close this file for the moment. Now we configure main configurations of Postfix. open file and edit this lines as mine. If you dont have these lines, please add they.
cp /etc/postfix/main.cf /etc/postfix/main.cf.orig
vim /etc/postfix/main.cf
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_loglevel = 1
smtp_tls_loglevel = 1
#Force TLSv1.3 or TLSv1.2
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
myhostname = mail.example.com
mydomain = example.com
message_size_limit = 31457280
Save and close file. Now restart postfix to ensure, that the change of settings take effect:
systemctl restart postfix
If you run the following command, you will see Postfix is now listening on port 587 and 465.
netstat -lnpt | grep master
bash: netstat: command not found
#if we havent's these command, check who provide it:
dnf provides netstat
Output:
net-tools-2.0-0.51.20160912git.el8.x86_64 : Basic networking tools
Repo : BaseOS
Matched from:
Filename : /usr/bin/netstat
So install it:
dnf install net-tools
netstat -lnpt | grep master
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 62343/master
tcp 0 0 127.0.0.1:587 0.0.0.0:* LISTEN 62343/master
tcp 0 0 127.0.0.1:465 0.0.0.0:* LISTEN 62343/master
Installing Dovecot IMAP Server and configuring
So, as usual, install imap server dovecot with mysql support:
dnf install dovecot dovecot-mysql
dovecot --version
2.3.8 (9df20d2db)
Now start it and enable after boot:
systemctl start dovecot
systemctl enable dovecot
Open dovecot config and edit or add this line:
cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig
vim /etc/dovecot/dovecot.con
protocols = imap
Save and close file. then restart dovecot:
systemctl restart dovecot.service
systemctl status dovecot.service
● dovecot.service - Dovecot IMAP/POP3 email server
Loaded: loaded (/usr/lib/systemd/system/dovecot.service; enabled; vendor preset: disabled)
Active: active (running) since Fri 2020-09-18 11:32:49 CEST; 28s ago
For storing messages, we use Maildir format. Every mail is stored in separate file in precise directory structure. So, create a directory for your domain/domains and edit line/lines like next:
mkdir -p /var/vmail/vhosts/example.com
cp /etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf.orig
vim /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/var/vmail/vhosts/%d/%n
mail_privileged_group = mail
Save file and exit. Now assign user dovecot to group mail and vmail, for reading Inbox and writing to folder destinations:
groupadd -g 5000 vmail
useradd -u 5000 -g vmail -s /usr/bin/nologin -d /var/vmail -m vmail
gpasswd -a dovecot mail
usermod -a -G vmail dovecot
semanage fcontext --add --type mail_home_rw_t --range s0 '/var/vmail/vhosts(/.*)?'
restorecon -Rv /var/vmail/vhosts
Set a database for users, domains and aliases
So, log in mysql as root and create a database, in which we will be storring informations about used domains, users, passwords and mail aliases for users. Then create tables, for this informations. Adjust your informations…
mysql -u root -p
CREATE DATABASE maildb;
GRANT SELECT ON maildb.* TO 'usermail'@'localhost' IDENTIFIED BY 'PASSWORD';
GRANT update ON maildb.* TO 'usermail'@'localhost' IDENTIFIED BY 'PASSWORD';
FLUSH PRIVILEGES;
USE maildb;
CREATE TABLE virtual_domains
(
id
INT NOT NULL AUTO_INCREMENT,
name
VARCHAR(50) NOT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE virtual_users
(
id
INT NOT NULL AUTO_INCREMENT,
domain_id
INT NOT NULL,
password
VARCHAR(106) NOT NULL,
email
VARCHAR(120) NOT NULL,
PRIMARY KEY (id
),
UNIQUE KEY email
(email
),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE virtual_aliases
(
id
INT NOT NULL AUTO_INCREMENT,
domain_id
INT NOT NULL,
source
varchar(200) NOT NULL,
destination
varchar(100) NOT NULL,
PRIMARY KEY (id
),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO maildb
.virtual_domains
(id
,name
)
VALUES
('1', 'example.com');
INSERT INTO maildb
.virtual_users
(id
, domain_id
, password
, email
)
VALUES
('1', '7', 'UserPassword', 'user1@example.com');
INSERT INTO maildb
.virtual_aliases
(id
, domain_id
, source
, destination
)
VALUES
('1', '1', 'alias1@example.com', 'user1@example.com');
Now, we can see, that the password is stored in our databases in plaintext:
MariaDB [maildb]> select * from virtual_users;
+----+-----------+------------+----------------------+
| id | domain_id | password | email |
+----+-----------+------------+----------------------+
| 1 | 7 | UserPassword | user1@example.com |
+----+-----------+------------+----------------------+
1 row in set (0.000 sec)
So we change it:
update virtual_users set password = ENCRYPT('UserPassword', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))) where email = 'user1@example.com';
select * from virtual_users;
MariaDB [maildb]> select * from virtual_users;
+----+-----------+------------------------------------------------------------------------------------------------------------+----------------------+
| id | domain_id | password | email |
+----+-----------+------------------------------------------------------------------------------------------------------------+----------------------+
| 1 | 7 | $6$b308975352080ba6$TFt0bZNCPZdgLtn2S9hHMQSdxFikxGDpLqNVap7r/q9OgHGP/EddEzc9Oc3Ww4nvinbrR2pGNgLUpK.PQ1JVD/ | user1@example.com |
+----+-----------+------------------------------------------------------------------------------------------------------------+----------------------+
1 row in set (0.000 sec)
And if you want, now you can add your user right with shit encrypt form:
INSERT INTO maildb
.virtual_users
(id
, domain_id
, password
, email
)
VALUES
('2', '7', ENCRYPT('password2', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'user2@example.com');
And now, we can see:
select * from virtual_users;
+----+-----------+------------------------------------------------------------------------------------------------------------+----------------------+
| id | domain_id | password | email |
+----+-----------+------------------------------------------------------------------------------------------------------------+----------------------+
| 1 | 7 | $6$b308975352080ba6$TFt0bZNCPZdgLtn2S9hHMQSdxFikxGDpLqNVap7r/q9OgHGP/EddEzc9Oc3Ww4nvinbrR2pGNgLUpK.PQ1JVD/ | user1@example.com |
| 2 | 7 | $6$93809b2da2242ede$toapOIav4kqmLiFl03xvZiEe9LXvqDs.nT5Ristkmy0zCyk6fc.JjjlekElcJ9MczPv5e9b4eH/lumkgOpZq6/ | user2@example.com |
+----+-----------+------------------------------------------------------------------------------------------------------------+----------------------+
Now we can exit from mariadb server with command “exit;”and continue
Now, we add som configuration files for postfix to be sure, that postfix will understand, where our information about users, domains and aliases are and how to connect to them:
cat > /etc/postfix/mysql-virtual-mailbox-domains.cf << EOF
user = usermail
password = PASSWORD
hosts = 127.0.0.1
dbname = maildb
query = SELECT 1 FROM virtual_domains WHERE name='%s'
EOF
cat > /etc/postfix/mysql-virtual-mailbox-maps.cf << EOF
user = usermail
password = PASSWORD
hosts = 127.0.0.1
dbname = maildb
query = SELECT 1 FROM virtual_users WHERE email='%s'
EOF
cat > /etc/postfix/mysql-virtual-alias-maps.cf << EOF
user = usermail
password = PASSWORD
hosts = 127.0.0.1
dbname = maildb
query = SELECT destination FROM virtual_aliases WHERE source='%s'
EOF
cat > /etc/postfix/mysql-virtual-email2email.cf << EOF
user = usermail
password = PASSWORD
hosts = 127.0.0.1
dbname = maildb
query = SELECT email FROM virtual_users WHERE email='%s'
EOF
cat > mysql-virtual-sender-alias-maps.cf << EOF
user = usermail
password = PASSWORD
hosts = 127.0.0.1
dbname = maildb
query = select destination from virtual_aliases where source='%s'
EOF
Now, we can check, if postifx understand this:
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
1
postmap -q user1@example.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
1
postmap -q alias1@example.com mysql:/etc/postfix/mysql-virtual-alias-maps.cf
user1@example.com
Now, we can continue with configuring again Postfix and Dovecot. In Postfix add these lines, we created before:
vim /etc/postfix/main.cf
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
smtpd_sender_login_maps = mysql:/etc/postfix/mysql-virtual-email2email.cf, mysql:/etc/postfix/mysql-virtual-sender-alias-maps.cf
smtpd_helo_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_non_fqdn_hostname,
reject_invalid_hostname,
reject_unknown_hostname,
permit
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination,
reject_invalid_hostname,
reject_unknown_recipient_domain,
reject_non_fqdn_recipient,
permit
smtpd_sender_restrictions =
reject_authenticated_sender_login_mismatch,
permit_sasl_authenticated,
check_sender_access hash:/etc/postfix/access,
reject_unknown_sender_domain,
reject_non_fqdn_sender
Check, if ist Postfix properly configured and there is no syntax mistake:
postfix check
#if nothing is displayed, is OK and then:
systemctl restart postfix.service
Now, continue with dovecot. Uncoment lines, or modify/add:
cp /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf.orig
vim /etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = yes
auth_mechanisms = plain login
!include auth-sql.conf.ext
Now, edit /etc/dovecot/conf.d/auth-sql.conf.ext and adjust:
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=vmail gid=vmail home=/var/vmail/vhosts/%d/%n
}
And edit/or add file:
/etc/dovecot/dovecot-sql.conf.ext
driver = mysql
connect = host=127.0.0.1 dbname=maildb user=usermail password=PASSWORD
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';
Next, edit an adjust dovecot ssl for using our LE certificate:
cp /etc/dovecot/conf.d/10-ssl.conf /etc/dovecot/conf.d/10-ssl.conf.orig
vim /etc/dovecot/conf.d/10-ssl.conf
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_dh = </etc/dovecot/dh.pem
ssl_min_protocol = TLSv1
ssl_prefer_server_ciphers = yes
save and close. Now generate dh.pem. It take a long time, in my case, 10minutes:
openssl dhparam -out /etc/dovecot/dh.pem 4096
Now, edit SASL authentication between Postfix and Dovecot:
cp /etc/dovecot/conf.d/10-master.conf /etc/dovecot/conf.d/10-master.conf.orig
vim /etc/dovecot/conf.d/10-master.conf
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
Then, we set Dovecot, to auto-create folders, after user first login. To enable this, edit lines like below:
cp /etc/dovecot/conf.d/15-mailboxes.conf /etc/dovecot/conf.d/15-mailboxes.conf.orig
vim /etc/dovecot/conf.d/15-mailboxes.conf
mailbox Trash {
auto = create
special_use = \Trash
}
mailbox Drafts {
auto = create
special_use = \Drafts
}
...
Now, restart dovecot and check, if working, for now somehow 🙂
systemctl restart dovecot
systemctl restart postfix
netstat -lnpt | grep dovecot
tcp 0 0 0.0.0.0:993 0.0.0.0:* LISTEN 164849/dovecot
tcp 0 0 0.0.0.0:143 0.0.0.0:* LISTEN 164849/dovecot
tcp6 0 0 :::993 :::* LISTEN 164849/dovecot
tcp6 0 0 :::143 :::* LISTEN 164849/dovecot
#if problem, watch log why:
systemctl status dovecot
By default, Postfix uses its builtin local delivery agent (LDA) to move inbound emails to the message store (inbox, sent, trash, Junk, etc). We can configure it to use Dovecot to deliver emails, via the LMTP protocol, which is a simplified version of SMTP. LMTP allows for a highly scalable and reliable mail system. This step is required if you want to use the sieve plugin to filter inbound messages to different folders.
Edit Dovecot main configuration files, and next postfix configuration:
vim /etc/dovecot/dovecot.conf
protocols = imap lmtp
vim /etc/dovecot/conf.d/10-master.conf
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
vim /etc/postfix/main.cf
mailbox_transport = lmtp:unix:private/dovecot-lmtp
virtual_transport = lmtp:unix:private/dovecot-lmtp
smtputf8_enable = no
systemctl restart postfix dovecot
Now, we can set up our desktop clients for serving our mails. I prefer Mozilla Thunderbird. So , in settings, use this variables:
IMAP, port 993, SSL/TLS, normal password
or
IMAP, port 143, STARTTLS, normal password
or
SMTP, port 587, STARTTLS, normal password
or
SMPT, port 465, SSL/TLS, normal password
username: user1@example.com
password: yours
server for incoming and outgoing: mail.example.com
I prefer using port 465/SSL and 993/SSL. Because this are native SSL ports.
Improoving email delivery with SPF and DKIM records
Until now, we have working mail server with Postfix and Dovecot with dekstop email clients (Thunderbird). We can send mail to the world, and world to us (of couse, we must have correct DNS names/records, like MX, A and PTR. But sometimes, may happend, that our email is mark as SPAM. So we are going to look at how to improve email delivery to recipient’s inbox by setting up SPF and DKIM on CentOS/RHEL server.
So, what is SPF?
I use wikipedia: Sender Policy Framework (SPF) is an email authentication method designed to detect forging sender addresses during the delivery of the email. SPF alone, though, is limited only to detect a forged sender claimed in the envelope of the email which is used when the mail gets bounced.
SPF record specifies which hosts or IP addresses are allowed to send emails on behalf of a domain. You should allow only your own email server or your ISP’s server to send emails for your domain.
Now, we must add SPF record to our domain. It is TXT record, like this:
TXT @ "v=spf1 mx -all"
Where:
- TXT indicates this is a TXT record.
- @ in the name field represent the apex domain name.
- v=spf1 indicates this is a SPF record and the SPF record version is SPF1.
- mx means all hosts listed in the MX records are allowed to send emails for your domain and all other hosts are disallowed.
- -all indicates that emails from your domain should only come from hosts specified in the SPF record. Emails sent from other hosts will be flagged as fail.
If we test for our domain MX record, this SPF record is pointing only to this one. So receiving mailserver can evaluate, that sender is authorized to use this mail server:
dig -t txt example.com
;; ANSWER SECTION:
example.com. 600 IN TXT "v=spf1 a mx -all"
dig -t mx example.com
;; ANSWER SECTION:
example.com. 600 IN MX 10 mail.example.com.
dig mail.example.com
;; ANSWER SECTION:
mail.example.com. 599 IN A 192.0.2.1
Of course, you can use online SPF validator such as https://mxtoolbox.com/SuperTool.aspx to see which hosts are allowed to send emails for your domain and debug your SPF record if any error occurs.
Configuring SPF Policy Agent
We also need to tell our Postfix SMTP server to check the SPF record of incoming emails to detect forged emails. First install required packages:
dnf install pypolicyd-spf
Then add a user for policyd-spf.
adduser policyd-spf --user-group --no-create-home -s /bin/false
And dit the Postfix master process configuration file. Add the following lines at the end of the file, which tells Postfix to start the SPF policy daemon when it’s starting itself. Policyd-spf will run as the policyd-spf
user.
vim /etc/postfix/master.cf
policyd-spf unix - n n - 0 spawn
user=policyd-spf argv=/usr/libexec/postfix/policyd-spf
Save and close the file. Next, edit Postfix main configuration file. Append the following line at smtpd_recipient_restriction.
vim /etc/postfix/main.cf
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination,
check_policy_service unix:private/policyd-spf
Save and close the file. Then restart Postfix.
systemctl restart postfix
Next time, when you receive an email from a domain that has an SPF record, you can see the SPF check results in the raw email header. The following header indicates the sender sent the email from an authorized host:
Received-SPF: Pass (mailfrom) identity=mailfrom;
If you see in log receiver=<UNKNOWN>, then you can add variable, to show receiver:
vim /etc/python-policyd-spf/policyd-spf.conf
Hide_Receiver = No
To test the SPF records with your domain, try:
https://vamsoft.com/support/tools/spf-policy-tester
So, what is DKIM?
According to wiki:
DomainKeys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in emails (email spoofing), a technique often used in phishing and email spam.
DKIM allows the receiver to check that an email claimed to have come from a specific domain was indeed authorized by the owner of that domain. It achieves this by affixing a digital signature, linked to a domain name, to each outgoing email message. The recipient system can verify this by looking up the sender’s public key published in the DNS. A valid signature also guarantees that some parts of the email (possibly including attachments) have not been modified since the signature was affixed. Usually, DKIM signatures are not visible to end-users, and are affixed or verified by the infrastructure rather than the message’s authors and recipients.
Simply: DKIM uses a private key to add a signature to emails sent from your domain. Receiving SMTP servers verify the signature by using the corresponding public key, which is published in your domain’s DNS records.
Now, we must install som package:
dnf install opendkim opendkim-tools
At beginig, we must edit main configuration file of opendkim and adjust the line:
/etc/opendkim.conf
Mode sv
By default, OpenDKIM runs in verification mode (v), which will verify the DKIM signature of incoming email messages. We need to sign outgoing emails, so change this line to the following to enable signing mode.
Find the following line and comment it out, because we will use separate keys for each domain name.
KeyFile /etc/opendkim/keys/default.private
Next, find the following 4 lines and uncomment them.
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
Create Signing Table, Key Table and Trusted Hosts File
Edit the signing table file.
vim /etc/opendkim/SigningTable
Add the following line at the end of this file. This tells OpenDKIM that if a sender on your server is using a @your-domain.com
address, then it should be signed with the private key identified by 20200925._domainkey.example.com
.
*@example.com 20200925._domainkey.example.com
20200925 is the DKIM selector. A domain name might have multiple DKIM keys. The DKIM selector allows you to choose a particular DKIM key. You can use whatever name for the DKIM selector, but I found it’s convenient to use the current date (September 25, 2020) as the DKIM selector. Save and close the file. Then edit the key table file.
vim /etc/opendkim/KeyTable
Add the following line, which specifies the location of the DKIM private key.
20200925._domainkey.example.com example.com:20200925:/etc/opendkim/keys/example.com/20200925.private
Save and close the file. Next, edit the trusted hosts file.
vim /etc/opendkim/TrustedHosts
127.0.0.0.1 and ::1 are included in this file by default. Now add the following line. This tells OpenDKIM that if an email is coming from your own domain name, then OpenDKIM should not perform DKIM verification on the email.
*.example.com
Save and close the file.
Generate Private/Public Keypair
Since DKIM is used to sign outgoing messages and verify incoming messages, you need to generate a private key to sign outgoing emails and a public key for receiving SMTP servers to verify the DKIM signature of your email. Public key will be published in DNS.
Create a separate folder for the domain.
mkdir /etc/opendkim/keys/example.com
Generate keys using opendkim-genkey
tool.
opendkim-genkey -b 2048 -d example.com -D /etc/opendkim/keys/example.com -s 20200925 -v
The above command will create 2048 bits keys. -d (domain)
specifies the domain. -D (directory)
specifies the directory where the keys will be stored. I use 20200925
as the DKIM selector. Once the command is executed, the private key will be written to 20200925
.private
file and the public key will be written to 20200925
.txt
file:
opendkim-genkey: generating private key
opendkim-genkey: private key written to 20200925.private
opendkim-genkey: extracting public key
opendkim-genkey: DNS TXT record written to 20200925.txt
And adjust ownership:
chown opendkim:opendkim /etc/opendkim/keys/ -R
Publish Your Public Key in DNS Records
Display the public key. The string after the p
parameter is the public key:
cat /etc/opendkim/keys/example.com/20200925.txt
20200925._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dFfxvjFLHGpPX4chiCrcq+88WkARccudstwfchyioXkzZOicB7cxRd992H3JQFYvdGXQ+KEAO8eEelcQBv4ee+SCQjzVH70k/gvVxbVwJ6IicYyMELy1Mh0alQ7oOAFNIhyafueEzy/sSefXva1dw7lYh6t4NjypFUbMpIWH/sUyLEZqkWBTYfbKzbj52kML8LbWeWoQJHB2a"
"7jd9GPRkXpwMpbumKPdLD+wINIyr9L4r31/TIVpVDq7ZP6JrksyBHVFSWZQsODLIHjLL2ln/o/VSUcPXxy8H/44Xpzw2RHwcGXrMdQ44IXenhel+4A3M/FTK3cLS8EuHVJ2YDwUQIDAQAB" ) ; ----- DKIM key 20200925 for example.com
In you DNS manager, create a TXT record, enter 20200925._domainkey
in the name field. (You need to replace 20200925 with your own DKIM selector.) Then go back to the terminal window, copy everything in the parentheses and paste it into the value field of the DNS record. You need to delete all double quotes and line breaks in the value field. If you don’t delete them, then key test in the next step will probably fail.
Test DKIM Key
Enter the following command on your server to test your key.
sudo opendkim-testkey -d example.com -s 20200925 -vvv
If everything is OK, you will see the key OK
message.
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: checking key '20200925._domainkey.example.com'
opendkim-testkey: key OK
Now we can start the opendkim service.
systemctl start opendkim
And enable auto-start at boot time.
systemctl enable opendkim
OpenDKIM listens on 127.0.0.1:8891
.
Connect Postfix to OpenDKIM
Edit Postfix main configuration file:
vim /etc/postfix/main.cf
Add the following lines at the end of this file, so Postfix will be able to call OpenDKIM via the milter protocol. Note that you should use 127.0.0.1
as the address. Don’t use localhost
.
# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
Save and close the file. Then add postfix
user to opendkim
group.
gpasswd -a postfix opendkim
Restart postfix
service.
systemctl restart postfix
Now, you can send email from gmail.com, maybe, to you. In maillog of your server, you will see that dkim works on incoming mails:
opendkim[]: : mail.example.comk [IP of domain] not internal
opendkim[]: : DKIM verification successful
Now, you can send email from you to anybody. In maillog of your server, you will see that dkim works on outcoming mails and that opendkim add signature:
opendkim[]: : DKIM-Signature field added (s=20200925, d=example.com)
Or you can send an empty mail to adress: check-auth@verifier.port25.com
And during few seconds, you will get back report with some things. If SPF is fine and if dkim works:
Summary of Results
SPF check: pass
"iprev" check: pass
DKIM check: pass
SpamAssassin check: ham
Details:
SPF check details:
DNS record(s): example.com. 188 IN TXT "v=spf1 a mx -all"
example.com. 188 192.0.2.1
DKIM check details:
Result: pass (matches From: user1@example.com)
ID(s) verified: header.d=example.com
How to fight with spam? I will use spamassasin
So what is Spamassasin? According the project web site:
SpamAssassin is a mature, widely-deployed open source project that serves as a mail filter to identify Spam. SpamAssassin uses a variety of mechanisms including header and text analysis, Bayesian filtering, DNS blocklists, and collaborative filtering databases. SpamAssassin runs on a server, and filters spam before it reaches your mailbox.
So we install it first:
dnf install spamassassin
The server binary installed by the spamassassin
package is called spamd
, which will be listening on TCP port 783 on localhost. Spamc
is the client for SpamAssassin spam filtering daemon. By default, the spamassassin
systemd service is disabled, you can enable auto start at boot time with:
systemctl enable spamassassin
systemctl start spamassassin
There are several ways you can use to integrate SpamAssassin with Postfix. I use SpamAssassin via the sendmail milter interface, because it allows me to reject an email when it gets a very high score such as 8, so it will never be seen by the recipient.
dnf install spamass-milter-postfix
systemctl start spamass-milter
systemctl enable spamass-milter
Now edit the Postfix main configuration file and add/edit this lines:
vim /etc/postfix/main.cf
# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891,unix:/run/spamass-milter/postfix/sock
non_smtpd_milters = $smtpd_milters
Save and close the file. Now open the /etc/sysconfig/spamass-milter
file and find the following line.
#EXTRA_FLAGS="-m -r 15"
Uncomment this line and change 15 to your preferred reject score such as 8.
EXTRA_FLAGS="-m -r 8"
In this tutorial, we are going to learn how to use SpamAssassin (SA) to detect spam on CentOS/RHEL mail server. SpamAssassin is a free, open-source, flexible and powerful spam-fighting tool.
SpamAssassin is a score-based system. It will check email message against a large set of rules. Each rule adds or removes points in the message’s score. If the score is high enough (by default 5.0), the message is considered spam.
Install antivirus clamd and content filter amavis
What is amavis?
According to wiki:
Amavis is an open-source content filter for electronic mail, implementing mail message transfer, decoding, some processing and checking, and interfacing with external content filters to provide protection against spam and viruses and other malware. It can be considered an interface between a mailer (MTA, Mail Transfer Agent) and one or more content filters.
Amavis can be used to:
- detect viruses, spam, banned content types or syntax errors in mail messages
- block, tag, redirect (using sub-addressing), or forward mail depending on its content, origin or size
- quarantine (and release), or archive mail messages to files, to mailboxes, or to a relational database
- sanitize passed messages using an external sanitizer
- generate DKIM signatures
- verify DKIM signatures and provide DKIM-based whitelisting
And what is clamv?
ClamAV® is an open source antivirus engine for detecting trojans, viruses, malware & other malicious threats.
To install Amavisd and Clamav Server run:
dnf install amavis clamd -y
#it takes 250MB with dependencies
Now edit Clamav configuration file and adjust lines:
vim /etc/clamd.d/scan.conf
#Example
LogFile /var/log/clamd.scan
PidFile /run/clamd.scan/clamd.pid
TemporaryDirectory /var/tmp
LocalSocket /run/clamd.scan/clamd.sock
Save and close. Now create log file for Clamav and start it:
touch /var/log/clamd.scan
chown clamscan. /var/log/clamd.scan
restorecon -v /var/log/clamd.scan
systemctl start clamd@scan.service
systemctl enable clamd@
And now, allow Clamav to scan system for Selinux:
setsebool -P antivirus_can_scan_system on
Now configure Amavisd:
vim /etc/amavisd/amavisd.conf
$mydomain = 'example.com'; # a convenient default for other settings
$myhostname = 'host.example.com'; # must be a fully-qualified domain name!
$inet_socket_bind = '127.0.0.1';
$notify_method = 'smtp:[127.0.0.1]:10025';
$forward_method = 'smtp:[127.0.0.1]:10025'; # set to undef with milter!
And enable it and start it:
systemctl start amavisd.service
systemctl enable amavisd.service
Now edit Postfix main configuration file and add at the end of file:
vim /etc/postfix/main.cf
content_filter=smtp-amavis:[127.0.0.1]:10024
Now edit master.cf and add at the end there lines:
vim /etc/postfix/master.cf
smtp-amavis unix - - n - 2 smtp
-o smtp_data_done_timeout=1200
-o smtp_send_xforward_command=yes
-o disable_dns_lookups=yes
127.0.0.1:10025 inet n - n - - smtpd
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes
-o smtpd_error_sleep_time=0
-o smtpd_soft_error_limit=1001
-o smtpd_hard_error_limit=1000
And restart Postfix:
systemctl restart postfix.service
And if everything is OK, you can see in the detailed headers of mail, that it has been scanned:
X-Virus-Scanned: Amavisd-new at example.com
And in the maiilog:
amavis[]: () Passed CLEAN {RelayedInbound}
or
amavis[]: () Passed UNCHECKED-ENCRYPTED
or
amavis[]: () Blocked INFECTED (Win.Test.EICAR_HDB-1) {DiscardedInbound,Quarantined}
test: http://www.aleph-tec.com/eicar/index.php
Webmail roundcube 1.6.x
To use roundcube webmail, we download and extract downloaded package manually. Extract into apache web server folder (this is not included in this tutorial) You must create a database for roundcube, set permissions for LOG nad TMP folders for write and setup some steps for begin. I configure and explain only connect to database for user, to change their password by own on mailserver. You can connect roundcube from localhost, or from other web server throught imap/smtp ports for sending/receiving mails.
Do, edit roundcube config file config.inc.php and add plugin for password:
$config['plugins'] = ['password'];
Now, we can see a new page in our roundcube web, under settings > password. But changing password is not impossible, because roudcube doesnt know, how and where we store passwords. So, edit config file for our password plugin and add/change parameters below. We must create a user in our mariadb, who can edit (update) tables in our mail database. We there can enable more variables for new passwords: length, strengh and more…
#mariadb:
CREATE USER exampleuser@localhost IDENTIFIED BY 'sjfaADW34356';
GRANT SELECT,UPDATE ON maildb.virtual_users TO 'exampleuser'@'localhost';
FLUSH PRIVILEGES;
quit
#roundcube folder
cd plugins/password/
cp config.inc.php.dist config.inc.php
vi config.inc.php
$config['password_driver'] = 'sql';
$config['password_query'] = 'UPDATE virtual_users SET password = %P WHERE email = %u';
$config['password_crypt_hash'] = 'sha512';
$config['password_db_dsn'] = 'mysql://exampleuser:sjfaADW34356@localhost/maildb';
$config['password_hash_algorithm'] = 'sha512-crypt';
$config['password_algorithm'] = 'sha512-crypt';
Total Page Visits: 170895 - Today Page Visits: 90