postfix + dovecot + mysql on FreeBSD

Introduction



I wanted to study the mail server for a long time, but my hands reached only now, and I did not manage to find much correct information, so I decided to write as detailed a publication as possible. This publication will discuss not only postfix, dovecot, mysql, postfixadmin, but also spamassassin, clamav-milter (a special version of clamav for mail servers), postgrey, as well as the possibility of transferring spam to the Spam folder (dovecot- pigeonhole).



Training



First of all, we will install the packages that you need to work (postfix, dovecot and dovecot-pigeonhole must be installed from the ports, dovecot-sieve can be installed from packages, in principle, there are newer versions in the ports and for this reason there may be incompatibility between dovecot and dovecot- sieve). Install the following packages:



pkg install apache24 php73 mod_php73 php73-extensions php73-mysqli php73-mbstring php73-openssl clamav-milter postgrey spamassassin mysql57-server openssl wget
      
      





After installation, we will put the necessary services in autorun:



 #postfix  dovecot  ,       sysrc postfix_enable="YES" sysrc dovecot_enable="YES" sysrc mysql_enable="YES" sysrc apache24_enable="YES" sysrc spamd_flags="-u spamd -H /var/spool/spamd" sysrc spamd_enable="YES" sysrc postgrey_enable="YES" sysrc clamav_clamd_enable="YES" sysrc clamav_milter_enable="YES" sysrc clamav_freshclam_enable="YES" #freshclam        12  sysrc clamav_freshclam_flags="--daemon --checks=12"
      
      





Run the services:



 service apache24 start service mysql-server start #  spamassassin       sa-update sa-compile service sa-spamd start #   clamav   freshclam service clamav-clamd start service clamav-freshclam start service clamav-milter start #  postgrey    (/usr/local/etc/rc.d/postgrey),       ""   4-   ,    : ${postgrey_flags:=--inet=10023}     : : ${postgrey_flags:=--inet=10023 --auto-whitelist-clients=4} service postgrey start
      
      





Do not forget to add to httpd.conf the lines necessary for php to work in apache and for postfixadmin to work correctly:



 <FilesMatch "\.php$"> SetHandler application/x-httpd-php </FilesMatch> <FilesMatch "\.phps$"> SetHandler application/x-httpd-php-source </FilesMatch> <IfModule dir_module> DirectoryIndex index.php </IfModule> #         postfixadmin DocumentRoot "/usr/local/www/apache24/data/postfixadmin-3.2/public"
      
      





Next, go to the directory and download postfixadmin



 cd /usr/local/www/apache24/data
      
      





Download postfixadmin (at the time of writing, the current version was 3.2)



 wget --no-check-certificate https://sourceforge.net/projects/postfixadmin/files/postfixadmin/postfixadmin-3.2/postfixadmin-3.2.tar.gz
      
      





After that, you need to unzip the archive into this directory and change the owner of the directory:



 gzip -d postfixadmin-3.2.tar.gz tar -xvf postfixadmin-3.2.tar chown -R www:www /usr/local/www/apache24/data service apache24 restart
      
      





Next, prepare the database for postfixadmin, run the mysql-secure-installation script (the password that you create in this script will need to be created in mysql with the alter user command), for the initial setup of mysql, then enter mysql, create the database and rights for her:



 mysql -p -r alter user 'root'@'localhost' identified by 'password123'; create database postfix; grant all privileges on postfix.* to 'postfix'@'localhost' identified by 'password123'; exit
      
      





After the database is configured, you need to edit the config.inc.php file, in this example, this file is in the /usr/local/www/apache24/data/postfixadmin-3.2/ directory, in this file you need to edit several lines and bring them to mind, after changing the settings, restart apache, you also need to create the templates_c directory in the /usr/local/www/apache24/data/postfixadmin-3.2 directory and assign www owner to it:



 mkdir /usr/local/www/apache24/data/postfixadmin-3.2/templates_c chown -R www:www /usr/local/www/apache24/data/postfixadmin-3.2/templates_c $CONF['configured'] = true #       postfixadmin     . $CONF['setup_password'] = 'dd28fb2139a3bca426f02f60e6877fd5:13d2703c477b0ab85858e3ac5e076a0a7a477315'; $CONF['default_language'] = 'ru' $CONF['database_type'] = 'mysqli'; $CONF['database_host'] = 'localhost'; $CONF['database_user'] = 'postfix'; #           $CONF['database_password'] = 'password123'; $CONF['database_name'] = 'postfix'; service apache24 restart
      
      





SSL



