Setup Email Server From Scratch On FreeBSD - 08 Create Virtual Domains

07 RoundCube WebMail <- Intro -> 09 Blocking Spam

This tutorial is partially complete 2025-05-14
Postfix, Dovecot, & PostfixAdmin work with MySQL and virtual accounts
can be created with PostfixAdmin and used with an email client. SPF, OpenDKIM,
and DMARC milters, Roundcube with sieve filters and password plugins all work.
Roundcube kolab calendar works (2025-05-20)

################################
# Howto Create Virtual Domains #
################################

This page of the "FreeBSD Email From Scratch" tutorial assumes successful setup of the following ...

1. FreeBSD Server
2. FAMP - FreeBSD Apache MySQL/MariaDB PHP
3. Postfix - smtp server
4. Dovecot - imap server
5. PostfixAdmin - Postfix Web Administration
6. SPF DMARC and DKIM - email authetication and spam filters
7. Roundcube - Webmail

Setup DNS for New Domain(s)

A	mail		147.135.65.97
AAAA	mail		2604:2dc0:100:1261::10
A	mx		147.135.65.97
AAAA	mx		2604:2dc0:100:1261::10
CNAME   autoconfig      mx.okbsd.com
CNAME   autodiscover    mx.okbsd.com
CNAME   www     coragarden.com
TXT     @       v=spf1 ip4:147.135.65.97 ip6:2604:2dc0:100:1261::10/64 mx ~all
TXT     _dmarc  v=DMARC1; p=quarantine; rua=mailto:postmaster@okbsd.com; ruf=mailto:postmaster@okbsd.com; sp=quarantine
SRV Record    _autodiscover   _tcp      5         0       443   mx.okbsd.com

# In Custom MX Records
MX      @       mx.okbsd.com    0
MX      @       mail.okbsd.com  10

# We use mx.okbsd.com and mail.okbsd.com as the MX host so we don't need to create A and AAAA records and certs for 
# additional mx servers like mx.coragarden.com and mail.coragarden.com, but this example includes the A and AAAA 
# records. Also DMARC reports are sent to the admin@okbsd.com email address. This means okbsd.com needs a DNS TXT entry 
# to allow coragarden.com to send dmarc reports to okbsd.com. There is some benefit to adding A and AAAA records
# if users will access IMAP or webmail at mail.coragarden.com for example.

# DNS TXT Entry for okbsd.com
TXT	coragarden.com._report._dmarc	v=DMARC1;

# Setup DKIM Records For New Domain
mkdir /usr/local/etc/mail/keys/coragarden.com

# In this example we create a random selector but it is more important that the selector be unique and contain information
# to identify department date and key length.

Example: Like 2025-coragarden-sales-2024b or 2025-coragarden

# Generate a random DKIM selector -
openssl rand -base64 23
23 char Cy9jmkzC2USPfcJ4JIuzA07rpB0gSbA=
20 char Cy9jmkzC2USPfcJ4JIuzA07rpB0gS

opendkim-genkey -b 2048 -d coragarden.com -D /usr/local/etc/mail/keys/coragarden.com -s Cy9jmkzC2USPfcJ4JIuzA07rpB0gS -v

# new example without the subdirectory - modify path as needed below
opendkim-genkey -b 2048 -d coragarden.com -D /usr/local/etc/mail/keys -s 2025-coragarden -v

chown -R opendkim:opendkim /usr/local/etc/mail/keys
find /usr/local/etc/mail/keys -type d -exec chmod 500 {} \;
find /usr/local/etc/mail/keys -type f -exec chmod 400 {} \;

