Postfix
Installation
We want to enable ClamAV and Spamassassin scanning, but without AMaViS inbetween:
# apt-get install postfix spamassassin spamc clamav-daemon clamsmtp
ClamSMTP is needed as a proxy between the MTA and clamd
. Although clamd
can listen on a network socket, postfix
cannot talk directly to it.
Configuration
This is basically explained here, here's the short version again, with snippets from the configuration files:
master.cf
# ClamSMTPd smtp inet n - - - - smtpd -o content_filter=scan:127.0.0.1:10025 [...] # Maildrop maildrop unix - n n - - pipe flags=DRhu argv=/usr/bin/maildrop -d ${recipient} # SpamAssassin spamassassin unix - n n - - pipe user=mail argv=/usr/bin/spamc -u ${user} -e /usr/sbin/sendmail -oi -f ${sender} ${recipient} # ClamAV scan unix - - n - 16 smtp -o smtp_send_xforward_command=yes 127.0.0.1:10026 inet n - n - 16 smtpd -o content_filter=spamassassin -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks -o smtpd_helo_restrictions= -o smtpd_client_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks_style=host -o smtpd_authorized_xforward_hosts=127.0.0.0/8
Note: somehow it's important that the spamassassin
filter has a user=
option, otherwise we might get errors like:
postfix/pipe[9911]: fatal: missing user= command-line attribute postfix/qmgr[9876]: warning: private/spamassassin socket: malformed response postfix/qmgr[9876]: warning: transport spamassassin failure -- see a previous warning/fatal/panic logfile record for the problem description postfix/master[9874]: warning: process /usr/lib/postfix/pipe pid 9911 exit status 1 postfix/master[9874]: warning: /usr/lib/postfix/pipe: bad command startup -- throttling postfix/error[9922]: DD05E3DD5C: to=<bob@localhost>, relay=none, delay=1.1, delays=0.07/1/0/0.05, dsn=4.3.0, status=deferred (unknown mail transport error
main.cf
A few basic configuration parameters[1]:
[...] home_mailbox = Maildir/ mailbox_command = /usr/bin/maildrop -d ${USER} maildrop_destination_recipient_limit = 1 mailbox_size_limit = 0 message_size_limit[2] = 0 smtpd_tls_cert_file=/etc/ssl/private/server.crt smtpd_tls_key_file=/etc/ssl/private/server.key smtp_tls_security_level[3] = may mydestination = mail.example.com, mail, localhost, 127.0.0.1 mynetworks = 127.0.0.0/8 10.0.0.0/24 [::ffff:127.0.0.0]/104 [::1]/128
Postfix settings can be queried (and modified) via postconf:
$ postconf inet_protocols inet_protocols = ipv4 $ postconf inet_protocols="ipv4 ipv6" $ service postfix restart
clamsmtpd.conf
OutAddress: 10026 Listen: 127.0.0.1:10025 ClamAddress: /var/run/clamav/clamd.ctl TempDirectory: /var/spool/clamsmtp Action: pass
Note: setting TempDirectory
is important too, otherwise clamsmtpd
may not be able to access TMPDIR
, which may be set in root's environment and will produce errors like:
clamsmtpd: 100058: clamav error: /var/spool/clamsmtp/clamsmtpd.1Lp6Vz: Can't create temporary directory ERROR clamsmtpd: 100058: from=alice@example.com, to=bob@localhost, status=CLAMAV-ERROR postfix/smtp[16155]: E563C11E89: to=<bob@localhost>, relay=127.0.0.1[127.0.0.1]:10025, delay=0.08, delays=0.03/0/0.04/0, dsn=4.0.0, status=deferred (host 127.0.0.1[127.0.0.1] said: 451 Local Error (in reply to end of DATA command))
While we're at it, we might want to set "LocalSocketMode 0660
" in /etc/clamav/clamd.conf
to restrict permissions to the control socket. Be sure to make clamav
(i.e. the group "clamd" is running under) the primary group of clamsmtp
:
$ usermod -g clamav -G clamsmtp clamsmtp $ service clamsmtp restart $ ps -e -o pid,user,group,euser,egroup,comm | grep cla[m] 10103 clamav clamav clamav clamav clamd 10396 clamsmtp clamav clamsmtp clamav clamsmtpd
For added security, disable the login shell for the system users:
for i in postfix clamav clamsmtp mail; do echo $i; chsh -s /bin/false $i; done
TODO: When clamsmtp
is scanning, it sets the following variables, which can be used by VirusAction afterwards:
RECIPIENTS SENDER VIRUS SERVER CLIENT REMOTE
I'm still looking for a way to pass these variables to maildrop
...
Rspamd
With Rspamd instead of SpamAssassin:
$ grep milter /etc/postfix/main.cf smtpd_milters = inet:localhost:11332 non_smtpd_milters = inet:localhost:11332 milter_default_action = accept
$ cat /etc/rspamd/rspamd.conf [...] worker "rspamd_proxy" { bind_socket = "localhost:11332"; .include "$CONFDIR/worker-proxy.inc" [...]
DKIM
As OpenDKIM development seems to have stalled, we'll use dkimpy-milter now.[4]
We need to generate some keys:
dknewkey --ktype rsa key0 dknewkey --ktype ed25519 key1
This should have produced:
$ ls -go key* -rw------- 1 420 Jul 12 22:47 key0.dns -rw------- 1 1675 Jul 12 22:47 key0.key -rw------- 1 66 Jul 12 22:47 key1.dns -rw------- 1 44 Jul 12 22:47 key1.key
Note: if opendkim
were installed, RSA key generation would look like this:
$ opendkim-genkey --bits=2048 --hash-algorithms=sha256 --domain=example.net --selector=key0
But opendkim-genkey
is not able to generate Ed25519, so this could be done manually:[5]
$ openssl genpkey -algorithm ed25519 -out key1.private $ openssl pkey -in key1.private -pubout -out key1.txt $ ls -go key* -rw------- 1 1679 Jul 12 22:36 key0.private -rw------- 1 501 Jul 12 22:36 key0.txt -rw------- 1 119 Jul 12 22:35 key1.private -rw------- 1 113 Jul 12 22:35 key1.txt
While key0.txt
already contains the DNS record needed, we'll need to manually construct the same for the Ed25519 key:
$ openssl asn1parse -in key1.txt -offset 12 -noout -out /dev/stdout | openssl base64 | sed 's/^/key1._domainkey IN TXT ( "v=DKIM1; k=ed25519; p=/;s/$/")/' key0._domainkey IN TXT ( "v=DKIM1; k=ed25519; p=U12Vr3xzvXBjzNErIk5CRQxS3NniRxCWocrjnmZWNCs=")
Once the TXT records have been published in DNS, dkimpy-milter
itself can be configured:
$ grep ^[A-Z] /etc/dkimpy-milter/dkimpy-milter.conf Syslog yes UMask 007 Domain example.net KeyFile /etc/dkimkeys/key0.private Selector key0 KeyFileEd25519 /etc/dkimkeys/key1.private SelectorEd25519 key1 Canonicalization relaxed/relaxed Mode sv Socket inet:8892@localhost PidFile /run/dkimpy-milter/dkimpy-milter.pid UserID dkimpy-milter SyslogSuccess yes
With dkimpy-milter
running on localhost:8892
now, we can configure Postfix to use it:
smtpd_milters = inet:localhost:8892 non_smtpd_milters = inet:localhost:8892 # smtpd_milters = unix:private/opendkim # non_smtpd_milters = unix:private/opendkim milter_default_action = tempfail
We'll use milter_default_action with:
tempfail - Reject all further commands in this session with a temporary status code.
DMARC
SPF
TBD
Troubleshooting
Sometimes postfix
is running, but its transports (spam engines, virus scanners) or its destinations (/home not mounted to deliver mails) are still missing. Nasty error messages are generated:
postfix/master[21760]: daemon started -- version 2.5.5, configuration /etc/postfix postfix/qmgr[21763]: 4B63511EA5: from=<alice@example.com>, size=7763, nrcpt=1 (queue active) postfix/qmgr[21763]: warning: connect to transport spamassassin: Connection refused [...] postfix/error[21765]: 4B63511EA5: to=<bob@localhost>, relay=none, status=deferred (mail transport unavailable)
Often it's just a matter of requeueing the messages:
postqueue -p # print mail queue (mailq) postsuper -r ALL # requeue all messages postqueue -f or postfix flush # flush mail queue (exim -qff), should happen automatically[6]
Open Relay Tests
Once our MTA is configured, we should make sure that it's not an open relay. While this could be accomplished by telnet
or netcat, we can use more modern tools as well,[7] like swaks:
$ swaks --to foo@example.org --from root@localhost --server mail.example.net
=== Trying mail.example.net:25...
=== Connected to mail.example.net.
<- 220 mail.example.net ESMTP Postfix
-> EHLO localhost
<- 250-mail.example.net
<- 250-PIPELINING
<- 250-SIZE 10240000
<- 250-VRFY
<- 250-ETRN
<- 250-STARTTLS
<- 250-ENHANCEDSTATUSCODES
<- 250-8BITMIME
<- 250-DSN
<- 250-SMTPUTF8
<- 250 CHUNKING
-> MAIL FROM:<root@localhost>
<- 250 2.1.0 Ok
-> RCPT TO:<foo@example.org>
<** 454 4.7.1 <foo@example.org>: Relay access denied
-> QUIT
<- 221 2.0.0 Bye
=== Connection closed with remote host.
Links
- The GTUBE ("Generic Test for Unsolicited Bulk Email") & EICAR
- SpamAssassinRules -
"if you use spamd, rules placed in user_prefs will be IGNORED by default."
- IPv6 bei localhost -
warning: hostname localhost does not resolve to address ::1
- Re: /var/spool/postfix/private File Permission
- Check a published DKIM Core Key
- Own mail server based on Dovecot, Postfix, MySQL, Rspamd and Debian 9 Stretch
- mailcow