To generate the key, we will use the method proposed on postfix.org, with the creation of our own certification authority, we need to go to the / etc / ssl directory and execute the script:



 cd /etc/ssl /usr/local/openssl/misc/CA.pl -newca
      
      





During the execution of the script, a name for the certificate will be requested, do not enter anything, press Enter, then the script will ask you to create a password for the certificate, then there will be standard questions for creating a certificate.



Next, you need to create a secret key (without password) and a non-signed public key certificate (Organizational Unit Name (eg, section) [] should be different from what is specified in the certificate created above):



 openssl req -new -newkey rsa:4096 -nodes -keyout foo-key.pem -out foo-req.pem
      
      





We will sign the public key certificate (specify the number of days as many as you need):



 openssl ca -out foo-cert.pem -days 365 -infiles foo-req.pem
      
      





Leave the created certificates in this directory, or transfer them to the directory that is more convenient for you, the “postfix” and “dovecote” configs will be configured taking into account the fact that the certificates will be in this directory.



Vmail user



Before installing postfix, dovecot and dovecot-pigeonhole, create a user and a group (a group will be created automatically) vmail, as well as the directory where the mail will be located.



 pw useradd -n vmail -s /usr/sbin/nologin -u 1000 -d /var/vmail
      
      





Create a directory for mail and set the owner of the vmail user:



 mkdir /var/vmail chown -R vmail:vmail /var/vmail chmod -R 744 /var/vmail
      
      





Postfix, dovecot, dovecot-pigeonhole



As I wrote earlier, we will assemble the application data from the ports, run the command to download and unpack the ports:



 portsnap fetch extract
      
      





After unpacking the ports, go to the dovecot directory, configure the port (note the support for mysql) and run the build (BATCH = yes will tell make not to ask questions during installation):



 cd /usr/ports/mail/dovecot make config make BATCH=yes install clean
      
      





Do the same with postfix and dovecot-pigeonhole



dovecot-pigeonhole:



 cd /usr/ports/mail/dovecot-pigeonhole make BATCH=yes install clean
      
      





postfix: also check mysql support in port settings



 cd /usr/ports/mail/postfix-sasl make config make BATCH=yes install clean
      
      





