Milter & SPF
Geoffrey D. Bennett

Sendmail Milter (Mail fILTER)
http://www.milter.org/

- An API (protocol + library) to interface with sendmail to read and
  possibly modify message headers and bodies as they are processed.

- An alternative to:

  - delivering to a program (eg. procmail) (which can only touch
    locally delivered messages)

  - configuring sendmail to only queue and running through
    /var/spool/mqueue periodically

  - dual-sendmail setup

  - writing sendmail .cf rules

  - altering sendmail's source code

Interesting points:

- Can reject email based on envelope to/from before accepting the
  mail.  Personally, I get annoyed when I see bounce messages sitting
  around that won't be delivered because the original sender is
  obviously bogus.

- Multiple milters can be installed for one copy of sendmail to use.

- C, Perl, and Python libraries available, and (experimental) C++,
  PHP, and JAVA.

- Milters run as daemons; communication is usually through a
  UNIX-domain socket.

- Connect, HELO, MAIL FROM, RCPT TO, DATA callbacks.

- Return values at each step: continue, reject, discard, accept.

- Message changes: add/replace/delete header, add/delete recipient,
  replace body.

Obvious applications:

- Virus-scanning

- Spam filtering

- Logging

Interesting applications (some implemented at http://www.snert.com/Software/)

- milter-ahead (verify recipient addresses at secondary MX)

- milter-date (validate message dates)

- milter-gris (grey-listing)

- milter-sender (sender address verification)

- per-user filtering before accepting mail (eg. checking quota)

- bouncing too-large or non-subscriber mailing list emails before
  accepting mail

Demonstrating a Milter written using Sendmail::Milter:

- From http://www.city-fan.org/ftp/contrib/perl-modules/ install
  perl-Sendmail-Milter

- Write the milter (see <A HREF="test-milter">this example</A>).

  - No global variables.

- Edit /etc/mail/sendmail.mc then rebuild sendmail.cf

- Run the milter.

- Restart sendmail.

- Test it.

SPF - Sender Policy Framework
(formerly Sender Permitted From)
http://spf.pobox.com/
also see these Linux Journal articles:
http://www.linuxjournal.com/article.php?sid=7327
http://www.linuxjournal.com/article.php?sid=7328

- A way to verify sender domains to help reduce spam, worms, and
  viruses (or at least make them more accountable).

- Personally, I get annoyed when I receive emails from myself that I
  didn't write.

- Like "reverse MX" records:

  - MX records say "these mail servers receive mail for these domains"

  - SPF records say "these mail servers send mail for these domains"

- Uses TXT records beginning with "v=spf1".

- Implementation has two parts:

  - Publish SPF records for your domain (means that other mail servers can
    reject mail that says it's from you but isn't)

  - Adjust your mail server to reject (means that you can reject mail
    that fails SPF checks).

SPF Record Examples:

- "prefixes" (+ (default), -, ?, ~)

- "mechanisms" (a, ip4, mx, ...)

- dig internode.com.au txt

  internode.com.au. IN TXT "v=spf1 +mx +ip4:192.83.231.0/24 +ip4:203.16.214.0/24 ~all"

- dig adam.com.au txt

  adam.com.au. IN TXT "v=spf1 ip4:203.6.132.64/27 ~all"

- dig aol.com txt

  aol.com. IN TXT "v=spf1 ip4:152.163.225.0/24 ip4:205.188.139.0/24 ip4:205.188.144.0/24 ip4:205.188.156.0/23 ip4:205.188.159.0/24 ip4:64.12.136.0/23 ip4:64.12.138.0/24 ptr:mx.aol.com ?all"

- dig netcraft.com.au txt

  netcraft.com.au. IN TXT "v=spf1 ip4:203.16.230.0/23 ip4:150.101.234.78 -all"

- dig april.netcraft.com.au txt

  april.netcraft.com.au. IN TXT "v=spf1 include:netcraft.com.au"

- dig mawsonlakes.org txt

  mawsonlakes.org IN TXT "v=spf1 a mx ptr -all"

- dig websentric.com txt

  websentric.com IN TXT "websentric.com sends no E-mail.  All E-mail claiming to be from websentric.com is forged."
  websentric.com IN TXT "v=spf1 -all"

- funkyness with "exists" and macros:

  "v=spf1 exists:%{ir}.%{l1r+-}._spf.%{d} -all"

  The target-name might expand to "1.2.0.192.someuser._spf.example.com".

Forwarding (eg. .forward files, pobox.com, etc.):

- Breaks ("to make an omlette, you need to break some eggs").

- See SRS (Sender Rewriting Scheme).

Roaming/Remote (or otherwise distant to the domain hoster) Users:

- Changes what we've always told users -- "Use your dial-up ISP's MX".

- Now need to use SMTP AUTH and use the mail server of the ISP who
  hosts your domains.

Installation on RH9/FC1 with sendmail:

From http://www.city-fan.org/ftp/contrib/perl-modules/
and http://www.city-fan.org/ftp/contrib/mail/ install:

perl-Net-CIDR-Lite
perl-Sys-Hostname-Long
perl-Sendmail-Milter
perl-Net-DNS
perl-Mail-SPF-Query
perl-FreezeThaw
perl-MLDBM
perl-Mail-SRS
sendmail-milter-spf

Add these two lines to /etc/mail/sendmail.mc:

define(`confMILTER_MACROS_HELO', confMILTER_MACROS_HELO`, {verify}')dnl
INPUT_MAIL_FILTER(`spf-milter', `S=local:/var/spf-milter/spf-milter.sock, F=T, T=C:4m;S:4m;R:8m;E:16m')dnl

Rebuild sendmail.cf, start spf-milter, and reload sendmail:

m4 < sendmail.mc > sendmail.cf
service spf-milter start
service sendmail reload

Monitor /var/log/maillog

No SPF records:

May 18 17:47:18 april sendmail[11110]: i4I8HFPh011110: from=<clamav-users-admin@lists.sourceforge.net>, size=4410, class=-60, nrcpts=1, msgid=<40A9BD8E.1000403@telkom.co.id>, proto=ESMTP, daemon=MTA, relay=lists.sourceforge.net [66.35.250.206]
May 18 17:47:18 april sendmail[11110]: i4I8HFPh011110: Milter add: header: Received-SPF: none (april.netcraft.com.au: domain of clamav-users-admin@lists.sourceforge.net does not designate permitted sender hosts)
May 18 17:47:18 april sendmail[11112]: i4I8HFPh011110: to=<chris@netcraft.com.au>, delay=00:00:01, xdelay=00:00:00, mailer=local, pri=142817, dsn=2.0.0, stat=Sent

SPF accept:

May 18 06:36:29 april sendmail[23890]: i4HL6NfK023890: from=<Fool@foolsubs.com>, size=4602, class=0, nrcpts=1, msgid=<1084827973213-71792-32035682-2222-m@foolsubs.com>, proto=ESMTP, daemon=MTA, relay=mail2.foolsubs.com [69.25.30.112]
May 18 06:36:29 april sendmail[23890]: i4HL6NfK023890: Milter add: header: Received-SPF: pass (april.netcraft.com.au: domain of fool@foolsubs.com designates 69.25.30.112 as permitted sender)
May 18 06:36:30 april sendmail[23892]: i4HL6NfK023890: to=g, delay=00:00:04, xdelay=00:00:01, mailer=local, pri=34934, dsn=2.0.0, stat=Sent

SPF reject:

May 18 15:04:30 april sendmail[5283]: i4I5YReF005283: Milter: to=<g@netcraft.com.au>, reject=550 5.7.1 [RCPT TO: <g@netcraft.com.au>] Please see http://spf.pobox.com/why.html?sender=geoffrey@netcraft.com.au&ip=195.56.204.159&receiver=april.netcraft.com.au
May 18 15:04:30 april sendmail[5283]: i4I5YReF005283: lost input channel from host-204-159.fibernet.bacs-net.hu [195.56.204.159] to MTA after rcpt
May 18 15:04:30 april sendmail[5283]: i4I5YReF005283: from=<geoffrey@netcraft.com.au>, size=0, class=0, nrcpts=0, proto=SMTP, daemon=MTA, relay=host-204-159.fibernet.bacs-net.hu [195.56.204.159]