<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><title>IT Notes - e-mail</title><link>https://it-notes.dragas.net/categories/e-mail/</link><description>Articles in category e-mail</description><language>en</language><lastBuildDate>Thu, 21 Mar 2024 07:15:00 +0000</lastBuildDate><atom:link href="https://it-notes.dragas.net/categories/e-mail/feed.xml" rel="self" type="application/rss+xml"></atom:link><item><title>Make your own E-Mail server - Part 2 - Adding Webmail and More with Nextcloud</title><link>https://it-notes.dragas.net/2024/03/21/make-your-own-email-server-freebsd-adding-nextcloud-part2/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/mail_iphone.webp" alt="Make your own E-Mail server - Part 2 - Adding Webmail and More with Nextcloud"&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="https://it-notes.dragas.net/2024/03/08/make-your-own-email-server-freebsd-opensmptd-rspamd-dovecot-part1/"&gt;In the first part of this series, I detailed how to install and configure a complete mail server within a FreeBSD jail&lt;/a&gt;. While accessible via IMAP and SMTP, I intentionally did not cover installing a webmail interface.&lt;/p&gt;
&lt;p&gt;Installing a "simple" webmail is not complex, but nowadays, we expect more integrated solutions, including contact books (synchronizable with computers and mobile devices) and calendars. For this reason, in this second part, I will be utilizing &lt;a href="https://nextcloud.com/"&gt;Nextcloud&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nextcloud represents a comprehensive groupware solution today, extendable through "apps" that can be installed directly from the administration menu. This setup offers numerous advantages, including authenticating Nextcloud users through the IMAP server (thus, a mail server user will automatically have a Nextcloud account, and password changes on the mail will reflect on Nextcloud too), the ability to use integrated Contacts (also in webmail) and Calendars, and the option to attach or save attachments directly in Nextcloud.&lt;/p&gt;
&lt;p&gt;We will use the reverse proxy configured in part 1, which will "pass" connections to Nextcloud. First, a domain name must be defined, which will be used to connect to the server. In this example, I will use "&lt;em&gt;nextcloud.example.com&lt;/em&gt;."&lt;/p&gt;
&lt;p&gt;To do this, enter the proxy's jail:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bastille console nginx-proxy&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;Modify the file &lt;code&gt;/usr/local/etc/nginx/nginx.conf&lt;/code&gt;, adjust the &lt;em&gt;worker_processes&lt;/em&gt; (so nginx will start a number of processes equal to the number of cores on the machine it's running on), and create the virtual host for Nextcloud:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;worker_processes auto;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the &lt;code&gt;http&lt;/code&gt; block, change the maximum file size limit and create the virtual host on http - we will generate the https block and related certificates later:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;client_max_body_size 0;

