# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
PortSystem 1.0
PortGroup active_variants 1.1
name mail-server
version 1.5
revision 1
categories mail net
platforms darwin
supported_archs noarch
maintainers {ieee.org:s.t.smith @essandess} openmaintainer
license GPL-3
distfiles
description Mail server configuration
long_description Mail server working configuration that \
provides a basic, working, easily modifiable \
mail server. The configuration is built using \
postfix for the MTA, dovecot for the MDA, solr \
for fast search, rspamd for a milter, and \
clamav for email virus scans. The \
configuration includes a surrogate TLS \
certificate, DKIM, and Apple Push Notification \
Service (APNS) capability for iOS devices.
homepage https://www.postfix.org/
set postfix_required_variants \
[lsort {dovecot_sasl pcre smtputf8 tls}]
set dovecot_required_variants \
[lsort {solr}]
depends_lib-append port:dcc \
port:dovecot \
port:dovecot-sieve \
path:bin/openssl:openssl \
port:postfix \
port:rspamd \
port:sf-pwgen
# these dependencies are from variants that may not be active
depends_lib-append port:apache-solr8 \
port:clucene \
port:curl \
port:expat \
port:pcre
depends_run-append port:clamav-server
variant initialize_always \
description {Always initialize all configuration files. Intended \
for development and troubleshooting only. Working deployments \
must disable this variant to prevent configuration files \
being overwritten at the next upgrade. Existing configuration \
files are not overwritten by default.} {
ui_warn \
"
\tAll configuration files will be initialized because
\tthe variant +initialize_always is set. Please disable
\tthis variant for working deployments.
"
}
variant logrotate \
description {Use mail-server logrotate configuration.} {
depends_lib-append port:logrotate
}
default_variants-append +logrotate
use_configure no
require_active_variants postfix ${postfix_required_variants}
require_active_variants dovecot ${dovecot_required_variants}
build {}
set certificates_dir ${prefix}/etc/certificates
set tls_ca_dir ${certificates_dir}/ca.macports
# random 4-word-based passphrase
proc correct_horse_battery_staple {} {
# ignore errors from sf-pwgen if the password is shorter than requested
return \
[join [exec sh -c "sf-pwgen \
--algorithm memorable --count 2 --length 16 \
2>/dev/null || true"] -]
}
destroot {
# configuration design: MacPorts file and/or directory templates installed
# to *.macports, then edited with local network settings, then in
# post-activate copied to actual configuration files if such don't exist
# common
foreach d [list \
var/log/mail \
etc/certificates \
var/spool/postfix${prefix}/var/run/rspamd \
var/run/redis \
] {
xinstall -m 0750 -o root -g mail -d ${destroot}${prefix}/${d}
destroot.keepdirs-append ${destroot}${prefix}/${d}
}
# postfix
xinstall -m 0755 -d ${destroot}${prefix}/etc/postfix
xinstall -m 0755 -d ${destroot}${prefix}/etc/postfix/etc/pam.d
xinstall -m 0750 -o root -g _postfix -d ${destroot}${prefix}/etc/postfix/sasl
xinstall -m 0755 -o root -g _postfix -d ${destroot}${prefix}/var/spool/postfix/etc
xinstall -m 0755 -o root -g mail -d \
${destroot}${prefix}/etc/postfix/etc \
${destroot}${prefix}/etc/postfix/etc/certificates \
${destroot}${prefix}/etc/postfix/etc/pam.d
# *.macports templates
foreach f {
main.cf
master.cf
sasl/passwd
} {
xinstall -m 0644 \
${filespath}/prefix/etc/postfix/${f} \
${destroot}${prefix}/etc/postfix/${f}.macports
}
# generic templates
foreach f {
master.cf.chroot
master.cf.nochroot
smtp.keytab.README.sh
etc/pam.d/smtp
} {
xinstall -m 0644 \
${filespath}/prefix/etc/postfix/${f} \
${destroot}${prefix}/etc/postfix/${f}
}
# chroot jail necessities
xinstall -m 0644 /etc/hosts ${destroot}${prefix}/var/spool/postfix/etc
xinstall -m 0644 /etc/services ${destroot}${prefix}/var/spool/postfix/etc
# dovecot
xinstall -m 0755 -d ${destroot}${prefix}/etc/dovecot
# *.macports templates
foreach d {
conf.d
} {
xinstall -m 0755 -d \
${destroot}${prefix}/etc/dovecot/${d}.macports
destroot.keepdirs-append \
${destroot}${prefix}/etc/dovecot/${d}.macports
}
foreach d {
sieve
sieve-before.d
sieve-afer.d
} {
xinstall -m 0755 -g mail -d \
${destroot}${prefix}/etc/dovecot/${d}.macports
destroot.keepdirs-append \
${destroot}${prefix}/etc/dovecot/${d}.macports
}
foreach f {
dovecot.conf
} {
xinstall -m 0644 \
${filespath}/prefix/etc/dovecot/${f} \
${destroot}${prefix}/etc/dovecot/${f}.macports
}
foreach d {
conf.d
sieve
sieve-before.d
sieve-afer.d
} {
foreach f [glob -nocomplain ${filespath}/prefix/etc/dovecot/${d}/*] {
if {[file isfile ${f}]} {
xinstall -m 0644 ${f} \
${destroot}${prefix}/etc/dovecot/${d}.macports/[file tail ${f}]
}
}
}
# generic templates
foreach d {
default
example-config
etc/pam.d
} {
xinstall -m 0755 -d \
${destroot}${prefix}/etc/dovecot/${d}
}
foreach f {
etc/pam.d/imap
imap.keytab.README.sh
} {
xinstall -m 0644 \
${filespath}/prefix/etc/dovecot/${f} \
${destroot}${prefix}/etc/dovecot/${f}
}
foreach d {
default
example-config
} {
foreach f [glob -nocomplain ${filespath}/prefix/etc/dovecot/${d}/*] {
if {[file isfile ${f}]} {
xinstall -m 0644 ${f} \
${destroot}${prefix}/etc/dovecot/${d}/[file tail ${f}]
}
}
}
# rspamd
xinstall -m 0755 -d ${destroot}${prefix}/etc/rspamd
foreach d {
local.d
} {
xinstall -m 0755 -d ${destroot}${prefix}/etc/rspamd/${d}.macports
foreach f [glob -nocomplain ${filespath}/prefix/etc/rspamd/${d}/*] {
if {[file isfile ${f}]} {
xinstall -m 0644 ${f} \
${destroot}${prefix}/etc/rspamd/${d}.macports/[file tail ${f}]
}
}
}
foreach f {
dkim_paths.map
dkim_selectors.map
} {
xinstall -m 0644 ${filespath}/prefix/etc/rspamd/${f} \
${destroot}${prefix}/etc/rspamd/${f}.macports
}
# redis
foreach f {
redis.conf
} {
xinstall -m 0644 ${filespath}/prefix/etc/${f} \
${destroot}${prefix}/etc/${f}.macports
}
# dcc
xinstall -m 0755 -d ${destroot}${prefix}/etc/dcc
foreach f {
dcc_conf
} {
xinstall -m 0644 ${filespath}/prefix/etc/dcc/${f} \
${destroot}${prefix}/etc/dcc/${f}.macports
}
# logrotate
if { [variant_isset "logrotate"] } {
foreach d {
logrotate.d
} {
xinstall -m 0755 -d ${destroot}${prefix}/etc/${d}.macports
foreach f [glob -nocomplain ${filespath}/prefix/etc/${d}/*] {
if {[file isfile ${f}]} {
xinstall -m 0644 ${f} \
${destroot}${prefix}/etc/${d}.macports/[file tail ${f}]
}
}
}
foreach f {
logrotate.conf
} {
xinstall -m 0644 ${filespath}/prefix/etc/${f} \
${destroot}${prefix}/etc/${f}.macports
}
}
# TLS certificate surrogate
xinstall -m 0755 -d ${destroot}${certificates_dir}
xinstall -m 0700 -d ${destroot}${certificates_dir}/private
destroot.keepdirs-append \
${destroot}${certificates_dir} \
${destroot}${certificates_dir}/private
xinstall -m 0755 -d ${destroot}${tls_ca_dir}
xinstall -m 0755 -d ${destroot}${tls_ca_dir}/intermediate
xinstall -m 0644 \
${filespath}/prefix/etc/certificates/ca/openssl.cnf \
${destroot}${tls_ca_dir}
xinstall -m 0644 \
${filespath}/prefix/etc/certificates/ca/intermediate/openssl_intermediate.cnf \
${destroot}${tls_ca_dir}/intermediate
if { [variant_isset "initialize_always"]
&& [file exists ${tls_ca_dir}]
} {
delete ${tls_ca_dir}.previous
move \
${tls_ca_dir} \
${tls_ca_dir}.previous
}
}
destroot.keepdirs ${destroot}${prefix}/var/log/mail
# Workaround for issue with dovecot version 2.3.17 on macOS 12
# https://www.mail-archive.com/dovecot@dovecot.org/msg84784.html
if {${os.platform} eq "darwin" && ${os.major} >= 21} {
post-destroot {
system -W ${destroot}${prefix}/etc/dovecot/conf.d.macports \
"${patch.cmd} ${patch.pre_args} < ${filespath}/patch-10-master.conf.diff"
}
}
pre-activate {
# mail-server 1.1 inadvertently installed org.macports.logrotate.plist
# into /Library/LaunchDaemons
# https://trac.macports.org/ticket/60273
# This cleanup hack can be removed after December 2023.
if { ![variant_isset "logrotate"] } {
delete /Library/LaunchDaemons/org.macports.logrotate.plist
}
}
proc plutil_startup {plcmds label} {
global prefix startupitem.location
foreach cmd ${plcmds} {
system -W ${prefix}/etc/${startupitem.location}/${label} \
"/usr/bin/plutil ${cmd} ${label}.plist"
}
}
# Network configuration
# hard-coded examples
set host host
set domain domain
set tld tld
set fullhost ${host}.${domain}.${tld}
set domaintld ${domain}.${tld}
set HOST [string toupper ${host}]
set DOMAIN [string toupper ${domain}]
set TLD [string toupper ${tld}]
set FULLHOST [string toupper ${fullhost}]
set DOMAINTLD [string toupper ${domaintld}]
set relayhost mymailrelay.tld
post-activate {
# modify the launch daemons
plutil_startup [list \
"-remove KeepAlive" \
"-insert RunAtLoad -bool YES" \
] \
org.macports.${name}
# Cf. port logrotate's ${prefix}/share/logrotate/org.macports.logrotate.plist.example
if { [variant_isset "logrotate"] } {
plutil_startup [list \
"-remove KeepAlive" \
"-insert RunAtLoad -bool YES" \
"-replace ProgramArguments \
-xml ' \
${prefix}/sbin/logrotate \
${prefix}/etc/logrotate.conf \
'" \
"-insert StartCalendarInterval \
-xml ' \
Hour \
5 \
Minute \
30 \
'" \
] \
org.macports.${name}.logrotate
}
# use network settings for installed example configuration
set fullhost [exec /bin/hostname -f]
if { [llength [split ${fullhost} .]] >= 3 } {
set host [lindex [split ${fullhost} .] 0]
set domaintld [join [lrange [split ${fullhost} .] 1 end] .]
set domain [lindex [split ${domaintld} .] 0]
set tld [lindex [split ${domaintld} .] end]
}
set HOST [string toupper ${host}]
set DOMAIN [string toupper ${domain}]
set TLD [string toupper ${tld}]
set FULLHOST [string toupper ${fullhost}]
set DOMAINTLD [string toupper ${domaintld}]
set rspamd_control_password \
[correct_horse_battery_staple]
set rspamd_control_password_hash \
[exec rspamadm pw --password ${rspamd_control_password}]
ui_msg "Configuring Mail Server with:
host : ${host}
domain : ${domain}
tld : ${tld}
"
proc install_initial_configuration {f_or_d} {
if { [variant_isset "initialize_always"]
&& [file exists ${f_or_d}]
} {
delete ${f_or_d}.previous
move \
${f_or_d} \
${f_or_d}.previous
}
if { [variant_isset "initialize_always"]
|| ![file exists ${f_or_d}]
} {
if { [file isfile ${f_or_d}.macports] } {
xinstall -m 0644 \
${f_or_d}.macports \
${f_or_d}
} elseif { [file isdirectory ${f_or_d}.macports] } {
xinstall -m 0755 -d ${f_or_d}
foreach f [glob -nocomplain ${f_or_d}.macports/*] {
xinstall -m 0644 ${f} \
${f_or_d}/[file tail ${f}]
}
}
}
}
# postfix configuration
foreach f_or_d {
main.cf
master.cf
sasl/passwd
} {
install_initial_configuration ${prefix}/etc/postfix/${f_or_d}
}
# postfix relay host and password surrogate
file attributes ${prefix}/etc/postfix/sasl/passwd \
-group _postfix -permissions 0640
# dovecot configuration
foreach f_or_d {
dovecot.conf
conf.d
sieve
sieve-before.d
sieve-afer.d
} {
install_initial_configuration ${prefix}/etc/dovecot/${f_or_d}
}
xinstall -m 0770 -g mail -d /private/var/mail/${tld}.${domain}.mail/
xinstall -m 0777 -g mail -d /private/var/mail/${tld}.${domain}.mail/attachments/
# solr configuration
if { [variant_isset "initialize_always"] } {
system "sudo -u solr -g solr sh </dev/null || true
solr8 start -p 8983 2>/dev/null || true
solr8 delete -c dovecot 2>/dev/null || true
solr8 stop -p 8983 2>/dev/null || true
SOLR_DELETE_DOVECOT
"
}
# create dovecot core; wrap commands in shell to avoid non-zero
# return value if it already exists
system "sudo -u solr -g solr sh -c \
\"solr8 stop -p 8983 2>/dev/null || true\""
system "sudo -u solr -g solr sh -c \
\"solr8 start -p 8983 2>/dev/null || true\""
set solr_version [exec solr8 version]
system "sudo -u solr -g solr sh -c \
\"solr8 create -c dovecot -n dovecot 2>/dev/null || true\""
system "sudo -u solr -g solr sh -c \
\"solr8 stop -p 8983 2>/dev/null || true\""
if { ![file exists ${prefix}/var/solr/dovecot/conf/] } {
ui_error \
"solr directory ${prefix}/var/solr/dovecot/conf/ doesn't exist."
exit 1
}
# See Tips at https://wiki.dovecot.org/Plugins/FTS/Solr
# wget -O solrconfig.xml https://github.com/dovecot/core/raw/master/doc/solr-config-7.7.0.xml
xinstall -b -B.orig -g solr -m 0644 -o solr \
${filespath}/prefix/var/solr/dovecot/conf/solrconfig.xml \
${prefix}/var/solr/dovecot/conf/solrconfig.xml
# wget -O schema.xml https://github.com/dovecot/core/raw/master/doc/solr-schema-7.7.0.xml
xinstall -b -B.orig -g solr -m 0644 -o solr \
${filespath}/prefix/var/solr/dovecot/conf/schema.xml \
${prefix}/var/solr/dovecot/conf/schema.xml
# Note: solr will replace schema.xml with managed-schema
if { [file exists ${prefix}/var/solr/dovecot/conf/managed-schema.orig] } {
delete ${prefix}/var/solr/dovecot/conf/managed-schema.orig
}
move ${prefix}/var/solr/dovecot/conf/managed-schema \
${prefix}/var/solr/dovecot/conf/managed-schema.orig
# Edit in the Lucene version; see lucene-spec at http://localhost:8983/solr
reinplace -E -q \
"s|()\[\[:digit:\]\]\\.\[\[:digit:\]\]\\.\[\[:digit:\]\]()|\\1${solr_version}\\2|" \
${prefix}/var/solr/dovecot/conf/solrconfig.xml
reinplace -E -q \
"s|||" \
${prefix}/var/solr/dovecot/conf/solrconfig.xml
# rspamd configuration
if { ![file exists ${prefix}/var/lib/rspamd/dkim] } {
xinstall -m 0750 -o _rspamd -g _rspamd -d \
${prefix}/var/lib/rspamd/dkim
}
foreach f_or_d {
local.d
dkim_paths.map
dkim_selectors.map
} {
install_initial_configuration ${prefix}/etc/rspamd/${f_or_d}
}
foreach f [list \
${prefix}/var/lib/rspamd/dkim/${domaintld}.dkim_rsa2048.key \
${prefix}/var/lib/rspamd/dkim/${domaintld}.dkim_ed25519.key
] {
if { ![file exists ${f}] } {
set f_txt [strsed ${f} {s|\.key$|.txt|}]
if { [string match "*rsa2048*" ${f}] } {
system -W ${prefix}/var/lib/rspamd/dkim \
"sh -c \"rspamadm dkim_keygen \
-k ${f} -s dkim_rsa2048 -t rsa -b 2048 \
-d ${domaintld} \
1>${f_txt}\""
} elseif { [string match "*ed25519*" ${f}] } {
system -W ${prefix}/var/lib/rspamd/dkim \
"sh -c \"rspamadm dkim_keygen \
-k ${f} -s dkim_ed25519 -t ed25519 \
-d ${domaintld} \
1>${f_txt}\""
}
file attributes ${f} \
-owner _rspamd -group _rspamd -permissions 0640
file attributes ${f_txt} \
-owner _rspamd -group _rspamd -permissions 0644
}
}
# redis configuration
foreach f_or_d {
redis.conf
} {
install_initial_configuration ${prefix}/etc/${f_or_d}
}
# dcc configuration
foreach f_or_d {
dcc_conf
} {
install_initial_configuration ${prefix}/etc/dcc/${f_or_d}
}
# logrotate configuration
if { [variant_isset "logrotate"] } {
foreach f_or_d {
logrotate.conf
logrotate.d
} {
install_initial_configuration ${prefix}/etc/${f_or_d}
}
}
# TLS certificate surrogate -- certificate authority chain of trust
xinstall -m 0700 -d ${tls_ca_dir}/private
xinstall -m 0700 -d ${tls_ca_dir}/intermediate/private
foreach f { \
openssl.cnf \
intermediate/openssl_intermediate.cnf \
} {
reinplace "s|@TLS_CA_DIR@|${tls_ca_dir}|g" ${tls_ca_dir}/${f}
}
# CA and Intermediate CA encrypted key passphrases in ./private
# CA passphrase
set tls_ca_passphrase \
[correct_horse_battery_staple]
set tls_ca_passphrase_fd \
[open ${tls_ca_dir}/private/passphrase.txt w 0600]
puts ${tls_ca_passphrase_fd} \
${tls_ca_passphrase}
close ${tls_ca_passphrase_fd}
# Intermediate CA passphrase
set tls_intermediate_ca_passphrase \
[correct_horse_battery_staple]
set tls_intermediate_ca_passphrase_fd \
[open ${tls_ca_dir}/intermediate/private/passphrase_intermediate.txt w 0600]
puts ${tls_intermediate_ca_passphrase_fd} \
${tls_intermediate_ca_passphrase}
close ${tls_intermediate_ca_passphrase_fd}
# Client certificate passphrase
set tls_client_certificate_passphrase \
[correct_horse_battery_staple]
set tls_client_certificate_passphrase_fd \
[open ${tls_ca_dir}/intermediate/private/passphrase_client.txt w 0600]
puts ${tls_client_certificate_passphrase_fd} \
${tls_client_certificate_passphrase}
close ${tls_client_certificate_passphrase_fd}
# create the chain of trust
system -W ${tls_ca_dir} \
"sh < serial
touch index.txt
# Intermediate CA certificate
openssl ca -config openssl.cnf \\
-days 730 -notext -md sha384 -extensions v3_intermediate_ca \\
-in intermediate/intermediate.csr.pem \\
-out intermediate/intermediate.cert.pem \\
-passin file:private/passphrase.txt -batch
# Intermediate CA chain
cat intermediate/intermediate.cert.pem ca.cert.pem \\
> intermediate/ca-chain.cert.pem
# Intermediate CA chain openssl verification
openssl verify -CAfile ca.cert.pem intermediate/ca-chain.cert.pem
##################
# Client Certs
##################
# Client certificate encrypted key
openssl genpkey \\
-out intermediate/private/${fullhost}.key.pem \\
-algorithm EC -pkeyopt ec_paramgen_curve:P-384 -aes256 \\
-pass file:intermediate/private/passphrase_client.txt
chmod go-r intermediate/private/${fullhost}.key.pem
# Client certificate decrypted key
openssl pkey -in intermediate/private/${fullhost}.key.pem \\
-passin file:intermediate/private/passphrase_client.txt \\
-out intermediate/private/${fullhost}.key.pem.decrypted
chmod go-r intermediate/private/${fullhost}.key.pem.decrypted
# Client certificate CSR
openssl req -config intermediate/openssl_intermediate.cnf \\
-new -sha384 \\
-key intermediate/private/${fullhost}.key.pem \\
-passin file:intermediate/private/passphrase_client.txt \\
-out intermediate/${fullhost}.csr.pem -batch
# Intermediate CA initialize database
echo 01 > intermediate/serial
touch intermediate/index.txt
# Client certificate
openssl ca -config intermediate/openssl_intermediate.cnf \\
-days 375 -notext -md sha384 \\
-in intermediate/${fullhost}.csr.pem \\
-out intermediate/${fullhost}.cert.pem \\
-passin file:intermediate/private/passphrase_intermediate.txt \\
-subj '/CN=${fullhost}' -batch
# Client chain openssl verification
openssl verify -CAfile intermediate/ca-chain.cert.pem \\
intermediate/${fullhost}.cert.pem
# Client chain of trust
cat intermediate/${fullhost}.cert.pem \\
intermediate/intermediate.cert.pem ca.cert.pem \\
> intermediate/${fullhost}.chain.pem
# Client certificate of the forms
# @host@.@domain@.@tld@.@CERTIFICATE_SHA1@.{cert,key,chain}.pem
TLS_CERTIFICATE_SURROGATE
"
set certificate_sha1 [exec \
openssl x509 -noout -fingerprint -sha1 -inform pem \
-in ${tls_ca_dir}/intermediate/${fullhost}.cert.pem]
regsub -nocase "^sha1 Fingerprint=" ${certificate_sha1} "" certificate_sha1
set certificate_sha1 [string tolower [strsed ${certificate_sha1} "g|:||"]]
xinstall -m 0600 \
${tls_ca_dir}/intermediate/private/${fullhost}.key.pem.decrypted \
${certificates_dir}/private/${fullhost}.${certificate_sha1}.key.pem.decrypted
xinstall -m 0600 \
${tls_ca_dir}/intermediate/private/${fullhost}.key.pem \
${certificates_dir}/private/${fullhost}.${certificate_sha1}.key.pem
xinstall -m 0600 \
${tls_ca_dir}/intermediate/private/passphrase_client.txt \
${certificates_dir}/private/${fullhost}.${certificate_sha1}.key.passphrase
xinstall -m 0644 \
${tls_ca_dir}/intermediate/${fullhost}.cert.pem \
${certificates_dir}/${fullhost}.${certificate_sha1}.cert.pem
xinstall -m 0644 \
${tls_ca_dir}/intermediate/${fullhost}.chain.pem \
${certificates_dir}/${fullhost}.${certificate_sha1}.chain.pem
# configure all template files with local settings
set d_or_f_templates { \
postfix \
dovecot \
rspamd \
redis.conf \
}
if { [variant_isset "logrotate"] } {
append d_or_f_templates { \
logrotate.conf \
logrotate.d \
}
}
foreach d_or_f ${d_or_f_templates} {
fs-traverse f ${prefix}/etc/${d_or_f} {
if { [file isfile ${f}]
&& ![string match ".macports" ${f}]
&& [string match "text/*" \
[lindex [exec /usr/bin/file --mime-type ${f}] end]]
} then {
foreach cmd [list \
"s|@PREFIX@|${prefix}|g" \
"s|@host@.@domain@.@tld@|${fullhost}|g" \
"s|@domain@.@tld@|${domaintld}|g" \
"s|@host@|${host}|g" \
"s|@domain@|${domain}|g" \
"s|@tld@|${tld}|g" \
"s|@HOST@.@DOMAIN@.@TLD@|${FULLHOST}|g" \
"s|@DOMAIN@.@TLD@|${DOMAINTLD}|g" \
"s|@HOST@|${HOST}|g" \
"s|@DOMAIN@|${DOMAIN}|g" \
"s|@TLD@|${TLD}|g" \
"s|@RSPAMD_CONTROL_PASSWORD@|${rspamd_control_password}|g" \
"s|@RSPAMD_CONTROL_PASSWORD_HASH@|${rspamd_control_password_hash}|g" \
"s|@CERTIFICATE_SHA1@|${certificate_sha1}|g" \
"s|@RELAYHOST@|${relayhost}|g" \
] {
reinplace -q ${cmd} ${f}
}
}
}
}
# postfix relay host and password surrogate
system -W ${prefix}/etc/postfix/sasl "postmap passwd"
# postfix permissions
# system -W ${prefix}/etc/postfix "postfix set-permissions"
# dovecot sieve functions
# dovecot must be built with `--with-lucene` for sievec to work here
if { ![catch {set result [registry_active dovecot]}]
&& [string match "*solr*" [lindex [lindex ${result} 0] 3]] } {
foreach d {
sieve
sieve-before.d
sieve-afer.d
} {
foreach f [glob -nocomplain ${prefix}/etc/dovecot/${d}/*.sieve] {
system -W ${prefix}/etc/dovecot/${d} "sievec ${f}"
}
}
} else {
ui_msg "dovecot plugin 'fts_lucene' not installed. Please install:
sudo port -pN install dovecot +[join ${dovecot_required_variants} +]
and rerun `sudo port install ${name}`. Ensure that the sieve scripts
in ${prefix}/etc/dovecot/sieve*/*.sieve are compiled with sievec.
"
}
# PAM authentication
if { ![file exists /etc/pam.d/smtp] } {
xinstall -m 0644 ${prefix}/etc/postfix/etc/pam.d/smtp /etc/pam.d/
}
if { ![file exists /etc/pam.d/imap] } {
xinstall -m 0644 ${prefix}/etc/dovecot/etc/pam.d/imap /etc/pam.d/
}
# TLS PFS
if { ![file exists ${prefix}/var/lib/postfix/dh2048.pem] } {
system -W ${prefix}/var/lib/postfix "sudo -u _postfix openssl dhparam -out dh2048.pem 2048"
}
if { ![file exists ${prefix}/etc/dovecot/dh2048.pem] } {
# create a shorter, faster DH parameter file for the default installation
system -W ${prefix}/etc/dovecot "openssl dhparam -out dh2048.pem 2048"
}
# mail group membership
# dscacheutil -q group -a name mail
foreach u {
_postfix
_dovecot
_dovenull
_rspamd
} {
system "dseditgroup -o edit -a ${u} -t user mail"
}
}
startupitem.create yes
startupitems \
name ${name} \
start {
"port load clamav-server"
"port load apache-solr8"
"port load redis"
"port load dcc"
"port load postfix"
"port load dovecot"
"port load rspamd"
} \
stop {
"port unload apache-solr8"
"port unload dcc"
"port unload postfix"
"port unload dovecot"
"port unload rspamd"
} \
restart {
"port reload apache-solr8"
"port reload redis"
"port reload dcc"
"port unload postfix"
"sleep 1"
"port load postfix"
"port unload dovecot"
"sleep 1"
"port load dovecot"
"port reload rspamd"
}
if { [variant_isset "logrotate"] } {
startupitems-append \
name ${name}.logrotate \
executable ${prefix}/sbin/logrotate
}
notes "A mail server is a complex, interdependent set of tools that must \
all be configured correctly to provide secure, reliable email.
Users must reconfigure this installation for their own system, network, \
and security model specifics by editing all necessary files and checking \
file permissions. A subset of these settings are visible in the files:
port contents mail-server
port file mail-server
Full deployment also requires a working DNS configuration on both the LAN \
and the internet, including SPF and DKIM records, trusted TLS certificates, \
port forwarding, possibly a mail replay, and more.
Postfix and dovecot must be installed with these variants:
sudo port -pN install postfix +[join ${postfix_required_variants} +]
sudo port -pN install dovecot +[join ${dovecot_required_variants} +] \[+apns]
These are the locations and network settings for the default configuration:
MTA (postfix):
${prefix}/etc/postfix/main.cf
${prefix}/etc/postfix/master.cf
ports: smtp/tcp (25), submission/tcp (587)
MDA (dovecot):
${prefix}/etc/dovecot/dovecot.conf
${prefix}/etc/dovecot/conf.d/*
port: imaps/tcp (993)
FTS (solr):
http://localhost:8983/
Milter (rspamd):
${prefix}/etc/rspamd/rspamd.conf
${prefix}/etc/rspamd/local.d/*
A default Rspamd controller password and its hash appear in the files:
${prefix}/etc/dovecot/sieve/train-spam.sh
${prefix}/etc/dovecot/sieve/train-ham.sh
${prefix}/etc/rspamd/local.d/worker-controller.inc
Rspamd controller:
http://localhost:11334/
Spam/Ham training (default behavior):
Move/Copy email to the folders Spam_train or Notspam_train.
The configuration also includes a surrogate TLS certificate and DKIM settings \
that must be changed before deployment.
TLS:
${prefix}/etc/certificates
DKIM:
${prefix}/var/lib/rspamd/dkim
The ports dns-server provide necessary DNS service on the LAN; variant \
+logrotate provides log rotation capabilities:
sudo port install dns-server
sudo port install mail-server +logrotate
This port assume indepedent installation and management of DNS and \
log rotation; mail-server includes example logrotate configuration files \
and a logroate launchdaemon.
The port's launch daemon controls launching for each of the dependendent \
services. These may be controlled independently, e.g.
sudo port load clamav-server
sudo port load apache-solr8
sudo port load redis
sudo port load dcc
sudo port load postfix
sudo port load dovecot
sudo port load rspamd
and if installed independently,
sudo port load dns-server
sudo port load logrotate
TLS certificate updates must be included in mail-server dovecot's \
conf.d/10-ssl.conf, postfix's master.cf, and, if installed, \
calendar-contacts-server's proxy nginx.conf. Instructions are \
included as comments in:
sudo vi ${prefix}/etc/dovecot/conf.d/10-ssl.conf
sudo vi ${prefix}/etc/postfix/main.cf
sudo vi \\
${prefix}/var/calendarserver/Library/CalendarServer/etc/nginx.conf
References:
* http://www.postfix.org/documentation.html
* https://wiki.dovecot.org/
* https://www.rspamd.com/doc/index.html
* https://www.c0ffee.net/blog/mail-server-guide/
* _The Book of Postfix_, by Patrick Koetter and Ralf Hildebrandt
Known issues:
* The Postfix service does not reliably start after reboot, \
presumably due to an issue with launchd. A workaround \
after rebooting is to issue the commands:
sudo port unload postfix ; sleep 5 ; sudo port load postfix"
if { [variant_isset "initialize_always"] } {
if {[exists notes]} {
# leave a blank line after the existing notes
notes-append ""
}
notes-append \
"The variant +initialize_always is set, which initializes \
all configuration files. Please disable this variant for \
working deployments."
}
livecheck.type none