User Tools

Site Tools


computing:mailserver-trixie

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
computing:mailserver-trixie [2025/05/31 18:39] oemb1905computing:mailserver-trixie [2025/11/09 05:46] (current) oemb1905
Line 11: Line 11:
 ------------------------------------------- -------------------------------------------
  
-This tutorial is for users of Debian GNU/Linux who want to set up a proper email serverThis particular version is a work in progress and will contain adjustments needed for TrixieSo far, I've found the following differences:+  * [[https://nextcloud.haacksnetworking.org/index.php/s/s6wAL5p7jKHc9Db|Slides]] 
 +  * [[https://content.haacksnetworking.org/w/nsMwnJhLnfMrs17W5cAdWg|SeaGL Presentation]] 
 +  * [[https://tech.haacksnetworking.org/2025/06/10/your-email-your-rules-self-hosting-simplified/|Blog Post]]
  
-  # Editing authentication settings +====== Introduction ====== 
-  sudo nano /etc/dovecot/conf.d/10-auth.conf +~~NOTOC~~
-  auth_username_format %{user|username|lower} +
-  #auth_username_format %n +
-  # Editing mailbox location settings +
-  sudo nano /etc/dovecot/conf.d/10-mail.conf +
-  mail_driver maildir +
-  mail_path ~/Maildir +
-  mail_inbox_path ~/Maildir/.INBOX +
-  #old one was +
-  #mail_location mbox:~/mail:INBOX=/var/mail/%u +
-  #mail_location maildir:~/Maildir +
-  # Editing SSL/TLS settings +
-  sudo nano /etc/dovecot/conf.d/10-ssl.conf +
-  #new format for cert and key, longer name, removes the classic “<” character +
-  ssl_server_cert_file /etc/letsencrypt/live/mail.outsidebox.club/fullchain.pem +
-  ssl_server_key_file /etc/letsencrypt/live/mail.outsidebox.club/privkey.pem +
-  #comment out diffy helman, now deprecated +
-  #ssl_server_dh_file /etc/dovecot/dh.pem +
-  # Editing Sieve filter settings +
-  sudo nano /etc/dovecot/conf.d/90-sieve.conf +
-  #updating where dovecot looks for sieve rules by default, new format +
-  sieve_script personal { +
-    driver file +
-    path = ~/sieve +
-    active_path = ~/.dovecot.sieve +
-  } +
-  #old one was +
-  #plugin {  +
-  #sieve = file:~/sieve;active=~/.dovecot.sieve +
-  #} +
-  # Editing main Dovecot configuration +
-  sudo nano /etc/dovecot/dovecot.conf +
-  #declare versions +
-  dovecot_storage_version = 2.4.1 +
-  dovecot_config_version = 2.4 +
-  #comment out dictionary (unless you set it up) +
-  #dict { +
-  #  quota = file:/var/lib/dovecot/quota +
-  #}+
  
-Over the weeks and months ahead, I will merge the changes documented above into the body of the tutorial belowIt will be some months before mergehoweversince Trixie is not yet stable and there will likely be more changes on the server I am testing in productionBelowplease find the Bookworm tutorial+Contrary to popular belief, it's entirely possible to self-host email servers. Like others, I listened to the propaganda that "it's no longer feasible to self-host email" or "it's too complex and servers won't respect your mail health anyway" or other such explanations. In 2014, while running workshops for students on security and networking, one of my student's parents (a Ruby dev) said he agreed with me and that as far as he knew, postfix was fairly straightforward. From that day forward, I decided to approach self-hosting email servers the same way as I approach self-hosting any instance ([[https://nextcloud.haacksnetworking.org|Nextcloud]], [[https://music.outsidebox.club|Airsonic]], [[https://repo.haacksnetworking.org|Gitlab]], etc.). I decided that it must be entirely possible and that it was merely a question of how. Sofrom 2014 - 2018from Wheezy to Buster, I began using my spare hacking time to create my first smtp relay in 2015 and later setup my first proper email server in 2018As it turns outit is relatively straight forward to setup a functional base server. Getting the ecosystem to respect your email, however, takes a little tender care. It's entirely doable though[[https://mail.haacksnetworking.org|My Business Webmail]].
  
-This tutorial assumes you know how to set up AAAAASPFDKIMDMARCMX, and PTR recordsSet an A record for example.org and mail.example.org and make sure you or your ISP has set PTR record to mail.example.org for the IPv4 and IPv6 addressesIf you don't know howthen learn up, and do not proceed//Thanks to LinuxBabe for a great jumping off point//. Let's begin by editing our hosts file ''sudo nano /etc/hosts'' as follows:+I did not migrate my personal emails and/or business infrastructure until 2021. During 2018 - 2021I would intermittently testidentify and fix failuresbreakagesand read up more on DNS records and worked to gain a deeper understanding of the ecosystem. I spent countless hobbyist hours reviewing forumsStack Exchange, and, of course, Linux BabeI also shared notes and perspectives with a local colleague and fellow IT/networking professional, [[http://schaeferconsulting.com|Schaefer Consulting]]. I was not in any rush to migrate, and I also wanted to develop a system that balanced complexity with reliability/convenience. After initially developing a [[https://wiki.haacksnetworking.org/doku.php?id=computing:exim4|mail relay recipe]] both under/alongside Schaefer and on my own, I ultimately decided to switch to postfix for incoming/outgoing, or what I call proper email server (not merely a relay or send-only MTA)This was pure chance, namely, as the first server recipe I got working for IMAP/dovecot was on my postfix VPS not the exim4 VPS, so I simply got motivated to keep fixing it until it all workedUntil that timecirca 2018I was tinkering back and forth on two different VPSs, one with exim4 and another with postfix, testing different strategiesTo this day, I continue to use exim4 for relaying email from hosts behind NAT. For proper email servers, I currently use postfix. In getting everything to work, my goal was to only increase complexity if/when it was required for proper functioning. For this reason, I chose to use simple UNIX users.
  
-  127.0.1.1 example.org example +{{ :computing:screenshot_from_2025-11-04_20-48-13.png?direct&800 |}}
-  127.0.0.1 mail.example.org localhost+
  
-Install postfix and mailutils ''sudo apt-get install mailutils postfix -y'' picking ''Internet Site'' and set your domain to ''example.org''.+Results: [[https://www.mail-tester.com/test-8hia4koy2|Mail Tester Results]]
  
-Install firewallopen common ports for front facing website, and for imap/smtp:+In my casethe servers I built are on VMs that reside on a [[https://wiki.haacksnetworking.org/doku.php?id=computing:vmserver|custom virtualization stack]] that uses virsh and kvm/qemu on a Debian SuperMicro server (Xeon Silvers) that I co-locate at Brown Rice data center. My virtualization stack has roughly 30 VMs at presentwith the host boasting over 500 virtual cores and 384GB of RAM. My primary business email server, for haacksnetworking.org, is an 8-core Virtual Machine with 8GB of RAM that resides on that same SuperMicro. Remember, though, as long as your VPS host provides PTR (reverse DNS) access, you can do this on a very basic ($5-$10 per month) VPS. My Taos server is also the proud host of the following services:
  
-  sudo apt install ufw +  * **Mastodon**: [[https://gnulinux.social|GNU/Linux Social]] 
-  sudo ufw allow 22/tcp +  * **Matrix**: [[https://gnulinux.club|GNU/Linux Club]] 
-  sudo ufw allow 25/tcp +  * **PeerTube**: [[https://gnulinux.tube|GNU/Linux Tube]] 
-  sudo ufw allow 587/tcp +  * **Navidrome**: [[https://gnulinux.studio|GNU/Linux Studio]] 
-  sudo ufw allow 143/tcp +  * **Pixelfed**(forthcoming)
-  sudo ufw allow 465/tcp +
-  sudo ufw allow 993/tcp +
-  sudo ufw allow 80 +
-  sudo ufw allow 443 +
-   +
-Increase quota / message size:+
  
-  sudo postconf -e message_size_limit=52428800+This particular tutorial is [[https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en|CC BY-NC-SA 4.0]] instead of FDL 1.3 (like the majority of posts on this Wiki). If you like the tutorials and/or services I provide, please consider giving back at [[https://liberapay.com/oemb1905/|Libera Pay]]. Alright, let's get this email server set up!
  
-Set hostname and aliases in ''sudo nano /etc/postfix/main.cf'' and make sure that the hostname, origin, destination, mailbox size, and quota are set. Also, in my case, I only have ipv4 support, so explicitly set that as well. +====== Part - DNS Records ======
-   +
-  myhostname mail.example.com +
-  myorigin /etc/mailname +
-  mydestination example.com, $myhostname, localhost.$mydomain, localhost +
-  mailbox_size_limit +
-  inet_protocols ipv4 +
-  message_size_limit 52428800 +
-   +
-Let's also make sure that system emails are sent to the user we created above instead of root by ''sudo nano /etc/aliases'' and then:+
  
-  postmaster: root +Ultimately, I chose postfix for delivery, dovecot for IMAP/LMTP, and eventually added a LAMP stack for Roundcude serving. (Make sure you know how to set up a basic LAMP stackApache Survival.) I only added Roundcube last year! I am a devout Thunderbird user so there was little reason for this at the outset. Howeverspam and the need to easily set spam rules for different accounts ultimately changed this sentiment after 5 years of using client-side spam filtering. This recipe is designed for 20-100 users tops. After that, you would want some form of graphical and backend user management (LDAP, AD, etc.). This tutorial, however, leverages simple UNIX users names for its scope and targeted audience. This is because for use-cases in the range specified above, it is both more practical and technically beneficial to manually edit and control user names and server access. It's a controlled environment where you as the sysadmin know and have rights to every email, either because there is business-level trust, or family trust. In addition to setting up a sufficiently hardened VM/VPS, the sysadmin must establish A, AAAA, MX, DMARC, SPF, and PTR records for their domain. As for the DKIM record, that'created later when the keypairs are generated. Let's review the records, why they are required and/or important, and what each part of the records mean or drive.
-  root: user +
-   +
-Now, set up the server block for your mail server'website:+
  
-  sudo nano /etc/nginx/conf.d/mail.example.com.conf +  PTR (A): 125.86.28.8.in-addr.arpa domain name pointer mail.haacksnetworking.org
-  sudo mkdir -p /usr/share/nginx/html/ +  PTR (AAAA)8.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.0.0.0.0.4.a.f.4.0.6.2.ip6.arpa domain name pointer mail.haacksnetworking.org.
-   +
-The contents looking something like:+
  
-  server { +As a data center co-locater, I have authority to cut and/or request my own PTR entries for both A and AAAA records. The examples above are the results from running host 8.28.86.125 and host 2604:fa40:0:10::18As you can see from the output, the IP address returns the domain, i.e., reverse DNS. If you don't get that output, stop and sort your PTR issue before proceeding. I do understand that not everyone co-locates and, therefore, it is imperative you discuss or research PTR with your hosting provider before committing to this tutorial. If you don't have access to change/edit PTR, your emails will not reach their destination. To limit spam, rejecting emails without PTR is standard for GoogleMicrosoft, and even small email servers because this means you can't verify that the sender's IP originates from that domain. In short, lacking this record indicates misconfiguration and/or potential spoofing/abuse of the domain. I noticed that Gmail starting rejecting all of my non-PTR emails in 2015, so that'when I began ensuring they were established. In 2024, Google began additionally requiring that senders have either an SPF or DKIM record established in order to avoid rejection. It should be noted that Google emails account for roughly 30% of the world's email ecosystem. Microsoft servers and others, moreover, employ similar policies.
-      listen 80; +
-      #listen [::]:80; +
-      server_name mail.example.com; +
-      root /usr/share/nginx/html/; +
-      location ~ /.well-known/acme-challenge { +
-        allow all; +
-     } +
-  } +
-   +
-Once that is donerestart the service ''sudo systemctl reload nginx'' and then let'generate cert:+
  
-  sudo apt install certbot +  A: 8.28.86.125 
-  sudo apt install python3-certbot-nginx +  AAAA: 2604:fa40:0:10::18
-  sudo certbot certonly -a nginx --agree-tos --no-eff-email --staple-ocsp --email email@email.com -d mail.example.com +
-   +
-Now, let's configure postfix to work together with Dovecot/submission on 587 and 465 and to use TLS by editing ''sudo nano /etc/postfix/master.cf'' as follows:+
  
-  submission     inet        -    y    -    -    smtpd +Creating A and AAAA records are fairly straightforward. Just ensure that your VM/VPS has those addresses established in your interfaces file if using ifupdownand established in your yaml configuration if using netplan. I prefer ifupdown and I bridge the ipv6 and ipv4 routes to the VMs (e.g.mail.haacksnetworking.org) using virsh and kvm/qemu. As I mentioned earlierthese interfaces get their routes from my virtualization environmentbut you can just as easily do all of this on a VPS host that supports PTR. Ifhowever, you are also interested in setting up your own hosting environment, you can review my Virtualization Stack design.
-    -o syslog_name=postfix/submission +
-    -o smtpd_tls_security_level=encrypt +
-    -o smtpd_tls_wrappermode=no +
-    -o smtpd_sasl_auth_enable=yes +
-    -o smtpd_relay_restrictions=permit_sasl_authenticated,reject +
-    -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject +
-    -o smtpd_sasl_type=dovecot +
-    -o smtpd_sasl_path=private/auth +
-  smtps     inet  n                               smtpd +
-    -o syslog_name=postfix/smtps +
-    -o smtpd_tls_wrappermode=yes +
-    -o smtpd_sasl_auth_enable=yes +
-    -o smtpd_relay_restrictions=permit_sasl_authenticated,reject +
-    -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject +
-    -o smtpd_sasl_type=dovecot +
-    -o smtpd_sasl_path=private/auth+
  
-It's now time to configure postfix ''sudo nano /etc/postfix/main.cf'' to use TLS:+  MX (domain.tld)0 mail.haacksnetworking.org 
 +  MX (mail.domain.tld): 10 mail.haacksnetworking.org
  
-  #Enable TLS Encryption when Postfix receives incoming emails +Establishing MX records and priority is essentialIn my case, I establish an MX record for ''haacksnetworking.org'' with the highest priority (0) and ''mail.haacksnetworking.org'' with the second highest priority (10)Having both the domain and subdomain MX records established helps ensure redundancy and/or deliverabilityThe primary record is obviously a hard requirement; the subdomain MX record covers verificationscompliance and error handlingFor exampleGmail servers will query subdomain MX during SMTP handshakes and DMARC/SPF alignment checksand lacking this record means there is no way to validate the target domain via the root MX.
-  smtpd_tls_cert_file=/etc/letsencrypt/live/mail.example.com/fullchain.pem +
-  smtpd_tls_key_file=/etc/letsencrypt/live/mail.example.com/privkey.pem +
-  smtpd_tls_security_level=may  +
-  smtpd_tls_loglevel = 1 +
-  smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache +
-  #Enable TLS Encryption when Postfix sends outgoing emails +
-  smtp_tls_security_level = may +
-  smtp_tls_loglevel = 1 +
-  smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache +
-  #Enforce TLSv1.3 or TLSv1.+
-  smtpd_tls_mandatory_protocols = !SSLv2!SSLv3, !TLSv1, !TLSv1.+
-  smtpd_tls_protocols = !SSLv2!SSLv3, !TLSv1, !TLSv1.1 +
-  smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 +
-  smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1!TLSv1.1+
  
-Now, let's configure and enable SASL supportOpen ''/etc/postfix/main.cf'' and enter:+  DMARC (domain.tld): v=DMARC1; p=none; fo=1; pct=100; rua=mailto:dmarc@oemb1905 
 +  DMARC (mail.domain.tld): v=DMARC1; p=none; fo=1; pct=100; rua=mailto:dmarc@oemb1905
  
-  # SASL Authentication with Dovecot +The dmarc record, or domain-based message authentication reporting and conformance record, is a TXT policy that governs how failures (against SPF/DKIM) are handled and how domain owners are notified. I strongly recommend that you keep p=none, which means that your policy is to monitor only, instead of rejecting and/or quarantining failures. It is crucial that you keep records and mail server policies as lenient and permissive as possible during initial setup (or even indefinitely). This eliminates the potential for phantom rejections, i.e., being unable to pinpoint why an email failed to arrive in your server. Once you've mastered this setup and ensured that your base setup has no failures for a reasonably large period of time, it is okay to consider adjusting this flag to be more restrictive (e.g., quarantine, reject). The ''fo=1'' instructs recipient servers to email me at the specified address if and when either SPF or DKIM records fail. Using ''fo=0'' will only notify the domain owner if both fail simultaneously and using fo=d or ''fo=s'' are for individually reporting DKIM and SPF failures, respectively. I recommend starting with ''fo=1'' so you can receive reports any time either record fails. For ease, make sure that the destination email you specify in the DMARC record uses the same domain. If you are unable to do that, you must specify a TXT record on the target domain for ''v=DMARC1;'' for the receiving subdomain ''targetdomain.org._report._dmarc.'' If you do not do this, servers will not send you reports since they will be unable to authenticate the destination email as being a valid target. This is because common servers, for example Gmail, will perform a verification record check (RFC 7489 Section 7.2.1, etc.) in order to ensure their dmarc reports are not being sent to unintended recipients.
-  smtpd_sasl_auth_enable yes +
-  smtpd_sasl_type dovecot +
-  smtpd_sasl_path private/auth +
-  smtpd_sasl_security_options noanonymous+
  
-Now, we can install dovecot and configure it to use IMAP and lmtpInstall the packages with ''sudo apt install dovecot-core dovecot-imapd dovecot-lmtpd'' and then edit ''sudo nano /etc/dovecot/dovecot.conf'':+  SPF (domain.tld): v=spf1 a mx ip4:8.28.86.125 ip6:2a10:e780:10:28::2 ~all 
 +  SPF (mail.domain.tld): v=spf1 a mx ip4:8.28.86.125 ip6:2a10:e780:10:28::2 ~all
  
-  <protocols imap lmtp>+The SPF record, or sender policy framework, instructs mail servers about which IPs are permitted to send email from a given domain. I include the a flag for redundancy and or for safety. Allowing any ''A'' record in the domain to send using this domain is very permissive and also helps in case you misconfigure an explicit record while testing. The mx flag ensures that email sent from my own MX servers is explicitly permitted. Although not essentially required to include ''a'' or ''mx'' if your server's IP is specified, I find it helpful to provide extra resilience for cases where you change or scale. For example, if I decide to add a backup mail server, the mx flag automatically approves it, i.e., I don't need to change the record each time. If I migrate to a different node and/or IP, the A specification allows sending immediately and I can edit the individual TXT records later. So, specifying all three together (''A'', ''MX'', and IPs) is beneficial for updating, changing, or scaling up your email offering. Additionally, bounces are often sent from the ''mx'' server, not the outbound IP, which could potentially differ from the IP of the primary MTA. Some email servers, moreover, compare spf IP to sender domain, so including a and mx alongside the IPs provides full alignment and therefore better email health. As for the ''~all'', I use that because it specifies "soft fail" if an origin IP does not align. This is important because mistakes happen and sysadmins don't want phantom rejections. If you instead set this flag to ''-all'' (reject aka hard fail), then recipients won't receive emails if you configured an IP or DNS record in error. Conversely, setting the flag to ''+all'' defeats the purpose of the record by permitting everything. The soft fail flag ''~all'' allows sysadmins to identify misconfigurations and fix them, while ensuring that no emails are auto-rejected by the recipient due to your own spf policy. Of course, after sufficient testing, you can optionally change this flag to hard fail, but proceed with caution. The ''pct'' flag controls what portion of emails should be evaluated by ''dmarc'', which I keep at 100%. By specifying 100%, ''pct=100'', you are stating that all your emails should be checked. In thus testing the whole population (instead of a random sample) of your emails, you increase your own chances of failing merely by increasing the number of evaluated emails. However, it is important to remember that mail servers rarely reject incoming email due to dmarc failure alone (almost never), so just because you increase dmarc failures, those won't result in rejections per se. As long as all the other records are solid, I prefer to error on the side of getting more reports and every email evaluated, rather than permitting phantom allowances.
  
-After that, open ''sudo nano /etc/dovecot/conf.d/10-mail.conf'' and change the default mail director location as follows:+====== Part II Installing Postfix+Dovecot, Setting up LAMP ======
  
-  <mail_location = maildir:~/Maildir>+The first part of the tutorial, i.e., setting up DNS records, is now complete. It's now time to address installation and setup of postfix+dovecot. Do not proceed to this part of the tutorial unless you completed the DNS steps above. Put simply, postfix+dovecot both require proper DNS resolution to work. The only exception to this rule is for our DKIM record, which requires us first configuring the server before we cut the keypair and create the associated TXT record. Other than that, make sure DNS is ready to go before proceeding. Okay, let's ssh into the VM/VPS and do the following:
  
-Let's make sure dovecot is part of the mail group, including any users you intend to use email:+  sudo apt update && sudo apt upgrade -y 
 +  sudo apt install mailutils postfix ufw fail2ban nginx apache2 php8.4-fpm php8.4-mysql php8.4-curl php8.4-gd php8.4-mbstring php8.4-xml php8.4-zip dovecot-core dovecot-imapd dovecot-lmtpd
  
-  sudo adduser dovecot mail +It's also important that that the host knows how to identify itself properly. Let's open it up ''/etc/hosts'' and make it is setup correctly. 
-  sudo adduser username mail+ 
 +<code bash> 
 +127.0.0.1 localhost 
 +127.0.1.1 mail.haacksnetworking.org haacksnetworking 
 +::1 localhost ip6-localhost ip6-loopback 
 +ff02::1 ip6-allnodes 
 +ff02::2 ip6-allrouters 
 +8.28.86.125 mail.haacksnetworking.org localhost 
 +</code> 
 + 
 +Make sure that /etc/hostname and /etc/mailname are both set to mail.domain.com. The last line ensures that local processes will resolve the fully qualified domain name of the host to the external IP instantly and without the need for external DNS. In short, you are declaring that the fqdn is always 8.28.86.125. Next, let's install common mail utilities and postfix. Remember, postfix is going to be used for SMTP and/or as the outgoing mail server. 
 + 
 +  sudo apt-get install mailutils postfix -y
      
-Now we can configure dovecot over at ''sudo nano /etc/dovecot/conf.d/10-master.conf'' in order to be able to leverage lmtp:+{{ :computing:postfix1.png?direct&600 |}}
  
-  service lmtp { +For the mail name, put haacksnetworking.org or domain.com.
-    unix_listener /var/spool/postfix/private/dovecot-lmtp { +
-     mode = 0600 +
-     user = postfix +
-     group = postfix +
-    } +
-   }+
  
-Similarly, we need to edit postfix for lmtp as well with ''sudo nano /etc/postfix/main.cf'' and then specifying:+{{ :computing:postfix2.png?direct&600 |}}
  
-  mailbox_transport = lmtp:unix:private/dovecot-lmtp +Leave most fields at default values; make sure other destinations populated correctly.
-  smtputf8_enable = no+
  
-Next, let's configure dovecot authorization with ''sudo nano /etc/dovecot/conf.d/10-auth.conf'' plain login as follows:+{{ :computing:postfix3.png?direct&600 |}}
  
-  disable_plaintext_auth = yes +{{ :computing:postfix4.png?direct&600 |}}
-  auth_username_format = %n +
-  auth_mechanisms = plain login +
-   +
-Now, configure SSL/TLS encryption in dovecot using your website/domain certs from earlier with ''sudo nano /etc/dovecot/conf.d/10-ssl.conf'':+
  
-  ssl = required +{{ :computing:postfix5.png?direct&600 |}} 
-  ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem + 
-  ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem +{{ :computing:postfix6.png?direct&600 |}} 
-  ssl_prefer_server_ciphers = yes + 
-  ssl_min_protocol TLSv1.2+Do not select All unless you have properly configured records and interfaces for both. Only select and specify what you have records for, otherwise they will fail if they hop to the unsupported protocol. I speak from direct experience. 
 + 
 +{{ :computing:postfix7.png?direct&600 |}} 
 + 
 +For the mail name, put ''haacksnetworking.org'' or ''domain.com''. Leave most fields at default values; make sure other destinations populated correctly. Do not select All unless you have properly configured records and interfaces for both. Only select and specify what you have records for, otherwise they will fail if they hop to the unsupported protocol. I speak from direct experience. Now that a basic postfix setup is in place, you can optionally install a firewall. If you choose to do this, I recommend the Uncomplicated Firewall, or ufw. Install it and open up all the ports required by postfix+dovecot and no others. 
 + 
 +  sudo apt install ufw 
 +  sudo ufw allow 22/tcp 25/tcp 587/tcp 143/tcp 465/tcp 993/tcp 80 443 4190 
 +  sudo ufw enable 
 + 
 +You should also setup fail2ban, which is outside the scope of this tutorial, but refer to my write-up if you need help! Next, I specified the incoming mail size quota, which I set to 50MBThis is plenty large for attachments and most folks know to use DAV or something else instead if they need to send more and/or larger attachments, etc.  
 + 
 +  sudo postconf -e message_size_limit=52428800
      
-Set up the SASL listener by editing ''sudo nano /etc/dovecot/conf.d/10-master.conf'' and adding this block:+Next, I edited the aliases file in order for messages internal to the host to get routed to an external address. Presuming that you've created the user webmaster on the mail server, all messages will for postmaster and/or root, will be delivered to ''webmaster@haacksnetworking.org'' or ''webmaster@domain.com''
 + 
 +  sudo nano /etc/aliases 
 +  <postmaster: root> 
 +  <root: webmaster> 
 +  sudo newaliases 
 + 
 +Make sure that 00-default.conf in ''/etc/apache2/sites-enabled/'' has the ''ServerName'', ''ServerAdmin'', and ''DocumentRoot'' parameters specified for the ''domain.com'', or ''haacksnetworking.org''. Additionally, make a new vhost, ''mail.haacksnetworking.org.conf'' or ''mail.domain.com'' for what will later be the Roundcube instance. Once both sites are enabled with ''a2ensite'', you can proceed to setting up TLS with Let's Encrypt on both. Trust me when I say that setting up the default vhost and using this command below - and then later editing the Roundcube vhost config - is far simpler than doing stand-alone and/or trying to directly add the cert to the reverse proxy vhost. 
 + 
 +  sudo apt install certbot letsencrypt python3-certbot-apache\ 
 +  sudo certbot --authenticator standalone --installer apache -d domain.com --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2" 
 + 
 +Make sure to create ''vhost-mail.conf'' and enable it with ''a2ensite'' for the ''mail.haacksnetworking.org'' or ''mail.domain.com'' and run the same ''certbot'' command again for Let's Encrypt to create a cert for this subdomain as well. You need to specify and create a different webroot for ''ServerName'', ''ServerAdmin'', and ''DocumentRoot'' this subdomain, just like the parent domain. Don't run the certbot commands on both domains together as it might interpret that command as being wildcards and/or nested certificates (e.g., multi-site WordPress). When you installed postfix, it automatically populated the global config file over in ''/etc/postfix/main.cf'' with the specified choices. It's now time to configure individual services for postfix (smtpd, submission, etc.), so open up ''/etc/postfix/master.cf'' and add these blocks where appropriate. You can view an actual config of mine in the link below this block. 
 + 
 +<code bash> 
 +submission inet n - y - - smtpd 
 +  -o syslog_name=postfix/submission 
 +  -o smtpd_tls_security_level=encrypt 
 +  -o smtpd_tls_wrappermode=no 
 +  -o smtpd_sasl_auth_enable=yes 
 +  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject 
 +  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject 
 +  -o smtpd_sasl_type=dovecot 
 +  -o smtpd_sasl_path=private/auth 
 +smtps inet n - y - - smtpd 
 +  -o syslog_name=postfix/smtps 
 +  -o smtpd_tls_wrappermode=yes 
 +  -o smtpd_sasl_auth_enable=yes 
 +  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject 
 +  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject 
 +  -o smtpd_sasl_type=dovecot 
 +  -o smtpd_sasl_path=private/auth 
 +</code> 
 + 
 +Download [[https://repo.haacksnetworking.org/haacknet/haackingclub/-/blob/master/configs/mailservers/postfix/master.cf?ref_type=heads|master.cf]]  
 + 
 +The changes above add service definitions and configurations for smtps (465) and submission (587), both of which handle mail submission, or the sending of email. Examples of these configurations can be found in ''/usr/share/doc/postfix/examples/''. Submission is the newer protocol, but I retain smtps for compatibility with older clients. Once that's done, it's time to edit ''/etc/postfix/main.cf'' to account for the TLS certificate we just cut with Let's Encrypt a few steps earlier. We can additionally specify for postfix to use auth mechanisms that leverage this cert and are consistent with the services setup in master.cf. Let's open ''/etc/postfix/main.cf'' and review/add/edit the following: 
 + 
 +<code bash> 
 +# A) Leave the following upper-block defaults 
 +smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) 
 +biff = no 
 +append_dot_mydomain = no 
 +compatibility_level = 2 
 + 
 +# B) TLS parameters - comment out the self-signed and/or redundant 
 +#smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem 
 +#smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key 
 +#smtpd_tls_security_level=may 
 +#smtp_tls_CApath=/etc/ssl/certs 
 +#smtp_tls_security_level=may 
 +#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache 
 + 
 +# C) Enable TLS Encryption for incoming mail (smtpd) using the LE pair 
 +smtpd_tls_cert_file=/etc/letsencrypt/live/mail.haacksnetworking.org/fullchain.pem 
 +smtpd_tls_key_file=/etc/letsencrypt/live/mail.haacksnetworking.org/privkey.pem 
 +smtpd_tls_security_level = may 
 +smtpd_tls_loglevel = 1 
 +smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache 
 + 
 +# D) Enable TLS Encryption for outgoing mail (smtp)  
 +smtp_tls_security_level = may 
 +smtp_tls_loglevel = 1 
 +smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache 
 + 
 +# E) Reject all incoming/outgoing protocols besides TLSv1.3 or TLSv1.2 
 +smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 
 +smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 
 +smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 
 +smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 
 + 
 +# F) Primary Block; additional services go below or in dedicated config 
 +smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination 
 +myhostname = mail.haacksnetworking.org 
 +alias_maps = hash:/etc/aliases 
 +alias_database = hash:/etc/aliases 
 +myorigin = /etc/mailname 
 +mydestination = mail.haacksnetworking.org, haacksnetworking.org, mail.haacksnetworking.org, localhost.haacksnetworking.org, localhost 
 +relayhost = 
 +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 
 +mailbox_size_limit = 0 
 +recipient_delimiter = + 
 +inet_interfaces = all 
 +inet_protocols = all 
 +message_size_limit = 52428800 
 +</code> 
 + 
 +Download [[https://repo.haacksnetworking.org/haacknet/haackingclub/-/blob/master/configs/mailservers/postfix/main.cf?ref_type=heads|main.cf]] 
 + 
 +The options above have comments that describe each block. The block which rejects old protocols was taken from Linux Babe, but the basis for that is covered in the Postfix documentation. Using the Let's Encrypt keypair is straightforward as are most of the lines in the primary block. This base config reflects months and even years of fine-tuning. It's tested and reilable. It's now time to install dovecot, which is our IMAP server for incoming email. We will have to return to postfix's main.cf later and specify that we are using dovecot. But, to do that, we need to install dovecot, which is our incoming mail server (imap). 
 + 
 +  sudo apt install dovecot-core dovecot-imapd dovecot-lmtpd 
 + 
 +In order for dovecot to work, we need to specify which protocols we allow, the mail directory location, ensure that dovecot is a member of the mail group, and enable lmtp the for delivery agent. Lmtp is a local delivery agent and hands off processed incoming emails to the storage directories without requiring external authentication. This ensures everything is delivered locally, or within the hardened host. Larger distributed email systems require smtp for network hand offs between servers, but that's not required or helpful for mail server self-hosters. Open up ''/etc/dovecot/dovecot.conf'' and specify the protocols. It is also required to specfify the storage and config versions (updated for Trixie): 
 + 
 +<code bash> 
 +protocols = imap lmtpdovecot_storage_version = 2.4.1 
 +dovecot_config_version = 2.4 
 +# If you upgraded from Bookworm, comment out dictionary 
 +# dict { 
 +#   quota = file:/var/lib/dovecot/quota 
 +# } 
 +</code> 
 + 
 +Next, we need to edit /etc/dovecot/conf.d/10-mail.conf (updated for Trixie): 
 + 
 + 
 +<code bash> 
 +mail_driver = maildirmail_path = ~/Maildirmail_inbox_path = ~/Maildir/.INBOX 
 +</code> 
 + 
 +Make sure that the dovecot user and the simple UNIX users are both part of the mail group. Remember, this tutorial does not use virtual users and/or an associated database. This tutorial uses simple UNIX user names which means the yourusername listed below will have the email ''yourusername@haacksnetworking.org'', or ''yourusername@domain.com''
 + 
 +  sudo adduser yourusernamesudo adduser dovecot mailsudo adduser yourusername mail 
 + 
 +It is required that lmtp be configured for use with dovecot. To do that, edit ''/etc/dovecot/conf.d/10-master.conf'':
  
-  service auth +<code bash> 
-    unix_listener /var/spool/postfix/private/auth +service lmtp 
-      mode = 0660 +  unix_listener /var/spool/postfix/private/dovecot-lmtp 
-      user = postfix +    mode = 0600 
-      group = postfix +    user = postfix 
-    }+    group = postfix
   }   }
-   +} 
-If you have errors or can't connect your email client at this point, you can test your handshakes as follows:+</code>
  
-  openssl s_client -connect mail.example.com:465 +Postfix also needs to know the delivery agent that's being used, otherwise it won't know what service to which it should send incoming email. In short, lmtp hands off emails from postfix'mail queue to dovecot's mail storage. This is done via a socket, so that needs to be configured in the main postfix configuration. Open up ''/etc/postfix/main.cf'' and define the socket as follows. It is recommended that you also disable UTF-8 support in headers and addresses so that legacy mail servers can handle emails from your server.
-  openssl s_client -starttls smtp -connect mail.example.com:25 +
-   +
-Now it is time to setup an spf policy agent so that the incoming email that is received checks for validity of spf records**Do not confuse this with creating an spf TXT record for your outgoing email.** Let'install spf policy with ''sudo apt install postfix-policyd-spf-python''and then edit ''sudo nano /etc/postfix/master.cf'' as follows+
-   +
-  policyd-spf  unix  -                               spawn +
-  user=policyd-spf argv=/usr/bin/policyd-spf +
-   +
-After that, let's set up ''sudo nano /etc/postfix/main.cf'' as follows:+
  
-  policyd-spf_time_limit = 3600 +<code bash> 
-  smtpd_recipient_restrictions = +mailbox_transport lmtp:unix:private/dovecot-lmtpsmtputf8_enable = no 
-   permit_mynetworks, +</code>
-   permit_sasl_authenticated, +
-   reject_unauth_destination, +
-   check_policy_service unix:private/policyd-spf+
  
-You also need to make sure that your spf policy is not set to reject emails by default+It's now time to instruct dovecot as to what authorization mechanisms to accept. Remember, plain login, contrary to popular belief, is not in any way insecure. Remeber that they both use Base64 encoding to begin with, which is then wrapped in TLS. Therefore, as long as you enforce TLS, there's very little security concern. Adding strong passwords with 14 or more characters and regularly rotating client credentials limits the surface vector even moreso. Okay, to setup the login mechanisms, open up ''/etc/dovecot/conf.d/10-auth.conf'' and configure the following parameters (updated for Trixie):
  
-  nano /etc/postfix-policyd-spf-python/policyd-spf.conf +<code bash> 
-   +auth_username_format = %{user|username|lower} 
-Make sure that ''Fail'' is changed to ''False'' for the top two entries. The policy will ensure that those spf violations are logged, but the change to False ensures no email is lost as a result+auth_mechanisms = plain login 
 +</code> 
 + 
 +Now, let's enforce ssl so that the plain and login mechanisms above are wrapped - end to end - in TLS handshakes. It is also important to explicitly define the least secure and/or oldest TLS handshake your server will permit. Additionally, make sure that you comment out ssl_prefer_server_ciphers and/or anything like ''ssl_server_dh'' file, both of which are now deprecated. To do this, open up ''/etc/dovecot/conf.d/10-ssl.conf'' and edit the following parameters (updated for Trixie): 
 + 
 +<code bash> 
 +ssl = required 
 +ssl_server_cert_file = /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem 
 +ssl_server_key_file = /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem 
 +ssl_min_protocol = TLSv1.2 
 +</code> 
 + 
 +Add SASL listener in ''/etc/dovecot/conf.d/10-master.conf'' so that postfix can query dovecot and this socket for user credentials for incoming email. Combined with the configuration ''smtpd_sasl_path=private/auth'' in ''main.cf'', this also allows postfix to authenticate smtp requests. In all cases, dovecot leverages the UNIX users for credentials. 
 + 
 +<code bash> 
 +service auth { 
 +  unix_listener /var/spool/postfix/private/auth { 
 +    mode = 0660 
 +    user = postfix 
 +    group = postfix 
 +  } 
 +
 +</code> 
 + 
 +Lastly, before testing, make sure that you only authorize your mynetworks and properly authenticated users. Failing to do this will mean your server could potentially be used for public relay. This block rejects any unauthenticated senders (besides localhost) and requires senders to be authenticated (or to be localhost) while only permitting incoming email directed to ''@haacksnetworking.org'' or ''@domain.com''. Please note that if you continue with the optional configurations later in this tutorial, you will integrate these stanzas into other blocks. 
 + 
 +<code bash> 
 +smtpd_sender_restrictions = permit_mynetworks, permit_sasl_authenticated, rejectsmtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination 
 +</code> 
 + 
 +At this time, the bare minimum requirements are in place for sending and receiving email. You can either fire up Thunderbird and field test it all, or use swaks, ncat, telnet, or openssl to test the TLS handshakesHere's the ones I use in addition to field testing with Thunderbird: 
 + 
 +<code bash> 
 +openssl s_client -connect mail.yourdomain.com:465openssl s_client -connect mail.yourdomain.com:587 
 +openssl s_client -connect mail.yourdomain.com:993openssl s_client -starttls smtp -connect mail.yourdomain.com:25swaks --tls --server mail.yourdomain.com -p 587swaks --tls --server mail.yourdomain.com -p 465 
 +</code> 
 + 
 +The above steps conclude what I call the base server. You must complete the testing above and ensure that your TLS handshakes and base server communications are functional before you proceed. 
 + 
 +====== Part III - DKIM for smtp, SPF/DKIM/DMARC Policy Formation ====== 
 + 
 +In the section that follows, I will cover the following: 
 + 
 +  * Outgoing Server Step: DKIM keypair for authenticating outgoing email. 
 +  * Incoming Server Steps: Create policies for SPF, DKIM, and DMARC 
 + 
 +The last outgoing server step, namely creating a DKIM keypair for smtp, will raise the server's email health to 10/10 once configured properly. Please remember to use the mail-tester.com website to test your server's outgoing health and pinpoint misconfigurations during this tutorial. The remaining incoming server refinements are how one manages incoming email according to the policies they create, including policies for spam. In this tutorial, you are taught how to create three server policies for spf, dkim, and dmarc, which spam assassin will use to rank your email. From there, I show you how to use the sieve language - either on its own via the CLI or via Roundcube's GUI - both of which instruct dovecot what to do with email based on certain conditions that spam assassin gathers about your email. 
 + 
 +As with elsewhere, this tutorial does not use any of the policies for rejecting email - everything is accepted. As your server matures and you complete testing, you can optionally harden your server's policies, but bear in mind, this means you are explicitly configuring it to reject incoming email in some cases. So, if those rules aren't perfect, then you are guaranteeing the possibility of an invalid rejection, or a false negative. In lay terms, that means you are choosing to not get emails from people that send to you. So, proceed with caution if you choose to make server-wide reject rules, either in postfix generally and/or with your policies. Personally, I don't ever do that. I want to receive any and all incoming emails. Let's start this second part by installing our spf policy with ''sudo apt install postfix-policyd-spf-python''. After it's installed, you can configure the policy daemon and socket in ''/etc/postfix/master.cf'' by adding this at the bottom of the config: 
 + 
 +<code bash> 
 +#spf policy\ 
 +policyd-spf unix - n n - 0 spawn\ 
 +user=policyd-spf argv=/usr/bin/policyd-spf 
 +</code> 
 + 
 +After that, open up ''/etc/postfix/main.cf'' and put the spf policy together with postfix's recipient restrictions. The reason I organize the block this way is because it makes logical sense to group together the criteria under which email is received, whether because it is postfix's general policy and/or your SPF policy. 
 + 
 +<code bash> 
 +#spf incoming policy and recipient restrictions\ 
 +policyd-spf_time_limit = 3600\ 
 +smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policyd-spf 
 +</code> 
 + 
 +Make sure you understand what each line does. In short, this mandates authentication (except for localhost), and does not allow use of the server for public relay, i.e., to deliver emails for other domains besides haacksnetworking.org or domain.com. Okay, I now disable the SPF policy's default behavior of rejecting emails when the HELO/EHLO hostname does not match the sender's SPF policy. I also disable the second line as well, which checks the origin IP against the SPF policy of the senderIn short, I instruct the SPF policy to log failures instead of rejecting emails because of it. This is also essential for spam assassin, which will later use this policy to identify and rank the health of incoming email, thus allowing you to create management rules for priority senders or to manage spam. Head over to ''/etc/postfix-policyd-spf-python/policyd-spf.conf'' and edit:
  
   HELO_reject = False   HELO_reject = False
   Mail_From_reject = False   Mail_From_reject = False
  
-Now, it is time to set up DKIM on your serverAfter creating the DKIM record/key on your server, you will need to create a corresponding TXT record for it to establish that anything over smtp with that signature is, in fact, you/your serverLet's install opendkim with ''sudo apt install opendkim opendkim-tools'' and add postfix to its group with ''sudo adduser postfix opendkim'' and then adjust the configuration in ''sudo nano /etc/opendkim.conf'' as follows:+Since I just took care of recipient restrictions, it makes sense to cover sender restrictionsRemember, in the base server, both recipient and sender configurations were covered in brief. If you do these recommended and optional steps, you should expand your sender restrictions block and include other forms of checking. Some of the those options are belowPlease note that similar hardening can also be done via postfix on the recipient-side as well:
  
-  Canonicalization   relaxed/simple +<code bash> 
-  Mode               sv +smtpd_sender_restrictions = 
-  SubDomains         no +permit_mynetworks, 
-  Nameservers     8.8.8.8,1.1.1.1 +permit_sasl_authenticated
-  KeyTable           refile:/etc/opendkim/key.table +#reject_unknown_reverse_client_hostname, 
-  SigningTable       refile:/etc/opendkim/signing.table +#reject_unknown_client_hostname, 
-  ExternalIgnoreList  /etc/opendkim/trusted.hosts +#reject_unknown_sender_domain, 
-  InternalHosts       /etc/opendkim/trusted.hosts+reject_unauthenticated_sender_login_mismatch, 
 +reject_sender_login_mismatch, 
 +permit 
 +</code>
  
-Now that the configuration for DKIM is readylet'create the keys and content for the locations specified above:+Now that the SPF/recipient and PTRI am now going to set up the DKIM policy for incoming email but/andI will also use the same package to cut a DKIM keypair for use with the server'outgoing email. First, I install the policy and add opendkim to the postfix group.
  
-  sudo mkdir -p /etc/opendkim/keys +<code bash> 
-  sudo chown -R opendkim:opendkim /etc/opendkim +sudo apt install opendkim opendkim-tools 
-  sudo chmod 711 /etc/opendkim/keys +sudo adduser postfix opendkim 
-   +</code>
-Once all the directories and key locations are created, let's open the signing table with ''sudo nano /etc/opendkim/signing.table'' and enter the following (without the single quotes required here due to markdown conflict):+
  
-  '*@example.com      default._domainkey.example.com' +Once that's done, it's time to open ''/etc/opendkim.conf'' and setup the configuration for the server's keypair:
-  '*@*.example.com    default._domainkey.example.com'+
  
-Now that the signing table is setupwe need to edit the key table with ''sudo nano /etc/opendkim/key.table'' and enter the following:+<code bash> 
 +Canonicalization relaxed/simple 
 +Mode sv 
 +SubDomains no 
 +Nameservers 8.8.8.8,1.1.1.1 
 +KeyTable refile:/etc/opendkim/key.table 
 +SigningTable refile:/etc/opendkim/signing.table 
 +ExternalIgnoreList /etc/opendkim/trusted.hosts 
 +InternalHosts /etc/opendkim/trusted.hosts 
 +</code>
  
-  default._domainkey.example.com     example.com:default:/etc/opendkim/keys/example.com/default.private+It's now time to create the keys:
  
-The trusted hosts is next, over in ''sudo nano /etc/opendkim/trusted.hosts'' which we simply enter:+<code bash> 
 +sudo mkdir -p /etc/opendkim/keys 
 +sudo chown -R opendkim:opendkim /etc/opendkim 
 +sudo chmod 711 /etc/opendkim/keys 
 +sudo mkdir /etc/opendkim/keys/yourdomain.com 
 +sudo opendkim-genkey -b 2048 -d yourdomain.com -D /etc/opendkim/keys/yourdomain.com -s default -v 
 +sudo chown opendkim:opendkim /etc/opendkim/keys/yourdomain.com/default.private 
 +sudo chmod 600 /etc/opendkim/keys/yourdomain.com/default.private 
 +</code>
  
-  127.0.0.1 +After creating the keys, edit the signing table so that outgoing emails can verify against the keypairOpen up ''/etc/opendkim/signing.table'' and add:
-  localhost +
-  .domain.com +
-   +
-We now need to cut the DKIM keys (and make sure to add TXT records on your DNS host later) as follows: +
-   +
-  sudo mkdir /etc/opendkim/keys/example.com +
-  sudo opendkim-genkey -b 2048 -d example.com -D /etc/opendkim/keys/example.com -s default -v +
-  sudo chown opendkim:opendkim /etc/opendkim/keys/example.com/default.private +
-  sudo chmod 600 /etc/opendkim/keys/example.com/default.private+
  
-To get the information you need for the DNS record, you can run ''sudo cat /etc/opendkim/keys/example.com/default.txt'' and then copy everything between the parentheses into your TXT record with ''default._domainkey'' as the hostAfter the DKIM TXT record caches, test it as follows:+<code bash> 
 +'*@yourdomain.com default._domainkey.yourdomain.com' 
 +'*@*.yourdomain.com default._domainkey.yourdomain.com' 
 +</code>
  
-  sudo opendkim-testkey -d example.com -s default -vvv +Similarly, in the key tableover in ''/etc/opendkim/key.table'' make sure specify the selector that your server (and associated DNS record) will use for DKIM verification:
-   +
-Note that that output will display "key not secure" unless you configure DNSSECwhich this tutorial has not done. It's now time to configure postfix to leverage this DKIM key. Let'make a directory for the socket and set up the configuration:+
  
-  sudo mkdir /var/spool/postfix/opendkim +  default._domainkey.yourdomain.com yourdomain.com:default:/etc/opendkim/keys/yourdomain.com/default.private
-  sudo chown opendkim:postfix /var/spool/postfix/opendkim +
-  sudo nano /etc/opendkim.conf +
-   +
-In the dkim config file, enter: +
-   +
-  Socket    local:/var/spool/postfix/opendkim/opendkim.sock +
-   +
-After establishing the socket directory and location, let's configure our dkim defaults in ''sudo nano /etc/default/opendkim'' and editing:+
  
-  SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"+Lastly, instruct you server to trust only localhost and your domain. Open up /etc/opendkim/trusted.hosts and enter:
  
-Lastly, we need to configure postfix to be able to use opendkim in ''sudo nano /etc/postfix/main.cf'' by editing:+<code bash> 
 +127.0.0.1\ 
 +localhost\ 
 +.yourdomain.com 
 +</code>
  
-  milter_default_action = accept +Use the built-in tool to test your DKIM keypair:
-  milter_protocol = 6 +
-  smtpd_milters = local:opendkim/opendkim.sock +
-  non_smtpd_milters = $smtpd_milters+
  
-In addition to spf and dkim policies, it's also best practice to have a dmarc policyLet'install opendmarc with ''sudo apt install opendmarc'' and make sure to say no to db configure in the ncurses prompt, after which you should open ''sudo nano /etc/opendmarc.conf'' and enter something like:+  sudo opendkim-testkey -d yourdomain.com -default -vvv
  
-  AuthservID OpenDMARC +It's now time to build your last DNS recordTo do that, you need your public key you created above. To view that on the CLIrun the cat command belowYou then take that output to your DNS host of choice, and create a TXT record containing this value. In the subdomain field, you enter your selector, i.e., default._domainkey and in the target, you enter the output of the command below.
-  TrustedAuthservIDs mail.yourdomain.com +
-  RejectFailures false #track only, do not stop at gate +
-  IgnoreAuthenticatedClients true +
-  RequireHeaders true +
-  SPFSelfValidate true +
-  Socket local:/var/spool/postfix/opendmarc/opendmarc.sock +
-   +
-The opendmarc socket also needs its directory created, similar to dkimSo let'create a directory for it ''sudo mkdir -p /var/spool/postfix/opendmarc'' and then set permissions and restart the service:+
  
-  sudo chown opendmarc:opendmarc /var/spool/postfix/opendmarc -R +  sudo cat /etc/opendkim/keys/yourdomain.com/default.txt
-  sudo chmod 750 /var/spool/postfix/opendmarc/ -R +
-  sudo adduser postfix opendmarc +
-  sudo systemctl restart opendmarc +
-   +
-Now, we need to configure postfix to work with openDMARCExpand the milter to include openDMARC's socket by opening ''sudo nano /etc/postfix/main.cf'' and adjusting the following block:+
  
-  milter_default_action = accept +Okay, now that your keypair for smtp has been created and DNS updated, it's a good time to create the server's DKIM policyFirstyou need to give postfix access to opendkim's tooling:
-  milter_protocol = 6 +
-  smtpd_milters = local:opendkim/opendkim.sock,local:opendmarc/opendmarc.sock +
-  non_smtpd_milters = $smtpd_milters+
  
-It's about time to setup spam controls, but before we do that, we need to make sure mission critical IMAP folders populate on clients. Let's open ''sudo nano /etc/dovecot/conf.d/15-mailboxes.conf'' and enter the auto-create line on folders that you need:+  sudo mkdir /var/spool/postfix/opendkim\ 
 +  sudo chown opendkim:postfix /var/spool/postfix/opendkim
  
-  mailbox Drafts { +After that's done, you need to open ''/etc/opendkim.conf'' and instruct your DKIM policy as to which socket to utilize. This is not a distributed mail system, but a small selfhosted one. Accordingly, this specifies to use localhost for the socket.
-    **auto = create** +
-    special_use = \Drafts +
-  }+
  
-Since I use lmtp, which is case-sensitive, and sends incoming mail to dovecot, which is also case-sensitive, I created virtual aliases in order to avoid emails being rejected when auto-mailers capitalize or otherwise punctuate the email user name. Let's open ''sudo nano /etc/postfix/main.cf'' and add another block:+  Socket local:/var/spool/postfix/opendkim/opendkim.sock
  
-  virtual_alias_maps = regexp:/etc/postfix/virtual_alias +It is also required to specify this socket in the global configuration file for the DKIM policy. Head over to /etc/default/opendkim and add the following:
-   +
-After you enter that block, let's open the file ''sudo nano /etc/postfix/virtual_alias'' and edit it with every spelling combination. Here's an example:+
  
-  /^[Jj][Oo][Nn][Aa][Tt][Hh][Aa][Nn]@haacksnetworking.org               jonathan +  SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
-  /^[Ww][Ee][Bb][Mm][Aa][Ss][Tt][Ee][Rr]@haacksnetworking.org           webmaster+
  
-It's super important to include the domain above after the regex or all emails you send to that begin with your user name will wind up in that user'inbox. When finished configuringrun ''postmap /etc/postfix/virtual_alias'' to honor the changes. So long as everything is workingwe can now setup a very reasonable and appropriate way to manage spamwithout playing wack-a-mole and without rejecting or discarding emailsLet's first discuss what we can do with postfix itself. It can be configured to reject or discard very poor health emails, such as but not exclusive to setting PTR rules or other fundamentals in ''sudo nano /etc/postfix/main.cf'' as follows:+Now that the server'DKIM policy is configuredit's required to let postfix know of the policy, where the socket is, and how to leverage the policy (reject/accept). Please note that the other policies will add to and/or expand this blocknotably the smtpd_milters stanza, which will expand with each additional policy and associated socket that gets addedOpen up the main postfix configuration in ''/etc/postfix/main.cf'' and enter the following:
  
-  smtpd_sender_restrictions +<code bash> 
-      permit_mynetworks, +milter_default_action accept 
-      permit_sasl_authenticated, +milter_protocol = 6 
-      #reject_unknown_reverse_client_hostname, +smtpd_milters = local:opendkim/opendkim.sock 
-      #reject_unknown_client_hostname, +non_smtpd_milters = $smtpd_milters 
-      #reject_unknown_sender_domain, +</code>
-      #reject_unauthenticated_sender_login_mismatch, +
-      reject_sender_login_mismatch +
-      permit +
-     +
-Personally, I disable most of the above rejects by design. I include them here more for others' reference. Just remember, there's nothing worse than never getting an email. You can also have postfix do expression checking with ''sudo apt install postfix-pcre'' and then edit ''sudo nano /etc/postfix/main.cf'' with expression checking, for example:+
  
-  header_checks = pcre:/etc/postfix/header_checks +The most important line is the uppermost line, which specifies that email should not be rejected as a result of leveraging the policy. Againthis ensures that spam assassin has what it needs to help users sort and organize email, without the possibility of phantom rejections, prohibiting email from having ever arrived in your inbox. After your DKIM keypair and DKIM policy are setup, you can setup a DMARC policy as well. Install the policy with sudo apt install opendmarc. After it installs, open up ''/etc/opendmarc.conf'' and enter the following:
-  body_checks = pcre:/etc/postfix/body_checks +
-   +
-In these locations, ''sudo nano /etc/postfix/header_checks'' and ''sudo nano /etc/postfix/body_checks'', setup expression checks as follows:+
  
-  /free mortgage quote   REJECT   +<code bash> 
-  /free mortgage quote   DISCARD+AuthservID OpenDMARC 
 +TrustedAuthservIDs mail.yourdomain.com 
 +RejectFailures false 
 +IgnoreAuthenticatedClients true 
 +RequireHeaders true 
 +SPFSelfValidate true 
 +Socket local:/var/spool/postfix/opendmarc/opendmarc.sock 
 +</code>
  
-Once they are to your likingmake sure to compile them with ''sudo postmap /etc/postfix/body_checks'' and ''sudo postmap /etc/postfix/header_checks''Again, in my personal setup, keep postfix out of the spam-management gameInsteadI prefer to accept ALL email and let spam assassin do the liftingLet's install spamassasin and integrate it with dovecot'managesieve plugin. Let's do ''sudo apt install dovecot-sieve dovecot-managesieved spamassassin spamc spamass-milter'' and then edit ''sudo nano /etc/dovecot/dovecot.conf'' as follows:+Similarly to the DKIM policywe need to give postfix permissions to the DMARC tooling and socket. It's also required to add opendmarc to the postfix group: 
 + 
 +<code bash> 
 +sudo mkdir -p /var/spool/postfix/opendmarc 
 +sudo chown opendmarc:opendmarc /var/spool/postfix/opendmarc -R 
 +sudo chmod 750 /var/spool/postfix/opendmarc/ -R 
 +sudo adduser postfix opendmarc 
 +sudo systemctl restart opendmarc 
 +</code> 
 + 
 +Similarly to SPF and DKIM, it's essential to define the policy in the main postfix configuration file. Open up ''/etc/postfix/main.cf'' and add a block for dmarcExpand the ''smtpd_milters'' section to include DMARC's socket. Note again how specify accept so as to ensure proper logging for spam assassin without any possibility of phantom rejections, or email lost at the gates. 
 + 
 +<code bash> 
 +#dmarc policy 
 +milter_default_action = accept 
 +milter_protocol = 6 
 +smtpd_milters = local:opendkim/opendkim.sock,local:opendmarc/opendmarc.sock 
 +non_smtpd_milters = $smtpd_milters 
 +</code> 
 + 
 +Now that all three policies are established and postfix is sufficiently hardened for recipients and senders, it's time to install and configure spam assassin (for assessing email health) and then set up sieve rules (to manage spam and/or email). Sieve rules can be set up server-wide for simpler or more granular approaches, and/or you can setup Roundsube to manage sieve for the server and/or usersI have instances that do both. It just depends on use-case. First, let's install spam assassin and dovecot'sieve tooling: 
 + 
 +  sudo apt install dovecot-sieve dovecot-managesieved spamassassin spamc spamass-milter 
 + 
 +After that's done, open up dovecot's primary configuration ''/etc/dovecot/dovecot.conf'' and add the sieve to the permitted protocols stanza:
  
   protocols = imap lmtp sieve   protocols = imap lmtp sieve
  
-In ''sudo nano /etc/dovecot/conf.d/15-lda.conf'' please adjust the local delivery agent as follows:+First, let's add sieve to the local delivery agent over in /etc/dovecot/conf.d/15-lda.conf. This is essential for any time you are manually testing and/or processing emails on the shell, which will utilize the LDA instead of LMTP. This ensures that the local delivery agent is sieve-ready. Head over to ''/etc/dovecot/conf.d/15-lda.conf'' and edit the block:
  
-  protocol lda { +<code bash> 
-    mail_plugins = $mail_plugins sieve +protocol lda { 
-  +  mail_plugins = $mail_plugins sieve 
-   +
-Let's also adjust the lmtp agent in ''sudo nano /etc/dovecot/conf.d/20-lmtp.conf'' as follows:+</code>
  
-  protocol lmtp { +It is also imperative that LMTP be configured for using sieve as well. Head over to ''/etc/dovecot/conf.d/20-lmtp.conf'' and edit the block:
-    mail_plugins = quota sieve +
-  }+
  
-Now it's time to setup spamassassin in postfix with the other milters. Let's open ''sudo nano /etc/postfix/main.cf'' and adjust the milter block once again:+<code bash> 
 +protocol lmtp { 
 +  mail_plugins = quota sieve 
 +
 +</code>
  
-  milter_default_action = accept +As with any service, it's essential to configure postfix to use it. So, head to ''/etc/postfix/main.cf'' and add the spam assassin socket to the primary milter. Just as with DKIM and DMARC, you are expanding the existing smtpd_milters clause, not making redundant and/or duplicate stanzas. Head over to ''/etc/postfix/main.cf'' and edit the policy block we created above for DKIM, DMARC, and now spam assassin. 
-  milter_protocol = 6 + 
-  smtpd_milters = local:opendkim/opendkim.sock,local:opendmarc/opendmarc.sock,local:spamass/spamass.sock +<code bash> 
-  non_smtpd_milters = $smtpd_milters +milter_default_action = accept 
-   +milter_protocol = 6 
-It's now time to edit spam assassin's default configuration file in ''sudo nano /etc/default/spamass-milter''. As with elsewhere, I make sure the REJECT line is commented out, in order to ensure that false positives won't happen. If you understand the risks and want to reject emails at the gate, go ahead and edit ''sudo nano /etc/default/spamass-milter'' and uncomment these lines:+smtpd_milters = local:opendkim/opendkim.sock,local:opendmarc/opendmarc.sock,local:spamass/spamass.sock 
 +non_smtpd_milters = $smtpd_milters 
 +</code> 
 + 
 +Configure the spam assassin users and restrict it to being managed by localhost by opening ''/etc/default/spamass-milter'' and ensuring this line is uncommented:
  
-  #optional custom message 
-  #OPTIONS="-u spamass-milter -i 127.0.0.1 -R SPAM_ARE_NOT_ALLOWED_HERE" 
   OPTIONS="-u spamass-milter -i 127.0.0.1"   OPTIONS="-u spamass-milter -i 127.0.0.1"
-  #Reject emails with spamassassin scores > 15 [or desired score] + 
-  OPTIONS="${OPTIONS} -r 15" +====== Part IV Setting Up Sieve & Roundcube ====== 
-   + 
-AgainI do not use the reject or discard options but rather leverage spam assassin's scoring and header assessing together with dovecot, which can move emails to locations fitting their scoresThere are two fundamental ways to configure the scoring and sieve rules, either with the CLI or with RoundcubeWhen I first beganI used the CLI and created a simple rule in ''sudo nano /etc/dovecot/conf.d/90-sieve.conf'' and entered this block:+The next part of the tutorial covers: 
 + 
 +  * Setting up sieve rules server-wide vs. user-specific 
 +  * Setting up and Installing Rouncubeincluding using Roundcube to manage userssieve rules 
 + 
 +Sieve is already enabled in dovecot and postfix. That was done up above. Now, the tutorial is discussing how to leverage the sieve syntax or language to get desired user results for incoming email. The most basic way to do this is by setting up a global, or server-wide rule, that filters emails before dovecot, via lmtp, delivers the emails to their final destinationThis is done by spam assassin adding custom fields and scoring to incoming email headers. The sieve plugin assesses these headers and then makes the correct determination for the final destination, which dovecot, via lmtp, carries outIn summaryspam assassin does the ranking and header-adding, sieve assesses the spam assassin scores and uses the global sieve rule to make determinations for all users, and finally dovecot+limtp handles the mail delivery. To create a global config for all users in this way, head over to ''/etc/dovecot/conf.d/90-sieve.conf'' and then add/uncomment this line:
  
   sieve_before = /var/mail/SpamToJunk.sieve   sieve_before = /var/mail/SpamToJunk.sieve
-   
-The sieve_before rule ensures spam assassin assesses the email right after they arrive and before sending them to dovecot and its configured delivery agents. Let's open ''sudo nano /var/mail/SpamToJunk.sieve'' and enter the following: 
  
-  require "fileinto"; +Now, create the file that you just referenced above in ''/var/mail/SpamToJunk.sieve'' and enter the following:
-  if header :contains "X-Spam-Flag" "YES" +
-  { +
-    fileinto "Junk"; +
-    stop; +
-  }+
  
-After creating the sieve rule, compile it.+<code bash> 
 +require "fileinto"; 
 +if header :contains "X-Spam-Flag" "YES"
 +  fileinto "Junk"; 
 +  stop; 
 +
 +</code>
  
-  sudo sievec /var/mail/SpamToJunk.sieve +Load the rule by issuing the ''sudo sievec /var/mail/SpamToJunk.sieve'' commandI would also recommend restarting postfix, dovecot, and spam assassin. Now, the header called X-Spam-Flag is populated with either a Yes or No response by the spam assassin serviceSpam assassin determines the yes/no score based on the scoring rules specified in the configuration fileThere are many other options and far more complex rules one can establish than this basic example. This is just to get folks startedHere'an example set of rules from the ''/etc/spamassassin/local.cf'' main configuration file:
-   +
-This rule does one thingIt checks whether spam assassin identified the message as spam, and if so, it uses dovecot to file it in JunkThis means your spam assassin scores and config are what drive the success rate of this sieve rule. It's also important to note that this rule is global, and impacts all user names on the mail serverIt's a good approach for the most heinous spam, leaving more customized rules to roundcube's sieve implementation, later in this tutorialLet'open ''sudo nano /etc/spamassassin/local.cf'' and adjust it as follows.+
  
-  report_contact webmaster@domain.com +<code bash> 
-  #adjust score below to your use-case +report_contact webmaster@yourdomain.com 
-  required_score 5.0  +required_score 5.0 
-  #rewrite_header Subject **Possible Spam** +report_safe 0 
-  report_safe +ifplugin Mail::SpamAssassin::Plugin::Shortcircuitendif Mail::SpamAssassin::Plugin::Shortcircuit 
-  add_header all Spam-Flag _YESNO_ +# uncomment the line below once unbound is working (optional) 
-  add_header all Score _SCORE_ +# dns_server 127.0.0.1score MISSING_FROM 5.0 
-  add_header all Report _REPORT_ +score MISSING_DATE 5.0 
-  add_header all Level _STARS_ +score MISSING_HEADERS 3.
-  add_header all Status "_YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_" +score PDS_FROM_2_EMAILS 3.0 
-  add_header all Checker-Version "SpamAssassin _VERSION_ (_DATE_) on _HOSTNAME_" +score FREEMAIL_FORGED_REPLYTO 3.5 
-  #legacy/deprecated header config - do not use, retained for historical record +score DKIM_ADSP_NXDOMAIN 5.0 
-  #always_add_headers = 1+score FORGED_GMAIL_RCVD 2.5 
 +score FREEMAIL_FORGED_FROMDOMAIN 3.0 
 +score HEADER_FROM_DIFFERENT_DOMAINS 3.0 
 +score FREEMAIL_FROM 3.0 
 +score ACCT_PHISHING 3.0 
 +score AD_PREFS 3.0 
 +score ADMAIL 3.0 
 +score ADMITS_SPAM 3.0 
 +score CONFIRMED_FORGED 3.0 
 +score FROM_PAYPAL_SPOOF 3.0 
 +score SPF_SOFTFAIL 2.0 
 +score SPF_FAIL 5.0 
 +whitelist_from *@statefarm.com 
 +blacklist_from *@email.freethinkerdaily.com 
 +</code>
  
-I included some header options, which can help with debugging. Also, I disable safe reporting and Subject rewriting because they alter the original email, which I think is overkill. In order to activate all that spam assassin can do, we need to have our own recursive DNS resolver, required by RBL services. Let's use the DNS server unbound and install it as follows ''sudo apt install unbound''. It works out of the box, but you can also tweak it by looking at my tutorial here: [[https://wiki.haacksnetworking.org/doku.php?id=computing:unbounddns|Unbound DNS]]. Okay, let's now insruct spamassassin to use our dns server by opening ''sudo nano /etc/spamassassin/local.cf'' and entering the DNS server. We will also add some common scores and white and black lists while at it.+Download [[https://repo.haacksnetworking.org/haacknet/haackingclub/-/blob/master/configs/mailservers/spamassassin/local.cf?ref_type=heads|local.cf]]: 
  
-  dns_server 127.0.0.+This basic configuration shows you where to place the rules/scoring, etcThe names above are referred to technically as symbolic headers and I found the examples above by searching documentation and/or forum huntingI also spent time reviewing the rules for accuracy and testing with ''spamassassin -t -D < example.eml'' regularly until assassing scored emails properlyIt takes time to perfec this, so keep it permissive for startersMake sure to check logs regularly for errors/clues using ''journalctl -u spamass-milter -u postfix -u dovecot -u opendkim -u opendmarc''This will help you track what is and is not working for spam assassin and for you, and to thereby adjust/alter/remove scores or change points to fit your use-case and preferencesThe whitelist and blacklist options can be scaled as needed and are self-explanatoryThis setup is very elegant and helpful for single user email servers and/or tight-knit and close groups of family/peopleAs more users are needed, the ability of a one-sizefits-all rule to meet everyone's individual needs becomes more and more difficultFor this reason, I chose to install Roundcube in order to leverage the filters feature in the webgui to more easily manage spam rulesHere's how to install Roundcube and use it to manage sieve.
-  score MISSING_FROM                      5.+
-  score MISSING_DATE                      5.+
-  score MISSING_HEADERS                   3.+
-  score PDS_FROM_2_EMAILS                 3.+
-  score FREEMAIL_FORGED_REPLYTO           3.+
-  score DKIM_ADSP_NXDOMAIN                5.+
-  score FORGED_GMAIL_RCVD                 2.+
-  score FREEMAIL_FORGED_FROMDOMAIN        3.0 +
-  score HEADER_FROM_DIFFERENT_DOMAINS     3.0 +
-  score FREEMAIL_FROM                     3.0 +
-  score ACCT_PHISHING                     3.0 +
-  score AD_PREFS                          3.0 +
-  score ADMAIL                            3.0 +
-  score ADMITS_SPAM                       3.0 +
-  score CONFIRMED_FORGED                  3.0 +
-  score FROM_PAYPAL_SPOOF                 3.0 +
-  score SPF_SOFTFAIL                      2.0 +
-  score SPF_FAIL                          5.0 +
-  whitelist_from *@statefarm.com +
-  blacklist_from *@email.freethinkerdaily.com+
  
-Additionally, if you check the full headers, you will see that the RBLs can now be queried without issueNote that whitelisting adds a -100 score and that blacklisting adds a +100 scoreTo understand how to tweak the symbolic headers better, one should review their spam and headers periodically and update rules based on the headers you see in the full message sourceMake sure to compile the sieve file with ''sievec'' each time you adjust the config, and restart postfix and dovecotThis approach above is good to do for egregiously bad email, but individual users will likely need their own controlsSo, for larger servers, you can alternately use Roundcube insteadRemember, you must pick one or the other because the ''sieve_before'' rules above will bypass Roundcube's sieve logicBearing this in mind, if you want to use Roundcube for sieve rules, let'navigate to roundcube > settings > filters > edit filter setTo replicate similar functionality as above, I created the following:+<code bash> 
 +cd /var/www 
 +wget https://github.com/roundcube/roundcubemail/releases/download/1.6.1/roundcubemail-1.6.1-complete.tar.gz 
 +tar xvf roundcubemail-1.6.1-complete.tar.gz 
 +ln -roundcubemail-1.6.1/ roundcube 
 +chown root:root -R roundcube 
 +cd roundcube     
 +sudo chown www-data:www-data temp/ logs/ -R 
 +sudo apt install software-properties-common php-net-ldap2 php-net-ldap3 php-imagick php8.4-fpm php8.4-common php8.4-gd php8.4-imap php8.4-mysql php8.4-curl php8.4-zip php8.4-xml php8.4-mbstring php8.4-bz2 php8.4-intl php8.4-gmp php8.4-redis 
 +</code>
  
-  require ["fileinto"]; +Obviouslygo check the git repo and make sure to download the latest stable versionRoundcube requires a databaseso let's set that up:
-  # rule:[whitelist] +
-  if anyof ( +
-      header :contains "from"+
-          "noreply@dmarc.yahoo.com", +
-          "noreply@dmarc.google.com", +
-          "Friend@protonmail.com" +
-      ] +
-  ) { +
-      keep; +
-      stop; +
-  } +
-  # rule:[blacklist] +
-  if anyof ( +
-      header :contains "from"+
-          "awakening-minds.com", +
-          "porn@yahoo.com", +
-          "bounce-1.public.govdelivery.com" +
-      ] +
-  ) { +
-      fileinto "Junk"; +
-      stop; +
-  } +
-  # rule:[spamcheck] +
-  if anyof ( +
-      header :contains "x-spam-status" "Yes", +
-      header :contains "x-spam-flag" "YES", +
-      header :contains "x-spam-level" "*****" +
-  ) { +
-      fileinto "Junk"; +
-      stop; +
-  }+
  
-These rules are processed sequentially. Monitor the Junk folder periodically and refine whitelists as needed. If something escapes, like a full health dirty marketing scam, adjust your blacklist. That's all there is to it. Now that spam controls are setup, we need to setup some auditing tools to monitor how well our server is doing these tasks. For postfix, that tool is pflogsumm. Let's install it with ''sudo apt install pflogsumm'' and let's use rsyslog and log rotate to manage the logs, requiring us to also install rsyslog with ''sudo apt install rsyslog'' +<code bash> 
-   +sudo mysql -u root 
-Disable the ''/var/log/mail.log'' entries that are in the ''rsyslog'' logrotate rule in ''/etc/logrotate.d/''. This is because we are going to make our own logrotate rule that works nicely with pflogsumm. Create the file ''sudo nano postfix-log'' inside ''/etc/logrotate.d/'' and enter the following:+CREATE DATABASE roundcube DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 
 +CREATE USER roundcube@localhost IDENTIFIED BY 'password'
 +GRANT ALL PRIVILEGES ON roundcube.* TO roundcube@localhost; 
 +FLUSH PRIVILEGES; 
 +EXIT; 
 +</code>
  
-  /var/log/mail.log { +After the database is setup, import the initial tables into the db for first-time setup: 
-      missingok + 
-      daily +  sudo mysql -u root -p roundcube < /var/www/roundcube/SQL/mysql.initial.sql 
-      rotate 7 + 
-      create +Earlier in the tutorial, you were instructed to setup a vhost for ''mail.haacksnetworking.org'' / ''mail.domain.com''. You already created vhost-mail.conf for the purposes of having an associated A record and website destination, which is helpful for complete validation of email's record health. Now, you are going leverage this certificate for the purpose of encrypting the apache2 front-end, or reverse proxy. Apache receives the public requests, and then the new vhost config, which enables the reverse proxy, forwards those requests upstream to the roundcube service. First, replace ''vhost-mail.conf'' with something like the following: 
-      compress + 
-      start 0 +<code bash> 
-  }+<VirtualHost *:80> 
 +      ServerName mail.domain.com 
 +      ServerAdmin email@email.com 
 +      DocumentRoot /var/www/roundcube/ 
 +      ErrorLog ${APACHE_LOG_DIR}/roundcube_error.log 
 +      CustomLog ${APACHE_LOG_DIR}/roundcube_access.log combined 
 +      <Directory /> 
 +              Options FollowSymLinks 
 +              AllowOverride All 
 +      </Directory> 
 +      <Directory /var/www/roundcube/> 
 +              Options FollowSymLinks MultiViews 
 +              AllowOverride All 
 +              Order allow,deny 
 +              Allow from all 
 +      </Directory> 
 +      <FilesMatch ".+\.ph(ar|p|tml)$"> 
 +              SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost" 
 +      </FilesMatch> 
 +      RewriteEngine on 
 +      RewriteCond %{SERVER_NAME} =mail.domain.com 
 +      RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] 
 +</VirtualHost> 
 +</code> 
 + 
 +After that's done, edit the vhost-mail-le.conf file with something like the following: 
 + 
 +<code bash> 
 +<IfModule mod_ssl.c> 
 +<VirtualHost *:443> 
 +      ServerName mail.domain.com 
 +      ServerAdmin email@email.com 
 +      DocumentRoot /var/www/roundcube/ 
 +      ErrorLog ${APACHE_LOG_DIR}/roundcube_error.log 
 +      CustomLog ${APACHE_LOG_DIR}/roundcube_access.log combined 
 +      <Directory /> 
 +              Options FollowSymLinks 
 +              AllowOverride All 
 +      </Directory> 
 +      <Directory /var/www/roundcube/> 
 +              Options FollowSymLinks MultiViews 
 +              AllowOverride All 
 +              Order allow,deny 
 +              Allow from all 
 +      </Directory> 
 +      <FilesMatch ".+\.ph(ar|p|tml)$"> 
 +              SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost" 
 +      </FilesMatch> 
 +      SSLCertificateFile /etc/letsencrypt/live/mail.domain.com/fullchain.pem 
 +      SSLCertificateKeyFile /etc/letsencrypt/live/mail.domain.com/privkey.pem 
 +</VirtualHost> 
 +</IfModule> 
 +</code> 
 + 
 +Since the certificate was already created, these configs can just be dropped in to the http vhost and https vhost, respectively. These two vhosts were created earlier by you (http) and subsequently by Let's Encrypt (https). It's now time to connect Roundcube to the database that was created earlier: 
 + 
 +<code bash> 
 +cd /var/www/roundcube/config/ 
 +sudo cp config.inc.php.sample config.inc.php 
 +sudo nano config.inc.php 
 +# Remove the "<" and ">" !! 
 +<$config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcube';> 
 +<$config['des_key'] = 'rcmail-!24ByteDESkey*Str';? 
 +<$config['imap_host'] = 'startls://domain.com:143';> 
 +<$config['smtp_host'] = 'tls://mail.domain.com:587';> 
 +<$config['enable_spellcheck'] = true;> 
 +</code> 
 + 
 +In addition to configuring the database and renaming the sample config, it's imperative to edit the plugins block at the end of the primary configuration as well. Add and/or replace the bottom plugin block as follows: 
 + 
 +<code bash> 
 +$config['plugins'] = [ 
 +  'archive', 
 +  'zipdownload', 
 +  'acl', 
 +  'additional_message_headers', 
 +  'attachment_reminder', 
 +  'autologon', 
 +  'debug_logger', 
 +  'emoticons', 
 +  'enigma', 
 +  'filesystem_attachments', 
 +  'help', 
 +  'hide_blockquote', 
 +  'http_authentication', 
 +  'identicon', 
 +  'identity_select', 
 +  'jqueryui', 
 +  'krb_authentication', 
 +  'managesieve', 
 +  'markasjunk', 
 +  'new_user_dialog', 
 +  'new_user_identity', 
 +  'newmail_notifier', 
 +  'password', 
 +  'reconnect', 
 +  'redundant_attachments', 
 +  'show_additional_headers', 
 +  'squirrelmail_usercopy', 
 +  'subscriptions_option', 
 +  'userinfo', 
 +  'vcard_attachments', 
 +  'virtuser_file', 
 +  'virtuser_query' 
 +]; 
 +</code> 
 + 
 +At this point, you should be able to log in to mail.haacksnetworking.org / mail.domain.com using a common web browser and your credentials. Your user name is the UNIX user name, i.e., only the handle without the domain. So, my user name is jonathan, for example ... and without haacksnetworking.org at the end. Your password is whatever you adduser yourusername specified when creating your simple UNIX user. At this point, you want to open your web browser and login. Head to the Filters section under Settings. Click the cog at the top and select Edit Filter Set. In the block to the right, replace the contents with the following: 
 + 
 +<code bash> 
 +require ["fileinto"]; 
 +# rule:[whitelist] 
 +if header :contains "from" ["nice@goodemail.com","happy@ham.com"
 +
 + keep; 
 + stop; 
 +
 +# rule:[blacklist] 
 +if header :contains "from" ["powell@supportbaggies.com","bad@email.com"
 +
 + fileinto "Junk"; 
 + stop; 
 +
 +# rule:[spamcheck] 
 +if anyof (header :contains "x-spam-status" "Yes", header :contains "x-spam-flag" "YES", header :contains "x-spam-level" "*****"
 +
 + fileinto "Junk"; 
 + stop; 
 +
 +</code> 
 + 
 +[[https://repo.haacksnetworking.org/haacknet/haackingclub/-/blob/master/configs/mailservers/sieve/roundcube-filter-set.sieve?ref_type=heads|roundcube-edit-filter-set.sieve]] 
 + 
 +Use this as a jumping-off point. Once you save it, it will change your user interface and allow you to add/edit rules using the nested filters that were just created with this sieve config. Remember that the Roundcube filters and associated sieve configs it is creating on the underbelly are processed in order linearly. Consider this well when developing additional AND/OR logic rules beyond the scope of whitelist, blacklist, and spam check sequence. Now, subsequent editing is very convenient, and user-based. Make sure to comment out ''sieve_before = /var/mail/SpamToJunk.sieve'' in ''/etc/dovecot/conf.d/90-sieve.conf'' if you choose to only use this method. It's also fine to retain the server-wide rule and use that for exclusively global changes that fit the community. 
 + 
 +As you can see in the asset above, you now have access to refined sieve rules for each user. Once I decided that I needed or wanted user-level spam controls, it no longer made sense for me to manage this exclusively on the CLI. For this reason, I added Roundcube and began managing spam for email accounts individually. When Trixie came out, Dovecot's sieve implementation needed a bit of fine-tuning. First, head to ''/etc/dovecot/conf.d/90-sieve.conf'' and change the block as follows: 
 + 
 +<code bash> 
 +# Comment out the old block: 
 +#plugin {  
 +#sieve = file:~/sieve;active=~/.dovecot.sieve 
 +#} 
 + 
 +# Enable the new block: 
 +sieve_script personal { 
 +  driver = file 
 +  path = ~/sieve 
 +  active_path = ~/.dovecot.sieve 
 +
 +</code> 
 + 
 +====== Part V - Setting up pflogsumm ====== 
 + 
 +It's important to be able to monitor how your setup is performing and what is or is not working correctly. No better way to do that than to get some analytics emailed to you each day. To do that, let's install pflogsumm and use rsyslog for logging. Install the packages sudo apt install pflogsumm rsyslog and then create the log rotation rule over in ''/etc/logrotate.d/postfix-log'' and enter the following: 
 + 
 +<code bash> 
 +/var/log/mail.log { 
 +  missingok 
 +  daily 
 +  rotate 7 
 +  create 
 +  compress 
 +  start 0 
 +} 
 +</code>
  
-Once that'done, let'create script and cronjob to send us daily reports by creating a file ''sudo nano /usr/local/bin/pflog-run.sh'' and entering something like:+You need to make sure to comment out or remove #mail.log from ''/etc/logrotate.d/rsyslog''. This is done so as to replace the default rotation. NowI will create script to run pflogsumm on the archives. Create ''/usr/local/bin/pflog-run.sh'' or something similar and enter the following:
  
-  #!/bin/sh +<code bash> 
-  #/usr/sbin/logrotate -f /etc/logrotate.d/postfix-log [helpful for manual testing] +#!/bin/sh 
-  gunzip /var/log/mail.log.0.gz +#/usr/sbin/logrotate -f /etc/logrotate.d/postfix-log 
-  /usr/sbin/pflogsumm /var/log/mail.log.0 --problems-first --rej-add-from --verbose-msg-detail -q |  mail -s "[pflog-lastlog]-$(hostname -f)-$(date)" email@email.com +gunzip /var/log/mail.log.0.gz 
-  gzip /var/log/mail.log.0 +/usr/sbin/pflogsumm /var/log/mail.log.0 --problems-first --rej-add-from --verbose-msg-detail -q |  mail -s "[pflog-lastlog]-$(hostname -f)-$(date)" alerts@haacksnetworking.org 
-  sleep 2s +gzip /var/log/mail.log.0 
-  systemctl restart rsyslog +sleep 2s 
-  systemctl restart postfix +systemctl restart rsyslog 
-  systemctl restart dovecot +systemctl restart postfix 
-  exit 0+systemctl restart dovecot 
 +exit 0 
 +</code>
  
-The key here is that your script and zip and unzipping rules match the retention and naming conventions specified in logrotate. Since I floored the rotation at 0the script always unzips the ''0.gz'' log. This is why it is preferable and easier to remove the ''mail.log'' stanza from the other rotations. This allows one to easily customize it for email logs without messing with other rotations and settings. Once that'done, set up a cronjob and you are all set.+Nextopen crontab -e and create a job to run this script daily at a time of your choosingHere'mine:
  
   30 12 * * * /bin/bash /usr/local/bin/pflog-run.sh >> /home/logs/pflog-run.log   30 12 * * * /bin/bash /usr/local/bin/pflog-run.sh >> /home/logs/pflog-run.log
-   
-You can also use the ''-d yesterday'' flag in pflogsumm and wildcard your domains, which I later found out. But, this works too and I retain its use in my production servers. This ends our primary configuration. If you don't have Roundcube setup, look here: [[https://wiki.haacksnetworking.org/doku.php?id=computing:roundcube|Roundcube Tutorial]]. The rest of this tutorial is miscellaneous information that has come up along the way. 
  
-------------------------------------------- +I later learned this is far easier and you can just use the yesterday flag. Who knew?!
-   +
-The SASL module packages should be brought in as dependencies of postfix and/or dovecot. However, on upgrades, etc., they might be removed during dependency resolution. If you get "no sasl" report on your logs suddenly, despite everything working prior, use:+
  
-  sudo apt-get install libsasl2-modules +  usr/sbin/pflogsumm /var/log/mail.log -d yesterday --problems-first --rej-add-from --verbose-msg-detail -q | mail -s "[pflog-lastlog]-$(hostname -f)-$(date)" alerts@haacksnetworking.org
-   +
-If/when things are going wrong, turn on your detailed debugging logs and study them:+
  
-  nano /etc/dovecot/conf.d/10-logging.conf +====== Part VI Recursive DNS ======
-  <mail_debug yes>+
  
-To setup autodiscoverysetup separate vhost in apache with autodiscover.domain.com, and then create your AAAAA, and discovery records:+Some of the spam assassin tooling that uses RBL will not work unless you use your own recursive DNS instead offor example, ''8.8.8.8''. It should also be noted that using your own DNS is better from both speed and privacy perspective, so setting this up kills two birds with one proverbial stoneI have a separate tutorial about this on my blog and it has an associated wiki entry as wellAs you review these tutorialsplease remember that unbound can be used for both LAN and WAN settings. For this tutorialan email serverone would use the WAN configuration. Here's a concise recap of how to set that up, which you will do directly on the email server. First, let's install unbound sudo apt install unbound, by far the most hassle free way to do recursive DNS. Once it is up and running, it's time to put a WAN-based config in place in ''/etc/unbound/unbound.conf''. In that file, add the following:
  
-  _imap._tcp          10    1                   143        mail.haacksnetworking.org +<code bash> 
-  _submission._tcp    10                      587        mail.haacksnetworking.org +server: 
-  _imaps._tcp             1                   993        mail.haacksnetworking.org +  # Bind to localhost only 
-  _submissions._tcp       1                   465        mail.haacksnetworking.org +  interface: 127.0.0.1 
-  _autodiscover._tcp  10                      443        mail.haacksnetworking.org +  interface: ::
-  autodiscover        A     8.28.86.125          +  port: 53 
-  autodiscover        AAAA  2604:fa40:0:10::18  +  do-ip4: yes 
 +  do-ip6: yes 
 +  prefer-ip6: yes 
 +  access-control: 127.0.0.0/8 allow 
 +  access-control: 0.0.0.0/0 refuse 
 +  access-control: ::0/0 refuse 
 +  # Optimize for 8 cores 
 +  num-threads:
 +  msg-cache-slabs:
 +  rrset-cache-slabs:
 +  infra-cache-slabs:
 +  key-cache-slabs:
 +  # Cache settings for high query volume 
 +  cache-max-ttl: 86400 
 +  cache-min-ttl: 3600 
 +  rrset-cache-size: 128m 
 +  msg-cache-size: 64m 
 +  key-cache-size: 32m 
 +  neg-cache-size: 8m 
 +  # Enable prefetch and expired responses 
 +  prefetch: yes 
 +  prefetch-key: yes 
 +  serve-expired: yes 
 +  serve-expired-ttl: 3600 
 +  # DNSSEC validation for DANE 
 +  #do-dnssec: yes 
 +  harden-dnssec-stripped: yes 
 +  harden-referral-path: yes 
 +  harden-below-nxdomain: yes 
 +  harden-algo-downgrade: no 
 +  # Performance tweaks 
 +  #so-rcvbuf: 4m 
 +  #so-sndbuf: 4m 
 +  edns-buffer-size: 1232 
 +  outgoing-range: 4096 
 +  num-queries-per-thread: 1024 
 +  jostle-timeout: 200 
 +  #low-resolver-mem: no 
 +  # Logging (minimal) 
 +  verbosity: 
 +  log-queries: no 
 +  log-replies: no 
 +  use-syslog: yes 
 +  # Security and privacy 
 +  hide-identity: yes 
 +  hide-version: yes 
 +  use-caps-for-id: yes 
 +  qname-minimisation: yes 
 +  harden-large-queries: yes 
 +  harden-glue: yes 
 +  aggressive-nsec: yes 
 +  # Protocol settings 
 +  do-tcp: yes 
 +  do-udp: yes 
 +  # Enable full recursion - no longer needed, retained for history 
 +  # do-not-query-localhost: no 
 +  # root-hints: "/usr/share/dns/root.hints" 
 +  # Disable subnetcache 
 +  module-config"validator iterator" 
 +# Forward to upstream resolvers 
 +# forward-zone: 
 +#    name"." 
 +#    forward-addr1.1.1.1  # Cloudflare 
 +#    forward-addr8.8.8.8  # Google 
 +</code>
  
-After thatsetup your ''autodiscover.xml'' file:+You should certainly familiarize yourself with all of these settings. This tutorial itselftook a few months of research and time. In addition to this primary config, you also need to set up logging and app armor. For added safety, I disable systemd's built-in resolver as well. Open up ''/etc/rsyslog.d/unbound.conf'' and add
  
-  sudo nano /var/www/autodiscover.haacksnetworking.org/public_html/autodiscover/autodiscover.xml +  if $programname == 'unbound' then /var/log/unbound/unbound.log 
-   +  & stop
-Inside that file, enter something similar to this entry below, obviously adjusting for your priority, weight, and desired client configuration behavior:+
  
-  <?xml version="1.0" encoding="UTF-8"?> +Next, open up ''/etc/logrotate.d/unbound'' and enter the rotation logic:
-  <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> +
-    <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> +
-      <Account> +
-        <AccountType>email</AccountType> +
-        <Action>settings</Action> +
-        <Protocol> +
-          <Type>IMAP</Type> +
-          <Server>mail.haacksnetworking.org</Server> +
-          <Port>993</Port> +
-          <LoginName>%EMAILADDRESS%</LoginName> +
-          <Domain>haacksnetworking.org</Domain> +
-          <Encryption>SSL</Encryption> +
-        </Protocol> +
-        <Protocol> +
-          <Type>IMAP</Type> +
-          <Server>mail.haacksnetworking.org</Server> +
-          <Port>143</Port> +
-          <LoginName>%EMAILADDRESS%</LoginName> +
-          <Domain>haacksnetworking.org</Domain> +
-          <Encryption>STARTTLS</Encryption> +
-        </Protocol> +
-        <Protocol> +
-          <Type>SMTP</Type> +
-          <Server>mail.haacksnetworking.org</Server> +
-          <Port>465</Port> +
-          <LoginName>%EMAILADDRESS%</LoginName> +
-          <Domain>haacksnetworking.org</Domain> +
-          <Encryption>SSL</Encryption> +
-        </Protocol> +
-        <Protocol> +
-          <Type>SMTP</Type> +
-          <Server>mail.haacksnetworking.org</Server> +
-          <Port>587</Port> +
-          <LoginName>%EMAILADDRESS%</LoginName> +
-          <Domain>haacksnetworking.org</Domain> +
-          <Encryption>STARTTLS</Encryption> +
-        </Protocol> +
-      </Account> +
-    </Response> +
-  </Autodiscover>+
  
-Pretty much everything one needs is now setup. To check record health after you set your DNS records, you can do the following: +<code bash> 
-   +/var/log/unbound/unbound.log { 
-  dig txt +short _dmarc.jonathanhaack.com +daily 
-  dig txt +short _dmarc.haacksnetworking.org +rotate 7 
-  dig default._domainkey.jonathanhaack.com txt +missingok 
-  dig default._domainkey.haacksnetworking.org txt +create 0640 root adm 
-  dig txt +short jonathanhaack.com +postrotate 
-  dig txt +short haacksnetworking.org +/usr/lib/rsyslog/rsyslog-rotate 
-  dig -x 8.28.86.130 +short +endscript 
-  dig -x 8.28.86.125 +short +} 
-  sudo opendkim-testkey -d jonathanhaack.com -s default -vvv +</code>
-  sudo opendkim-testkey -d haacksnetworking.org -s default -vvv +
-   +
-You should test email health with the CLI and/or use a service like [[https://www.mail-tester.com/|Mail Tester]]. I recommend using both CLI to send email and a common client. Both domain.com and mail.domain.com should work if you set everything right. Here's how to send a simple email at the CLI:+
  
-  echo "HiI am testing the subdomain email health." | mail -"CLI Email Test" oemb1905@jonathanhaack.com +Nextdisable systemd'resolver:
-   +
-Postfix has its own CLI control tools, such as but not exclusive to viewing email, deleting email, etc.:+
  
-  mailq +<code bash> 
-  postcat -q E900C4780073 +systemctl disable --now unbound-resolvconf.service 
-  postsuper -d E900C4780073 +sed -Ei 's/^unbound_conf=/#unbound_conf=/' /etc/resolvconf.conf 
-  postsuper -d ALL+rm /etc/unbound/unbound.conf.d/resolvconf_resolvers.conf 
 +</code> 
 + 
 +After that, set up apparmor. In nano ''/etc/apparmor.d/local/usr.sbin.unbound'' add the following: 
 + 
 +  /var/log/unbound/unbound.log rw, 
 + 
 +After that, run the following commands to set everything up: 
 + 
 +  sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.unbound 
 +  sudo service apparmor restart 
 +  sudo mkdir -p /var/log/unbound 
 +  sudo touch /var/log/unbound/unbound.log 
 +  sudo chown unbound /var/log/unbound/unbound.log 
 + 
 +Once you've completed this, you can change ''/etc/resolv.conf'' to reflect this setup and then reboot: 
 + 
 +  nameserver ::1 
 +  nameserver 127.0.0.1 
 + 
 +Use ping4 and/or ping6 to test routing after reboot. Remember, the primary reason to do this is so as to enable the server's unbound recursive DNS server, which allows direct RBL querying and more advanced spam controls. Up above, I pointed out that there was a line commented out in ''/etc/spamassassin/local.cf'' which you can now add. Open that config and uncomment: 
 + 
 +  dns_server 127.0.0.1 
 + 
 +Now, your email server can directly query the default RBLs that spam assassin has access to. You should see the errors and failures disappear from your logs now. 
 + 
 +====== Part VII - Autodiscovery ====== 
 + 
 +Setting up autodiscovery is a nicety that I never did at first. But, at some point, I got bored and added it, somewhere in the middle and/or late in 2024. It turned out to be helpful when setting up new clients manually, as they would, as the name suggests, simply autdiscover the server settings. First, just like we did with domain.com and mail.domain.com, let's setup another A and AAAA record for ''autodiscover.domain.com'' and an associated virtual host, e.g., ''vhost-autodiscover.conf'' and then after enabling the vhost ''a2ensite'' make sure to cut it a cert: 
 + 
 +  sudo certbot --authenticator standalone --installer apache -d autodiscover.domain.com --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2"
      
-These tools prove helpful if/when emails get stuck, etc.+Set up the following SRV records on your DNS host. Adapt them for ''autodiscover.domain.com''
 + 
 +  * _imaps._tcp 0 1 993 mail.haacksnetworking.org 
 +  * _submission._tcp 0 1 587 mail.haacksnetworking.org 
 +  * _autodiscover._tcp 0 1 443 mail.haacksnetworking.org 
 +  * _imap._tcp 10 1 143 mail.haacksnetworking.org [Optional, not needed if enforcing TLS] 
 +  * _submissions._tcp 0 1 465 mail.haacksnetworking.org 
 + 
 +Again, make sure that you also setup A and AAAA records up above. Once that's done, the next step is to create an ''.xml'' file that the autodiscover subdomain can serve to clients/queries. First, create the autodiscovery subdirectory and ''.xml'' file: 
 + 
 +  sudo mkdir /var/www/autodiscover.haacksnetworking.org/public_html/autodiscover\ 
 +  sudo nano /var/www/autodiscover.haacksnetworking.org/public_html/autodiscover/autodiscover.xml 
 + 
 +In the .xml file, put the following, adjusting for ''domain.com'': 
 + 
 +<code bash> 
 +<?xml version="1.0" encoding="UTF-8"?> 
 +<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> 
 +  <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> 
 +    <Account> 
 +      <AccountType>email</AccountType> 
 +      <Action>settings</Action> 
 +      <Protocol> 
 +        <Type>IMAP</Type> 
 +        <Server>mail.haacksnetworking.org</Server> 
 +        <Port>993</Port> 
 +        <LoginName>%EMAILADDRESS%</LoginName> 
 +        <Domain>haacksnetworking.org</Domain> 
 +        <Encryption>SSL</Encryption> 
 +      </Protocol> 
 +      <Protocol> 
 +        <Type>IMAP</Type> 
 +        <Server>mail.haacksnetworking.org</Server> 
 +        <Port>143</Port> 
 +        <LoginName>%EMAILADDRESS%</LoginName> 
 +        <Domain>haacksnetworking.org</Domain> 
 +        <Encryption>STARTTLS</Encryption> 
 +      </Protocol> 
 +      <Protocol> 
 +        <Type>SMTP</Type> 
 +        <Server>mail.haacksnetworking.org</Server> 
 +        <Port>465</Port> 
 +        <LoginName>%EMAILADDRESS%</LoginName> 
 +        <Domain>haacksnetworking.org</Domain> 
 +        <Encryption>SSL</Encryption> 
 +      </Protocol> 
 +      <Protocol> 
 +        <Type>SMTP</Type> 
 +        <Server>mail.haacksnetworking.org</Server> 
 +        <Port>587</Port> 
 +        <LoginName>%EMAILADDRESS%</LoginName> 
 +        <Domain>haacksnetworking.org</Domain> 
 +        <Encryption>STARTTLS</Encryption> 
 +      </Protocol> 
 +    </Account> 
 +  </Response> 
 +</Autodiscover> 
 +</code> 
 + 
 +Once this is done, clients should have an easy time finding your settings. If not, remember that this tutorial uses: 
 + 
 +  * IMAP: mail.haacksnetworking.org (993, SSL/TLS) 
 +  * User: yourusername 
 +  * Pass: your password that you created when you added the user 
 +  * SMTP: mail.haacksnetworking.org (587, STARTLS) 
 +  * User: yourusername 
 +  * Pass: your password that you created when you added the user 
 + 
 +If your client does not honor autodiscovery and/or you choose to enter manually, use the port recommendations and protocols above. Other options also exist. 
 + 
 +====== Part VIII - Additional Options ====== 
 + 
 +The options below are results of small things that came up while using my own server over the last 5 years or so. First, I noticed that clients would not set up the standard directories and it turns out you need to tell dovevot to do that over in ''/etc/dovecot/conf.d/15-mailboxes.conf'' by enabling the ''auto = create'' in the folder blocks for which you desire auto-population. 
 + 
 +<code bash> 
 +mailbox Drafts { 
 +  auto = create 
 +  special_use = \Drafts 
 +
 +</code> 
 + 
 +The next issue that came up was almost immediate. Upon switching to my own server, I noticed that E*Trade and StateFarm emails stopped arriving. Not all of them, just some. After years of using alternate emails and testing when time permitted, I discovered that the issue was due to the mass email tools changing my email from, for example, user@haacksnetworking.org to USER@haacksnetworking.org. However, as it turns out, according to RFC 2821, Section 2.4, local-parts of email addresses "MUST BE" treated as case sensitive. Dovecot and lmtp enforce this RFC, so these emails were getting rejected. After investigating work arounds, I found a Stack Exchange comment on a related but different thread that suggested using postfix's virtual aliases. To do that, add a block in the main configuration over in ''/etc/postfix/main.cf'' and put the following inside: 
 + 
 +  virtual_alias_maps = regexp:/etc/postfix/virtual_alias 
 + 
 +Inside the file ''/etc/postfix/virtual_alias'', enter your aliases. Make sure to append @domain.com otherwise any emails you send to users of the same name will get sent to that user instead of the destination. For exampleif I omit the domain in this config, but then send to ''JONATHAN@jonathanhaack.com'' OR ''jonathan@jonathanhaack.com'' BOTH of those emails will be sent to ''jonathan@haacksnetworking.org'' (because the domain was left off the alias). So, make sure to pay attention to how I included the ''@domain.com'' in the alias table and why that's essential. 
 + 
 +  /^[Jj][Oo][Nn][Aa][Tt][Hh][Aa][Nn]@yourdomain.com/ jonathan\ 
 +  /^[Ww][Ee][Bb][Mm][Aa][Ss][Tt][Ee][Rr]@yourdomain.com/ webmaster 
 + 
 +Once you edited the file, load the changes with sudo postmap /etc/postfix/virtual_alias. 
 +Another thing I researched when reviewing Linux Babe's tutorial, but ultimately rejected doing was body and header inspection. To do that, install postfix's regular expression tooling with sudo apt install postfix-pcre and then edit the main configuration ''/etc/postfix/main.cf'' and enter the following stanzas: 
 + 
 +  header_checks = pcre:/etc/postfix/header_checks 
 +  body_checks = pcre:/etc/postfix/body_checks 
 + 
 +Once the tool is installed and postfix has been instructed where to look for the configuration files, you can create the files and put in expression checking. Here are some options: 
 +In ''/etc/postfix/header_checks'' and/or in ''/etc/postfix/body_checks'', you could put entries such as but not limited to: 
 + 
 +  /free mortgage quote/ REJECT 
 +  /free mortgage quote/ DISCARD 
 + 
 +When that's done, be sure to load the changes: 
 + 
 +  sudo postmap /etc/postfix/header_checks 
 +  sudo postmap /etc/postfix/body_checks 
 + 
 +Before, during, and after the creation of this email server tutorial, I've had a need to use messaging/chat apps. I've used them all, whether Signal, Telegram, Nextcloud Talk, and loads of more boutique and experimental platforms. After years of debate with friends and colleagues, a friend suggested [[https://delta.chat/en/download|Delta Chat]], a chat app that - wait for it - uses email servers for chatting. Given my email server was already set up and purring, I gave it a try and I've used it since for family and business conversations, that is, small and trusted audiences. If you get through this tutorial, it's worth giving it a try! Just edit ''/etc/dovecot/conf.d/20-imap.conf'' and ensure the ''imap_idle_notify_interval = 1min'' idle notify interval is 1 or 2 mins. For small use cases, increasing this frequency will harm nothing and improve the snappiness of the Delta Chat experience. Everything else is already perfectly compatible with Delta Chat. Just export and save your keys! 
 + 
 +If other quirky issues come up, I'll besure to add them right here! 
 + 
 +====== Part IX - What's next? ====== 
 + 
 +Next Steps 
 + 
 +  * Allowing users secure password changing via Roundcube and/or php without sacrificing the simplicity of UNIX users 
 +  * Be aware of compliance, FERPA, HIPPA, and where this solution is NOT adviseable 
 +  * Automating this recipe with a massive shell script (others have already done this FYI) ;O 
 +  * Setting up a chatmail server for use with Delta Chat; this would include virtual users, public registration via mailadm 
 +  * Creating a fork of this project that's redesigned scope for medium to large small businesses, i.e., over 50 users and up to 500, where virtual users and mailadm and direct end user management provides value 
 + 
 +I rewrote the mail server tutorial for the presentation [[https://tech.haacksnetworking.org/2025/06/10/your-email-your-rules-self-hosting-simplified/|Your Email, Your Rules: Self-Hosting Simplified]]. The SeaGL presentation can be found [[https://pretalx.seagl.org/2025/talk/VLM7AS/|on their calendar]].
  
- --- //[[alerts@haacksnetworking.org|oemb1905]] 2025/05/31 18:33//+ --- //[[alerts@haacksnetworking.org|oemb1905]] 2025/11/09 05:45//
computing/mailserver-trixie.1748716792.txt.gz · Last modified: by oemb1905