Note: This howto works with Potsfix 2.10 and Dovecot 2.2 (standard on CentOS 7.x)
Make sure port connection to port 25 is not allowed from outside: firewall-cmd --zone=public --list-all
# yum -y install postfix mailx mutt # vim /etc/postfix/main.cf myhostname = mail.example.com # this could be the same as $mydomain if you want: myhostname = example.com mydomain = example.com myorigin = $mydomain #make sure both ipv4 ip and ipv6 ip have DNS PTR record (check with 'ip addr' and 'dig -x <ip_address>') #otherwise, e.g. if only ipv4 has PTR, you might need to change inet_protocols to: #inet_protocols = ipv4 #servers like gmail check PTR and refuse to accept any email from IP that does not have PTR #make sure that DNS name PTR points to is not hosting default, but real FQDN (some email servers don't accept emails from IP's that have "hosting default PTR records), e.g. email.com #btw, some hostings allow to change DNS PTR dynamically, e.g. cloudsigma: https://www.cloudsigma.com/how-to-dynamically-update-and-manage-ptr-records-in-your-cloudsigma-infrastructure/ #this is if you your server is also MX (and you want to collect incoming emails on this server) mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain #if your MX is different (gmail, protonmail, ...) and you just want the server to be able to send out notification emails, but not collecting emails itself, use following: #mydestination = localhost.$mydomain, localhost mynetworks = 127.0.0.0/8, [::1]/128 # adjust if your server is in different (or multiple) private networks and you want to acepting sending email from other machines in your private network also, for example: # mynetworks = 127.0.0.0/8, [::1]/128, 10.0.0.0/24, ipv6_version_of_network_range, .... home_mailbox = Maildir/ smtpd_banner = $myhostname ESMTP # systemctl restart postfix # systemctl enable postfix
Make sure your server hostname and/or IP is in SPF record:
@ 3600 IN TXT "v=spf1 ...original record ... ip4:our_ip_address a:our_server_hostname ~all"
Try to send test email via commandline (connecting via TCP port 25 works but sending emails outside is not possible because relaying is disabled)
$ mail -s "Test subject" your@email.com test test test .
Test sending emails over TCP/IP:
# yum install -y telnet $ telnet localhost 25 EHLO mail.example.com<Enter> 250-STARTTLS 250-AUTH PLAIN LOGIN 250-AUTH=PLAIN LOGIN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN MAIL FROM: <web@example.com><Enter> 250 2.1.0 Ok RCPT TO: <outside_email@gmail.com><Enter> 250 2.1.0 Ok DATA<Enter> SUBJECT: Test Subject<Enter><Enter> some random text, end with <Enter>.<Enter> 250 2.0.0 Ok: queued as EDA473680D5 QUIT<Enter> 221 2.0.0 Bye # vim /var/log/maillog The email needs to be sent fine in maillog
You can also use website https://www.mail-tester.com/ and send email to temporary testing email address to see the SPAM level of your server
Taken from: https://sendgrid.com/docs/for-developers/sending-email/postfix/
# yum -y install cyrus-sasl-plain # vim /etc/postfix/sasl_passwd [smtp.sendgrid.net]:587 <sendgrid_account_username>:<sendgrid_account_password> # vim /etc/postfix/main.cf ... # at the end of file: SendGrid config smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd smtp_sasl_security_options = noanonymous smtp_sasl_tls_security_options = noanonymous smtp_tls_security_level = encrypt header_size_limit = 4096000 relayhost = [smtp.sendgrid.net]:587 # chmod 600 /etc/postfix/sasl_passwd # postmap /etc/postfix/sasl_passwd # systemctl restart postfix
Test by sending email and checking /var/log/maillog (& sendgrid activity tab)
Make sure dig mx example.com
returns your server.
Install dovecot:
# yum -y install dovecot # vim /etc/dovecot/dovecot.conf # line 24: uncomment protocols = imap pop3 lmtp # line 30: uncomment and change ( if not use IPv6 ) listen = * # vim /etc/dovecot/conf.d/10-auth.conf # line 10: uncomment and change ( allow plain text auth ) disable_plaintext_auth = no # line 100: add auth_mechanisms = plain login # vim /etc/dovecot/conf.d/10-mail.conf # line 30: uncomment and add mail_location = maildir:~/Maildir # vim /etc/dovecot/conf.d/10-master.conf # line 96-98: uncomment and add like follows # Postfix smtp-auth unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } # vim /etc/dovecot/conf.d/10-ssl.conf # line 8: change (not require SSL) ssl = no # systemctl start dovecot # systemctl enable dovecot
Update Postfix config and restart postfix:
# vim /etc/postfix/main.cf inet_interfaces = all # add follows to the end # limit an email size for 10M message_size_limit = 10485760 # limit a mailbox for 1G mailbox_size_limit = 1073741824 # for SMTP-Auth smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_sasl_security_options = noanonymous smtpd_sasl_local_domain = $myhostname smtpd_recipient_restrictions = permit_mynetworks,permit_auth_destination,permit_sasl_authenticated,reject
Restart postfix:
# systemctl restart postfix
Allow connections from outside
# firewall-cmd --add-service=smtp --permanent # firewall-cmd --add-service={pop3,imap} --permanent # firewall-cmd --reload
Make sure postfix correctly listens on all interfaces (not just localhost):
# netstat -tunlp | grep 25
Make sure postfix correctly receives email:
1. send test email from any gmail account to "root@example.com"
2. check log & check correctly received email in maildir with mutt:
# tail /var/log/maillog # mutt -f ~/Maildir y
First generate keys (adapted from https://www.server-world.info/en/note?os=CentOS_7&p=ssl)
# cd /etc/pki/tls/certs # openssl genrsa -out example.com.key 2048 # openssl req -new -sha256 -key example.com.key -out example.com.csr # openssl req -x509 -sha256 -days 365 -key example.com.key -in example.com.csr -out example.com.crt
Configure postfix & dovecot (taken from https://www.server-world.info/en/note?os=CentOS_7&p=mail&f=4):
# vim /etc/postfix/main.cf # add to the end (replace certificates to your own one) smtpd_use_tls = yes smtp_tls_mandatory_protocols = !SSLv2, !SSLv3 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3 smtpd_tls_cert_file = /etc/pki/tls/certs/host.example.com.crt smtpd_tls_key_file = /etc/pki/tls/certs/host.example.com.key smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache #outgoing emails from server should also use TLS smtp_use_tls = yes # vim /etc/postfix/master.cf #adjust submission inet n - n - - smtpd -o syslog_name=postfix/submission # -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes #adjust smtps inet n - n - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes # vim /etc/dovecot/conf.d/10-ssl.conf ssl = yes ssl_cert = </etc/pki/tls/certs/host.example.com.crt ssl_key = </etc/pki/tls/certs/host.example.com.key ssl_protocols = !SSLv2 !SSLv3 # systemctl restart postfix dovecot # firewall-cmd --add-service={smtp-submission,smtps,pop3s,imaps} --permanent # firewall-cmd --reload
Check imap and submission with email client (providing we have created OS user example)
Thunderbird:
Edit -> Account Settings -> Account Actions -> Add Mail Account ...
Your name: example
Email address: example@example.com
Password: <ssh_password>
Manual config
IMAP - host.example.com - 143 - STARTTLS - Normal password
SMTP - host.example.com - 587 - STARTTLS - Normal password
Username: Incoming: example Outgoing: example
Go to https://www.mail-tester.com/ and check sending outgoing mail from example@example.com
(Adapted from http://www.linuxtechi.com/configure-domainkeys-with-postfix-on-centos-7/)
Install openDKIM and configure it:
# yum install -y opendkim # opendkim-default-keygen # cd /etc/opendkim/keys/ # ll # vim /etc/opendkim.conf Mode sv Socket inet:8891@127.0.0.1 Canonicalization relaxed/simple Domain example.com File /etc/opendkim/keys/default.private KeyTable refile:/etc/opendkim/KeyTable SigningTable refile:/etc/opendkim/SigningTable ExternalIgnoreList refile:/etc/opendkim/TrustedHosts InternalHosts refile:/etc/opendkim/TrustedHosts # vim /etc/opendkim/KeyTable default._domainkey.example.com example.com:default:/etc/opendkim/keys/default.private # vim /etc/opendkim/SigningTable *@example.com default._domainkey.example.com # vim /etc/opendkim/TrustedHosts host.example.com example.com
Configure postfix:
# vim /etc/postfix/main.cf smtpd_milters = inet:127.0.0.1:8891 non_smtpd_milters = $smtpd_milters milter_default_action = accept
Start opendkim and restart postfix:
# hash -r # systemctl start opendkim ; systemctl enable opendkim ; systemctl restart postfix
Put publick key into DNS TXT entry:
# cat /etc/opendkim/keys/default.txt
Send an email and check if email is properly signed.
SPF: https://antipaucity.com/2016/04/25/turn-on-spf-filtering-with-postfix-and-centos-7/
Taken from https://www.linode.com/docs/email/postfix/email-with-postfix-dovecot-and-mariadb-on-centos-7
# yum install -y dovecot-mysql # sudo mysql -u root -p CREATE DATABASE mailserver; GRANT SELECT ON mailserver.* TO 'mailuser'@'127.0.0.1' IDENTIFIED BY 'mailuserpass'; FLUSH PRIVILEGES; USE mailserver; CREATE TABLE `virtual_domains` ( `id` int(11) NOT NULL auto_increment, `name` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `virtual_users` ( `id` int(11) NOT NULL auto_increment, `domain_id` int(11) NOT NULL, `password` varchar(106) NOT NULL, `email` varchar(100) 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(11) NOT NULL auto_increment, `domain_id` int(11) NOT NULL, `source` varchar(100) 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 `mailserver`.`virtual_domains` (`id` ,`name`) VALUES ('1', 'example.com'); NSERT INTO `mailserver`.`virtual_users` (`id`, `domain_id`, `password` , `email`) VALUES ('1', '1', ENCRYPT('password', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'email1@example.com'); INSERT INTO `mailserver`.`virtual_aliases` (`id`, `domain_id`, `source`, `destination`) VALUES ('1', '1', 'webmaster@example.com', 'email1@example.com'), ('2', '1', 'postmaster@example.com', 'email1@example.com'), ('3', '1', 'abuse@example.com', 'email1@example.com'), ('4', '1', 'admin@example.com', 'email1@example.com'), ('5', '1', 'info@example.com', 'email1@example.com');
Check:
# mysql -u root -p mailserver SELECT * FROM virtual_domains; SELECT * FROM virtual_users; SELECT * FROM virtual_aliases;
Update postfix configuration:
# vim /etc/postfix/main.cf # make sure $mydomain is not in mydestination configuration option if this domain is in virtual_mailbox_domains mydestination = $myhostname, localhost.$mydomain, localhost # virtual mailboxes in mysql virtual_transport = lmtp:unix:private/dovecot-lmtp 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,mysql:/etc/postfix/mysql-virtual-email2email.cf # vim /etc/postfix/mysql-virtual-mailbox-domains.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_domains WHERE name='%s' # vim /etc/postfix/mysql-virtual-mailbox-maps.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_users WHERE email='%s' # vim /etc/postfix/mysql-virtual-alias-maps.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT destination FROM virtual_aliases WHERE source='%s' # vim /etc/postfix/mysql-virtual-email2email.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT email FROM virtual_users WHERE email='%s' # systemctl restart postfix # postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf 1 # postmap -q support@example.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf 1 # postmap -q support@example.com mysql:/etc/postfix/mysql-virtual-email2email.cf support@example.com # postmap -q abuse@example.com mysql:/etc/postfix/mysql-virtual-alias-maps.cf support@example.com
Configure dovecot:
# mkdir -p /var/mail/vhosts/example.com # groupadd -g 5000 vmail # useradd -g vmail -u 5000 vmail -d /var/mail/ # chown -R vmail:vmail /var/mail/ # chown -R vmail:vmail /var/mail/vhosts # vim /etc/dovecot/conf.d/10-auth.conf disable_plaintext_auth = yes auth_mechanisms = plain login !include auth-system.conf.ext !include auth-sql.conf.ext # vim /etc/dovecot/conf.d/auth-sql.conf.ext passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } userdb { driver = static args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n } # vim /etc/dovecot/dovecot-sql.conf.ext driver = mysql connect = host=127.0.0.1 dbname=mailserver user=mailuser password=mailuserpass default_pass_scheme = SHA512-CRYPT password_query = SELECT email as user, password FROM virtual_users WHERE email='%u'; # chown -R vmail:dovecot /etc/dovecot # chmod -R o-rwx /etc/dovecot # vim /etc/dovecot/conf.d/10-master.conf service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { #mode = 0666i mode = 0600 user = postfix group = postfix } } service auth { unix_listener auth-userdb { mode = 0600 user = vmail } user = dovecot } service auth-worker { user = vmail } # systemctl restart dovecot
Test:
Final main.cf:
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination # When changing helo_client_exceptions, sender_checks or rbl_client_exceptions, db file needs to be refreshed: postmap <file> smtpd_recipient_restrictions = check_client_access hash:/etc/postfix/helo_client_exceptions check_sender_access hash:/etc/postfix/sender_checks, reject_invalid_hostname, # Following can cause issues with Auth SMTP, be careful reject_non_fqdn_hostname, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_sender_domain, reject_unknown_recipient_domain, permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_client_access hash:/etc/postfix/rbl_client_exceptions, reject_rbl_client cbl.abuseat.org, reject_rbl_client sbl-xbl.spamhaus.org, reject_rbl_client bl.spamcop.net, reject_rhsbl_sender dsn.rfc-ignorant.org, # here might go greylisting line (check_policy_service inet:127.0.0.1:60000) permit
# vim /etc/postfix/main.cf debug_peer_level = 2 debug_peer_list = 127.0.0.1,localhost # postfix reload
Postfix: https://www.server-world.info/en/note?os=CentOS_7&p=mail&f=1
Dovecot: https://www.server-world.info/en/note?os=CentOS_7&p=mail&f=2
$ telnet mail.provider.com 587 ehlo my.server.com<enter> 250-relay03.plus.net Hello relay [80.229.xxx.xxx] 250-SIZE 104857600 250-PIPELINING 250-AUTH PLAIN LOGIN 250 HELP auth login<enter> 334 VXNlcm5hbWU6 enter base64encoded username (http://www.webpan.com/Customers/Email/base64_conversion.htm): dXNlcm5hbWU=<enter> 334 UGFzc3dvcmQ6 enter base64encoded password (http://www.webpan.com/Customers/Email/base64_conversion.htm): dXNlcm5hbWU=<enter> 235 Authentication succeeded mail from: test_user@my.server.com<enter> 250 2.1.0 Sender OK rcpt to: reliable-email-address@gmail.com<enter> 250 2.1.5 Recipient OK data<enter> 354 Start mail input; end with . Subject: This line is optional This is test body of the email . 250 2.6.0 <ae80543d-cb8a-5c45-ad80-23b1985df753@mail.woshub.com> [InternalId=6334284] Queued mail for delivery quit<enter>
# mailq
or
# postqueue -p
Assuming the message has the ID XXXXXXX (you can see the ID form the QUEUE)
# postcat -vq XXXXXXXXXX
# postqueue -f
# postsuper -d ALL
# mailq | tail -n +2 | grep -v '^ *(' | awk 'BEGIN { RS = "" } { if ($8 == "email@address.com" && $9 == "") print $1 } ' | tr -d '*!' | postsuper -d -