Postfix Submission Server

In this post I will go over how I set up a server for accepting emails over the internet from authorized users. In my case the users are those with mobile phones and tablets.

I am using Postfix 3.3.2 on Debian 9.6. Auxiliary programs include: clamav, sophos-av, and mimedefang. All servers are hosted locally as we do not believe in the cloud providers.

This server is directly accessed from the internet so I have limited its functionality and isolated it physically (it lives in the DMZ). Its only purpose is to accept email from selected users and relay them to an internal “smart” host. It also proxies IMAP access for those same users. This allows the mobile phones and tablets to use the “private” email.

So I installed the following packages (not all of which are used):

apt-get install postfix postfix-cdb postfix-doc postfix-ldap postfix-pcre postfix-sqlite mimedefang amavisd-new clamav clamav-base clamav-daemon clamav-docs clamav-freshclam clamav-unofficial-sigs clamdscan libclamav7 libclamunrar7 altermime ldap-utils libldap-common libldap-2.4-2 libnet-ldap-perl libsasl2-modules-ldap spamassassin spamc sa-compile

As this server is for submission I do not need to worry about SPF, DKIM, or DMARC. The smart host will take care of those for me if required. This server also sits behind a NAT firewall so only the required ports are forwarded to it.

I wanted to have more than one anti-virus engine checking each message. So I used VirusTotal to get a good list to check against. You can see my research here. I ended up just adding Sophos to the server. I downloaded sav-linux-free-9.tgz and installed it.

Configuring Mimedefang

Edit the /etc/default/mimedefang file setting the following:

SOCKET=inet:10028
MD_ALLOW_GROUP_ACCESS=yes
MX_EMBED_PERL=yes
LOOPBACK_RESERVED_CONNECTIONS=-1
ALLOW_NEW_CONNECTIONS_TO_QUEUE=no
MX_RECIPOK_PERDOMAIN_LIMIT=0
SUBFILTER=/etc/mimedefang-filter

Since mimedefang is a framework where you can really do a lot of things to an email message I am only including some basic settings from my mimedefang-filter file:

#***********************************
$MaxMIMEParts = 50;

$ClamdSock = '/var/run/clamav/clamd.ctl';
$Features{"Virus:CLAMD"} = '/usr/sbin/clamd';
$Features{"Virus:CLAMAV"} = '/usr/bin/clamscan';
$Features{"Virus:SOPHOS"} = '/usr/local/bin/sweep';
$Features{"Virus:SAVSCAN"} = '/opt/sophos-av/bin/savscan';

#************************************
# Set various stupid things your mail client does below.

Then in the filter_begin procedure make sure the virus scanning is enabled.

Configuring clamav

Edit the /etc/clamav/clamd.conf file. I do not think I made any changes but here is what I have:

#Automatically Generated by clamav-daemon postinst
#To reconfigure clamd run #dpkg-reconfigure clamav-daemon
#Please read /usr/share/doc/clamav-daemon/README.Debian.gz for details
LocalSocket /var/run/clamav/clamd.ctl
FixStaleSocket true
LocalSocketGroup clamav
LocalSocketMode 666
#TemporaryDirectory is not set to its default /tmp here to make overriding
# the default with environment variables TMPDIR/TMP/TEMP possible
User clamav
ScanMail true
ScanArchive true
ArchiveBlockEncrypted false
MaxDirectoryRecursion 15
FollowDirectorySymlinks false
FollowFileSymlinks false
ReadTimeout 180
MaxThreads 12
MaxConnectionQueueLength 15
LogSyslog false
LogRotate true
LogFacility LOG_LOCAL6
LogClean false
LogVerbose false
DatabaseDirectory /var/lib/clamav
OfficialDatabaseOnly false
SelfCheck 3600
Foreground false
Debug false
ScanPE true
MaxEmbeddedPE 10M
ScanOLE2 true
ScanPDF true
ScanHTML true
MaxHTMLNormalize 10M
MaxHTMLNoTags 2M
MaxScriptNormalize 5M
MaxZipTypeRcg 1M
ScanSWF true
DetectBrokenExecutables false
ExitOnOOM false
LeaveTemporaryFiles false
AlgorithmicDetection true
ScanELF true
IdleTimeout 30
CrossFilesystems true
PhishingSignatures true
PhishingScanURLs true
PhishingAlwaysBlockSSLMismatch false
PhishingAlwaysBlockCloak false
PartitionIntersection false
DetectPUA false
ScanPartialMessages false
HeuristicScanPrecedence false
StructuredDataDetection false
CommandReadTimeout 5
SendBufTimeout 200
MaxQueue 100
ExtendedDetectionInfo true
OLE2BlockMacros false
ScanOnAccess false
AllowAllMatchScan true
ForceToDisk false
DisableCertCheck false
DisableCache false
MaxScanSize 100M
MaxFileSize 25M
MaxRecursion 16
MaxFiles 10000
MaxPartitions 50
MaxIconsPE 100
PCREMatchLimit 10000
PCRERecMatchLimit 5000
PCREMaxFileSize 25M
ScanXMLDOCS true
ScanHWP3 true
MaxRecHWP3 16
StatsEnabled false
StatsPEDisabled true
StatsHostID auto
StatsTimeout 10
StreamMaxLength 25M
LogFile /var/log/clamav/clamav.log
LogTime true
LogFileUnlock false
LogFileMaxSize 0
Bytecode true
BytecodeSecurity TrustSigned
BytecodeTimeout 60000

