Skip to content

Instantly share code, notes, and snippets.

@fishphhd
Last active July 29, 2023 19:23
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fishphhd/99768a73657f4ac2bbfe2eb9f6332232 to your computer and use it in GitHub Desktop.
Save fishphhd/99768a73657f4ac2bbfe2eb9f6332232 to your computer and use it in GitHub Desktop.

Private Mailserver with Postfix, Dovecot and Fetchmail on macOS Big Sur M1

This is a simple installation and configuration manual to setup a private mailserver. The instructions will be continued in future...

Requirements

  • Apple M1 with macOS Big Sur but it should working at Intel Macs 64-bit too.
  • Replacement for the missing mail and imap services in the macOS Server
  • Receiving and sending emails of all accounts (private and business)
  • Using Postfix, Dovecot and Fetchmail
  • Homebrew
  • The local username in this gist is username1 for example. Change it to an existing username.
  • Terminal like iTerm2.
  • sudo rights

Installations

  • Dovecot
brew install dovecot
  • Fetchmail
brew install fetchmail

Configuration

Use careful with the following instructions. Make a copy of all files before changed.

Postfix

Since MacOS Ventura postfix is secured and we need to compile for self. Download postfix, extract it and generate the make files. It needs the following libraries (brew install openssl, brew install cyrus-sasl, brew install pcre).

Beware at point sudo make install we want to install postfix into the folder /opt/postfix. Set all to this folder and use the user _postfix and the group _postdrop.

tar -xzvf postfix-3.7.3.tar.gz
cd postfix-3.7.3

make makefiles CCARGS='-DDEF_CONFIG_DIR=\"/opt/postfix/etc\" -DDEF_COMMAND_DIR=\"/opt/postfix/sbin\" -DDEF_DAEMON_DIR=\"/opt/postfix/libexec\" -DDEF_MANPAGE_DIR=\"/opt/postfix/man\" -DDEF_NEWALIAS_PATH=\"/opt/postfix/newaliases\" -DDEF_SENDMAIL_PATH=\"/opt/postfix/sbin\" -DDEF_MAILQ_PATH=\"/opt/postfix/bin/mailq\" -fPIC -DUSE_TLS -DUSE_SSL -DUSE_SASL_AUTH -DUSE_CYRUS_SASL -DHAS_LDAP -DLDAP_DEPRECATED=1  -DHAS_PCRE -I/opt/homebrew/opt/openssl/include -I/opt/homebrew/opt/cyrus-sasl/include/sasl  -I/opt/homebrew/opt/pcre/include' AUXLIBS='-L/opt/homebrew/opt/openssl/lib -lssl -lcrypto -L/opt/homebrew/opt/cyrus-sasl/lib/sasl2 -lsasl2 -lpcre -L/opt/homebrew/opt/pcre/lib -lz -lm -lldap -llber -Wl,-rpath,/usr/lib/openssl -Wl' OPT='-O' DEBUG='-g'

make

sudo make install

Postfix is located in folder /etc/postfix. Open file main.cf and make some changes and inserts. Postfix is secured with self signed key and certificate and runs on ports 25 and 587. The other parameters in the main.cf will not be changed.

  • Edit file /etc/postfix/main.cf.
### Set myhostname with hostname of your choice.
### This will be the name of your private email account(s).
myhostname = mailserver.my.home

### The myorigin parameter specifies the domain that locally-posted
### mail appears to come from. The default is to append $myhostname,
### which is fine for small sites.  If you run a domain with multiple
### machines, you should (1) change this to $mydomain and (2) set up
### a domain-wide alias database that aliases each user to
### user@that.users.mailhost.
myorigin = $myhostname

mydestination = $myhostname, mailserver.my.home, localhost.localdomain, localhost

### Add the standard existing relayhost and port. Change smtp.example.com with real name.
### The other relayhosts will be configured at sender_dependent_relayhost_maps later.
relayhost = [smtp.example.com]:587

### The default mail delivery transport and next-hop destination for final delivery
### to domains listed with $virtual_mailbox_domains. For this we have to activate
### protocol lmtp and plugins quota sieve later.
virtual_transport = lmtp:unix:private/dovecot-lmtp

### The mailserver have to use only IP4.
inet_protocols = ipv4

### 100 MB limit for message size.
message_size_limit = 104857600
mailbox_size_limit = 0
biff = no

### Add your local network(s) like IP/Subnet
mynetworks = 127.0.0.0/8, [::1]/128

smtpd_client_restrictions = permit_mynetworks permit_sasl_authenticated permit
recipient_delimiter = +
tls_random_source = dev:/dev/urandom
smtpd_tls_ciphers = medium

### Change from loopback-only to all.
inet_interfaces = all

### TLS parameters with paths to key and certificate file.
smtpd_use_tls=yes
smtpd_tls_cert_file=/etc/certs/mailserver.my.home.crt
smtpd_tls_key_file=/etc/ssl/private/mailserver.my.home.key
smtpd_tls_security_level=may