server {
    listen 80;
    server_name nextcloud.example.com;

    location / {
        proxy_buffering off;
        proxy_http_version 1.1;
        fastcgi_hide_header X-Powered-By;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection &amp;quot;upgrade&amp;quot;;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_pass https://192.168.123.5;
        proxy_connect_timeout   10m;
        proxy_send_timeout      10m;
        proxy_read_timeout      10m;
    }

    location /.well-known/carddav {
        return 301 $scheme://$host/remote.php/dav;
    }

    location /.well-known/caldav {
        return 301 $scheme://$host/remote.php/dav;
    }
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;proxy_pass&lt;/code&gt; is in https because, later on, Nextcloud will respond in https. Although not necessary, this approach is simpler given the method used to install Nextcloud. Moreover, it allows for the reverse proxy to be on a different server than Nextcloud, without the need for a VPN.&lt;/p&gt;
&lt;p&gt;Now, enable and start nginx:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;service nginx enable
service nginx start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After setting the listener to port 80, it's now appropriate to generate the certificate.&lt;/p&gt;
&lt;p&gt;A simple command will request the certificate and modify the nginx configuration to accept connections over https:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;certbot --nginx -d nextcloud.example.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Upon completion of this command, the virtual host for https will be correctly generated, and an automatic redirect will be set up for all connections arriving on http to be "moved" to https.&lt;/p&gt;
&lt;p&gt;Exit the jail, and it's time to create the jail for Nextcloud.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bastille create -B nc 14.2-RELEASE 192.168.123.5/24 bridge0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Enter the jail and add the gateway in &lt;code&gt;rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;defaultrouter=&amp;quot;192.168.123.1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After restarting the jail, it will now be possible to install Nextcloud.&lt;/p&gt;
&lt;p&gt;FreeBSD has a fairly updated package, but when it comes to web applications, I prefer to have more control. Usually, I install everything manually, but for Nextcloud, I found an interesting script made by &lt;a href="https://github.com/theGeeBee"&gt;Gibran Khan&lt;/a&gt; that takes care of everything, i.e., installing Apache, PHP, ClamAV, all dependencies. It will also configure Nextcloud and make it (almost) ready to use.&lt;/p&gt;
&lt;p&gt;Since Gibran's script was structured for version 27, used PHP 8.2, and had other small things that were not perfect for my use, I decided to modify it slightly, mainly to support Nextcloud 28 and PHP 8.3. Therefore, the description that follows will be related to my fork available here: &lt;a href="https://github.com/draga79/NextCloudOnFreeBSD"&gt;https://github.com/draga79/NextCloudOnFreeBSD&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So, enter the "nc" jail, download the script, and start with the configuration:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;bastille console nc
fetch https://github.com/draga79/NextCloudOnFreeBSD/archive/refs/heads/release.zip
unzip release.zip
cd NextCloudOnFreeBSD-release
./pre-install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A configuration file will be generated that we need to modify. The file is &lt;code&gt;install.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You will certainly need to change the &lt;code&gt;HOST_NAME&lt;/code&gt; (inserting the one you have chosen, in this case, "nextcloud.example.com") and, unless you are in Italy, I would also suggest changing the &lt;code&gt;COUNTRY_CODE&lt;/code&gt; and &lt;code&gt;TIME_ZONE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once completed, you can proceed with the actual installation:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./install.sh&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;At the end of the installation, the login data will appear (which will also be saved in a file named &lt;code&gt;nextcloud.example.com_reference.txt&lt;/code&gt;). The installer will also set up a cron job executed by the user "www", which will handle, every 5 minutes, the background operations required for Nextcloud to function properly. In some cases, I've noticed that it's necessary to modify the cronjob, save it, and then it will start working correctly. If that's the case, simply open it with
&lt;code&gt;crontab -u www -e&lt;/code&gt;, save, and exit.&lt;/p&gt;
&lt;p&gt;Now, modify the Nextcloud configuration file, &lt;code&gt;/usr/local/www/nextcloud/config/config.php&lt;/code&gt;, and:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Replace &lt;code&gt;overwrite.cli.url&lt;/code&gt; with your domain (&lt;a href="https://nextcloud.example.com"&gt;https://nextcloud.example.com&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the proxy to the trusted proxies (if not present, insert the entire block):&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code&gt; 'trusted_proxies' =&amp;gt;
  array (
    0 =&amp;gt; '192.168.123.2',
  ),
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Modify the file &lt;code&gt;/usr/local/etc/apache24/httpd.conf&lt;/code&gt; and change the Listen directive to:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;Listen *:443
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Restart Apache:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;service apache24 restart&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;To simplify things, it's advisable to make a modification: in &lt;code&gt;/etc/hosts&lt;/code&gt;, add a line to ensure the mail server can be reached via LAN through its domain name. Therefore, add the line:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;192.168.123.3  mail.example.com&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;In this manner, when Nextcloud attempts to connect to the mail server, it will do so directly over the LAN. This direct connection is essential for the IMAP authentication plugin, as it verifies that the SSL certificate of the mail server matches the hostname being called.&lt;/p&gt;
&lt;p&gt;Now log in to Nextcloud with the admin credentials that the installer provided you earlier. Nextcloud is now installed and initialized. We need to perform two operations - install the Snappymail app (if you want to use it, but generally, I recommend it over the integrated mail client in Nextcloud) and enable IMAP authentication to avoid having duplicate users (i.e., mail user and Nextcloud user).&lt;/p&gt;
&lt;p&gt;Click on the menu at the top right (the big A) and go to Apps. Under "Integration," you will now be able to install "Snappymail" and "External User Authentication," which, however, is currently listed as "Untested" because it has been tested for a previous version of Nextcloud (but it works on version 28 too). You will have to click it twice, because the first click will only enable untested apps.&lt;/p&gt;
&lt;p&gt;Once the apps are installed, go to "Administration Settings" and "Additional Settings," then to the Snappymail administration panel. Now add your domain, so click Add Domain. The domain name will be that of your emails (example.com). Therefore, in the IMAP server field, enter the name of the mail server (mail.example.com), and set the security to STARTTLS. Do the same for SMTP and SIEVE.&lt;/p&gt;
&lt;p&gt;Perform the test (using the username and password of an existing and valid mailbox) and, once everything is validated, save.&lt;/p&gt;
&lt;p&gt;You will now be able to log in into your webmail - in the top bar of Nextcloud there will be two icons, one for the integrated webmail client and the other for Snappymail. The integrated webmail client has some features that are not present in Snappymail (such as delayed sending of messages), so it may be useful to keep both active. Currently, Snappymail will require a username and password every time you connect (even if you are already logged into Nextcloud), but it's possible, in user settings, to enable automatic login once authenticated in Nextcloud.&lt;/p&gt;
&lt;p&gt;Now is the time to configure IMAP authentication. Return to the Nextcloud jail and modify the Nextcloud configuration file, i.e., &lt;code&gt;/usr/local/www/nextcloud/config/config.php&lt;/code&gt;. Add a block like this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;'user_backends' =&amp;gt; array(
    array(
        'class' =&amp;gt; '\OCA\UserExternal\IMAP',
        'arguments' =&amp;gt; array(
            'mail.example.com', 993, 'ssl', 'example.com', false, false
        ),
    ),
),
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;'example.com'&lt;/code&gt; in the fourth field is used to limit the authentication scope to that domain. More information can be found &lt;a href="https://github.com/nextcloud/user_external#readme"&gt;on the official plugin page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After saving, you will be able to log out as admin and log in with your mail account. Unless there are errors, you will be catapulted into your new user, created and authorized through IMAP authentication.&lt;/p&gt;
&lt;p&gt;Congratulations, you are now able to use Nextcloud by logging in with your email credentials and also use it as a basis for webmail, leveraging the contacts module for the email address book as well.&lt;/p&gt;
&lt;p&gt;Nextcloud can offer intriguing prospects for everyday use. In addition to its seamless integration with mobile devices, tools like 'Nextcloud Talk' and 'Note' will enable users to avoid the use of external solutions and maintain control over their data.&lt;/p&gt;
&lt;p&gt;It will also be possible to synchronize contacts and calendars with mail clients (like Thunderbird) or the address books of various stationary and mobile devices. The procedure varies depending on the device used, but it's just a matter of synchronizing via CalDAV and CardDAV. Alternatively, you can set up Z-Push and use the Microsoft Exchange ActiveSync (EAS) protocol, but this will be described in part 3 of the series.&lt;/p&gt;
&lt;h3&gt;Addendum: Adding Antivirus Support to Spam Filtering&lt;/h3&gt;
&lt;p&gt;The automatic installation script, alongside Nextcloud, installed other components including ClamAV to check uploaded files in real time. We can leverage this installation (since ClamAV consumes significant resources, it makes no sense to install two instances) and integrate this instance of ClamAV with rspamd, to also perform antivirus checks on emails.&lt;/p&gt;
&lt;p&gt;Modify &lt;code&gt;/usr/local/etc/clamd.conf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Uncomment:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;TCPSocket 3310
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Restart clamd to listen on port 3310 as well:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;service clamav-clamd restart&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;Return to the mail jail:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bastille console mail&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;Create the file &lt;code&gt;/usr/local/etc/rspamd/local.d/antivirus.conf&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;clamav {
  # If set, force this action if any virus is found (default unset: no action is forced)
  action = &amp;quot;reject&amp;quot;;
  message = '${SCANNER}: virus found: &amp;quot;${VIRUS}&amp;quot;';
  # Scan mime_parts separately - otherwise, the complete mail will be transferred to AV Scanner
  #attachments_only = true; # Before 1.8.1
  #scan_mime_parts = true; # After 1.8.1
  # Scanning Text is suitable for some av scanner databases (e.g., Sanesecurity)
  #scan_text_mime = false; # 1.8.1 +
  #scan_image_mime = false; # 1.8.1 +
  # If `max_size` is set, messages &amp;gt; n bytes in size are not scanned
  #max_size = 20000000;
  # symbol to add (add it to metric if you want non-zero weight)
  symbol = &amp;quot;CLAM_VIRUS&amp;quot;;
  # type of scanner: &amp;quot;clamav&amp;quot;, &amp;quot;fprot&amp;quot;, &amp;quot;sophos&amp;quot;, or &amp;quot;savapi&amp;quot;
  type = &amp;quot;clamav&amp;quot;;
  # If set true, a log message is emitted for clean messages
  #log_clean = false;
  # Prefix used for caching in Redis: scanner-specific defaults are used. If Redis is enabled and
  # multiple scanners of the same type are present, it's important to set prefix to something unique.
  #prefix = &amp;quot;rs_cl_&amp;quot;;
  # For &amp;quot;savapi&amp;quot; you must also specify the following variable
  #product_id = 12345;
  # servers to query (if port is unspecified, scanner-specific default is used)
  # can be specified multiple times to pool servers
  # can be set to a path to a unix socket
  servers = &amp;quot;192.168.123.5:3310&amp;quot;;
  # if `patterns` is specified virus name will be matched against provided regexes and the related
  # symbol will be yielded if a match is found. If no match is found, the default symbol is yielded.
  patterns {
    # symbol_name = &amp;quot;pattern&amp;quot;;
    JUST_EICAR = '^Eicar-Test-Signature$';
  }
  # In version 1.7.0+ patterns could be extended
  #patterns = {SANE_MAL = 'Sanesecurity\.Malware\.*', CLAM_UNOFFICIAL = 'UNOFFICIAL$'};
  # `whitelist` points to a map of signature names. Hits on these signatures are ignored.
  whitelist = &amp;quot;/usr/local/etc/rspamd/local.d/antivirus.wl&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Restart rspamd in the “mail” jail and, from this point on, viruses will be automatically rejected.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Thu, 21 Mar 2024 07:15:00 +0000</pubDate><guid isPermaLink="false">https://it-notes.dragas.net/2024/03/21/make-your-own-email-server-freebsd-adding-nextcloud-part2/</guid><category>freebsd</category><category>ipv6</category><category>server</category><category>networking</category><category>e-mail</category><category>nextcloud</category><category>hosting</category><category>tutorial</category><category>security</category><category>groupware</category><category>ownyourdata</category><category>series</category></item><item><title>Make your own E-Mail server - Part 1 - FreeBSD, OpenSMTPD, Rspamd and Dovecot included</title><link>https://it-notes.dragas.net/2024/03/08/make-your-own-email-server-freebsd-opensmptd-rspamd-dovecot-part1/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/mail_iphone.webp" alt="Make your own E-Mail server - Part 1 - FreeBSD, OpenSMTPD, Rspamd and Dovecot included"&gt;&lt;/p&gt;&lt;p&gt;The main power of the Internet has always been one: decentralization.&lt;/p&gt;
&lt;p&gt;Ever since I've been able to, I've always managed my email boxes independently and provided mail hosting services to my clients. Over the years, things have become increasingly complex: on one hand, there's been a disproportionate increase in spam, and on the other, big players (like Google and Microsoft) have tried to gain a lot of ground in managing these services. Private users can obtain many free services, and business users no longer need to worry about the underlying infrastructure. However, in my opinion, the price to pay is very high: the loss of ownership of one's data.&lt;/p&gt;
&lt;p&gt;These operators, in fact, use full access to our emails to improve their services or analyze us with the aim of selling us advertisements.&lt;/p&gt;
&lt;p&gt;As often happens in these cases, many users appreciate this type of approach and have switched to the services of big players, causing a progressive increase in the level of influence that these companies can have on something free and decentralized like email.&lt;/p&gt;
&lt;p&gt;In my opinion, it still makes sense to manage one's own mail servers. Standards evolve, and it is therefore appropriate to follow innovations, adapt to best practices, and secure one's services and users.&lt;/p&gt;
&lt;p&gt;Over the last ten years, I have also used and installed various &lt;a href="https://www.zimbra.com/"&gt;Zimbra&lt;/a&gt; OSE servers. Aside from a few minor issues, the setup has proven stable and reliable. Recently, the company that develops this excellent tool has decided to no longer provide packages for the Open Source version. They can be generated with &lt;a href="https://github.com/ianw1974/zimbra-build-scripts"&gt;scripts provided by some (great!) users&lt;/a&gt;, but they cannot be a solid and stable base in the long term.&lt;/p&gt;
&lt;p&gt;I have therefore decided to gradually return to a modular, adaptable, customizable, and updatable mail host setup without worries or headaches. Such a system can include any kind of service (including webmail, integration with caldav/carddav, etc.) and it will be possible to disable what is not strictly necessary. Safer and more secure.&lt;/p&gt;
&lt;p&gt;This will be the first in a series of articles, and at the end of this reading, you will have a secure, modern, reliable, and modular mail server. The instructions are designed for FreeBSD and related jails but with very few modifications can be applied to any BSD as well as Linux or other similar operating systems.&lt;/p&gt;
&lt;h2&gt;Setup Planning&lt;/h2&gt;
&lt;p&gt;Over the years, I've used many different SMTP servers. Originally, the good old &lt;a href="https://man.freebsd.org/cgi/man.cgi?sendmail"&gt;Sendmail&lt;/a&gt;. Subsequently, and for many years, I used &lt;a href="https://www.exim.org/"&gt;Exim&lt;/a&gt;, also because it was the default system in Debian. I then migrated almost all setups towards &lt;a href="https://www.postfix.org/"&gt;Postfix&lt;/a&gt;, probably the most widespread smtp server on the net, and I never had any particular problems. In recent years, I have decided to use, when possible, the excellent &lt;a href="https://www.opensmtpd.org/"&gt;OpenSMTPD&lt;/a&gt;. Being based on OpenBSD and easily installable on other OSes, it shares the primary concepts of security and reliability of OpenBSD as well as the syntax (both in command line and in configuration files) of all other OpenBSD tools. For this type of setup, I will use opensmtpd.&lt;/p&gt;
&lt;p&gt;The choice to use FreeBSD (rather than OpenBSD) stems from two main factors:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The possibility of dividing into &lt;a href="https://wiki.freebsd.org/Jails"&gt;jails&lt;/a&gt;, physically separating the various services and minimizing dependencies.&lt;/li&gt;
&lt;li&gt;The possibility of using ZFS, with all the advantages it brings: the ability to take snapshots, the ability to perform backups quickly and efficiently, and the ability to migrate entire jails without particular problems.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The other tools that will be used are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rspamd.com/"&gt;Rspamd&lt;/a&gt; - one of the best integrated systems for checking incoming mail. Customizable, adaptable, and modular, it also provides interesting monitoring systems on the type of incoming and outgoing mail.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt; - which will be used by rspamd to manage its "memory".&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dovecot.org/"&gt;Dovecot&lt;/a&gt; and Sieve - for delivering mail to local mailboxes as well as allowing remote email consultation (via imap) and enabling the setting of rules thanks to &lt;a href="https://pigeonhole.dovecot.org/"&gt;Pigeonhole Sieve&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The users of the mail server will not be "physical" users of the server itself, but virtual users, totally unrelated to system users.&lt;/p&gt;
&lt;p&gt;This article will be long and detailed. The process may seem complex at first glance, but in reality, it's simpler than one might think. In the classic Unix philosophy, each component performs a task and does it well. Configuring the individual components separately will ensure greater scalability, reliability, and resilience to updates as well as simpler debugging compared to all-in-one solutions (like many of those found in convenient, ready-to-use docker-compose.yaml files).&lt;/p&gt;
&lt;h2&gt;Installation and Configuration&lt;/h2&gt;
&lt;p&gt;I won't describe the FreeBSD installation and configuration procedure. In my case, I activated a VPS on Hetzner - in this situation, the smallest of the available ARM servers. Yes, it's possible to efficiently manage a mail server for several users on a machine costing less than 4 euros a month. ZFS can be useful, but it's not necessary for this type of setup.&lt;/p&gt;
&lt;p&gt;The first step is therefore to choose a location to install everything. The main condition is that the VPS/server/host must have at least one static public IP and, in 2024, I now consider it necessary to have (at least) one IPv6 address. They must also be able to send/receive mail on port 25, which is not guaranteed and not all VPS providers allow. Hetzner, for example, blocks port 25 for new users.&lt;/p&gt;
&lt;p&gt;Once you have identified and acquired the server on which to install everything, it's appropriate to check that the IPs (both IPv4 and IPv6) to be used are not on any of the many blocklists/blacklists. My experience indicates that at least 60% of the IPs assigned to me are on one or more of these lists, so the first step is to request delisting, to ensure proper operation when the server is ready.&lt;/p&gt;
&lt;h3&gt;To Encrypt or Not to Encrypt?&lt;/h3&gt;
&lt;p&gt;A subsequent assessment is whether to use encryption for the data. Connections to and from the mail server will always be encrypted, but it's also possible to encrypt the data at rest. In the case of FreeBSD, it's possible to enable encryption for the entire installation (the entire virtual disk will be encrypted, and ZFS (or UFS) will be created on top of it) or, in the case of using ZFS, it will be possible to encrypt only the root dataset of the jails, the one that will be used by BastilleBSD. In the first case, it will be necessary to enter the password at every boot of FreeBSD, therefore connect via console at every restart and enter the password. In the second case, FreeBSD will be able to boot but it will be necessary to connect and enter the keys before mounting the BastilleBSD ZFS dataset. None of these solutions will prevent unauthorized access to emails in case of machine compromise. The main advantage will be "only" not to save the emails in clear on disks which, in the case of a VM, will still be shared and managed by third parties. In the case of physical servers, however, things will undoubtedly be different.&lt;/p&gt;
&lt;p&gt;Encrypting the entire disk must be done directly during the FreeBSD installation phase, very simply. As for the option to encrypt only the BastilleBSD dataset, you can proceed later, with a command similar to:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;zfs create -o encryption=on -o keylocation=prompt -o keyformat=passphrase zroot/bastille&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;at every reboot, it will be necessary to enter the password and mount the datasets:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;zfs load-key -r zroot/bastille
zfs mount -a
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Configuring FreeBSD and BastilleBSD&lt;/h3&gt;
&lt;p&gt;After the installation of the OS, the first step is to configure IPv6 on the VPS. In the case of Hetzner, unfortunately, they only provide a /64, so it will be necessary to segment the assigned network. In this example, it will be divided into /72 subnetworks - to find valid subclasses, it will be possible &lt;a href="https://subnettingpractice.com/ipv6-subnet-calculator.html"&gt;to use a calculator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;/etc/rc.conf&lt;/code&gt; file should have entries similar to:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;ifconfig_vtnet0=&amp;quot;DHCP&amp;quot;
ifconfig_vtnet0_ipv6=&amp;quot;inet6 2a01:4f8:cafe:cafe::1 prefixlen 72&amp;quot;
ipv6_defaultrouter=&amp;quot;fe80::1%vtnet0&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In short, keep the base address assigned by Hetzner, but change the prefix length to 72 - thus giving the possibility of having other networks available.&lt;/p&gt;
&lt;p&gt;It is now necessary to enable forwarding for IPv4 and IPv6. Add these lines to the &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After reboot, test it:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ping6 google.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If everything has been configured correctly, the ping will be executed and google.com will reply.&lt;/p&gt;
&lt;p&gt;It will also be necessary to set up reverse DNS, otherwise many mail servers will reject our emails. This must be done both for the IPv4 address and the designated IPv6 address (within the class - an operation that can also be done after creating the jails, but before starting to send email messages). Also, create the correct A and AAAA records on the DNS for the "mail.example.com" domain.&lt;/p&gt;
&lt;p&gt;The FreeBSD installer does not automatically update the operating system, so it is appropriate to do so now:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;freebsd-update fetch install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It seems, moreover, that there is still &lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059"&gt;an old bug still present&lt;/a&gt; and manifesting when, on a FreeBSD server installed in a VM based on KVM (thus also those of Hetzner), routing is performed (as in our case) between VNET jails and host.&lt;/p&gt;
&lt;p&gt;Adding this configuration to &lt;code&gt;/boot/loader.conf&lt;/code&gt; will solve the problem:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;hw.vtnet.X.tso_disable=&amp;quot;1&amp;quot;
hw.vtnet.tso_disable=&amp;quot;1&amp;quot;
hw.vtnet.lro_disable=&amp;quot;1&amp;quot;
hw.vtnet.X.lro_disable=&amp;quot;1&amp;quot;
hw.vtnet.csum_disable=&amp;quot;1&amp;quot;
hw.vtnet.X.csum_disable=&amp;quot;1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's time to install and configure &lt;a href="https://bastillebsd.org/"&gt;BastilleBSD&lt;/a&gt;. This handy tool will make managing jails much simpler and more straightforward, and &lt;a href="https://bastillebsd.org/getting-started/"&gt;its configuration is well described on the project page&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Bridge and Firewall&lt;/h3&gt;
&lt;p&gt;For some time now, I've been favoring the use of VNET jails for these types of setups. I believe that having a complete network stack within the jails gives more freedom in configuration, firewall management (applicable also within individual jails), and technical possibilities (such as creating VPNs within the jails themselves, ensuring greater portability).&lt;/p&gt;
&lt;p&gt;I suggest creating a bridge on the FreeBSD server and placing all the jails within this bridge. It will be enough to modify the &lt;code&gt;/etc/rc.conf&lt;/code&gt; file and change/add:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;cloned_interfaces=&amp;quot;lo1 bridge0&amp;quot;
ifconfig_bridge0=&amp;quot;inet 192.168.123.1 netmask 255.255.255.0&amp;quot;
ifconfig_bridge0_ipv6=&amp;quot;inet6 2a01:4f8:cafe:cafe:100::1 prefixlen 72&amp;quot;
ipv6_gateway_enable=&amp;quot;YES&amp;quot;
ipv6_activate_all_interfaces=&amp;quot;YES&amp;quot;
gateway_enable=&amp;quot;YES&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now let's configure the firewall. In this example, I will use the IPv4 and IPv6 addresses that I will later assign to the jails. 
Here's an example of a working &lt;code&gt;pf.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;ext_if=&amp;quot;vtnet0&amp;quot;

set block-policy return
scrub in on $ext_if all fragment reassemble
set skip on lo
set skip on bridge0

table &amp;lt;jails&amp;gt; persist
# IPv4 private address ranges
table &amp;lt;private&amp;gt; const { 10/8, 172.16/12, 192.168/16 }
nat on $ext_if from 192.168.123.0/24 to ! &amp;lt;private&amp;gt; -&amp;gt; ($ext_if:0)
nat on $ext_if from &amp;lt;jails&amp;gt; to any -&amp;gt; ($ext_if:0)
rdr-anchor &amp;quot;rdr/*&amp;quot;
#rdr via ipv4 to mail
rdr pass on $ext_if proto tcp from any to ($ext_if) port { 25, 465, 587, 143, 993 } -&amp;gt; 192.168.123.3
#rdr via ipv4 to nginx-proxy
rdr pass on $ext_if proto tcp from any to ($ext_if) port { 80, 443 } -&amp;gt; 192.168.123.2

block in all
#PASS ICMP
pass in quick proto icmp from any to any
# Pass ICMP on ipv6
pass quick proto ipv6-icmp
pass out quick keep state
antispoof for $ext_if inet
pass in inet proto tcp from any to any port ssh flags S/SA keep state
#Pass ipv6 to mail jail
pass in quick on $ext_if inet6 proto tcp from any to 2a01:4f8:cafe:cafe:100::25 port { smtp, 465, 587, 143, 993 }
#Pass ipv6 to nginx-proxy jail
pass in quick on $ext_if inet6 proto tcp from any to 2a01:4f8:cafe:cafe:100::80 port { 80, 443 }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At this point, it is advisable to reboot the machine, to be sure that everything comes back up in the right way.&lt;/p&gt;
&lt;h3&gt;Creating the "nginx-proxy" and "redis" Jails&lt;/h3&gt;
&lt;p&gt;Let's start with the first jail. It will be an nginx reverse proxy. At the moment it will not be used as such but will be useful as a machine for generating certificates and, in the future, to act as a reverse proxy for various internal web services.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bastille create -B nginx-proxy 14.3-RELEASE 192.168.123.2/24 bridge0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Once the jail is created, it will be necessary to modify some configurations. In the jail itself (bastille console nginx-proxy), modify the &lt;code&gt;/etc/rc.conf&lt;/code&gt; file and add the IPv4 gateway and IPv6 configurations, i.e., giving the address specified earlier in the &lt;code&gt;pf.conf&lt;/code&gt; of the FreeBSD server and as the gateway the IP address of the related bridge:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;defaultrouter=&amp;quot;192.168.123.1&amp;quot;
ifconfig_vnet0_ipv6=&amp;quot;inet6 2a01:4f8:cafe:cafe:100::80 prefixlen 72&amp;quot;
ipv6_defaultrouter=&amp;quot;2a01:4f8:cafe:cafe:100::1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Restart nginx-proxy with &lt;code&gt;bastille restart nginx-proxy&lt;/code&gt;, and you will be able to re-enter the jail. At that point, &lt;code&gt;ping google.com&lt;/code&gt; and &lt;code&gt;ping6 google.com&lt;/code&gt; should work. The jail will then be able to operate, responding on 80 and 443 both in IPv4 (thanks to the NAT configured previously on the FreeBSD server) and in IPv6, being directly connected in routing.&lt;/p&gt;
&lt;p&gt;Now install the necessary software:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkg install -y nginx py311-certbot py311-certbot-nginx&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To automatically renew the certificates, add this line to &lt;code&gt;/etc/periodic.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;weekly_certbot_enable=&amp;quot;YES&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's now possible to generate the certificate, which we will also use for all other exposed services:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;certbot certonly -d mail.example.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Once the certificates are generated, we will need to make a small change as opensmtpd (which we will install later in another jail) is (rightly) very restrictive on permissions:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;chmod -R 400 /usr/local/etc/letsencrypt/live/mail.example.com/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now let's create a jail for redis. I usually put it in a dedicated jail also in terms of clustering/multi-server setup:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bastille create -B redis 14.3-RELEASE 192.168.123.4/24 bridge0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Also for the redis jail, it's appropriate to set up the gateway, so in the &lt;code&gt;/etc/rc.conf&lt;/code&gt; of the jail:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;defaultrouter=&amp;quot;192.168.123.1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I don't configure IPv6 as it's not necessary (this jail will only be reachable from the LAN) but it's possible to do so. Once the jail is restarted (bastille restart redis), it will be able to reach the outside:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkg install -y redis&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Not wanting to disable the protected mode of redis and not wanting to open it without authentication, it will be appropriate to modify the &lt;code&gt;/usr/local/etc/redis.conf&lt;/code&gt; configuration file and add the password and some options to optimize its use with rspamd:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;requirepass cafecafe
maxmemory 512mb
maxmemory-policy volatile-lru
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, set a unique password. Also, change the line concerning the bind:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;bind *
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now execute:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;service redis enable
service redis start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Redis should be active and ready to receive connections.&lt;/p&gt;
&lt;h3&gt;The "mail" Jail&lt;/h3&gt;
&lt;p&gt;It's time to create the jail with the main services:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bastille create -B mail 14.3-RELEASE 192.168.123.3/24 bridge0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;and, also in this case, it will be necessary to enter the jail and modify some configurations in &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;defaultrouter=&amp;quot;192.168.123.1&amp;quot;
ifconfig_vnet0_ipv6=&amp;quot;inet6 2a01:4f8:cafe:cafe:100::25 prefixlen 72&amp;quot;
ipv6_defaultrouter=&amp;quot;2a01:4f8:cafe:cafe:100::1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once the jail is restarted, it will be possible to install the necessary packages:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkg install -y rspamd opensmtpd opensmtpd-filter-senderscore opensmtpd-filter-rspamd dovecot dovecot-pigeonhole&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please note&lt;/strong&gt;: the &lt;em&gt;opensmtpd-extras-&lt;/em&gt; packages are currently broken so we'll compile the table file support:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;pkg install autoconf automake git-tiny
git clone https://github.com/OpenSMTPD/table-passwd.git
cd table-passwd
sh bootstrap
./configure
make
make install
cp table-passwd /usr/local/libexec/opensmtpd/
cp table-passwd.5 /usr/local/share/man/man5/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It is now appropriate to give this jail an FQDN, as it will be the name with which it presents itself when connecting to the outside. Just modify &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;hostname=&amp;quot;mail.example.com&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The hostname should be equivalent to the reverse DNS set earlier, so the machine will present itself with a name that, doing a reverse lookup, will correspond to the IP of origin.&lt;/p&gt;
&lt;p&gt;This jail does not contain the certificates that are present in the nginx-proxy jail. There are various methods to share them, such as using rsync, putting them on an NFS share, etc., but the simplest in this case will be to do a bind mount between the two jails, an operation that BastilleBSD can handle automatically.&lt;/p&gt;
&lt;p&gt;Create the correct directory in the "mail" jail:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir /usr/local/etc/letsencrypt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Exit the jail, shut it down, and modify the fstab file of the jail (e.g., &lt;code&gt;/usr/local/bastille/jails/mail/fstab&lt;/code&gt;) by adding a line like this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;/usr/local/bastille/jails/nginx-proxy/root/usr/local/etc/letsencrypt /usr/local/bastille/jails/mail/root/usr/local/etc/letsencrypt nullfs ro 0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Start the jail, and at that point, the directory created earlier should display the directories and certificates from the nginx-proxy jail.&lt;/p&gt;
&lt;p&gt;Due to spammers who, year after year, become increasingly aggressive, many mail servers require perfect configuration of the main sender authentication methodologies. Today, the main (and now necessary, under penalty of message non-delivery) are &lt;a href="https://mxtoolbox.com/spf.aspx"&gt;SPF&lt;/a&gt;, &lt;a href="https://mxtoolbox.com/dkim.aspx"&gt;DKIM&lt;/a&gt;, and &lt;a href="https://mxtoolbox.com/dmarc.aspx"&gt;DMARC&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first step is to generate a DKIM key for the domain(s) that will send mail from the server we are configuring. This operation can be performed in a few steps, by placing the keys in the &lt;code&gt;/usr/local/etc/mail/dkim&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;mkdir /usr/local/etc/mail/dkim
cd /usr/local/etc/mail/dkim
openssl genrsa -out example.com.key 2048
openssl rsa -in example.com.key -pubout -out public.key
chmod 0400 example.com.key
chown rspamd *
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since rspamd will take care of signing outgoing messages, it is appropriate that only the related user be able to read the file with the private key.&lt;/p&gt;
&lt;p&gt;Once the pair of keys has been created, it will be necessary to configure DNS to provide the public key to mail servers that will request it. The record should look like this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;mail._domainkey   TXT   &amp;quot;v=DKIM1;k=rsa;p=your-public-key-goes-here&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once the record has been entered and propagated, it will be possible to test its correctness through one of the many websites that offer this service, remembering that the selector we have configured is "mail" (the first part of the record). Obviously, this is a free text, which can be modified at will (but will then need to be specified in the rspamd configuration, later on).&lt;/p&gt;
&lt;p&gt;Being in the DNS configuration phase, it will also be appropriate to create SPF and DMARC records:&lt;/p&gt;
&lt;p&gt;SPF record:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;example.com. IN TXT &amp;quot;v=spf1 a ip4:your.ip.address ip6:2a01:4f8:cafe:cafe:100::25 mx ~all&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;DMARC:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;_dmarc.example.com. IN TXT &amp;quot;v=DMARC1;p=none;pct=100;rua=mailto:postmaster@example.com&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These records, obviously modified based on the chosen domain, are configured conservatively but suitable for a test. Once the entire setup is in place, it will be possible to handle things in a more restrictive manner.&lt;/p&gt;
&lt;p&gt;The mail server users will be "virtual" users (i.e., not system users of the server), so all their mail will be "handled" by a unique user, which needs to be created:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;pw user add vmail -u 2000 -d /var/vmail -s /usr/sbin/nologin
mkdir /var/vmail
chown vmail /var/vmail
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let's now create some files and directories, useful for the subsequent configuration:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;touch /usr/local/etc/mail/passwd
touch /usr/local/etc/mail/virtuals
mkdir /usr/local/etc/rspamd/local.d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Remove the current &lt;code&gt;/usr/local/etc/mail/smtpd.conf&lt;/code&gt; and replace it with content like this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;table passwd passwd:/usr/local/etc/mail/passwd
table virtuals file:/usr/local/etc/mail/virtuals

pki mail.example.com cert &amp;quot;/usr/local/etc/letsencrypt/live/mail.example.com/fullchain.pem&amp;quot;
pki mail.example.com key &amp;quot;/usr/local/etc/letsencrypt/live/mail.example.com/privkey.pem&amp;quot;

filter check_dyndns phase connect match rdns regex { '.*\.dyn\..*', '.*\.dsl\..*' } \
    disconnect &amp;quot;550 no residential connections - Thou shalt not pass&amp;quot;

filter check_rdns phase connect match !rdns \
    disconnect &amp;quot;550 no rdns - Thou shalt not pass&amp;quot;

filter check_fcrdns phase connect match !fcrdns \
    disconnect &amp;quot;550 no FCrDNS - Thou shalt not pass&amp;quot;

filter senderscore \
    proc-exec &amp;quot;/usr/local/libexec/opensmtpd/opensmtpd-filter-senderscore -blockBelow 10 -junkBelow 70 -slowFactor 5000&amp;quot;

filter rspamd proc-exec &amp;quot;/usr/local/libexec/opensmtpd/opensmtpd-filter-rspamd&amp;quot;

listen on 0.0.0.0 tls pki mail.example.com \
    filter { check_dyndns, check_rdns, check_fcrdns, senderscore, rspamd } auth-optional &amp;lt;passwd&amp;gt;

listen on ::0 tls pki mail.example.com \
    filter { check_dyndns, check_rdns, check_fcrdns, senderscore, rspamd } auth-optional &amp;lt;passwd&amp;gt;

listen on 0.0.0.0 port submission tls-require pki mail.example.com  auth &amp;lt;passwd&amp;gt; filter rspamd mask-src

listen on ::0 port submission tls-require pki mail.example.com  auth &amp;lt;passwd&amp;gt; filter rspamd mask-src

listen on 0.0.0.0 port 465 smtps pki mail.example.com  auth &amp;lt;passwd&amp;gt; filter rspamd mask-src

listen on ::0 port 465 smtps pki mail.example.com  auth &amp;lt;passwd&amp;gt; filter rspamd mask-src

action &amp;quot;local_mail&amp;quot; lmtp &amp;quot;/var/run/dovecot/lmtp&amp;quot; rcpt-to virtual &amp;lt;virtuals&amp;gt;
action &amp;quot;outbound&amp;quot; relay helo mail.example.com

match from any for domain example.com action &amp;quot;local_mail&amp;quot;
match for local action &amp;quot;local_mail&amp;quot;

match from any auth for any action &amp;quot;outbound&amp;quot;
match for any action &amp;quot;outbound&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The beauty of OpenSMTPD lies in its simplicity. Essentially, that's all there is to it, but here's an explanation of what this file does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;table passwd&lt;/code&gt; and &lt;code&gt;table virtuals&lt;/code&gt; define tables for user credentials and virtual domains/users, crucial for authentication and email forwarding/aliasing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pki&lt;/code&gt; lines specify the SSL/TLS certificate and key for encrypted connections.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; directives apply various checks and filters to incoming connections, including dynamic DNS and reverse DNS validations, sender reputation scoring, and Rspamd filtering.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;listen&lt;/code&gt; lines configure the server to listen on all IPv4 and IPv6 addresses for incoming SMTP and submission (port 587) connections, applying TLS and authentication as specified.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;action&lt;/code&gt; lines define actions for email delivery, using LMTP for local delivery based on virtual mappings and setting up relay actions for outbound emails.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match&lt;/code&gt; rules determine how emails are processed, either delivered locally or relayed externally based on source, destination, and authentication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To test the newly inserted configuration, run &lt;code&gt;smtpd -n&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next step is to configure Rspamd. Rather than modifying the base configuration files, we'll proceed by creating "overrides", placing specific configurations in the directory created earlier (&lt;code&gt;/usr/local/etc/rspamd/local.d&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;For Redis configuration in &lt;code&gt;/usr/local/etc/rspamd/local.d/redis.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;servers = &amp;quot;192.168.123.4&amp;quot;;
password = &amp;quot;cafecafe&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For SPF settings in &lt;code&gt;/usr/local/etc/rspamd/local.d/spf.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;spf_cache_size = 1k;
spf_cache_expire = 1d;
max_dns_nesting = 10;
max_dns_requests = 30;
min_cache_ttl = 5m;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, configure DKIM signing, which rspamd will handle for every sent email. The selector must match the one entered in the DNS record previously:&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;/usr/local/etc/rspamd/local.d/dkim_signing.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;domain {
  example.com {
    path = &amp;quot;/usr/local/etc/mail/dkim/example.com.key&amp;quot;;
    selector = &amp;quot;mail&amp;quot;;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To enable support for some "known" phisher lists, 
create &lt;code&gt;/usr/local/etc/rspamd/local.d/phishing.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;# Configuration options can be added here`
openphish_enabled = true;
phishtank_enabled = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For SURBL (Spam URI Real-time Blocklists) configuration in &lt;code&gt;/usr/local/etc/rspamd/local.d/surbl.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;# follow redirects when checking URLs in emails for spaminess
redirector_hosts_map = &amp;quot;/usr/local/etc/rspamd/redirectors.inc&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For URL reputation checks in &lt;code&gt;/usr/local/etc/rspamd/local.d/url_reputation.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;# check URLs within messages for spaminess
enabled = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And for caching some URL tags in Redis, in &lt;code&gt;/usr/local/etc/rspamd/local.d/url_tags.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;# cache some URL tags in redis
enabled = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Rspamd should be ready for initial testing.&lt;/p&gt;
&lt;p&gt;Now it's time to configure Dovecot, which will handle the final phase of delivery and provide mail access, as well as use Sieve for filtering and rule application.&lt;/p&gt;
&lt;p&gt;The first step is to start with a default configuration:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cd /usr/local/etc/dovecot/ &amp;amp;&amp;amp; cp -R example-config/* .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Next, modify some files. Start with &lt;code&gt;conf.d/15-mailboxes.conf&lt;/code&gt; - add &lt;code&gt;auto = create&lt;/code&gt; to the main folders, like this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;namespace inbox {
  # These mailboxes are widely used and could perhaps be created automatically:
  separator = .

  mailbox Drafts {
    special_use = \Drafts
    auto = create
  }
  mailbox Junk {
    special_use = \Junk
    auto = create
  }
  mailbox Trash {
    special_use = \Trash
    auto = create
  }

  # For \Sent mailboxes there are two widely used names. We'll mark both of
  # them as \Sent. User typically deletes one of them if duplicates are created.
  mailbox Sent {
    special_use = \Sent
    auto = create
  }
  mailbox &amp;quot;Sent Messages&amp;quot; {
    special_use = \Sent
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Modify &lt;code&gt;/usr/local/etc/dovecot/dovecot.conf&lt;/code&gt; to include:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;protocols = imap lmtp sieve
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In &lt;code&gt;conf.d/10-auth.conf&lt;/code&gt;, switch to system authentication with passwdfile:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!include auth-system.conf.ext
!include auth-passwdfile.conf.ext
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, set up the &lt;code&gt;passdb&lt;/code&gt; and &lt;code&gt;userdb&lt;/code&gt; configuration in &lt;code&gt;conf.d/auth-passwdfile.conf.ext&lt;/code&gt;. Replace the entire file with these contents:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;passdb {
  driver = passwd-file
  args = scheme=CRYPT /usr/local/etc/mail/passwd
}

userdb {
  default_fields = uid=vmail gid=vmail home=/var/vmail/%d/%n
  args = /usr/local/etc/mail/passwd
  driver = passwd-file
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Edit &lt;code&gt;conf.d/10-ssl.conf&lt;/code&gt; to update &lt;code&gt;ssl_key&lt;/code&gt; and &lt;code&gt;ssl_cert&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;ssl_cert = &amp;lt;/usr/local/etc/letsencrypt/live/example.com/fullchain.pem
ssl_key = &amp;lt;/usr/local/etc/letsencrypt/live/example.com/privkey.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In &lt;code&gt;conf.d/20-imap.conf&lt;/code&gt;, include:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;(...)
mail_plugins = $mail_plugins imap_sieve zlib
mail_max_userip_connections = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And in &lt;code&gt;conf.d/20-lmtp.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;protocol lmtp {
  mail_fsync = optimized
  mail_plugins = $mail_plugins sieve
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Set the mail location in &lt;code&gt;conf.d/10-mail.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;mail_location = maildir:/var/vmail/%d/%n

maildir_stat_dirs=yes
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, let's configure Sieve in &lt;code&gt;conf.d/90-plugin.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;plugin {
  #setting_name = value
  sieve_plugins = sieve_imapsieve sieve_extprograms
  sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
  imapsieve_mailbox1_name = Junk
  imapsieve_mailbox1_causes = COPY APPEND
  imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve

  imapsieve_mailbox2_name = *
  imapsieve_mailbox2_from = Junk
  imapsieve_mailbox2_causes = COPY
  imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve

  imapsieve_mailbox3_name = Inbox
  imapsieve_mailbox3_causes = APPEND
  imapsieve_mailbox3_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve
  sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve

  #Move the spam to Junk folder
  sieve_after = /usr/local/lib/dovecot/sieve/spam-to-folder.sieve
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And in &lt;code&gt;conf.d/20-managesieve.conf.ext&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;service managesieve-login {
  inet_listener sieve {
    port = 4190
  }

  service_count = 1
  vsz_limit = 64M
}

service managesieve {
  process_limit = 1024
}

protocol sieve {
  mail_max_userip_connections = 10

  # Configuration for Sieve script management via ManageSieve protocol
}

plugin {
  sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
  sieve = file:/var/vmail/%d/%n/sieve;active=/var/vmail/%d/%n/.dovecot.sieve
  sieve_user_log = file:/var/vmail/%d/%n/sieve/sieve_error.log
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This setup outlines the Dovecot configuration necessary for handling mail delivery, access, and Sieve-based filtering. With these settings, Dovecot is prepared to manage both incoming and outgoing mails securely and efficiently, including support for managing Sieve scripts via the ManageSieve protocol.&lt;/p&gt;
&lt;p&gt;Sieve scripts train Rspamd on &lt;strong&gt;spam&lt;/strong&gt; and &lt;strong&gt;ham&lt;/strong&gt;. Moving email into and out of the junk folder triggers an event to train Rspamd.&lt;/p&gt;
&lt;p&gt;These files are located at &lt;code&gt;/usr/local/lib/dovecot/sieve&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create the &lt;code&gt;report-ham.sieve&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;require [&amp;quot;vnd.dovecot.pipe&amp;quot;, &amp;quot;copy&amp;quot;, &amp;quot;imapsieve&amp;quot;, &amp;quot;environment&amp;quot;, &amp;quot;variables&amp;quot;];

if environment :matches &amp;quot;imap.mailbox&amp;quot; &amp;quot;*&amp;quot; {
  set &amp;quot;mailbox&amp;quot; &amp;quot;${1}&amp;quot;;
}

if string &amp;quot;${mailbox}&amp;quot; &amp;quot;Trash&amp;quot; {
  stop;
}

if environment :matches &amp;quot;imap.user&amp;quot; &amp;quot;*&amp;quot; {
  set &amp;quot;username&amp;quot; &amp;quot;${1}&amp;quot;;
}

pipe :copy &amp;quot;sa-learn-ham.sh&amp;quot; [ &amp;quot;${username}&amp;quot; ];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create the &lt;code&gt;report-spam.sieve&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;require [&amp;quot;vnd.dovecot.pipe&amp;quot;, &amp;quot;copy&amp;quot;, &amp;quot;imapsieve&amp;quot;, &amp;quot;environment&amp;quot;, &amp;quot;variables&amp;quot;];

if environment :matches &amp;quot;imap.user&amp;quot; &amp;quot;*&amp;quot; {
  set &amp;quot;username&amp;quot; &amp;quot;${1}&amp;quot;;
}

pipe :copy &amp;quot;sa-learn-spam.sh&amp;quot; [ &amp;quot;${username}&amp;quot; ];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create the &lt;code&gt;spam-to-folder.sieve&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;require [&amp;quot;fileinto&amp;quot;,&amp;quot;mailbox&amp;quot;];

if header :contains &amp;quot;X-Spam&amp;quot; &amp;quot;Yes&amp;quot; {
 fileinto :create &amp;quot;Junk&amp;quot;;
 stop;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Compile the files:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;sievec report-ham.sieve
sievec report-spam.sieve
sievec spam-to-folder.sieve
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create the following two shell scripts in &lt;code&gt;/usr/local/lib/dovecot/sieve&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Add the following to &lt;code&gt;sa-learn-ham.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!/bin/sh
exec /usr/local/bin/rspamc -d &amp;quot;${1}&amp;quot; learn_ham
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add the following to &lt;code&gt;sa-learn-spam.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!/bin/sh
exec /usr/local/bin/rspamc -d &amp;quot;${1}&amp;quot; learn_spam
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make the files executable:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;chmod 755 sa-learn-ham.sh
chmod 755 sa-learn-spam.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Everything has been configured correctly. However, a fundamental part is missing: the users.&lt;/p&gt;
&lt;p&gt;To begin, encrypt the password by executing:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;smtpctl encrypt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Input the plain password and hit ENTER. Copy the displayed encrypted password for the next step. Now, insert the entry into &lt;code&gt;/usr/local/etc/mail/passwd&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;foo@example.com:your encrypted password goes here::::::
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Remember to replace the placeholder with your encrypted password and ensure there are &lt;em&gt;six colons&lt;/em&gt; at the end.&lt;/p&gt;
&lt;p&gt;Finally, add the virtual user and associate it with the &lt;code&gt;vmail&lt;/code&gt; system user in &lt;code&gt;/usr/local/etc/mail/virtuals&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;foo@example.com vmail
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: Additional aliases can be included, such as:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;postmaster@example.com foo@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let's enable and start the services:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;service rspamd enable
service rspamd start
service smtpd enable
service smtpd start
service dovecot enable
service dovecot start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At this point, take a look at the logs (especially &lt;code&gt;/var/log/maillog&lt;/code&gt;) to get an idea of what's happening. If everything is running correctly, you can now proceed with some tests. Any mail client (such as Thunderbird, etc.) should be able to connect via IMAP and send emails using SMTP, thanks to authentication. Remember that the username corresponds to the one entered in the &lt;code&gt;/usr/local/etc/mail/passwd&lt;/code&gt; file and typically matches the full email address.&lt;/p&gt;
&lt;p&gt;Once everything is in order, you can set the MX record of the domain "example.com" to "mail.example.com".&lt;/p&gt;
&lt;p&gt;One of the key tests to perform is to send an email to a Gmail inbox. If the email is correctly delivered and not marked as Spam, there's a good chance that the setup is correct. There are also several online tools available that can provide useful insights into any configuration errors.&lt;/p&gt;
&lt;p&gt;You have just installed and operationalized a complete mail server. &lt;/p&gt;
&lt;p&gt;It is now possible to &lt;a href="https://it-notes.dragas.net/2024/03/21/make-your-own-email-server-freebsd-adding-nextcloud-part2/"&gt;proceed with part 2, which involves the installation of Nextcloud and Snappymail&lt;/a&gt;, in order to have a comprehensive groupware system, while keeping one's data on their own servers.&lt;/p&gt;
&lt;h3&gt;Links:&lt;/h3&gt;
&lt;p&gt;This article was inspired by several other articles available online that provided me with a lot of useful information. Here is a non-exhaustive list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.c0ffee.net/blog/mail-server-guide"&gt;A Comprehensive Guide to Setting Up Your Mail Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tumfatig.net/2023/self-hosted-email-services-on-openbsd"&gt;Self-Hosted Email Services on OpenBSD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.davd.io/posts/2021-12-18-freebsd-mail-server-part-1/"&gt;Building a FreeBSD Mail Server: Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.vultr.com/an-openbsd-e-mail-server-using-opensmtpd-dovecot-rspamd-and-rainloop"&gt;An OpenBSD E-mail Server Using OpenSMTPD, Dovecot, Rspamd, and RainLoop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://poolp.org/posts/2019-09-14/setting-up-a-mail-server-with-opensmtpd-dovecot-and-rspamd/"&gt;Setting up a mail server with OpenSMTPD, Dovecot and Rspamd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://forums.FreeBSD.org/threads/opensmtpd-7-6-0-wont-start-after-upgrade-freebsd-14-2.96410/post-686266"&gt;OpenSMTPD 7.6.0 wont start after upgrade&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Fri, 08 Mar 2024 07:15:00 +0000</pubDate><guid isPermaLink="false">https://it-notes.dragas.net/2024/03/08/make-your-own-email-server-freebsd-opensmptd-rspamd-dovecot-part1/</guid><category>freebsd</category><category>ipv6</category><category>server</category><category>networking</category><category>e-mail</category><category>rspamd</category><category>hosting</category><category>tutorial</category><category>security</category><category>dovecot</category><category>opemsmtpd</category><category>ownyourdata</category><category>series</category></item></channel></rss>