I did change /etc/clamav/freshclam.conf where I set the number of checks to 12:

#Automatically created by the clamav-freshclam postinst
#Comments will get lost when you reconfigure the clamav-freshclam package
DatabaseOwner clamav
UpdateLogFile /var/log/clamav/freshclam.log
LogVerbose false
LogSyslog false
LogFacility LOG_LOCAL6
LogFileMaxSize 0
LogRotate true
LogTime true
Foreground false
Debug false
MaxAttempts 5
DatabaseDirectory /var/lib/clamav
DNSDatabaseInfo current.cvd.clamav.net
ConnectTimeout 30
ReceiveTimeout 30
TestDatabases yes
ScriptedUpdates yes
CompressLocalDatabase no
SafeBrowsing false
Bytecode true
NotifyClamd /etc/clamav/clamd.conf
#Check for new database 24 times a day
Checks 12
DatabaseMirror db.local.clamav.net
DatabaseMirror database.clamav.net

In /etc/clamav-unofficial-sigs.conf.d I added 50-local.conf:

#remove the SecuruteInfo Databases as they are no longer available
si_dbs=""
#remove the MalwarePatrol Databases as they are now different
mbl_dbs=""
#fix the SaneSecurity databases
ss_dbs="
blurl.ndb
junk.ndb
jurlbl.ndb
phish.ndb
rogue.hdb
sanesecurity.ftm
scam.ndb
sigwhitelist.ign2
spamattach.hdb
spamimg.hdb
malwarehash.hsb
hackingteam.hsb
badmacro.ndb
winnow.attachments.hdb
winnow_bad_cw.hdb
winnow_extended_malware.hdb
winnow_malware.hdb
winnow_malware_links.ndb
winnow_phish_complete_url.ndb
MiscreantPunch099-Low.ldb
doppelstern.hdb
bofhland_cracked_URL.ndb
bofhland_malware_attach.hdb
bofhland_malware_URL.ndb
bofhland_phishing_URL.ndb
crdfam.clamav.hdb
phishtank.ndb
porcupine.ndb
"

Configuring saslauthd

Edit the /etc/default/saslauthd file:

START=yes
DESC="SASL Authentication Daemon"
NAME="saslauthd"
MECHANISMS="ldap"
MECH_OPTIONS=""
THREADS=5
OPTIONS="-c -m /var/run/saslauthd"

Edit the /etc/saslauthd.conf file:

ldap_servers:           ldap://192.168.0.9
ldap_search_base: cn=Users,dc=samdom,dc=example,dc=net
ldap_filter: (sAMAccountName=%U)
ldap_bind_dn: cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
ldap_bind_pw: password

ldap_use_sasl: no
ldap_referrals: yes
ldap_auth_method: bind
ldap_mech: simple
ldap_start_tls: yes
ldap_tls_check_peer: no

Test this configuration using:

testsalsauthd -u useraccount -p password -f /var/runsaslauthd/mux

Configuring Postfix

I set my domains in /etc/postfix/access_sender:

@example.net    OK
@example.com OK

I set /etc/postfix/sasl/smtpd.conf as:

pwcheck_method: saslauthd
mech_list: PLAIN LOGIN

I edited /etc/postfix/master.cf making sure nothing was chroot. I can not run chroot as other programs need to interact. I set the submission entry as follows:

submission inet n       -       n       -       -       smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=reject_sender_login_mismatch
-o smtpd_recipient_restrictions=reject_non_fqdn_recipient,permit_sasl_authenticated,reject_unauth_destination,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_milters=inet:10028

For amavisd-new I also added the following to /etc/postfix/master.cf:

127.0.0.1:10025 inet    n       -       n       -       -       smtpd -v
-o syslog_name=postfix/amavis2
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_helo_restrictions=
-o smtpd_client_restrictions=
-o smtpd_sender_restrictions=
-o smptd_recipient_restrictions=permit
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes
-o smtpd_error_sleep_time=0
-o smtpd_soft_error_limit=1001
-o smtpd_hard_error_limit=1000
-o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
-o smtpd_authorized_xforward_hosts=127.0.0.0/8

I set a number of parameters in /etc/postfix/main.cf:

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

#appending .domain is the MUA's job.
append_dot_mydomain = no