Before starting dovecot, copy the "configs":



  cp -R /usr/local/etc/dovecot/example-config/* /usr/local/etc/dovecot
      
      





After installing postfix and dovecot, start the services:



 service postfix start service dovecot start
      
      





It is also necessary to create a directory in which the module for sending spam to the spam folder will be compiled, in my case this directory is located in the /usr/local/etc/dovecot/conf.d folder, the directory name is def, create this directory and a file with code for compilation and set the owner of the given vmail user directory:



 mkdir /usr/local/etc/dovecot/conf.d/def touch /usr/local/etc/dovecot/conf.d/def/default.sieve chown -R vmail:vmail /usr/local/etc/dovecot/conf.d/def chmod -R 744 /usr/local/etc/dovecot/conf.d/def
      
      





Put the lines in this file:



 require "fileinto"; if header :contains "X-Spam-Flag" "YES" { fileinto "Junk"; }
      
      





Configs



In this section I will give examples of “configs” with comments in them, I doubt only the “config” of spamassassin, since I did not find the correct descriptions on the network (I left the “config” by default), please add in the comments how to configure spamassassin better.



Postfix



First of all, you need to create files for pulling users, domains, quotas from the database. Create a directory to store the data files and the necessary files:



 mkdir /usr/local/etc/postfix/mysql touch /usr/local/etc/postfix/mysql/relay_domains.cf touch /usr/local/etc/postfix/mysql/virtual_alias_maps.cf touch /usr/local/etc/postfix/mysql/virtual_alias_domain_maps.cf touch /usr/local/etc/postfix/mysql/virtual_mailbox_maps.cf
      
      





The contents of these files will be:

relay_domains.cf



 hosts = 127.0.0.1 user = postfix password = password123 dbname = postfix query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '1'
      
      





virtual_alias_maps.cf



 hosts = 127.0.0.1 user = postfix password = password123 dbname = postfix query = SELECT goto FROM alias WHERE address='%s' AND active ='1'
      
      





virtual_alias_domain_maps.cf



 hosts = 127.0.0.1 user = postfix password = password123 dbname = postfix 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'
      
      





virtual_mailbox_maps.cf



 hosts = 127.0.0.1 user = postfix password = password123 dbname = postfix query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
      
      





master.cf



 # postfix  ,    dovecot    dovecot unix - nn - - pipe flags=DRhu user=vmail:vmail argv=/usr/local/libexec/dovecot/deliver -f ${sender} -d ${recipient} #  smtpd     sasl,    ,  spamassassin    smtp inet n - n - - smtpd -o content_filter=spamassassin -o smtpd_sasl_auth_enable=yes #  587     sasl submission inet n - n - - smtpd -o smtpd_sasl_auth_enable=yes #  smtp    SASL smtps inet n - n - - smtpd -o smtpd_sasl_auth_enable=yes -o smtpd_tls_wrappermode=yes # Spamassassin spamassassin unix - nn - - pipe flags=DROhu user=vmail:vmail argv=/usr/local/bin/spamc -f -e /usr/local/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop} #628 inet n - n - - qmqpd pickup unix n - n 60 1 pickup cleanup unix n - n - 0 cleanup qmgr unix n - n 300 1 qmgr #qmgr unix n - n 300 1 oqmgr tlsmgr unix - - n 1000? 1 tlsmgr rewrite unix - - n - - trivial-rewrite bounce unix - - n - 0 bounce defer unix - - n - 0 bounce trace unix - - n - 0 bounce verify unix - - n - 1 verify flush unix n - n 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - n - - smtp relay unix - - n - - smtp -o syslog_name=postfix/$service_name # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 showq unix n - n - - showq error unix - - n - - error retry unix - - n - - error discard unix - - n - - discard local unix - nn - - local virtual unix - nn - - virtual lmtp unix - - n - - lmtp anvil unix - - n - 1 anvil scache unix - - n - 1 scache postlog unix-dgram n - n - 1 postlogd
      
      





main.cf



 #       dovecot,       local_transport = dovecot #      ,  SMTP-      EHLO  SMTP  smtpd_discard_ehlo_keywords = CONNECT GET POST #            smtpd_delay_reject = yes #     smtpd_helo_required = yes #     ,   disable_vrfy_command = yes #       broken_sasl_auth_clients = yes #   smtpd_sasl_security_options = noanonymous noactive nodictionary smtp_sasl_security_options = noanonymous noactive nodictionary # dovecot  (  cyrus) smtpd_sasl_type = dovecot smtp_sasl_type = dovecot #    smtpd_sasl_path = private/auth #   local_recipient_maps = $virtual_mailbox_maps $virtual_alias_maps #   ,    smtpd_reject_unlisted_recipient = yes #   message_size_limit = 10485760 #     spamassassin spamassassin_destination_recipient_limit = 1 # milter_default_action = accept milter_protocol = 2 #   clamav smtpd_milters = unix:/var/run/clamav/clmilter.sock non_smtpd_milters = unix:/var/run/clamav/clmilter.sock #MYSQL relay_domains = mysql:/usr/local/etc/postfix/mysql/relay_domains.cf virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql/virtual_alias_maps.cf, mysql:/usr/local/etc/postfix/mysql/virtual_alias_domain_maps.cf virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql/virtual_mailbox_maps.cf # HELO smtpd_helo_restrictions = permit_sasl_authenticated, reject_non_fqdn_helo_hostname, reject_invalid_hostname #    smtpd_data_restrictions = permit_sasl_authenticated reject_unauth_pipelining, reject_multi_recipient_bounce #   smtpd_sender_restrictions = permit_sasl_authenticated reject_sender_login_mismatch,reject_unauthenticated_sender_login_mismatch, reject_non_fqdn_sender, reject_unknown_sender_domain #  (check_policy_service inet:127.0.0.1:10023  postgrey -      ) smtpd_recipient_restrictions = permit_sasl_authenticated reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_multi_recipient_bounce, reject_unknown_client_hostname, reject_unauth_destination, check_policy_service inet:127.0.0.1:10023 #   virtual_mailbox_base = /var/vmail #uid  gid vmail virtual_minimum_uid = 1000 virtual_uid_maps = static:1000 virtual_gid_maps = static:1000 #   virtual_transport = devecot dovecot_destination_recipient_limit = 1 #  smtp_use_tls=yes smtp_tls_note_starttls_offer=yes # smtp_tls_security_level=encrypt       ssl,        ssl,    smtp_tls_security_level=may(    ssl,     ) smtp_tls_security_level=encrypt smtp_tls_session_cache_database=btree:$data_directory/smtp_tls_session_cache smtp_tls_CAfile=/etc/ssl/demoCA/cacert.pem smtp_tls_key_file=/etc/ssl/foo-key.pem smtp_tls_cert_file=/etc/ssl/foo-cert.pem smtp_tls_session_cache_timeout=3600s smtp_tls_protocols=!TLSv1.2 smtp_tls_loglevel=1 # smtpd_tls_security_level=encrypt       ssl,        ssl,    smtpd_tls_security_level=may(    ssl,     ) smtpd_tls_security_level=encrypt smtpd_use_tls=yes smtpd_tls_auth_only=yes smtpd_tls_loglevel=1 smtpd_tls_received_header=yes smtpd_tls_session_cache_timeout=3600s smtpd_tls_session_cache_database=btree:$data_directory/smtpd_tls_session_cache smtpd_tls_key_file=/etc/ssl/foo-key.pem smtpd_tls_cert_file=/etc/ssl/foo-cert.pem smtpd_tls_CAfile= /etc/ssl/demoCA/cacert.pem smtpd_tls_protocols=!TLSv1.2 #      tls_random_source=dev:/dev/urandom #  compatibility_level = 2 #   ,    ,      ,    soft_bounce = no #   UNIX       postfix mail_owner = postfix #     postfix(        ) myhostname = $mydomain #       mydomain = virusslayer.su myorigin = $myhostname #    inet_interfaces = all #        mydestination = $mydomain, localhost, localhost.$mydomain #   550         unknown_local_recipient_reject_code = 550 #    localhost mynetworks_style = host #       , -         mynetworks = #  ip inet_protocols = ipv4 #  (   ) alias_maps = hash:/etc/mail/aliases alias_database = dbm:/etc/mail/aliases.db #          smtpd_banner = $myhostname ESMTP $mail_name #       debug_peer_level = 2 #      (   ,    yandex.ru gmail.ru mail.ru  ..) debug_peer_list = 127.0.0.1 #   debugger_command = PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin ddd $daemon_directory/$process_name $process_id & sleep 5 #  sendmail sendmail_path = /usr/local/sbin/sendmail mailq_path = /usr/local/bin/mailq setgid_group = maildrop #    html_directory = /usr/local/share/doc/postfix manpage_directory = /usr/local/man sample_directory = /usr/local/etc/postfix readme_directory = /usr/local/share/doc/postfix meta_directory = /usr/local/libexec/postfix shlib_directory = /usr/local/lib/postfix queue_directory = /var/spool/postfix command_directory = /usr/local/sbin daemon_directory = /usr/local/libexec/postfix data_directory = /var/db/postfix
      
      





Docot



dovecot.conf



 #     dovecot protocols = imap pop3 #    listen = *, :: #        mysql dict { quota = mysql:/usr/local/etc/dovecot/dovecot-dict-sql.conf.ext } #   !include conf.d/*.conf !include_try local.conf
      
      





dovecot-dict-sql.conf.ext



 connect = host=127.0.0.1 dbname=postfix user=postfix password=password123 map { pattern = priv/quota/storage table = quota2 username_field = username value_field = bytes } map { pattern = priv/quota/messages table = quota2 username_field = username value_field = messages }
      
      





dovecot-sql.conf.ext



 #    MYSQL driver = mysql connect = host=127.0.0.1 dbname=postfix user=postfix password=password123 #     default_pass_scheme = MD5 #  ,    user_query = SELECT '/var/mail/%d/%n/' AS home, 'maildir:/var/vmail/%d/%n' AS mail, 1000 AS uid, 1000 AS gid, concat('*:bytes=',quota) as quota_rule FROM mailbox \ WHERE username ='%u' AND active = '1' password_query = SELECT username as user, password, '/var/vmail/%d/%n' as userdb_home, 'maildir:/var/vmail/%d/%n' as userdb_mail, 1000 as userdb_uid, \ 1000 as userdb_gid, concat('*:bytes=',quota) AS userdb_quota_rule FROM mailbox WHERE username ='%u' AND active ='1'
      
      





10-auth.conf



 #   SSL disable_plaintext_auth = yes #   auth_realms = virusslayer.su auth_default_realm = virusslayer.su #    ( ,         ssl) auth_mechanisms = plain login #   ,  !include auth-sql.conf.ext,        mysql #!include auth-deny.conf.ext #!include auth-master.conf.ext #!include auth-system.conf.ext !include auth-sql.conf.ext #!include auth-ldap.conf.ext #!include auth-passwdfile.conf.ext #!include auth-checkpassword.conf.ext #!include auth-vpopmail.conf.ext #!include auth-static.conf.ext
      
      





10-mail.conf



 #    mail_location = maildir:/var/vmail/%d/%n #       namespace inbox { inbox = yes } #uid  gid vmail mail_uid = 1000 mail_gid = 1000 # ,    quota mail_plugins = quota
      
      





10-master.conf



 #     ssl service imap-login { inet_listener imap { port = 143 } inet_listener imaps { port = 993 ssl = yes } } service pop3-login { inet_listener pop3 { port = 110 } inet_listener pop3s { port = 995 ssl = yes } } service submission-login { inet_listener submission { port = 587 } } #           (   ,       ) service auth { unix_listener auth-userdb { mode = 0600 user = vmail group = vmail } # Postfix smtp-auth unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } } #  vmail   service dict { unix_listener dict { mode = 0660 user = vmail group = vmail } }
      
      





10-ssl.conf



 # ssl  (    sll  ) ssl = required #   ssl_cert = </etc/ssl/foo-cert.pem ssl_key = </etc/ssl/foo-key.pem ssl_ca = </etc/ssl/demoCA/cacert.pem #    ssl_min_protocol = TLSv1.2
      
      





15-lda.conf



 quota_full_tempfail = no lda_mailbox_autosubscribe = yes protocol lda { #      sieve,        mail_plugins = $mail_plugins sieve quota }
      
      





90-plugin.conf



 #             "",       chown -R vmail:vmail #          "" plugin { #setting_name = value sieve = /usr/local/etc/dovecot/conf.d/def/default.sieve }
      
      





auth-sql.conf.ext



 #      MYSQL passdb { driver = sql # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext args = /usr/local/etc/dovecot/dovecot-sql.conf.ext } userdb { driver = sql args = /usr/local/etc/dovecot/dovecot-sql.conf.ext }
      
      





Spamassassin



The "config" spamassassin looks like this, but something tells me the data is not enough, please help with the data in the "config":



local.cf



 rewrite_header Subject *****SPAM***** report_safe 0 required_score 5.0 use_bayes 1 bayes_auto_learn 1 ifplugin Mail::SpamAssassin::Plugin::Shortcircuit endif # Mail::SpamAssassin::Plugin::Shortcircuit
      
      





It is also necessary to conduct training on letters with spam and without it:



 sa-learn --spam /path/spam/folder sa-learn --ham /path/ham/folder
      
      





Additionally



In this section, I’ll specify the firewall settings based on pf, add pf to autorun, and specify the file with the rules:



 sysrc pf_enable="YES" sysrc pf_rules="/etc/0.pf"
      
      





Create a file with the rules:



 ee /etc/0.pf
      
      





And add the rules to it:



 # (   lo0)    ,     set skip on lo0 #      deovecot, postfix, root pass in quick proto { tcp, udp } from any to any port {53,25,465,587,110,143,993,995} user {dovecot,postfix,root} flags S/SA modulate state pass out quick proto { tcp, udp } from any to any port {53,25,465,587,110,143,993,995} user {dovecot,postfix,root} #      root pass out quick proto {tcp,udp} from any to any user root #     pass in quick proto tcp from any to any port 80 flags S/SA modulate state #SSH pass in quick proto tcp from any to any port 22 flags S/SA modulate state #     clamav  spamd pass out quick proto {tcp,udp} from any to any user {clamav,spamd} #DNS  ICMP pass out quick proto {tcp,udp} from any to any port=53 keep state pass out quick proto icmp from any to any block from any to any fragment block from any to any block all
      
      





You can run pf with the command:



 service pf start
      
      





Testing



To test all possible connections (STARTTLS, SLL), you can use the MyOffice Mail client for mobile devices (in my case for ios), in this application there are many parameters for configuring connections to the mail server.



To test spaassasin we use the GTUBE signature, add the line in the message:



 XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X
      
      





If everything is correct, then the message will be marked with spam and accordingly moved to the spam folder.



To test the antivirus, you need to send an email with a text file, in this file there will be an EICAR sequence:



 X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
      
      





Letters naturally need to be sent from external mailboxes.

To view the logs in real time, run:



 tail -f /var/log/maillog
      
      





Also, for the correct testing of sending mail to external mailboxes (for example, yandex.ru, mail.ru, gmail.com, etc.), you need to register the reverse DNS zone (PTR record), you can do this by contacting your provider (if you certainly do not have your own DNS server).



Output



It may of course seem that the mail server is a rather complicated thing, but if you figure it out, it is not at all like that, having spent a little time on the configuration, you can get a rather functional mail server, with protection against spam and viruses.



PS If you plan to “copy-paste” with comments, then you need to add the root user (and those who need it) to the Russian class logs:



 pw usermod root -L russian
      
      





After these actions, Russian characters will be displayed correctly.



All Articles