Last Updated: 02-04-2026
  1. You'll need a registered domain name and a VPS (Virtual Private Server) provider that allows uploading of iso images for the OS.

    I use namecheap to get my registered domain names.

    I use contabo.com as my VPS provider, and they work great for this, and have improved a lot over the past year. Contabo allows uploading of ISO and Qcow2 custom
    images to use for your OS. If you get a VPS at contabo for setting up a mailserver I'd highly recommend getting a VPS with a minimum of 16gb memory and 6 CPU
    cores. I've tried the cheaper VPS with 8gb memory and 4 CPU cores, and it was pretty sluggish for a mailserver.

    Note! Been having trouble lately with Contabo. I Just ordered another VPS from them, and custom images will not work properly on this new VPS. I think it's because
    they changed something about how the new VPS's work with custom images. I got Contabo support to help, but they couldn't fix it. I've got 3 other VPS's with Contabo
    that work perfect with custom images. Hopefully they will get this fixed soon.

    netcup.com didn't workout for me. Their prices were great, and their VPS and Root Servers were great, but to use their nameservers you have to get your domain through
    them. I've got too many domains already, and don't want or need anymore. Netcup seems like a very eccentric company to me, they have their own way of doing things,
    but you may want to check them out. They do have some very good servers to choose from, and you'll probably get twice the performance for the same money as Contabo.

    After you have your domain name and a VPS go to the DNS Zone Management panel at your domain name or VPS provider and setup the DNS records for the server.
    This includes the SPF records, DMARC records, and DKIM key.

    Note! - I switched back to using Contabo's nameservers, because Namecheap's nameservers are open resolvers, which means anyone can use them, and open resolvers
    will not work with spamhaus for filtering spam.

    Resource record      TTL      Type      Priority      Data
    ---------------------------------------------------------------------
    mail.example.org            3600      A           0     ip.address
    example.org                 3600      A           0     ip.address
    www.example.org             3600      A           0     ip.address
    example.org                 3600      MX          10    mail.example.org
    example.org                 3600      TXT         0     v=spf1 mx -all
    _dmarc.example.org          3600      TXT         0     v=DMARC1;p=quarantine;pct=100;fo=1;rua=mailto:admin@example.org
    dkim._domainkey.example.org 3600      TXT         0     v=DKIM1;p=dkim.public.key
    
    You will have to wait until SlackerMail-FreeBSD is installed to retrieve and enter the DKIM key in your DNS Zone Management.

    Then setup the reverse DNS at your VPS provider with:

    IP Address                      PTR Record
    ----------------------------------------------
    your.server.ip.address        mail.example.org
    
  2. Install FreeBSD

    a. Qcow2 Install - (Alternative to ISO Install)

    This is the fastest and easiest method to setup FreeBSD at your VPS provider, if they support it. Contabo does support qcow2 images, and it's the method I always
    use. I have two FreeBSD qcow2 images below. One uses the UFS filesystem, and the other uses the ZFS filesystem. They both have a 20gb partition, but will auto
    resize on firstboot to use the full hard drive of your server. These FreeBSD images are for a command line server only, no X-Windows.

    Important! - Make sure to read this How-To for directions on how to set these qcow2 images up properly at your provider!

    After you have uploaded one of the two qcow2 images below to your VPS provider as a custom image, you can initiate the install to your VPS. You'll need a VNC client
    to login to the server with the VNC ip the provider provided you. I use TigerVNC for my VNC client. TigerVNC is availible for Windows, Mac, Linux, and FreeBSD.

    The qcow2 image below uses the ufs filesystem.
    FreeBSD-15.0-p2-amd64-ufs-02032026.qcow2 (1.99gb) SHA256: d9053f79f07477f7aff1ce6885cfa4857f15b01bdb791e16f18d46274cb6cfe1

    The qcow2 image below uses the zfs filesystem.
    FreeBSD-15.0-p2-amd64-zfs-02032026.qcow2 (2.24gb) SHA256: eb4a9119f79021208c413cf868ed168a0d2e61a70129bd47aad7c1bfea110721

    After you install one of the above qcow2 images at your VPS provider, and have followed the How-To on how to set them up, you can then go to
    FreeBSD Server Initial Setup.

    b. ISO Install - (Traditional Install)

    Upload the FreeBSD-15.0-RELEASE-amd64-dvd1.iso (4.1gb) image to your VPS provider as a custom image.

    After the FreeBSD 15.0 ISO has been uploaded to your provider, initiate the install process in their control panel. Then you'll need to VNC to the ip address given for VNC
    access to your FreeBSD server. I use TigerVNC for my VNC client. TigerVNC is availible for Windows, Mac, Linux, and FreeBSD.

    FreeBSD-15.0 is the easiest and fastest OS install I've ever seen. When you VNC to your FreeBSD server you'll be met with the install menus below.

    01. Welcome: - I select "Install".
    02. Keymap Selection: - Pick your proper regional keymap.
    03. Set Hostname: - Enter your FQDN (Fully Qualified Doman Name) eg. mail.example.org.
    04. Select Installation Type:
        (Distribution Sets or Packages) - I select Packages. This is new for 15.0.
        (Network or Offline Installation) - I select Network.
    05. Network Configuration: - I select Auto.
    06. Partitioning: - Pick your preferred disk setup. I select either "Auto (ZFS)" or "Auto (UFS)".
        UFS Configuration:
        Partition - I select "Entire Disk".
        Partition Scheme - I select "GPT".
        Partition Editor - I delete the freebsd-swap partition, then select Finish and then Commit.
        ZFS Configuration: 
        I accept the defaults, but enter 0 for swap. In my opinion, with all the memory these days a 
        swap partition isn't needed.
        (Select Virtual Device type) - I select Stripe.
        (Select Disk) - I select "da0 QEMU QEMU HARDDISK"
        Then I chose YES to continue.
    07. Select System Components: - I select (base, devel, kernel-dbg, lib32, src)
    08. FreeBSD is installed:
    09. Setting the root password: - Set a password for root.
    10. Time Zone Selector: - Select your time zone.
        Set Time and Date - I skip on both.
    11. System Configuration: - Select what services will be started at boot. I choose 
        (sshd, moused, ntpd, ntpd_sync_on_start, dumpdev)
    12. Sytem Hardening: - I leave all of these unchecked.
    13. Add User Accounts: - Enter Username, then Full name. I then just hit enter for all the default 
        selections, except for "Invite user into other groups?", I enter "wheel", because you'll need 
        to be in the wheel group to be able to su to root. Lastly you'll need to enter a password for 
        the user.
    14. Final Configuration: - If you are satisfied with the setup select "Finish".
        Manual Configuration - I select "NO".
    15. Complete: - Reboot and logon to your server using PuTTY with the user you added during install, 
        then to become root run "su - root", and enter the root password.
    
    Now that FreeBSD is installed, you've rebooted, and logged in with PuTTY, there are a couple files you may want to edit. If you want to be able to
    login as root, then you'll need to edit /etc/ssh/sshd_config and change "#PermitRootLogin no" to "PermitRootLogin yes". You'll probably want to
    edit /boot/loader.conf, and add "autoboot_delay="0"", so bootup is faster.

    You can edit with "vi" or with "mc" (Midnight Commander). Midnight Commander is a very nice command line filemanager, and very easy to edit with.

    To install Midnight Commander (mc):

    pkg update
    pkg install mc
    
    To use mc in FreeBSD run: "mc -u".
  3. FreeBSD Server Initial Setup

    Now ssh to your new FreeBSD server with the ip address given to you by your VPS provider, with PuTTY or other ssh client.

    You may have already setup your hostname if you did the ISO install, but if you didn't, or you did the qcow2 install, you need
    to do that now. Run the command below to set your hostname. Adjust for your actual FQDN:

    sysrc hostname="mail.example.org"
    
    Then run the echo command below to set your /etc/hosts file, and of course adjust for your actual FQDN:
    echo "127.0.0.1 mail.example.org mail 
    127.0.0.1 localhost" > /etc/hosts
    
    Next we need to do a system update:
    If you installed with one of the FreeBSD qcow2 images, or you chose the new "Packages" update system 
    during the FreeBSD ISO install, then Just run the 2 commands below to make sure your server is completely 
    up to date
    
    pkg update
    pkg upgrade
    
    If you did the FreeBSD ISO install, and chose the "Distribution Sets" update system, then you'll need to 
    run the 3 commands below to make sure your server is completely up to date.
    
    pkg update
    pkg upgrade
    freebsd-update fetch install
    
    If your system was updated, then do a reboot:
    reboot
    
    Next we need to install some needed packages:
    pkg install gcc gmake cmake python php84 php84-pecl-imagick bind-tools 7-zip m4 git gnupg sudo perl5 openssl35 bash bsddialog \
    p5-App-cpanminus p5-App-cpanoutdated mc htop neofetch gzip unzip shared-mime-info ca_root_nss bzip2-1.0.8_1 pftop 
    
    A lot of stuff was added, so we better reboot:
    reboot
    
    We need to get the time right.

    First make sure ntpd is enabled to start at boot and will sync on start:

    sysrc ntpd_enable=YES
    sysrc ntpd_sync_on_start=YES
    
    Then we need to update the leap seconds file:
    service ntpd onefetch
    
    Then start ntpd:
    service ntpd restart
    
  4. PF Firewall Setup

    I have configured the PF Firewall to work with SlackerMail-FreeBSD.
    First download the PF config file and move it to the /etc directory:
    fetch https://the-slacker.com/download/pf.conf
    mv -f pf.conf /etc/
    
    Run "ifconfig" at the command prompt, and take note of the interface name at the very top line. If it doesn't say "vtnet0",
    then edit /etc/pf.conf and change "ext_if = "vtnet0"" to the actual name of your device.

    Next enable PF firewall and logging to be started at boot, then reboot:

    service pf enable
    service pflog enable
    reboot
    
    After the reboot check the status of PF:
    pfctl -s info
    
    To read the pflog run the below command:
    tcpdump -n -e -ttt -r /var/log/pflog
    
    Note! We are now ready to install SlackerMail with the SlackerMail-FreeBSD install script. The SlackerMail-FreeBSD install script is not
    ready yet, and it will take about 2-4 weeks to finish.

    You can also setup SlackerMail by hand by following all the directions below. It takes about 5-6 minutes to install from the script. It will take
    1-2 days to install by hand, but it's a good learning experience.

  5. Install Webmin as the server control panel - Optional

    Install Webmin:
    pkg install webmin
    
    then run:
    /usr/local/lib/webmin/setup.sh
    
    Make sure to answer:
    Use SSL (y/n): y
    
    Then enable webmin to start at boot:
    service webmin enable
    
    Then start webmin:
    service webmin start
    
    I edit /usr/local/etc/webmin/miniserv.conf and add the line "allow=your.ip.address" # Do this if you
    want to only allow your ip address to login with Webmin.
    
    You can safely upgrade Webmin from the Webmin control panel.
    
    You should now be able to reach your Webmin control panel at https://example.org:10000

    The reason I install Webmin so early into the setup is because it makes editing files and reading logs so easy.
    It'll save you many hours over using just the console, and is safe if you add the line "allow=your.ip.address"
    in /usr/local/etc/webmin/miniserv.conf.

    *Notice
    Webmin doesn't recognize the FreeBSD ZFS filesystem, so it doesn't report a disk, which is no problem anyway.

  6. Web Server - Apache or Nginx

    First we need to create a Diffie–Hellman key exchange (DH) that we will use in our webserver, postfix, dovecot, etc.
    openssl dhparam -out dh2048_param.pem 2048
    mv -f dh2048_param.pem /etc/ssl/
    
    Next we need to download and install a preconfigured php-fpm config file:
    fetch https://the-slacker.com/download/freebsd.www.conf
    mv -f freebsd.www.conf /usr/local/etc/php-fpm.d/www.conf
    
    Now we need to setup a web server. Take your choice from Apache or Nginx. Both are fine web servers.

    Apache:

    First we need to install Apache:
    pkg install apache24
    
    Then download and install a preconfigured httpd.conf file:
    fetch https://the-slacker.com/download/freebsd-httpd.conf
    mv -f freebsd-httpd.conf /usr/local/etc/apache24/httpd.conf
    
    Then edit /usr/local/etc/apache24/httpd.conf:
    Change ServerAdmin to your email address.
    Change ServerName to your domain name.
    
    Then edit /usr/local/etc/rc.d/apache24:
    Change the NO to YES in the line apache24_http_accept_enable="NO"
    This allows the use of a kernel module that can help the performance of Apache.
    
    Then enable Apache in the rc.conf file:
    service apache24 enable
    
    Then enable the needed php-fpm service:
    service php_fpm enable
    
    Then start Apache and php-fpm:
    service apache24 onestart
    service php_fpm start
    
    You should be able to reach your server with eg. http://example.org

    If you want to install a robots.txt file that is set to allow good robots and disallow bad robots, then download and install the following:

    fetch https://the-slacker.com/download/robots.txt
    mv -f robots.txt /usr/local/www/apache24/data/
    
    Apache will not work like we need it to yet without SSL certs. In the next section we'll get free Let's Encrypt certs.

    Nginx:

    If you want to use the Nginx web server instead of Apache follow instructions below.

    First we need to install Nginx:

    pkg install nginx
    
    Then enable Nginx and php-fpm:
    service nginx enable
    service php_fpm enable
    
    Then start Nginx and php-fpm:
    service nginx start
    service php_fpm start
    
    You should be able to reach your server with eg. http://example.org

    If you want to install a robots.txt file that is set to allow good robots and disallow bad robots, then download and install the following:

    fetch https://the-slacker.com/download/robots.txt
    mv -f robots.txt /usr/local/www/nginx/
    
    Nginx will not work like we need it to yet without SSL certs. In the next section we'll get free Let's Encrypt certs.
  7. Let's Encrypt SSL Certs - With Acme.sh

    Apache and Nginx are not configured for SSL out of the box, so we need to change that.

    First we'll need to install "acme.sh", so we can aquire and update our SSL Certificates:

    pkg install acme.sh
    
    Then configure the email address you want associated with your Let's Encrypt certs:
    acme.sh --register-account -m admin@the-slacker.org
    
    Then configure what SSL server you want to use. For some strange reason acme.sh defaults to ZeroSSL, but like most people I'm using Let's Encrypt:
    acme.sh --set-default-ca --server letsencrypt
    

    Let's Encrypt setup with Apache web server

    Make sure Apache is running and you can reach your server:
    http://example.org # Adjust for your actual domain name.
    
    Then run acme.sh to get our certs:
    Adjust the below command to match your actual FQDN and domain name.
    acme.sh --issue -d mail.example.org -d www.example.org -d example.org --keylength ec-384 -w /usr/local/www/apache24/data
    
    If the Let's Encrypt certs were installed successfully, then edit /usr/local/etc/apache24/httpd.conf:
    Down at the bottom of the file:
    Uncomment #Include etc/apache24/extra/httpd-ssl.conf
    
    Download and install a preconfigured httpd-ssl.conf:
    fetch https://the-slacker.com/download/freebsd.httpd-ssl.conf
    mv -f freebsd.httpd-ssl.conf /usr/local/etc/apache24/extra/httpd-ssl.conf
    
    Edit /usr/local/etc/apache24/extra/httpd-ssl.conf
    Change ServerName to match your domain name.
    Change ServerAdmin to match your email.
    
    Uncomment and change to match your Let's Encrypt certs.
    #SSLCertificateFile "/root/.acme.sh/mail.example.org_ecc/fullchain.cer"
    #SSLCertificateKeyFile "/root/.acme.sh/mail.example.org_ecc/mail.example.org.key"
    
    If you want to redirect all http traffic to https, then edit /usr/local/etc/apache24/httpd.conf:
    Put the following just below Listen 80:
    
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
    
    Restart Apache:
    service apache24 restart
    
    Hopefully you can now reach your server at https://example.org.

    Let's Encrypt setup with Nginx web server

    First we need to change from the /usr/local/www/nginx-dist folder:
    rm /usr/local/www/nginx
    cp -R /usr/local/www/nginx-dist /usr/local/www/nginx
    chmod 0755 /usr/local/www/nginx
    rm /usr/local/www/nginx/EXAMPLE_DIRECTORY-DONT_ADD_OR_TOUCH_ANYTHING
    
    Then make sure Nginx is running and you can reach your server:
    http://example.org # Adjust for your actual domain name.
    
    Then run acme.sh to get our certs:
    Adjust the below command to match your actual FQDN and domain name.
    acme.sh --issue -d mail.example.org -d www.example.org -d example.org --keylength ec-384 -w /usr/local/www/nginx
    
    If the Let's Encrypt certs were installed successfully, then you need to download and install the preconfigured Nginx
    config files:
    fetch https://the-slacker.com/download/freebsd.nginx.conf
    mv -f freebsd.nginx.conf /usr/local/etc/nginx/nginx.conf
    
    mkdir -p /usr/local/etc/nginx/conf.d
    
    fetch https://the-slacker.com/download/freebsd.mailserver.conf
    mv -f freebsd.mailserver.conf /usr/local/etc/nginx/conf.d/mailserver.conf
    
    Edit /usr/local/etc/nginx/conf.d/mailserver.conf:
    Uncomment and change to match your domain:
    
    #SSLCertificateFile "/root/.acme.sh/mail.example.org_ecc/fullchain.cer"
    #SSLCertificateKeyFile "/root/.acme.sh/mail.example.org_ecc/mail.example.org.key"
    
    Restart Nginx:
    service nginx restart
    
    Hopefully you can now reach your server at https://example.org.

    Use Let's Encrypt certs with Webmin

    If you installed Webmin, then you can use the Let's Encrypt ssl certs to avoid having to add an exception to get to the login.

    Edit the /usr/local/etc/webmin/miniserv.conf file. Delete the "keyfile=" line, and add the following:
    Of course adjust for your domain.

    keyfile=/root/.acme.sh/mail.example.org_ecc/mail.example.org.key
    certfile=/root/.acme.sh/mail.example.org_ecc/fullchain.cer
    
    Then restart Webmin:
    service webmin restart
    
    That should do it for Let's Encrypt for now.
  8. MySQL (Oracle)

    Install MySQL (Oracle):

    Notice!
    Oracle MySQL isn't the same as MariaDB MySQL. FreeBSD defaults to Oracle MySQL, so prebuilt FreeBSD packages that
    are built with MySQL support are built with Oracle MySQL support. I'll be installing and using Oracle MySQL, so I can use
    the FreeBSD prebuilt packages.

    pkg install mysql80-server mysql80-client p5-DBD-mysql
    
    Enable MySQL to start at boot and start it:
    service mysql-server enable
    service mysql-server start
    
    Secure the MySQL Installation:
    mysql_secure_installation
    
    Test it to see if it's working properly:
    mysql -u root -p
    
    We'll need to add php mysql support:
    pkg install php84-pdo_mysql
    
    Add the 4 lines below in the [mysqld] section of the /usr/local/etc/mysql/my.cnf file:
    Turning performance_schema off can save memory, and isn't really needed.
    performance_schema = 0
    character-set-server = utf8mb4
    collation-server = utf8mb4_unicode_ci
    log-error = /var/log/mysql/mysql.log
    
    Edit the line below in the [mysqld] section of the /usr/local/etc/mysql/my.cnf file:
    It uses too much memory set at 1G, so set it to 256M.
    innodb_buffer_pool_size = 128M
    
    Create the mysql log directory:
    mkdir /var/log/mysql
    chown mysql:mysql /var/log/mysql
    
    Restart mysql-server:
    service mysql-server restart
    
    That should do it for MySQL for now.
  9. Dovecot

    First we'll need to create the MySQL vmail user and database that we'll need for Dovecot, Postfix, Postfixadmin, and
    Roundcube. Make it a strong password with only upper and lower case letters and numbers, no special characters.
    mysql -u root -p
    CREATE DATABASE vmail;
    CREATE USER 'vmail'@'localhost' IDENTIFIED BY 'vmailpassword';
    GRANT ALL PRIVILEGES ON vmail.* TO 'vmail'@'localhost';
    FLUSH PRIVILEGES;
    QUIT;
    
    Next we create the vmail group and user with the following commands:
    pw groupadd -n vmail -g 150
    pw useradd -n vmail -d /var/vmail -s /usr/sbin/nologin -u 150 -g 150
    mkdir /var/vmail
    chmod 770 /var/vmail
    chown vmail:vmail /var/vmail
    
    Then we'll need to install Dovecot with builtin MySQL support:
    pkg install dovecot-mysql
    
    Install the needed Dovecot config files:
    cp -R /usr/local/etc/dovecot/example-config/* /usr/local/etc/dovecot
    
    Then we create a new /usr/local/etc/dovecot/conf.d/10-ssl.conf file. Make sure to change the ssl certs to match your domain.
    Copy and paste the full echo command below into PuTTY and run it:
    echo -e "ssl_min_protocol = TLSv1.2
    ssl = required
    verbose_ssl = no
    ssl_cert = </usr/local/etc/letsencrypt/live/mail.example.org/fullchain.pem
    ssl_key = </usr/local/etc/letsencrypt/live/mail.example.org/privkey.pem
    ssl_dh = </etc/ssl/dh2048_param.pem
    # Fix 'The Logjam Attack'
    ssl_cipher_list = EECDH+CHACHA20:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH
    ssl_prefer_server_ciphers = yes" > /usr/local/etc/dovecot/conf.d/10-ssl.conf
    
    Enable and start Dovecot:
    service dovecot enable
    service dovecot start
    
    Then we create a new /usr/local/etc/dovecot/dovecot-sql.conf.ext file with:
    Copy and paste the full echo command below into PuTTY and run it.
    echo -e "driver = mysql
    connect = host=127.0.0.1 port=3306 dbname=vmail user=vmail password=vmailpassword
    default_pass_scheme = SHA512-CRYPT
    
    password_query = \\
      SELECT username as user, password, '/var/vmail/%d/%n' as \\
      userdb_home, 'maildir:/var/vmail/%d/%n' as userdb_mail, \\
      150 as userdb_uid, 150 as userdb_gid \\
      FROM mailbox WHERE username = '%u' AND active = '1'
    
    user_query = \\
      SELECT '/var/vmail/%d/%n' as home, 'maildir:/var/vmail/%d/%n' \\
      as vmail, 150 AS uid, 150 AS gid, \\
      concat('dirsize:storage=', quota) AS quota \\
      FROM mailbox WHERE username = '%u' AND active = '1'" > /usr/local/etc/dovecot/dovecot-sql.conf.ext
    
    Make sure to edit /usr/local/etc/dovecot/dovecot-sql.conf.ext with the vmail password you made earlier.
    Then set secure permissions on /usr/local/etc/dovecot/dovecot-sql.conf.ext with:
    chmod 0600 /usr/local/etc/dovecot/dovecot-sql.conf.ext
    
    Next create a new /usr/local/etc/dovecot/conf.d/10-auth.conf file with:
    echo -e 'disable_plaintext_auth = yes
    auth_mechanisms        = plain login
    !include auth-sql.conf.ext' >  /usr/local/etc/dovecot/conf.d/10-auth.conf
    
    Next append to the /usr/local/etc/dovecot/conf.d/10-mail.conf file with the following:
    echo -e 'mail_location   = maildir:/var/vmail/%d/%n
    mail_uid        = vmail
    mail_gid        = vmail
    first_valid_uid = 150
    last_valid_uid  = 150' >> /usr/local/etc/dovecot/conf.d/10-mail.conf
    
    Next we download a preconfigured Dovecot 10-master.conf file, and move it to /usr/local/etc/dovecot/conf.d/:
    fetch https://the-slacker.com/download/10-master.conf
    mv -f 10-master.conf /usr/local/etc/dovecot/conf.d/
    
    Next append the following to /usr/local/etc/dovecot/conf.d/10-logging.conf file with the following echo command,
    and direct the log files to the /var/log/dovecot directory:
    echo -e '## Log destination.
    ##
    log_path       = /var/log/dovecot/dovecot.log
    info_log_path  = /var/log/dovecot/dovecot-info.log' >> /usr/local/etc/dovecot/conf.d/10-logging.conf
    
    Dovecot Pigeonhole for Sieve and ManageSieve support.

    Install dovecot-pigeonhole with MySQL support:

    pkg install dovecot-pigeonhole-mysql
    
    Next we'll need to copy the following example configuration files into the /usr/local/etc/dovecot/conf.d directory:
    cp /usr/local/share/doc/dovecot/example-config/conf.d/90-sieve.conf /usr/local/etc/dovecot/conf.d/
    cp /usr/local/share/doc/dovecot/example-config/conf.d/90-sieve-extprograms.conf /usr/local/etc/dovecot/conf.d/
    cp /usr/local/share/doc/dovecot/example-config/conf.d/20-managesieve.conf /usr/local/etc/dovecot/conf.d/
    
    We will need to create the following Dovecot configuration files now:

    Create new /usr/local/etc/dovecot/conf.d/20-lmtp.conf file with the following echo command, and edit for your domain.

    echo -e 'protocol lmtp {
      postmaster_address = admin@example.org
      mail_plugins       = $mail_plugins sieve quota
      log_path           = /var/log/dovecot/dovecot-lmtp-errors.log
      info_log_path      = /var/log/dovecot/dovecot-lmtp.log
    }' > /usr/local/etc/dovecot/conf.d/20-lmtp.conf
    
    Create new /usr/local/etc/dovecot/conf.d/15-lda.conf file with the following echo command, and edit for your domain.
    echo -e 'protocol lda {
      postmaster_address = admin@example.org
      mail_plugins       = $mail_plugins sieve quota
      auth_socket_path   = /var/run/dovecot/auth-master
      log_path           = /var/log/dovecot/dovecot-lda-errors.log
      info_log_path      = /var/log/dovecot/dovecot-lda.log
    }' > /usr/local/etc/dovecot/conf.d/15-lda.conf
    
    Append the following to the /usr/local/etc/dovecot/conf.d/10-mail.conf file with this echo command.
    echo -e 'mail_home       = /var/vmail/%d/%n/sieve' >> /usr/local/etc/dovecot/conf.d/10-mail.conf
    
    Create new /usr/local/etc/dovecot/conf.d/20-managesieve.conf file with the following echo command:
    echo -e 'protocols = $protocols sieve
    
    service managesieve-login {
      inet_listener sieve {
        port = 4190
      }
    }
    
    service managesieve {
      process_limit = 1024
    }
    
    protocol sieve {
      log_path                          = /var/log/dovecot/dovecot-sieve-errors.log
      info_log_path                     = /var/log/dovecot/dovecot-sieve.log
      managesieve_max_line_length       = 65536
      managesieve_implementation_string = Dovecot Pigeonhole
    }' > /usr/local/etc/dovecot/conf.d/20-managesieve.conf
    
    Create new /usr/local/etc/dovecot/conf.d/90-sieve.conf file with the following echo command:
    echo -e 'plugin {
        sieve = file:/var/vmail/%d/%n/sieve;active=/var/vmail/%d/%n/sieve/.dovecot.sieve
        sieve_default = /usr/local/etc/dovecot/sieve/default.sieve
        sieve_global = /usr/local/etc/dovecot/sieve/global/
    }
    lda_mailbox_autocreate = yes
    lda_mailbox_autosubscribe = yes' > /usr/local/etc/dovecot/conf.d/90-sieve.conf
    
    Now we need to create some directories that are needed for our configuration to work:
    mkdir -p /usr/local/etc/dovecot/sieve/global
    chown -R vmail:vmail /usr/local/etc/dovecot/sieve/
    mkdir -p /var/log/dovecot
    chown vmail:vmail /var/log/dovecot
    
    Then create the file /usr/local/etc/dovecot/sieve/default.sieve with the following commands.
    It will send Spam to the Junk folder.
    echo -e 'require "fileinto";
    if header :contains "X-Spam-Flag" "YES" {
        fileinto "Junk";
    }' > /usr/local/etc/dovecot/sieve/default.sieve
    
    chown vmail:vmail /usr/local/etc/dovecot/sieve/default.sieve
    
    Add www to the vmail group:
    pw groupmod vmail -m www
    
    That's it for Dovecot for now. Don't restart until after Postfix is setup.
  10. Postfix

    Install Postfix with MySQL support:
    pkg install postfix-mysql
    
    Then enable postfix and disable sendmail:
    sysrc postfix_enable="YES"
    sysrc sendmail_enable="NONE"
    
    Then install the mailer.conf file for postfix:
    install -d /usr/local/etc/mail
    install -m 0644 /usr/local/share/postfix/mailer.conf.postfix /usr/local/etc/mail/mailer.conf
    
    We'll need to disable daily sendmail specific task with the below echo command:
    echo -e '
    # Disable daily sendmail specific task.
    daily_clean_hoststat_enable="NO"
    daily_status_mail_rejects_enable="NO"
    daily_status_include_submit_mailq="NO"
    daily_submit_queuerun="NO"' >> /usr/local/etc/periodic.conf
    
    We'll need to make a mysql directory for postfix:
    mkdir -p /usr/local/etc/postfix/mysql
    
    Now we'll create the 5 needed mysql map files with the needed content.

    First the /usr/local/etc/postfix/mysql/mysql_virtual_alias_domainaliases_maps.cf file:

    echo -e "user = vmail 
    password = vmailpassword
    hosts = 127.0.0.1:3306 
    dbname = vmail 
    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" > /usr/local/etc/postfix/mysql/mysql_virtual_alias_domainaliases_maps.cf
    
    Second the /usr/local/etc/postfix/mysql/mysql_virtual_alias_maps.cf file:
    echo -e "user = vmail
    password = vmailpassword
    hosts = 127.0.0.1:3306
    dbname = vmail
    table = alias
    select_field = goto 
    where_field = address
    additional_conditions = and active = '1'" > /usr/local/etc/postfix/mysql/mysql_virtual_alias_maps.cf
    
    Third the /usr/local/etc/postfix/mysql/mysql_virtual_domains_maps.cf file:
    echo -e "user = vmail
    password = vmailpassword
    hosts = 127.0.0.1:3306
    dbname = vmail
    table = domain
    select_field = domain 
    where_field = domain
    additional_conditions = and backupmx = '0' and active = '1'" > /usr/local/etc/postfix/mysql/mysql_virtual_domains_maps.cf
    
    Fourth the /usr/local/etc/postfix/mysql/mysql_virtual_mailbox_domainaliases_maps.cf file:
    echo -e "user = vmail
    password = vmailpassword
    hosts = 127.0.0.1:3306
    dbname = vmail
    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" > /usr/local/etc/postfix/mysql/mysql_virtual_mailbox_domainaliases_maps.cf
    
    Fifth the /usr/local/etc/postfix/mysql/mysql_virtual_mailbox_maps.cf file:
    echo -e "user = vmail
    password = vmailpassword
    hosts = 127.0.0.1:3306
    dbname = vmail
    table = mailbox
    select_field = CONCAT(domain, '/', local_part) 
    where_field = username
    additional_conditions = and active = '1'" > /usr/local/etc/postfix/mysql/mysql_virtual_mailbox_maps.cf
    
    Make sure to set your vmail database password created earlier in the 5 files just created.

    Then set secure permissions on all the /etc/postfix/mysql/* files with:
    chmod 0600 /usr/local/etc/postfix/mysql/*
    
    Next download and install the /usr/local/etc/postfix/main.cf file that I use for this how-to, and of course adjust
    for your domain and Let's Encrypt ssl certs:
    fetch https://the-slacker.com/download/main.cf.freebsd
    mv -f main.cf.freebsd /usr/local/etc/postfix/main.cf
    
    Then download and install the needed aliases file for this mail serever, then run newaliases at the prompt.
    Notice! - Before you run newaliases at the prompt, make sure to adjust the bottom line of the aliases file to
    match the email address that gets root's mail.
    fetch https://the-slacker.com/download/aliases
    mv -f aliases /usr/local/etc/postfix/
    
    Edit /usr/local/etc/postfix/aliases - At the bottom of the file change admin@example.org
    to the address you want to get root's mail, then run:
    
    newaliases
    
    Then download and install the /usr/local/etc/postfix/master.cf file that I use for this how-to. Shouldn't have to make
    any adjustments here:
    fetch https://the-slacker.com/download/master.cf.freebsd
    mv -f master.cf.freebsd /usr/local/etc/postfix/master.cf
    
    Add Postfix to the Dovecot group with:
    pw groupmod dovecot -m postfix
    
    Restart Postfix:
    service postfix start
    service dovecot restart
    
    That should do it for Postfix for now.
  11. PostfixAdmin:

    PostfixAdmin will be our mail admin panel. We will install it with:
    pkg install postfixadmin33-php84
    cd /usr/local/www
    ln -s postfixadmin33 postfixadmin
    cd postfixadmin
    mkdir -p templates_c
    chown www:www templates_c
    
    Make a copy of config.inc.php to config.local.php and make your changes there:
    cd /usr/local/www/postfixadmin
    cp config.inc.php config.local.php
    chown root:www config.local.php
    chmod 0640 config.local.php
    cd /root
    
    Then we'll create the setup_password for postfixadmin. Copy, paste, and run all 7 lines of code
    below at once to get postfixadmin setup and hashed setup passwords. The postfixadmin setup
    password will be in the /root/SlackerMail/postfixadmin_setup.pass file, and the hashed password
    that we use in config.local.php file will be in the /root/SlackerMail/postfixadmin_setup_hashed.pass
    file.
    mkdir -p /root/SlackerMail
    PASETUPPASS=$(cat /dev/urandom | LC_CTYPE=C tr -dc '[:alnum:]' | fold -w 24 | head -n 1)
    PAHASHPASS=$(doveadm pw -p $PASETUPPASS | cut -c 8-)
    echo $PASETUPPASS > /root/SlackerMail/postfixadmin_setup.pass
    echo $PAHASHPASS > /root/SlackerMail/postfixadmin_setup_hashed.pass
    chmod 0600 /root/SlackerMail/postfixadmin_setup.pass /root/SlackerMail/postfixadmin_setup_hashed.pass
    
    Then enter the hashed password from /root/SlackerMail/postfixadmin_setup_hashed.pass in the
    setup_password field in /usr/local/www/postfixadmin/config.local.php like below. I left all settings at
    default values except what I changed below, and of course adjust for your domain and passwords.
    Here are the settings I set in config.local.php:
    $CONF['configured'] = true;
    $CONF['setup_password'] = 'hashed-setup-password-here';
    
    $CONF['database_type'] = 'mysqli';
    $CONF['database_host'] = 'localhost';
    $CONF['database_user'] = 'vmail';
    $CONF['database_password'] = "vmailpassword";
    $CONF['database_name'] = 'vmail';
    
    $CONF['database_port'] = '3306';
    //$CONF['database_socket'] = ''; Comment out, since we are using port instead of socket.
    
    $CONF['admin_email'] = 'admin@example.org';
    
    $CONF['encrypt'] = 'dovecot:SHA512-CRYPT';
    $CONF['dovecotpw'] = "/usr/local/bin/doveadm pw"; # FreeBSD
    
    $CONF['default_aliases'] = array (
        'abuse' => 'admin@example.org',
        'hostmaster' => 'admin@example.org',
        'postmaster' => 'admin@example.org',
        'webmaster' => 'admin@example.org',
        'virusalert' => 'admin@example.org',
        'root' => 'admin@example.org'
    );
     
    $CONF['domain_path'] = 'NO';
    $CONF['domain_in_mailbox'] = 'YES';
    
    $CONF['footer_text'] = 'Return to example.org';
    $CONF['footer_link'] = 'https://example.org';
    
    $CONF['emailcheck_resolve_domain']='NO';
    
    $CONF['password_expiration'] = 'NO';
    
    Next create the needed postfixadmin tables for the vmail mysql database with:
    php /usr/local/www/postfixadmin/public/upgrade.php
    
    Then create the superadmin user for postfixadmin. The password and password2 must be the same
    password that you want for the superadmin login of postfixadmin. The reason for entering the same
    password twice is because of the postfixadmin setup script asking to confirm password.
    First make postfixadmin-cli executable with:
    chmod 0755 /usr/local/www/postfixadmin/scripts/postfixadmin-cli
    
    Then create the superadmin user for postfixadmin with:
    /usr/local/www/postfixadmin/scripts/postfixadmin-cli admin add admin@example.org --superadmin 1 --active 1 --password admin-password --password2 admin-password
    
    Then add your domain to postfixadmin. You can adjust --aliases, --mailboxes, and --description to whatever you want.
    /usr/local/www/postfixadmin/scripts/postfixadmin-cli domain add example.org --aliases 100 --mailboxes 1000 --active 1 --description example.org
    
    Lastly add the mailbox for admin@example.org, and again enter the same password twice. You can adjust --name and --quota to your liking.
    /usr/local/www/postfixadmin/scripts/postfixadmin-cli mailbox add admin@example.org --name admin --quota 0 --active 1 --password roundcube-mailbox-password --password2 roundcube-mailbox-password
    
    If Apache is your webserver:
    service apache24 restart
    service php_fpm restart
    
    If Nginx is your webserver:
    service nginx restart
    service php_fpm restart
    
    Now you should be able to login to postfixadmin at https://example.org/postfixadmin as admin@example.org
    with your admin-password. After Roundcubemail is setup you will be able to login to your admin@example.org
    mailbox with your roundcube-mailbox-password.
  12. Roundcube Webmail Client:

    Important! - Don't use any special characters in the roundcubemail database password, just upper and lower
    case letters and numbers.

    You can use the following commands to create a roundcube database password for you, or you can make up your
    own password in the mysql creation of the roundcubemail database. You can adjust the length of the password by
    changing "fold -w 24" to any number you want. The resulting password will be at:
    /root/SlackerMail/roundcube_password.pass

    PASSWD=$(cat /dev/urandom | LC_CTYPE=C tr -dc '[:alnum:]' | fold -w 24 | head -n 1)
    echo $PASSWD > /root/SlackerMail/roundcube_password.pass
    chmod 0600 /root/SlackerMail/roundcube_password.pass
    
    We'll need to create the mysql database for roundcubemail with:
    mysql -u root -p
    CREATE DATABASE roundcubemail;
    CREATE USER 'roundcube'@'localhost' IDENTIFIED BY 'roundcube_password';
    GRANT ALL PRIVILEGES ON roundcubemail.* TO 'roundcube'@'localhost';
    FLUSH PRIVILEGES;
    QUIT;
    
    Then we'll install Roundcube with:
    Note! Make sure to put your roundcube_password in the bottom mysql command directly after the -p without any quotes.
    pkg install roundcube-php84 php84-gd php84-curl
    
    Then run:
    chown -R www /usr/local/www/roundcube
    cd /usr/local/www/roundcube
    
    Then run:
    mysql -u roundcube roundcubemail -proundcube_password < SQL/mysql.initial.sql
    cd /root
    
    Next I had to create a postfix file that roundcube expects:
    echo '#submission header checks file' >> /usr/local/etc/postfix/submission_header_checks
    
    You can setup Roundcube with the installer wizard, but it's easier for me to do it manually.
    First we need to generate the 24 character Des-Key that Roundcube needs with:
    DESKEY=$(cat /dev/urandom | LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1)
    echo $DESKEY > /root/SlackerMail/roundcubemail.deskey
    
    The 24-character-Des-Key will be at the /root/SlackerMail/roundcubemail.deskey.

    Then download the needed /usr/local/www/roundcube/config/config.inc.php file, and adjust
    for your domain, roudcube password, and 24-character-DES-Key with the following:

    fetch https://the-slacker.com/download/config.inc.php.rcm.freebsd
    mv -f config.inc.php.rcm.freebsd /usr/local/www/roundcube/config/config.inc.php
    chown www:www /usr/local/www/roundcube/config/config.inc.php
    chmod 0600 /usr/local/www/roundcube/config/config.inc.php
    
    You'll need to edit the following fields in /usr/local/www/roundcube/config/config.inc.php:
    $config['db_dsnw'] = 'mysql://roundcube:roundcube-password-here@localhost/roundcubemail';
    $config['support_url'] = 'https://example.org';
    $config['des_key'] = '24-character-Des-Key';
    
    Now you need to download and setup the roundcubemail password plugin configuration file, so users can change passwords:
    fetch https://the-slacker.com/download/config.inc.php.rcmp.freebsd
    mv -f config.inc.php.rcmp.freebsd /usr/local/www/roundcube/plugins/password/config.inc.php
    chmod 0600 /usr/local/www/roundcube/plugins/password/config.inc.php
    chown www /usr/local/www/roundcube/plugins/password/config.inc.php
    
    Then edit /usr/local/www/roundcube/plugins/password/config.inc.php and put the vmail mysql database password created earlier:
    $config['password_db_dsn'] = 'mysql://vmail:vmail-mysql-database-password-here@localhost/vmail';
    
    Your password plugin should be working now.
    Note! - There are many options in the config file you may want to change to your liking, but this setup should have it working.

    Lastly you'll need to remove the /usr/local/www/roundcube/installer directory for security reasons:

    rm -rf /usr/local/www/roundcube/installer
    
    If Apache is your webserver:
    service apache24 restart
    service php_fpm restart
    
    If Nginx is your webserver:
    service nginx restart
    service php_fpm restart
    
    You should be able to login to Roundcube at https://example.org/mail.
    Username: admin@example.org Password: admin@example.org mailbox password you created earlier while installing Postfixadmin.
  13. Amavisd with ClamAV and SpamAssassin:

    Install amavisd-new and enable:
    pkg install amavisd-new
    service amavisd enable
    
    Download and install a preconfigured /usr/local/etc/amavisd.conf file:
    fetch https://the-slacker.com/download/amavisd.freebsd.conf
    mv -f amavisd.freebsd.conf /usr/local/etc/amavisd.conf
    chown root:vscan /usr/local/etc/amavisd.conf
    chmod 0640 /usr/local/etc/amavisd.conf
    
    Edit /usr/local/etc/amavisd.conf, and change to match your domain and vmail database password.

    Spamassassin is installed with Amavisd-New, so we need to update it's database:

    Run:
    sa-update
    
    Then run:
    sa-compile
    
    Install ClamAV:
    pkg install clamav
    
    After ClamAV is installed run the following sed command to set LocalSocketGroup vscan:
    sed -i '' 's/#LocalSocketGroup virusgroup/LocalSocketGroup vscan/g' /usr/local/etc/clamd.conf
    
    Then enable clamav and freshclam:
    service clamav_freshclam enable
    service clamav_clamd enable
    
    Next add vscan to the clamav group and clamav to the vscan group:
    pw groupmod clamav -m vscan
    pw groupmod vscan -m clamav
    
    Then start clamav_freshclam and clamav_clamd:
    service clamav_freshclam start
    
    Wait about 1 minute, then run:
    
    service clamav_clamd start
    
    We'll be using the DKIM perl module to verify and sign emails.
    We'll first need to make our private and public keys (adjust for your domain) with:
    openssl genrsa -out example.org.priv 2048
    openssl rsa -in example.org.priv -pubout > example.org.pub
    
    Then we'll install the DKIM keys:
    mkdir -p /usr/local/etc/ssl/example.org
    mv -f example.org.priv /usr/local/etc/ssl/example.org/example.org.pem
    mv -f example.org.pub /usr/local/etc/ssl/example.org/
    chown vscan:vscan /usr/local/etc/ssl/example.org/example.org.pem /usr/local/etc/ssl/example.org/example.org.pub
    chmod 600 /usr/local/etc/ssl/example.org/example.org.pem 
    chmod 644 /usr/local/etc/ssl/example.org/example.org.pub
    
    Edit /usr/local/etc/amavisd.conf:
    Uncomment the dkim_key line, and change for your domain.
    #dkim_key('example.org', 'dkim', '/usr/local/etc/ssl/example.org/example.org.pem');
    
    Run the following line of piped commands to format your example.org.pub file for entering into a DNS Zone Record
    for DKIM, and of course adjust for your domain:
    sed '1d;$d' "/usr/local/etc/ssl/example.org/example.org.pub" | sed '1s/.*/v=DKIM1;p=&/' | tr -d '\n' > /root/SlackerMail/example.org.pub.txt
    
    The DKIM public key will be at /root/SlackerMail/example.org.pub.txt. Create a DKIM record in your DNS Zone, eg. "dkim._domainkey.example.org".
    Then in the data area paste the entire line from the /root/SlackerMail/example.org.pub.txt file.

    Then reboot to see if everything is working properly.

    reboot
    
    It's coming around. At this point you can test the mail server out, and see how it's doing:

    mail-tester.com - Great place to check if your mail server is sending mail properly. With this server I get a 10/10 score.

    SSL Labs - Great place to check how your SSL implementation is working. I get a score of A+ with this server. You can
    do this test with Let's Encrypt certs, but not self signed certs.

  14. Fail2ban - Optional:

    Fail2ban will limit malicious attacks to various services on your server by limiting the number of attempts from an ip
    within a set amount of time, and then ban that ip for a set amount of time. Fail2ban is a PITA to setup on FreeBSD.

    First we need to configure the PF Firewall to work with Fail2ban, so download and install the pf.conf file needed for fail2ban to work:

    fetch https://the-slacker.com/download/pf-fail2ban.conf
    mv -f pf-fail2ban.conf /etc/pf.conf
    
    Then reboot:
    reboot
    
    Now install Fail2ban:
    pkg install py311-fail2ban
    
    Next we setup fail2ban. First we need to create the local fail2ban config files where we put our settings in.

    Create the /usr/local/etc/fail2ban/fail2ban.local file with needed content with the following echo command:
    echo "[Definition]
    # Option: loglevel. Default is ERROR
    # Available options: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG
    loglevel = INFO
    # Set the log target
    logtarget = /var/log/fail2ban.log" > /usr/local/etc/fail2ban/fail2ban.local
    
    Create the /usr/local/etc/fail2ban/jail.local file with needed content with the following echo command:
    Adjust to your liking.
    echo "[DEFAULT]
    # time is in seconds. 3600 = 1 hour, 86400 = 24 hours (1 day)
    findtime    = 3600
    bantime     = 86400
    maxretry    = 3
    action      = %(action_)s
    banaction   = pf
    ignoreip    = 127.0.0.1 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16" > /usr/local/etc/fail2ban/jail.local
    
    You'll probably want to put your ip address at the end of the ignoreip line so fail2ban will ignore it, and of course you
    can make changes to the "findtime", "bantime", and "maxretry" settings as you want.

    Next we create our first jail for sshd.
    Create the /usr/local/etc/fail2ban/jail.d/sshd.local file with the following echo command:

    echo "[sshd]
    enabled = true
    filter  = bsd-sshd-session
    logpath = /var/log/auth.log" > /usr/local/etc/fail2ban/jail.d/sshd.local
    
    Enable and start Fail2ban to see if it will ban ip's.
    service fail2ban enable
    service fail2ban start
    
    Check /var/log/fail2ban.log to see if it is banning ip's. This PITA seems to be working like it should now. Had a hell of a time
    with it.

    Next we'll create a few more fail2ban jails.

    Create the postfix-pregreet jail:
    Create the /usr/local/etc/fail2ban/jail.d/postfix-pregreet.local file with the following echo command:

    echo "[postfix-pregreet]
    enabled  = true
    maxretry = 1
    filter   = postfix-pregreet
    logpath  = /var/log/maillog" > /usr/local/etc/fail2ban/jail.d/postfix-pregreet.local
    
    Create the /usr/local/etc/fail2ban/filter.d/postfix-pregreet.conf file with the following echo command:
    echo "# Got this from iRedMail
    [Definition]
    # Block clients which cannot pass Postfix postscreen pregreet test.
    # FYI: http://www.postfix.org/POSTSCREEN_README.html#pregreet
    #
    # The SMTP protocol is a classic example of a protocol where the server speaks
    # before the client. postscreen(8) detects zombies that are in a hurry and that
    # speak before their turn.
    failregex = postscreen\[\d+\]: PREGREET .* from \[<HOST>\]:\d+:
    
    # while setting up new account, Thunderbird doesn't wait for server connection
    # greeting/banner, this causes Thunderbird cannot pass the Postfix pregreet
    # test and caught by \`failregex\` rules listed above (the rule contains
    # 'PREGREET' line).
    # FYI: https://bugzilla.mozilla.org/show_bug.cgi?id=538809#c41
    ignoreregex = postscreen\[\d+\]: PREGREET .* from \[<HOST>\]:\d+: (EHLO|HELO) we-guess.mozilla.org" > /usr/local/etc/fail2ban/filter.d/postfix-pregreet.conf
    
    Create the postfix jail:
    Create the /usr/local/etc/fail2ban/jail.d/postfix.local file with the following echo command:
    echo "[postfix]
    enabled = true
    filter  = postfix-2
    logpath = /var/log/maillog" > /usr/local/etc/fail2ban/jail.d/postfix.local
    
    Create the /usr/local/etc/fail2ban/filter.d/postfix-2.local file with the following echo command:
    echo "# Got this from iRedMail
    [Definition]
    # *) '554 5.7.1' is 'Helo command rejected: ACCESS DENIED'
    #
    #   'ACCESS DENIED' is string defined in postfix restriction rule \`check_helo_access\`.
    #   no all rules contains 'ACCESS DENIED', so we use status code insead.
    
    failregex = \[<HOST>\]: SASL (PLAIN|LOGIN) authentication failed
                lost connection after AUTH from (.*)\[<HOST>\]
                reject: RCPT from .*\[<HOST>\]: .*: Relay access denied
                reject: RCPT from .*\[<HOST>\]: .*: Sender address rejected: Domain not found
                reject: RCPT from .*\[<HOST>\]: .*: Helo command rejected: Host not found
                reject: RCPT from .*\[<HOST>\]: .*: Helo command rejected: need fully-qualified hostname
                reject: RCPT from .*\[<HOST>\]: 554 5.7.1
                reject: RCPT from .*\[<HOST>\]:\d+: 550 5.5.1 Protocol error
                warning: Illegal address syntax from (.*)\[<HOST>\] in RCPT command
                postfix\/submission\/smtpd.*: too many errors after AUTH from .*\[<HOST>\]
    
    ignoreregex =" > /usr/local/etc/fail2ban/filter.d/postfix-2.conf
    
    Create the Roundcube jail:
    Create the /usr/local/etc/fail2ban/jail.d/roundcube.local file with the following echo command:
    echo "[roundcube]
    enabled  = true
    filter   = roundcube-auth
    logpath  = /var/log/maillog" > /usr/local/etc/fail2ban/jail.d/roundcube.local
    
    Restart Fail2ban to see if the jails are working.
    service fail2ban restart
    
    Check out /usr/local/etc/fail2ban/filter.d/ to see all of the other possible jails; dovecot, webmin, proftpd, etc.
  15. Fail2ban Alternative - Optional:

    I tried FreeBSD's blacklistd, but really couldn't get it to do what I wanted, plus it hasn't been maintained for 8 years. I
    really don't like Fail2ban, so I'm going to see if I can do what I need to do without it. I may have to go back to fail2ban,
    but maybe not.

    1. SSHD - Optional:

    Note! - The easiest and best way to prevent about 80%+ of sshd attacks is to change the port from 22 to some other port,
    6000 for instance. I knew a guy that was a server provider for 20+ years, and that's the first thing he would do, and he proved
    to me how well it worked.

    Fail2ban will prevent malicious sshd attacks by limiting the number of sshd login attempts from an ip within a set amount
    of time, and then ban them for a set amount of time. What I'm going to do with sshd is only allow a specific ip, or specific
    ip's, to be able to ssh the server.

    You'll need to edit /etc/host.allow:

    At the top of the file comment:
    #ALL : ALL : allow
    Then somewhere below that add:
    
    # sshd
    sshd : 192.201.2.241 : allow
    sshd : 127.0.0.1 : allow
    sshd : ALL : deny
    
    If you have multiple ip's you want to be able to access sshd, then enter a line for each one.

    That's it for sshd. No need at all for Fail2ban with sshd. Nice and simple!

    2. Postfix and Dovecot:

    I've got the settings in the Postfix and Dovecot config files so they will prevent many attacks. I'm sure there are attacks I don't
    have covered, but I'm going to try it like this without using Fail2ban to see how it goes.

    3. Roundcube - Optional:

    To prevent anyone but my ip address to access Roundcube I added some php code to /usr/local/www/roundcube/index.php:

    Add the below code to the top of /usr/local/www/roundcube/index.php, just below <?php
    
    $allowed_ip = '11.222.33.444'; // Replace with your allowed IP address
    
    // Get the user's IP address
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
        $user_ip = $_SERVER['HTTP_CLIENT_IP'];
    } else {
        $user_ip = $_SERVER['REMOTE_ADDR'];
    }
    
    // Check if the user's IP matches the allowed IP
    if ($user_ip !== $allowed_ip) {
        die("Access denied."); // Stop script execution if IP does not match
    }
    
    Note! - The above php code for allowing only your ip to access roundcube is probably not going to be something most will want
    to do, because you'll probably have multiple mail users needing to long in with it.

    4. Postfixadmin - Optional:

    To prevent anyone but my ip address to access Postfixadmin I added some php code to /usr/local/www/postfixadmin/public/index.php:

    Add the below code to the top of /usr/local/www/postfixadmin/public/index.php, just below <?php
    
    $allowed_ip = '11.222.33.444'; // Replace with your allowed IP address
    
    // Get the user's IP address
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
        $user_ip = $_SERVER['HTTP_CLIENT_IP'];
    } else {
        $user_ip = $_SERVER['REMOTE_ADDR'];
    }
    
    // Check if the user's IP matches the allowed IP
    if ($user_ip !== $allowed_ip) {
        die("Access denied."); // Stop script execution if IP does not match
    }
    
    This is all I'm going to do with the "Fail2ban Alternative" for now.
  16. Logwatch - Optional:

    Logwatch parses through your system's logs and creates a report analyzing areas that you specify. It will send you an emailed
    report daily.

    Install logwatch:

    pkg install logwatch
    
    Copy, paste, and run the following echo cammand to create the daily running of logwatch:
    echo '#!/bin/sh
    
    /usr/local/sbin/logwatch.pl --output mail' > /usr/local/etc/periodic/daily/logwatch
    
    Then make /usr/local/etc/periodic/daily/logwatch executable:
    chmod 0755 /usr/local/etc/periodic/daily/logwatch
    
    You can test to see if Logwatch is working by running the following:
    periodic daily
    
    You should get an email from Logwatch with your daily report.

    That should do it for Logwatch.

  17. Postgrey - Optional:

    Postgrey is a tool used to help prevent spam. It will greylist incoming email from new senders, which means it will deny the
    email initially for about 5 minutes, so the sender will have to reattempt before it is accepted. Most spammers don't reattempt.

    Install Postgrey:

    pkg install postgrey
    
    Copy, paste, and run the below Sed command to activate Postgrey in Postfix:
    sed -i '' 's/smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, permit/smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service inet:127.0.0.1:10023, permit/g' /usr/local/etc/postfix/main.cf
    
    Download and install the latest Postgrey whitelist file:
    fetch https://postgrey.schweikert.ch/pub/postgrey_whitelist_clients
    mv -f postgrey_whitelist_clients /usr/local/etc/postfix/
    
    Edit /usr/local/lib/perl5/site_perl/Net/Server/Multiplex.pm line 26: To keep from getting error message:
    Change from this:
    eval { require IO::Multiplex; import IO::Multiplex 1.05; };
    
    To this:
    eval { require IO::Multiplex; import IO::Multiplex; };
    
    Enable and start Postgrey:
    service postgrey enable
    service postgrey start
    
    Restart Postfix:
    service postfix restart
    
    That should do it for Postgrey.
  18. Netdata - Optional:

    Netdata is a monitoring and troubleshooting tool with a very nice web based graphical interface. It has tons of info about your
    server that you can use to troubleshoot problems. FreeBSD installs an older version of Netdata, so you'll see a warning to that
    effect from Netdata.

    Note! Netdata in FreeBSD uses a lot more memory than it does in Linux for some reason. I haven't been able to figure out why
    yet.

    FreeBSD will install an outdated version of Netdata if you install it with the pkg utility, so we'll have to update our ports, and
    build Netdata.

    Note! Portsnap will be EOL 4-2026, so I'll be using the recommended Git method of updating ports.

    Make sure git is installed and update ports:

    run 1 line at a time.
    
    pkg install git
    rm -rfq /usr/ports/* /usr/ports/.*
    git clone https://git.freebsd.org/ports.git /usr/ports
    
    When you want to update your /usr/ports tree run:
    cd /usr/ports
    git pull --rebase
    
    Make sure below dependencies are installed:
    pkg install bash libuuid git curl autoconf automake pkgconf pidof liblz4 libuv json-c cmake gmake
    

    Build and install the Netdata port:
    Note! You'll be presented with dialogue boxes during the build. I just hit enter on all, and accepted the defaults.

    cd /usr/ports/net-mgmt/netdata
    make install clean
    
    Download and install the Netdata config file:
    fetch https://the-slacker.com/download/freebsd-netdata.conf
    mv -f freebsd-netdata.conf /usr/local/etc/netdata/netdata.conf
    chown netdata:netdata /usr/local/etc/netdata/netdata.conf
    
    Create Netdata database:

    Create a netdata mysql user, and grant netdata PROCESS privileges. Put your chosen password in the "mysql-netdata-password" field.

    mysql -u root -p
    CREATE USER 'netdata'@'localhost' IDENTIFIED BY 'mysql-netdata-password';
    GRANT PROCESS ON *.* TO 'netdata'@'localhost';
    FLUSH PRIVILEGES;
    QUIT;
    
    Setup Netdata user password:

    I have Netdata set so authentication is required to sign into netdata, so you'll need to create a netdata.users file with username
    and password. I have the user name set to netdata as you can see in the printf command below, but feel free to change that if
    you want. You'll need to put the password you want in the password field in the printf command. The password will be hashed
    in the /usr/local/etc/apache24/netdata.users or /usr/local/etc/nginx/netdata.users file.

    For Apache webserver run the following:

    printf "netdata:$(openssl passwd -apr1 password)" > /usr/local/etc/apache24/netdata.users
    chown www:www /usr/local/etc/apache24/netdata.users
    chmod 0400 /usr/local/etc/apache24/netdata.users
    
    For Nginx webserver run the following:
    printf "netdata:$(openssl passwd -apr1 password)" > /usr/local/etc/nginx/netdata.users
    chown www:www /usr/local/etc/nginx/netdata.users
    chmod 0400 /usr/local/etc/nginx/netdata.users
    
    Next enable and start Netdata:
    service netdata enable
    service netdata start
    
    I am now able to get to netdata at https://example.org/netdata.

    nginx:

    Got nginx to show up in netdata by doing the following:

    touch /usr/local/etc/netdata/go.d/nginx.conf
    chown netdata:netdata /usr/local/etc/netdata/go.d/nginx.conf
    chmod 0640 /usr/local/etc/netdata/go.d/nginx.conf
    
    Add the following to /usr/local/etc/netdata/go.d/nginx.conf
    jobs:
      - name: local
        url: https://127.0.0.1/stub_status
        tls_skip_verify: yes
    
    apache:

    Got apache to show up in netdata by doing the following:

    touch /usr/local/etc/netdata/go.d/apache.conf
    chown netdata:netdata /usr/local/etc/netdata/go.d/apache.conf
    chmod 0640 /usr/local/etc/netdata/go.d/apache.conf
    
    Add the following to /usr/local/etc/netdata/go.d/apache.conf
    jobs:
      - name: local
        url: https://127.0.0.1/server-status?auto
        tls_skip_verify: yes
    
    mysql:

    Got mysql to show up in netdata by doing the following:

    touch /usr/local/etc/netdata/go.d/mysql.conf
    chown netdata:netdata /usr/local/etc/netdata/go.d/mysql.conf
    chmod 0400 /usr/local/etc/netdata/go.d/mysql.conf
    
    Add the following to /usr/local/etc/netdata/go.d/mysql.conf with the mysql netdata password you created earlier:
    jobs:
      - name: local
        dsn: netdata:mysql-netdata-password@tcp(127.0.0.1:3306)/
    
    php-fpm:

    Got php-fpm to show up in netdata by doing the following:

    touch /usr/local/etc/netdata/go.d/phpfpm.conf
    chown netdata:netdata /usr/local/etc/netdata/go.d/phpfpm.conf
    chmod 0640 /usr/local/etc/netdata/go.d/phpfpm.conf
    
    Add the following to /usr/local/etc/netdata/go.d/phpfpm.conf:
    jobs:
      - name: local
        url: https://127.0.0.1/status
        tls_skip_verify: yes
    
    Restart Netdata to see if it's working properly:
    service netdata restart
    
    Note! I can't get Fail2ban to show up in Netdata for some reason. Tried everything I could think of, but no luck yet.
  19. Finish Setup:

    1. PHP
    Important! For security reasons you need the following functions disabled in the /usr/local/etc/php.ini file.
    Just copy, paste, and run the sed command below at the command prompt, and restart php-fpm with "service php_fpm restart".
    sed -i '' 's/disable_functions =/disable_functions = system,posix_uname,eval,pcntl_wexitstatus,posix_getpwuid,xmlrpc_entity_decode,pcntl_wifstopped,pcntl_wifexited,pcntl_wifsignaled,phpAds_XmlRpc,pcntl_strerror,ftp_exec,pcntl_wtermsig,mysql_pconnect,proc_nice,pcntl_sigtimedwait,posix_kill,pcntl_sigprocmask,fput,phpinfo,phpAds_remoteInfo,ftp_login,inject_code,posix_mkfifo,highlight_file,escapeshellcmd,show_source,pcntl_wifcontinued,fp,pcntl_alarm,pcntl_wait,ini_alter,posix_setpgid,parse_ini_file,ftp_raw,pcntl_waitpid,pcntl_getpriority,ftp_connect,pcntl_signal_dispatch,pcntl_wstopsig,ini_restore,ftp_put,passthru,proc_terminate,posix_setsid,pcntl_signal,pcntl_setpriority,phpAds_xmlrpcEncode,pcntl_exec,ftp_nb_fput,ftp_get,phpAds_xmlrpcDecode,pcntl_sigwaitinfo,shell_exec,pcntl_get_last_error,ftp_rawlist,pcntl_fork,posix_setuid/g' /usr/local/etc/php.ini
    
    2. MySQL Databases Backup

    We'll need to do a daily backup of our MySQL databases. First we need to create a /root/.my.cnf file with root's login credentials,
    so we can run automated backups:

    echo '[client]
    user="root"
    password="your.root.mysql.password"' > /root/.my.cnf
    
    Then change permissions for /root/.my.cnf:
    chmod 0600 /root/.my.cnf
    
    Next we need to download the MySQL database backup bash script.

    This script will make a daily backup of the vmail, mysql, and roundcubemail mysql databases, remove backups older than 30 days,
    and send a daily email report to root. I got this from iRedMail, but made some changes. Run the following:

    mkdir -p /var/vmail/backup
    fetch https://the-slacker.com/download/mysql_backup.sh
    mv -f mysql_backup.sh /var/vmail/backup/
    chmod 0500 /var/vmail/backup/mysql_backup.sh
    
    Then we need to create a periodic daily job to run mysql-backup. Run the following echo command:
    echo -e '# Daily backup of the mysql databases.
    #!/bin/sh
    /var/vmail/backup/mysql_backup.sh > /dev/null 2>&1' > /usr/local/etc/periodic/daily/mysql-backup
    
    Then change permissions for mysql-backup:
    chmod 0755 /usr/local/etc/periodic/daily/mysql-backup
    
    You can test to see if the mysql database daily backup is working by running:
    periodic daily
    
    If the mysql database daily backup is working you should get an email with the results of the backup.
  20. SlackerMail-FreeBSD Install Script:

    Note! You will need to finish steps 1-4 above before you can install SlackerMail-FreeBSD with this script.

    The install script isn't ready yet, but should be ready in 2-4 weeks.







Powered by: Slackware64-15.0 Slackware Lunux Apache v2.4.66 Apache SlackerMail v0.57.1 SlackerMail

Please send any feedback to: wjack@the-slacker.com