cat /usr/local/etc/mail/keys/coragarden.com/*.txt

# new example - modify your key path as needed since we removed a directory level
cat /usr/local/etc/mail/keys/2025-coragarden.txt

# Copy the keys to your name server record and remember to remove the extrs quotes and spaces in 2 sections of the DKIM line.

# Add it to the keytable file.

cat /usr/local/etc/mail/keys/coragarden.com/*.txt >> /usr/local/etc/mail/keytable

nano /usr/local/etc/mail/keytable
# ---
CRHdAnOqqitUaWRuNkHLdIpbgw76._domainkey.okbsd.com okbsd.com:CRHdAnOqqitUaWRuNkHLdIpbgw76:/usr/local/etc/mail/keys/okbsd.com/CRHdAnOqqitUaWRuNkHLdIpbgw76.private
Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com coragarden.com:Cy9jmkzC2USPfcJ4JIuzA07rpB0gS:/usr/local/etc/mail/keys/coragarden.com/Cy9jmkzC2USPfcJ4JIuzA07rpB0gS.private
# ---

# new example
2025-coragarden._domainkey.coragarden.com coragarden.com:2025-coragarden:/usr/local/etc/mail/keys/2025-coragarden.private

nano /usr/local/etc/mail/signingtable
# ---
*@okbsd.com CRHdAnOqqitUaWRuNkHLdIpbgw76._domainkey.okbsd.com
*@coragarden.com Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com
# ---

# new example
*@coragarden.com 2025-coragarden._domainkey.coragarden.com

service milter-opendkim restart

# By the time this is created the DNS has hopefully propagated and can be tested, test the key

opendkim-testkey -d coragarden.com -s Cy9jmkzC2USPfcJ4JIuzA07rpB0gS -vvv -x /usr/local/etc/mail/opendkim.conf
opendkim-testkey: checking key 'Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com'
opendkim-testkey: key secure
opendkim-testkey: key OK

# new example
opendkim-testkey -d coragarden.com -s 2025-coragarden  -vvv -x /usr/local/etc/mail/opendkim.conf

# Setup Domain in PostfixAdmin

Domain List -> New Domain
Domain: coragarden.com
Description: gardening site
Pass expires : 3650
Add Domain

Virtual List - Add Mailbox
vuser		@coragarden.com
Add User

Virtual List - redirect the default email addresses to a valid account (postmaster@domain.tld is recommended)
I changed all of mine to point to admin@okbsd.com

# While you could add another domain to your webserver it is easier to just login with roundcube at the okbsd.com
# address. If you want to use roundcube with new domain name setup apache and letsencrypt certs for the new domain.
# You could also put multiple domains in one cert and use ServerAlias in the same virtual host configuration.

# See step 2 if you want to add more certs to the same virtual host. At this point I decided to separate the mx mail
# to a separate directory from the gereral website directory.

mkdir -p /var/www/mx.okbsd/html

# move the autodiscover autoconfig
cd /var/www/okbsd/html
mv /var/www/okbsd/html/mail /var/www/mx.okbsd/html

nano /usr/local/etc/apache24/Includes/10-mx.okbsd.conf
# ---
  ...
  ServerAlias mx.coragarden.com
  ServerAlias mail.coragarden.com
  ServerAlias autoconfig.coragarden.com
  ServerAlias autodiscover.coragarden.com

  ServerAdmin postmaster@okbsd.com

  DocumentRoot /var/www/mx.okbsd/html
  <Directory /var/www/mx.okbsd/html/>
  ...

  # autoconfig
  Alias /mail "/var/www/mx.okbsd/html/mail"
# ---

nano /usr/local/etc/apache24/Includes/10-mx.okbsd-le-ssl.conf
# ---
  ...
  ServerAlias mx.coragarden.com
  ServerAlias mail.coragarden.com
  ServerAlias autoconfig.coragarden.com
  ServerAlias autodiscover.coragarden.com

  ...
  DocumentRoot /var/www/mx.okbsd/html
  <Directory /var/www/mx.okbsd/html/>
  ...

  ...
  Alias /mail "/var/www/mx.okbsd/html/mail"
  Alias /Autodiscover/Autodiscover.xml "/var/www/mx.okbsd/html/mail/Autodiscover.xml"

# ---

nano /var/www/mx.okbsd/html/index.php
# ---
<?php

print "<!DOCTYPE html lang=\"en\">
<html>
<head>
 <title>OKBSD &amp; CoraGarden Home Page</title></head>
<body>
<h1>Home Page</h1>

It works!!!

</body>
</html>\n";

?>
# ---

# Restart apache with the new configurations or the certbot request will fail

apachectl restart

# Test you can access the website certbot will challenge. I had used old apache syntax for allow,deny and it was 
# failing. Once apache configuation was fixed everything worked as expected.

https://mx.coragarden.com

certbot certonly --webroot --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okbsd.com --cert-name mx.okbsd.com \
-w /var/www/mx.okbsd/html -d mx.okbsd.com,mail.okbsd.com \
-w /var/www/mx.okbsd/html -d mx.coragarden.com,mail.coragarden.com

(U)pdate certificate/(C)ancel: U
Renewing an existing certificate for mx.okbsd.com and 3 more domains

# Restart apache
apachectl restart

# Test the new domain https://mx.coragarden.com/webmail/

# Login with the new account and test email
Login: vmail@coragarden.com
Password: ***********

# In my case the email was sent and recieved but no DKIM signature. I had forgot to add the new domain to
# the signingtable and do  service milter-opendkim restart

nano /usr/local/etc/mail/signingtable
# ---
# Domain yourdomain.org
# *@okfigs.com 20250407._domainkey.okfigs.com
# You can specify multiple domains
# Example.net www._domainkey.example.net
*@okbsd.com CRHdAnOqqitUaWRuNkHLdIpbgw76._domainkey.okbsd.com
*@coragarden.com Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com
# ---

service milter-opendkim restart

# Check the source and the headers now have DKIM signature something like ...
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=coragarden.com;
	s=Cy9jmkzC2USPfcJ4JIuzA07rpB0gS; t=1747783045;
	bh=wiMQ3UTGd/zMN6+PeBSiAAUHXfKRtjj7J2UI7ZvJA+A=;
	h=Date:From:To:Subject:From;
	b=bkk87+VHY7HvSb1b9mq0b9bLc0XBMOzf6CnBMLcoLMAZ0yN1nDyrILNj9RiYUCJsq
	 hC6QsHu9S4t9Y8q85AoszhY78ddzfU8SLcg/IlTmWuiNWisrkKjAZ9ftPEtxVxkYKZ
	 VnpSveCK4O5Gw==

# Change password worked, but setting identity had an error in roundcube. Fix as follows.

# removed 'twofactor_gauthenticator', wasn't working right

cd /usr/local/www/roundcube/plugins/enigma
cp config.inc.php.dist config.inc.php
$config['enigma_pgp_homedir'] = "/usr/local/www/roundcube/plugins/enigma/home";
mkdir /usr/local/www/roundcube/plugins/enigma/home
chown www:www /usr/local/www/roundcube/plugins/enigma/home
chmod 750 /usr/local/www/roundcube/plugins/enigma/home
pkg install gnupg

07 RoundCube WebMail <- Intro -> 09 Blocking Spam