readme_directory = /usr/share/doc/postfix
html_directory = /usr/share/doc/postfix/html

inet_interfaces = all
inet_protocols = ipv4

message_size_limit = 26214400
maximal_queue_lifetime = 1d
delay_warning_time = 1h

myhostname = dmzemailsub02.example.net
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
myorigin = example.net

strict_rfc821_envelopes = yes
smtpd_reject_unlisted_sender = yes
smtpd_reject_unlisted_recipient = no

smtpd_data_restrictions = reject_unauth_pipelining
smtpd_sender_restrictions = reject_unknown_sender_domain reject_non_fqdn_sender check_sender_access hash:/etc/postfix/access_sender reject
smtpd_helo_restrictions = permit_sasl_authenticated reject_non_fqdn_helo_hostname reject_invalid_helo_hostname
smtpd_etrn_restrictions = permit_mynetworks, reject

smtpd_discard_ehlo_keywords = etrn vrfy

#TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/dmzemailsub02.crt
smtpd_tls_key_file=/etc/ssl/private/dmzemailsub02.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_tls_auth_only = yes
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_protocols = !SSLv2, !SSLv3
smtpd_tls_mandatory_ciphers = high
tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA
smtpd_tls_eecdh_grade = ultra
smtpd_tls_loglevel = 2
smtpd_tls_dh1024_param_file = /etc/postfix/dh2048.pem
smtpd_tls_dh512_param_file = /etc/postfix/dh0512.pem
smtpd_tls_received_header = yes

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

#
#this is a gateway so disable any local transport
#
mydestination =
local_recipient_maps =
local_transport = error:local mail delivery is disabled

#
# relay
#
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
relayhost = [192.168.0.16]:25

mailbox_command = procmail -a "$EXTENSION"
mailbox_size_limit = 0
recipient_delimiter = +

#
# Restrict the sender's MAIL FROM to an address found in AD-DC.
# For a user account, return the sAMAccountNAme where mail= or proxyAddresses=
# For an alias, return the member list where mail=
#
smtpd_sender_login_maps = ldap:/etc/postfix/ldap_sender_login_maps_people.cf, ldap:/etc/postfix/ldap_sender_login_maps_aliases.cf

#
# SASL
#
smtpd_sasl_path = smtpd
smtpd_sasl_type = cyrus
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options

I created the /etc/postfix/ldap_sender_login_maps_people.cf file as:

server_host=192.168.0.24
query_filter=(&(objectclass=user)(!(userAccountControl:1.2.840.113556.1.4.903:=2))(|(mail=%s)(proxyAddresses=smtp:%s)))
search_base=cn=users,dc=samdom,dc=example,dc=net
timeout=60
result_attribute=sAMAccountName
bind=yes
bind_dn=cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
bind_pw=password
server_port=389
scope=one
version=3
start_tls=yes
tls_require_cert=no

I created the /etc/postfix/ldap_sender_login_maps_aliases.cf file as:

server_host=192.168.0.24
query_filter=(&(objectclass=group)(mail=%s))
search_base=cn=users,dc=samdom,dc=example,dc=net
timeout=60
result_attribute=sAMAccountName
special_result_attribute=member
bind=yes
bind_dn=cn=postfix-connect,cn=users,dc=samdom,dc=example,dc=net
bind_pw=password
server_port=389
scope=one
version=3
start_tls=yes
tls_require_cert=no

Permissions

I also edited the /etc/group file to allow the various programs to access the required files:

sasl:x:45:postfix
clamav:x:113:postfix,amavis,defang
ssl-cert:x:114:postfix
postfix:x:115:amavis,defang,clamav,sophosav
amavis:x:117:defang,clamav,sophosav,postfix
defang:x:106:clamav,sophosav,postfix
sophosav:x:1001:postfix,defang,amavis

Things of note

I am using a Samba 4 AD-DC as the target of all LDAP queries. By default the install uses a self-signed certificate and requires a secure connection hence all the start_tls and tls_require_cert entries.

I do have a private certificate authority and I am slowly deploying certificates to the numerous servers and desktops. I have yet to deploy to the new Samba AD-DC though.

I am using a private certificate for Postfix though. This does cause some issues with the mobile devices and definitely causes issues with third party servers such as those operated by Google and Microsoft. By using a private certificate for this server I can prevent people from using gmail and outlook.com to access our “private” email. We do have concerns about using a “public” service to access a “private” system. We are in full control of our data access, there are no leaks via a public cloud system if a public cloud system can not be used.

We do have to trust our private certificate authorities on each mobile device. We do this as part of the device deployment. Some times Apple iPhones remove some of the trusted certificates without notice and the user can not send messages anymore. Re-trusting the certificates makes it work again.

This is not a complete account of what I did so your mileage may vary. Hopefully this post is somewhat helpful though.

Bookmark the permalink.

Comments are closed.