This is how I set up my mail server on
groupadd -g 5000 vmail useradd -d /home/vmail -g 5000 -m -s /bin/bash -u 5000 -p somepassword vmail id vmail
uid=5000(vmail) gid=5000(vmail) groups=5000(vmail)
/etc/postfix/main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version # Debian specific: Specifying a file name will cause the first # line of that file to be used as the name. The Debian default # is /etc/mailname. myorigin = /etc/mailname smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) biff = no # appending .domain is the MUAs job. append_dot_mydomain = no # Uncomment the next line to generate "delayed mail" warnings delay_warning_time = 4h readme_directory = no # TLS parameters smtpd_tls_cert_file = /etc/ssl/certs/ssl-mail.pem smtpd_tls_key_file = /etc/ssl/private/ssl-mail.key smtpd_tls_loglevel = 1 smtpd_tls_security_level = may smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache tls_random_source = dev:/dev/urandom tls_random_bytes = 32 tls_random_reseed_period = 3600s # home_mailbox = Maildir/ smtpd_sasl_auth_enable = yes smtpd_sasl_type = dovecot smtpd_sasl_path = private/dovecot-auth smtpd_sasl_authenticated_header = yes smtpd_sasl_security_options = noanonymous smtpd_sasl_local_domain = $myhostname broken_sasl_auth_clients = yes smtpd_use_tls = yes smtpd_tls_received_header = yes smtpd_tls_mandatory_protocols = SSLv3, TLSv1 smtpd_tls_mandatory_ciphers = medium #smtpd_tls_auth_only = yes # smtp_tls_cert_file=/etc/postfix/ssl/smtpd.pem smtp_tls_key_file=$smtp_tls_cert_file smtp_tls_loglevel = 1 smtp_tls_security_level = may smtp_tls_note_starttls_offer = yes smtp_use_tls = yes # # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. myhostname = example.org alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases myorigin = /etc/mailname #mydestination = example.org, www.example.org, example.cc, www.example.cc, myserverhostname, localhost.localdomain, localhost mydestination = relayhost = smtp-server.example.com # This was commented out as it gives a "unused parameter" warning on Precise - works on Hardy #relay_domain = $mydestination mynetworks_style = subnet #mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_command = /usr/lib/dovecot/deliver -c /etc/dovecot/conf.d/01-mail-stack-delivery.conf -m "${EXTENSION}" mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all owner_request_special = no message_size_limit = 32768000 # # Virtual Mailbox Domain Settings virtual_alias_maps = mysql:/etc/postfix/mysql/alias_maps.cf, mysql:/etc/postfix/mysql/alias_alias_maps.cf virtual_mailbox_domains = mysql:/etc/postfix/mysql/domains_maps.cf virtual_mailbox_maps = mysql:/etc/postfix/mysql/mailbox_maps.cf, mysql:/etc/postfix/mysql/mailbox_alias_maps.cf virtual_mailbox_limit = 51200000 virtual_minimum_uid = 5000 virtual_uid_maps = static:5000 virtual_gid_maps = static:5000 virtual_mailbox_base = /home/vmail virtual_transport = virtual # Additional for quota support # This was commented out as it gives a "unused parameter" warning on Precise - works on Hardy #virtual_create_maildirsize = yes #virtual_mailbox_extended = yes #virtual_mailbox_limit_maps = mysql:/etc/postfix/mysql/mailbox_limit_maps.cf #virtual_mailbox_limit_override = yes #virtual_maildir_limit_message = Sorry, the your maildir has overdrawn your diskspace quota, please free up some of spaces of your mailbox try again. #virtual_overquota_bounce = yes # # Spam reduction parameters. May be aggresive for some, but seems to work well. access_map_reject_code = 554 invalid_hostname_reject_code = 554 maps_rbl_reject_code = 554 multi_recipient_bounce_reject_code = 554 non_fqdn_reject_code = 554 plaintext_reject_code = 554 reject_code = 554 relay_domains_reject_code = 554 unknown_local_recipient_reject_code = 550 unknown_address_reject_code = 550 unknown_client_reject_code = 550 unknown_hostname_reject_code = 550 unknown_relay_recipient_reject_code = 550 unknown_virtual_alias_reject_code = 550 unknown_virtual_mailbox_reject_code = 550 unverified_recipient_reject_code = 550 unverified_sender_reject_code = 550 # default_process_limit = 20 smtpd_client_connection_count_limit = 10 # Value of 60 should translate to 1 per second limit smtpd_client_connection_rate_limit = 60 smtpd_client_message_rate_limit = 60 smtpd_client_new_tls_session_rate_limit = 60 # smtpd_helo_required = yes smtpd_delay_reject = yes address_verify_map = btree:${data_directory}/verify_cache smtpd_reject_unlisted_sender=yes # smtpd_recipient_restrictions = check_client_access hash:/etc/postfix/white_lists, permit_sasl_authenticated, permit_mynetworks, # permit_tls_clientcerts, reject_invalid_hostname, reject_non_fqdn_hostname, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_sender_domain, reject_unknown_recipient_domain, reject_unverified_sender, reject_unauth_destination, reject_rbl_client zen.spamhaus.org, reject_rbl_client cbl.abuseat.org, check_client_access hash:/etc/postfix/white_lists, permit smtpd_data_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_pipelining, permit # Added for trying to send email from PDA smtpd_client_restrictions = permit_sasl_authenticated smtpd_helo_restrictions = check_helo_access hash:/etc/postfix/check_helo_access, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_hostname, reject_invalid_hostname, permit #
/etc/postfix/mailname
example.org
Follow this for Postfix Client TLS Support setup
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> table=mailbox select_field=username where_field=username hosts=127.0.0.1:<mysql_db_port>
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> table=mailbox select_field=username where_field=username hosts=127.0.0.1:<mysql_db_port>
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> table=mailbox select_field=maildir where_field=username hosts=127.0.0.1:<mysql_db_port> additional_conditions = and active = 1
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> table=mailbox select_field=quota where_field=username hosts=127.0.0.1:<mysql_db_port> additional_conditions = and active = 1
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> table=alias select_field=goto where_field=address hosts=127.0.0.1:<mysql_db_port> additional_conditions = and active = 1
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> table=domain select_field=domain where_field=domain hosts=127.0.0.1:<mysql_db_port> additional_conditions = and backupmx = 0 and active = 1 and transport = 'virtual' and domain = '%s'
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> table=domain select_field=domain where_field=domain hosts=127.0.0.1:<mysql_db_port> additional_conditions = and active = 1 and backupmx = 0 and transport = 'relay' and domain = '%s'
The below two settings (alias_alias_maps.cf & mailbox_alias_maps.cf) are for alias domains:
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> hosts=127.0.0.1:<mysql_db_port> query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' AND alias.address=concat('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active = 1
user=<mysql_db_user> password=<mysql_db_password> dbname=<mysql_db_name> hosts=127.0.0.1:<mysql_db_port> query = SELECT maildir FROM mailbox, alias_domain WHERE alias_domain.alias_domain = '%d' AND mailbox.username=concat('%u', '@', alias_domain.target_domain ) AND mailbox.active = 1 AND alias_domain.active = 1
Change the following files at /etc/dovecot & /etc/dovecot/conf.d
The files in /etc/dovecot need NO changes from default
File(s) requiring change
driver = mysql connect = host=127.0.0.1 port=3306 user=root password=rootpassword dbname=postfixadmindbname default_pass_scheme = CRAM-MD5 user_query = \ SELECT \ concat('/home/vmail/', maildir) as home, \ 5000 as uid, 5000 as gid \ FROM mailbox \ WHERE username = '%u' AND active = '1' password_query = \ SELECT username as user, password, \ concat('/home/vmail/', maildir) as userdb_home, \ concat('maildir:/home/vmail/', maildir) as userdb_mail, \ 5000 as userdb_uid, 5000 as userdb_gid \ FROM mailbox \ WHERE username = '%u' AND active = '1'
The following files in /etc/dovecot/conf.d do NOT require change as well
File(s) requiring change
auth_verbose = yes auth_debug = yes plugin { # Events to log. Also available: flag_change append #mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename # Available fields: uid, box, msgid, from, subject, size, vsize, flags # size and vsize are available only for expunge and copy events. #mail_log_fields = uid box msgid size }
auth_mechanisms = plain cram-md5 !include auth-system.conf.ext
passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext # [session=yes] [setcred=yes] [failure_show_msg=yes] [max_requests=<n>] # [cache_key=<key>] [<service name>] #args = dovecot } userdb { # <doc/wiki/AuthDatabase.Passwd.txt> driver = sql args = /etc/dovecot/dovecot-sql.conf.ext # [blocking=no] #args = }
mail_location = maildir:/home/vmail/%d/%u mail_uid = vmail mail_gid = vmail maildir_copy_with_hardlinks = yes
All changes from the default go in config.local.php
$CONF['postfix_admin_url'] = 'http://pfa.example.org'; $CONF['domain_path'] = 'YES'; $CONF['database_type'] = 'mysql'; $CONF['database_host'] = 'localhost'; $CONF['database_user'] = 'upostfixadmin'; $CONF['database_password'] = 'somepassword'; $CONF['database_name'] = 'postfixadmin'; $CONF['admin_email'] = 'postmaster@example.com'; $CONF['smtp_server'] = 'smtp-server.example.com'; $CONF['encrypt'] = 'dovecot:CRAM-MD5'; $CONF['dovecotpw'] = "/usr/bin/doveadm pw"; $CONF['page_size'] = '100'; $CONF['default_aliases'] = array ( 'abuse' => 'abuse@example.com', 'hostmaster' => 'hostmaster@example.com', 'postmaster' => 'postmaster@example.com', 'webmaster' => 'webmaster@example.com' ); $CONF['aliases'] = '10000'; $CONF['mailboxes'] = '10000'; $CONF['maxquota'] = '10000'; $CONF['transport'] = 'YES'; $CONF['vacation_domain'] = 'autoreply.example.com'; $CONF['user_footer_link'] = "http://example.com/"; $CONF['footer_text'] = 'Return to example.com'; $CONF['footer_link'] = 'http://example.com'; $CONF['create_mailbox_subdirs_prefix']=''; $CONF['new_quota_table'] = 'YES';
If you are moving to a new machine make sure that you tar and untar files to preserver permissions and most importantly timestamps. Or all your old mail will have the same date/time!
/home/vmail/courier-dovecot-migrate.pl --to-dovecot --recursive --convert --overwrite /home/vmail/example.org/
Sample output:
Converting to Dovecot format Finding maildirs under /home/vmail/example.org/ Total: 70 mailboxes / 38 users 0 errors 46 dovecot-uidlist files written WARNING: Badly done migration will cause your IMAP and/or POP3 clients to re-download all mails. Read http://wiki.dovecot.org/Migration carefully.
One way to get the courier subfolders to show up in dovecot is to add this to the configuration:
namespace private { prefix = INBOX. separator = . inbox = yes }
Alternatively, I created the subfolders (from squirrel mail) and then delete the newly created folders from the Linux file folders and renamed the existing file folders to the name I just deleted. So here goes for example:
mv .INBOX.SomeSubFolder/ junk mv .SomeSubFolder .INBOX.SomeSubFolder
Here I moved the newly created folder to a “junk” location (an alternative to just deleting it). Then renamed the original folder to the new folder name.
Follow this link.
dovecot --build-options find /usr/lib/dovecot/modules/ doveconf -a
The pflogsumm.pl produces a daily summary of mail activity.
Install pflogsumm
at /usr/local/bin/pflogsumm
. Install the below script as a Daily cron. Make sure the /etc/cron.daily has the shell script above the daily log-rotate
script and the Daily cron runs at midnight. To change the timing of the daily cron run, update the run time at /etc/crontab
.
Daily cron job shell wrapper
#!/bin/bash # # Daily Postfix Log report # TS=$(date +%Y%m%d_%H%M%S); LOGFILEDIR="/var/log/postfixrep" LOGFILE="$LOGFILEDIR/pfrep_$TS.txt" PFLOGSUMM="/usr/local/bin/pflogsumm" PFMAILINF="/var/log/mail.info" PFMAILINF="/var/log/mail.log" REMAIL="report@example.org" REPSUB="Postfix Report" # #$PFLOGSUMM $PFMAILINF > $LOGFILE $PFLOGSUMM --detail 10 --problems_first --verbose_msg_detail $PFMAILINF > $LOGFILE cat $LOGFILE | mailx -s "$REPSUB" $REMAIL # # Delete log files older than 40 days /usr/bin/find $LOGFILEDIR/pfrep* -mtime +40 -exec rm {} \; # exit 0
Fail2ban is optional but highly recommended to reduce thrashing of the servers from brute-force attempts
/etc/fail2ban/jail.conf
enabled = true
recidive
ignoreip
(e.g. ignoreip = 127.0.0.1/8 192.168.0.0/16)
Notice the journalmatch = _SYSTEMD_UNIT=postfix@-.service
line. The default journalmatch = _SYSTEMD_UNIT=postfix.service
does not work. Not sure if this is an issue with Debian/Bookworm or fail2ban configuration.
# Fail2Ban filter for selected Postfix SMTP rejections # # [INCLUDES] # Read common prefixes. If any customizations available -- read them from # common.local before = common.conf [Definition] _daemon = postfix/smtpd failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.7\.1 .*$ ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.5\.2 .*$ ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.7\.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo= *$ ^%(__prefix_line)s\S+\: reject: RCPT from \S+\[<HOST>\]: 550 5\.1\.1 <\S*>: Recipient address rejected:.*$ ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 454 4\.7\.1 <\S*>: Relay access denied;.*$ ^%(__prefix_line)swarning: \S+\[<HOST>\]: SASL LOGIN authentication failed: Invalid authentication mechanism$ ^%(__prefix_line)swarning: Recipient address rate limit exceeded: \S+\ from unknown\[<HOST>\] for service smtp$ ignoreregex = [Init] journalmatch = _SYSTEMD_UNIT=postfix@-.service
# Fail2Ban filter for postfix authentication failures # [INCLUDES] before = common.conf [Definition] _daemon = postfix/smtpd failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$ ignoreregex = [Init] journalmatch = _SYSTEMD_UNIT=postfix@-.service
dovecot.conf
did not require any changes from the default install
Below are the list of failures that need to be checked
Jul 9 07:19:39 inthostname dovecot: imap-login: Disconnected: Too many invalid commands (no auth attempts in 0 secs): user=<>, rip=<hackerip>, lip=internalip, session=<MGpJ4QwAJgCn+IV+> Jul 9 00:23:02 inthostname dovecot: imap-login: Aborted login (no auth attempts in 4 secs): user=<>, rip=<hackerip>, lip=internalip, TLS, session=<g2xYDwcAcwBX7LCo> Jul 9 07:57:35 inthostname dovecot: imap-login: Disconnected (no auth attempts in 0 secs): user=<>, rip=<hackerip>, lip=internalip, TLS: Disconnected, session=<wqHyaA0AHgCrQ0bl> Jul 9 11:15:15 inthostname dovecot: imap-login: Disconnected (auth failed, 1 attempts in 2 secs): user=<someone@example.org>, method=PLAIN, rip=<hackerip>, lip=internalip, TLS, session=<mN+5KxAACACr92MJ> Jul 9 14:37:38 inthostname dovecot: imap-login: Disconnected (tried to use disallowed plaintext auth): user=<>, rip=<hackerip>, lip=internalip, session=<HXSc/xIAuAAl7S4R> Oct 6 23:17:35 inthostname dovecot: imap-login: Disconnected (disconnected before auth was ready, waited 0 secs): user=<>, rip=<hackerip>, lip=internalip, TLS handshaking: SSL_accept() failed: error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol, session=<HTcFoxgHmgBrquET> Oct 5 04:33:07 inthostname dovecot: imap-login: Disconnected (tried to use unsupported auth mechanism): user=<>, method=NTLM, rip=<hackerip>, lip=internalip, session=<MK/Dz/QGMAClmnge> Sep 25 04:55:41 inthostname dovecot: imap-login: Disconnected (client didn't finish SASL auth, waited 19 secs): user=<>, method=PLAIN, rip=<wanip>, lip=internalip, TLS: SSL_read() syscall failed: Connection reset by peer, session=<9yvt9CsGOQAYNzGR> hackerip=x.x.x.x internalip=y.y.y.y
fail2ban-regex systemd-journal /etc/fail2ban/filter.d/postfix.conf fail2ban-regex systemd-journal /etc/fail2ban/filter.d/postfix-sasl.conf
rsyslog filters duplicate messages as last message repeated X times
. This causes Fail2ban to not get the actual count of failed attempts. To change the configuration:
Edit /etc/rsyslog.conf
to update $RepeatedMsgReduction
from on to off
$RepeatedMsgReduction off
Restart rsyslog
service rsyslog restart
Run as root:
fail2ban-client status | grep "Jail list:" | sed "s/ //g" | awk '{split($2,a,",");for(i in a) system("fail2ban-client status " a[i])}'
Refer here to permanently ban!
To avoid error that there is no white list file.
The check_helo_access
allows helo specific white lists. For e.g. use when you an error such as Helo command rejected: Host not found;
.
cd /etc/postfix touch white_lists postmap white_lists # touch check_helo_access postmap check_helo_access
service postfix restart service dovecot restart
To turn on auth debug in dovecot. Update dovecot/conf.d/10-logging.conf
file with
auth_verbose = no auth_debug = no
Remember to turn if off once done.
Optionally update aliases so mail delivered to root (or someuser) at localhost can be forwarded to a remote mail user.
# See man 5 aliases for format postmaster: root someuser:someuser@example.org root:someuser@example.org
Run the below after changing /etc/aliases
postalias /etc/aliases # Alternative to postalias you can also run 'newaliases' command service postfix restart
Update logrotate
configuration at /etc/logrotate.d/rsyslog
to increase the retention period. Typically mail logs have a 4 week retention period (parameter rotate
). Increase it to 13 weeks or more as desired.
The above setup is for Postfix smart host. For the rest of the servers in the network, it is preferred to set them up as a Satellite host and route mail through the local host. The Postfix main.cf configuration for Postfix Satellite host aka Send-only, Relay, Forwarded host is below. Note:
your-host-name
with your hostname example.org
with the smart-host domain namemainsmtp
with the hostname/IP of the smart-host/etc/postfix/main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version # Debian specific: Specifying a file name will cause the first # line of that file to be used as the name. The Debian default # is /etc/mailname. myorigin = /etc/mailname smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) biff = no # appending .domain is the MUA's job. append_dot_mydomain = no # Uncomment the next line to generate "delayed mail" warnings #delay_warning_time = 4h readme_directory = no # TLS parameters smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key smtpd_use_tls=yes smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination myhostname = your-host-name alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases mydomain = example.org mydestination = $myhostname.$mydomain, localdomain, $myhostname, localhost.localdomain, localhost relayhost = mainsmtp smtp_host_lookup = native mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = loopback-only # Address rewriting #smtp_generic_maps = hash:/etc/postfix/generic
/etc/postfix/mailname
your-host-name.example.org
/etc/aliases
# See man 5 aliases for format
postmaster: root
user:system@example.org
root:system@example.org