smtp_tls_CApath=/opt/homebrew/etc/ssl/
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated permit_inet_interfaces defer_unauth_destination
smtpd_recipient_restrictions = permit_mynetworks permit_sasl_authenticated

### Path to the file with account informations for the relayhosts.
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps =  hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous

### Path to the file with senders informations.
sender_canonical_maps = hash:/etc/postfix/sender_canonical

### Path to the file with recipients informations.
virtual_maps = hash:/etc/postfix/virtual

### Email saving format.
home_mailbox = Maildir/

### Path to the file with relations for email addresses to the relayhosts.
sender_dependent_relayhost_maps = hash:/etc/postfix/relayhost_map
  • Create or edit file /etc/postfix/sasl_passwd. Change hostnames, ports and account informations.
[smtp.example.com]:587 smtp_username1:smtp_password1
[smtp.example.net]:587 smtp_username2:smtp_password2
...
  • Create or edit file /etc/postfix/sender_canonical.
username1   username1
username2   username2
...
  • Create or edit file /etc/postfix/virtual.
username1                     username1
username2                     username2

username1@localhost           username1
username1@mailserver.my.home  username1

email_address1@example.com     username1
email_address2@example.net     username1
...
  • Create or edit file /etc/postfix/relayhost_map.
email_address1@example.com     [smtp.example.com]:587
email_address2@example.net     [smtp.example.net]:587
...

Dovecot

First of all copy the example configuration files to /opt/homebrew/etc/dovecot. Change it to your installed local version of dovecot.

cp -a /opt/homebrew/Cellar/dovecot/2.3.14/share/doc/dovecot/example-config /opt/homebrew/etc/dovecot
  • Edit file /opt/homebrew/etc/dovecot/dovecot.conf. The other parameters in the dovecot.conf will not be changed.
protocols = imap pop3 lmtp

listen = *
  • Edit file /opt/homebrew/etc/dovecot/conf.d/10-mail.conf.
### This set the mail folder to ~/Maildir. Change it to your own choice.
mail_location = maildir:~/Maildir

last_valid_gid = 100
  • Edit file /opt/homebrew/etc/dovecot/conf.d/15-lda.conf.
### Set the postmaster address.
postmaster_address = username1@mailserver.my.home
  • Edit file /opt/homebrew/etc/dovecot/conf.d/20-lmtp.conf.
protocol lmtp {
  mail_plugins = quota sieve
}
  • Edit file /opt/homebrew/etc/dovecot/conf.d/10-master.conf.
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0600
    user = postfix
  }
}
  • Edit file /opt/homebrew/etc/dovecot/conf.d/10-ssl.conf.
ssl = yes
ssl_cert = </etc/certs/mailserver.my.home.crt
ssl_key = </etc/ssl/private/mailserver.my.home.key

ssl_ca = </etc/ssl/certs/myhomeCAChain.pem
  • Edit file /opt/homebrew/etc/dovecot/conf.d/10-logging.conf.
log_path = /var/log/dovecot.log
info_log_path = /var/log/dovecot-info.log
debug_log_path = /var/log/dovecot.log
auth_verbose = yes
auth_verbose_passwords = yes
auth_debug = yes
auth_debug_passwords = yes
mail_debug = yes
verbose_ssl = yes

Create the user _dovecot and _dovecot with the following commands:

sudo dscl . -create /Users/_dovenull
sudo dscl . -create /Users/_dovenull UniqueID 301
sudo dscl . -create /Users/_dovenull PrimaryGroupID 301
sudo dscl . -create /Users/_dovenull UserShell /usr/bin/false
sudo dscl . -create /Users/_dovenull RealName "Dovenull"
sudo dscl . -create /Users/_dovenull NFSHomeDirectory /var/empty
sudo dscl . -append /Users/_dovenull RecordName _dovenull

sudo dscl . -create /Groups/_dovenull
sudo dscl . -create /Groups/_dovenull PrimaryGroupID 301
sudo dscl . -append /Groups/_dovenull RecordName dovenull

sudo dscl . -create /Users/_dovecot
sudo dscl . -create /Users/_dovecot UniqueID 302
sudo dscl . -create /Users/_dovecot PrimaryGroupID 302
sudo dscl . -create /Users/_dovecot UserShell /usr/bin/false
sudo dscl . -create /Users/_dovecot RealName "Dovecot"
sudo dscl . -create /Users/_dovecot NFSHomeDirectory /var/empty
sudo dscl . -append /Users/_dovecot RecordName _dovecot

sudo dscl . -create /Groups/_dovecot
sudo dscl . -create /Groups/_dovecot PrimaryGroupID 302
sudo dscl . -append /Groups/_dovecot RecordName dovecot

Fetchmail

  1. Create file /opt/homebrew/etc/fetchmailrc with the following content.

