Cookie Consent by TermsFeed

Make your own E-Mail server - Part 2 - Adding Webmail and More with Nextcloud

Make your own E-Mail server - Part 2 - Adding Webmail and More with Nextcloud

In the first part of this series, I detailed how to install and configure a complete mail server within a FreeBSD jail. While accessible via IMAP and SMTP, I intentionally did not cover installing a webmail interface.

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 Nextcloud.

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.

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 “nextcloud.example.com.”

To do this, enter the proxy’s jail:

bastille console nginx-proxy

Modify the file /usr/local/etc/nginx/nginx.conf, adjust the worker_processes (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:

worker_processes auto;

In the http block, change the maximum file size limit and create the virtual host on http - we will generate the https block and related certificates later:

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 "upgrade";
        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;
    }
} 

The proxy_pass 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.

Now, enable and start nginx:

service nginx enable
service nginx start

After setting the listener to port 80, it’s now appropriate to generate the certificate.

A simple command will request the certificate and modify the nginx configuration to accept connections over https:

certbot --nginx -d nextcloud.example.com

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.

Exit the jail, and it’s time to create the jail for Nextcloud.

bastille create -B nc 14.0-RELEASE 192.168.123.5/24 bridge0

Enter the jail and add the gateway in rc.conf:

defaultrouter="192.168.123.1"

After restarting the jail, it will now be possible to install Nextcloud.

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 Gibran Khan 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.

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: https://github.com/draga79/NextCloudOnFreeBSD

So, enter the “nc” jail, download the script, and start with the configuration:

bastille console nc
fetch https://github.com/draga79/NextCloudOnFreeBSD/archive/refs/heads/release.zip
unzip release.zip
cd NextCloudOnFreeBSD-release
./pre-install.sh

A configuration file will be generated that we need to modify. The file is install.conf.

You will certainly need to change the HOST_NAME (inserting the one you have chosen, in this case, “nextcloud.example.com”) and, unless you are in Italy, I would also suggest changing the COUNTRY_CODE and TIME_ZONE.

Once completed, you can proceed with the actual installation:

./install.sh

At the end of the installation, the login data will appear (which will also be saved in a file named nextcloud.example.com_reference.txt). 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 crontab -u www -e, save, and exit.

Now, modify the Nextcloud configuration file, /usr/local/www/nextcloud/config/config.php, and:

  • Replace overwrite.cli.url with your domain (https://nextcloud.example.com).

  • Add the proxy to the trusted proxies (if not present, insert the entire block):

 'trusted_proxies' =>
  array (
    0 => '192.168.123.2',
  ),
  • Modify the file /usr/local/etc/apache24/httpd.conf and change the Listen directive to:
Listen *:443

Restart Apache:

service apache24 restart

To simplify things, it’s advisable to make a modification: in /etc/hosts, add a line to ensure the mail server can be reached via LAN through its domain name. Therefore, add the line:

192.168.123.3 mail.example.com

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.

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).

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.

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.

Perform the test (using the username and password of an existing and valid mailbox) and, once everything is validated, save.

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.

Now is the time to configure IMAP authentication. Return to the Nextcloud jail and modify the Nextcloud configuration file, i.e., /usr/local/www/nextcloud/config/config.php. Add a block like this:

'user_backends' => array(
    array(
        'class' => '\OCA\UserExternal\IMAP',
        'arguments' => array(
            'mail.example.com', 993, 'ssl', 'example.com', false, false
        ),
    ),
),

'example.com' in the fourth field is used to limit the authentication scope to that domain. More information can be found on the official plugin page.

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.

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.

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.

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.

Addendum: Adding Antivirus Support to Spam Filtering

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.

Modify /usr/local/etc/clamd.conf

Uncomment:

TCPSocket 3310

Restart clamd to listen on port 3310 as well:

service clamav-clamd restart

Return to the mail jail:

bastille console mail

Create the file /usr/local/etc/rspamd/local.d/antivirus.conf with the following content:

clamav {
  # If set, force this action if any virus is found (default unset: no action is forced)
  action = "reject";
  message = '${SCANNER}: virus found: "${VIRUS}"';
  # 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 > n bytes in size are not scanned
  #max_size = 20000000;
  # symbol to add (add it to metric if you want non-zero weight)
  symbol = "CLAM_VIRUS";
  # type of scanner: "clamav", "fprot", "sophos", or "savapi"
  type = "clamav";
  # 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 = "rs_cl_";
  # For "savapi" 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 = "192.168.123.5:3310";
  # 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 = "pattern";
    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 = "/usr/local/etc/rspamd/local.d/antivirus.wl";
}

Restart rspamd in the “mail” jail and, from this point on, viruses will be automatically rejected.


See also