# /etc/fetchmailrc for system-wide daemon mode
# This file must be chmod 0600, owner fetchmail

# The default for this option is 300, which polls the server every 5
# minutes.
#
set daemon      60

# By default, the system-wide fetchmail will output logging messages to
# syslog; uncomment the line below to disable this. This might be useful
# if you are logging to another file using the 'logfile' option.
#
# set no syslog

# Avoid loss on 4xx errors. On the other hand, 5xx errors get more
# dangerous.
#
set no bouncemail

# The following defaults are used when connecting to any server, and can
# be overridden in the server description below.
#
# Set antispam to -1, since it is far safer to use that together with no
# bouncemail.
#
defaults:
  antispam -1
  batchlimit 100

# Example server section.
#
#poll foo.bar.org with protocol pop3
#  user baka there is localbaka here smtphost smtp.foo.bar.org;
#
  1. Add a poll command to the created file /opt/homebrew/etc/fetchmailrc with account informations of pop3 or imap.

    poll pop3.example.com with protocol pop3
      user 'pop3_username1' there with password 'pop3_password1' is 'username1' here options no keep ssl
    

Start the services

Dovecot

  1. To start the dovecot service sudo brew services start dovecot

    This will create the file /Library/LaunchDaemons/homebrew.mxcl.dovecot.plist.

  2. To stop the dovecot service sudo brew services stop dovecot

  3. To restart the dovecot service sudo brew services restart dovecot

  4. Check that daemon has started.

    sudo launchctl list | grep org.postfix
    
  5. Check the listen ports 110, 143, 993 and 995 with: sudo lsof -i -P | grep LISTEN

  6. View log files sudo tail -F /var/log/dovecot.log and sudo tail -F /var/log/dovecot-info.log.

Postfix

With the following instructions configure postfix as service in macOS with launchctl.

  1. Copy the postfix master plist out of System folder.
sudo cp /System/Library/LaunchDaemons/com.apple.postfix.master.plist /Library/LaunchDaemons/org.postfix.custom.plist
  1. sudo vi /Library/LaunchDaemons/org.postfix.custom.plist

  2. Change the label value from com.apple.postfix.master to org.postfix.custom

    Remove these lines to prevent exiting after 60s

      <string>-e</string>
      <string>60</string>
    

    Add these lines before </dict>

      <key>KeepAlive</key>
      <true/>
      <key>RunAtLoad</key>
      <true/>
    
  3. Relaunch the daemon.

    sudo launchctl unload /Library/LaunchDaemons/org.postfix.custom.plist
    sudo launchctl load /Library/LaunchDaemons/org.postfix.custom.plist
    
  4. Check that daemon has started.

    sudo launchctl list | grep org.postfix
    
  5. Check the listen ports 25 and 587 with: sudo lsof -i -P | grep LISTEN

  6. Check log with log stream --predicate '(process == "smtpd") || (process == "smtp")' --info

Fetchmail

  1. Create and edit the file with sudo vi /Library/LaunchDaemons/home.my.fetchmail.plist.
  2. Insert the following lines into /Library/LaunchDaemons/home.my.fetchmail.plist.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>home.klaffer.fetchmail</string>
        <key>ProgramArguments</key>
        <array>
                <string>/opt/homebrew/bin/fetchmail</string>
                <string>--verbose</string>
                <string>-f</string>
                <string>/opt/homebrew/etc/fetchmailrc</string>
                <string>--logfile</string>
                <string>/opt/homebrew/var/log/fetchmail.log</string>
        </array>
        <key>KeepAlive</key>
        <true/>
        <key>RunAtLoad</key>
        <true/>
        <key>UserName</key>
        <string>username1</string>
</dict>
</plist>
  1. Start the fetchmail service with: sudo launchctl load -w /Library/LaunchDaemons/home.klaffer.fetchmail.plist

  2. Check the log file: tail -F /opt/homebrew/var/log/fetchmail.log

Configure email client like Mozilla Thunderbird

  1. Setup Existing E-Mail-Address

    • Your Name: Firstname Lastname
    • E-Mail-Address: username1@mailserver.my.home
    • Password: Your user password (in macOS)
  2. Setup SMTP which we use in this example configuration:

    • First SMTP-Server

      • Description: email_address1@example.com
      • Server: mailserver.my.home
      • Port: 587
      • Username: Your username (in macOS)
    • Second SMTP-Server

      • Description: email_address2@example.net
      • Server: mailserver.my.home
      • Port: 587
      • Username: Your username (in macOS)
    • ...

  3. Setup account settings

    • E-Mail-Address: email_address1@example.com (change it from username1@mailserver.my.home)
    • Outgoing-Mail-Server (SMTP): First SMTP-Server (from list)
  4. Setup more identities

    • Same like point 3. for the first account
    • ...

Hope it helps and works for you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment