Greyfix

Copyright 2007, 2008, 2009, 2013 by Kim Minh Kaplan

Greyfix is the greylisting policy daemon for Postfix written by Kim Minh Kaplan. Greylisting is an anti spam technique described by Evan Harris. Postfix is a popular mail transport agent developped by Wietse Zweitze Venema. Greyfix uses Postfix policy mechanism to enable greylisting with Postfix.

It is recommended that you use at least version 0.3.8.

Latest version

Stable version

greyfix-0.4.0.tar.gz (signature)

The database format has changed. Greyfix will automatically upgrade it’s database. But you will not be able to downgrade it.

Features

Requirements

Quickstart

Build and install Greyfix

Greyfix uses GNU’s build system. To install the greyfix daemon just type the following commands:

$ gzip -cd greyfix-0.4.0.tar.gz | tar xf -
$ cd greyfix-0.4.0
$ ./configure --localstatedir=/var
$ make
$ su -c 'make install'

Configure Postfix

First you need to create the directory where Greyfix stores its database:

$ install -d -o nobody -m 700 /var/lib/greyfix

Edit Postfix’s master configuration file, /etc/postfix/master.cf, and add the following (if you are running Solaris see below):

greyfix    unix  -       n       n       -       -       spawn
  user=nobody argv=/usr/local/sbin/greyfix -/ 24 -6 56

Edit Postfix’s main configuration file, /etc/postfix/main.cf and add the following (not for Solaris):

smtpd_recipient_restrictions = permit_mynetworks,
  reject_unauth_destination,
  check_policy_service unix:private/greyfix

Finally have postfix reload its configuration with postfix reload.

Detailed instructions

Compile Berkeley DB 4.8.30

Most distributions do not provide Berkeley DB 4 anymore. You can download Berkeley DB 4.8 from Oracle (MD5 f80022099c5742cd179343556179aa8c, SHA1 ab36c170dda5b2ceaad3915ced96e41c6b7e493c, SHA256 e0491a07cdb21fb9aa82773bbbedaeb7639cbd0e7f96147ab46141e0045db72a). Then compile it with:

$ gzip -cd db-4.8.30.tar.gz | tar xf -
$ cd db-4.8.30/build_unix
$ find .. -name atomic.h -exec sed -i- -e 's/__atomic_compare_exchange(/__atomic_compare_exchange_int(/' '{}' +
$ ../dist/configure CFLAGS="-Wno-implicit-function-declaration -Wno-implicit-int -Wno-deprecated-non-prototype" --disable-shared --enable-smallbuild
$ make

Of note: * with current GCC compilers we need to rename a function that conflicts with GCC’s builtin. It’s what the find+sed does, * Berkeley DB configure script uses an old C style that is not supported anymore. The -Wno-implicit-function-declaration -Wno-implicit-int options enables this dialect, * the -Wno-deprecated-non-prototype option just silences warnings spam.

Optionally you may want to actually install Berkeley DB. This will install the includes, library, documentation and utilities in /usr/local/BerkeleyDB.4.8.

$ su -c 'make install'

Build and install Greyfix

Greyfix uses GNU’s build system. To install the greyfix daemon in /usr/local/sbin just type the following commands:

$ gzip -cd greyfix-0.4.0.tar.gz | tar xf -
$ cd greyfix-0.4.0
$ ./configure CPPFLAGS=-I/path/to/db-4.8.30/build_unix LDFLAGS=-L/path/to/db-4.8.30/build_unix --localstatedir=/var
$ make
$ su -c 'make install'

The CPPFLAGS and LDFLAGS arguments direct the build process to use the Berkeley DB library that we just built in the previous step. The configure script supports the usual GNU configure options; you can type ./configure --help to get a list of options.

Configure Postfix

Edit Postfix’s master configuration file, /etc/postfix/master.cf, and add the following (if you are running Solaris see below):

greyfix    unix  -       n       n       -       -       spawn
  user=nobody argv=/usr/local/sbin/greyfix -/ 24 -6 56

Edit Postfix’s main configuration file, /etc/postfix/main.cf and add the following (not for Solaris):

smtpd_recipient_restrictions = permit_mynetworks,
  reject_unauth_destination,
  check_policy_service unix:private/greyfix

If there is already a smtpd_recipient_restrictions configuration line you should edit it rather than add a new one. The important part for Greyfix is that you should add check_policy_service unix:private/greyfix to it.

Finally have postfix reload its configuration with postfix reload.

Solaris

https://www.postfix.org/SMTPD_POLICY_README.html notes that some old Solaris or Postfix combinations do not work reliably with UNIX-domain sockets. Use TCP sockets instead. Here is what you should add to your /etc/postfix/master.cf:

127.0.0.1:9998  inet  n       n       n       -       9       spawn
  user=nobody argv=/usr/local/sbin/greyfix -/ 24 -6 56

and to your /etc/postfix/main.cf:

smtpd_recipient_restrictions = permit_mynetworks,
  reject_unauth_destination,
  check_policy_service inet:127.0.0.1:9998
127.0.0.1:9998_time_limit = 3600

Usage

greyfix [-V] [-v] [-d] [-h <Berkeley DB home directory>]
    [-g <greylist delay>]
    [-b <bloc maximum idle>] [-p <pass maximum idle>]
    [-r <reject action>] [-G <greylisted action>]
    [-/ <network bits>] [-6 <network bits]
    [--dump-triplets] [--help]

    -b <seconds>, --bloc-max-idle <seconds>

        This determines how many seconds of life are given to a record
        that is created from a new mail (ip, from, to) triplet.  Note
        that the window created by this setting for passing mails is
        reduced by the amount set for --greylist-delay.  NOTE: See
        also --pass-max-idle.  Defaults to 18000 (5 hours).

    -d, --debug

        Debug logging

    -g <seconds>, --greylist-delay <seconds>

        This determines how many seconds we will block inbound mail
        that is from a previously unknown (ip, from, to) triplet.  If
        it is set to zero, incoming mail association will be learned,
        but no deliveries will be tempfailed.  Use a setting of zero
        with caution, as it will learn spammers as well as legitimate
        senders.  Defaults to 3480 (58 minutes).

    -h <Berkeley DB home directory>, --home <Berkeley DB home directory>

        Location of the Berkeley DB environment home location (the
        default is autoconf's $localstatedir/greyfix
        i.e. /usr/local/var/lib/greyfix). You probably want to change
        that to /var/lib/greyfix.

    --help

        Show usage information.

    -p <seconds>, --pass-max-idle <seconds>

        How much life (in secs) to give to a record we are updating
        from an allowed (passed) email.

        The default is 36 days, which should be enough to handle
        messages that may only be sent once a month, or on things like
        the first monday of the month (which sometimes means 5 weeks).
        Plus, we add a day for a delivery buffer.

    -r <reject action>, --reject-action <reject action>

        The reject action directive that will be used.  See access(5)
        for valid actions.  The string expands %d to the number of
        seconds, %p to the empty string if %d expands to 1 or "s"
        otherwise, %s to " " and %% to "%".

        The default is "DEFER_IF_PERMIT Greylisted by Greyfix X.Y.Z,
        try again in %d second%p.  See
        https://www.kim-minh.com/pub/greyfix/ for more information.".

    -G <greylisted action>, --greylisted-action <greylisted action>

        The action that will be used the first time a triplet passes
        greylisting.  Same expansion as for --reject-action.

        The default is "PREPEND X-Greyfix: Greylisted by Grefix X.Y.Z
        for %d second%p.  See https://www.kim-minh.com/pub/greyfix/ for
        more information."

    -v, --verbose

        Verbose logging

    -V, --version

        Show version information.

    -/ <nbits>, --network-prefix <nbits>

        Only consider the first <nbits> bits of an IPv4 address.
        Defaults to 32 i.e. the whole adresse is significant.

    -6 <nbits>, --network6-prefix <nbits>

        Only consider the first <nbits> bits of an IPv6 address.
        Defaults to 128 i.e. the whole adresse is significant.

    --dump-triplets

        Dump the triplets database to stdout.  Mostly for debugging
        purposes.

Notes

Debian 13

If you are using Debian 13, Postfix is started by systemd with ProtectSystem=full. This prevents write access to files under /usr/. With the default Autoconf setting of --localstatedir=/usr/local/var Greyfix will fail and log:

Sep 10 12:30:08 enhd greyfix[6580]: dbenv->open: No such file or directory (/usr/local/var/lib/greyfix)
Sep 10 12:30:08 enhd greyfix[6580]: error: cleaning up
Sep 10 12:30:08 enhd greyfix[6580]: error: exiting after cleanup
Sep 10 12:30:08 enhd postfix/spawn[6578]: warning: command /usr/local/sbin/greyfix exit status 1
Sep 10 12:30:08 enhd postfix/smtpd[6574]: warning: premature end-of-input on private/greyfix while reading input attribute name
Sep 10 12:30:08 enhd postfix/smtpd[6574]: warning: problem talking to server private/greyfix: Application error

or

Sep 10 13:42:49 enhd greyfix[11605]: dbenv->open: Permission denied (/usr/local/var/lib/greyfix)
…

or

Sep 10 13:42:49 enhd greyfix[11605]: dbenv->open: Read-only file system (/usr/local/var/lib/greyfix)
…

One solution is to configure Greyfix to put its state database under /var. You can do this at compile time with ./configure --localstatedir=/var. Or you can add -h /var/lib/greyfix as an argument to greyfix in master.cf.

Another solution is to change the service file to allow access to the database under /usr. To do this add this line to postfix.service (systemctl edit postfix):

ReadWritePaths=/usr/local/var/lib/greyfix

then run:

systemctl restart postfix

Database location

GNU Autoconf’s default value for $(localstatedir) is /usr/local/var which is quite different from what most Unix distribution use. You’ll probably want to invoke configure like this:

$ ./configure --localstatedir=/var

This makes Greyfix DB be located in /var/lib/greyfix. Alternatively you can use the -h <DB home> command line option but do not forget to create the directory and give it correct permissions so that Greyfix can access it.

Logs

Greyfix uses syslog with facility LOG_MAIL. As such the log messages should appear along postfix’s.

Whitelisting

Should you need some sort of whitelisting for some servers you will find this feature already built into Postfix. Therefore refer to its extensive documentation. As a quick example to get you started create a file called /etc/postfix/whitelist_ip, each line consisting of the IP address or prefix you need whitelisted followed by the word OK (see the manual for access(5) for more on the format of this file):

# /etc/postfix/whitelist_ip
127.0.0.1 OK
192.168   OK
10        OK

Turn this into a Postfix map file with:

$ postmap /etc/postfix/whitelist_ip

Then add that as a check_client_access lookup before Greyfix therefore bypassing greylisting:

smtpd_recipient_restrictions = permit_mynetworks,
  reject_unauth_destination,
  check_client_access hash:/etc/postfix/whitelist_ip,
  check_policy_service unix:private/greyfix

A possible starting list of hosts to whitelist is whitelist_ip.txt. If you have downloaded that file you can easily create your whitelist_ip file:

# sed -e '/^[0-9]/s/\([.0-9]*\).*/\1 OK/' whitelist_ip.txt >/etc/postfix/whitelist_ip

Multiple mail exchangers (MX)

If you have multiple MX on your domain then greylisting has to be enabled on all of them to be effective. Otherwise a spamer will just pass through the MX that has no greylisting enabled. But if you install Greyfix on each of your MX, mail can be very long to come through as each of them is ignorant that a sender has already been greylisted on one of the other MX.

In that case you have to use a single Greyfix server and have each Postfix on your MX connect to that Greyfix instance. Let’s pretend we handle mail for the domain mydomain.example using the MX mx1.mydomain.example, mx2.mydomain.example and mx3.mydomain.example. We decide to install Greyfix on greyfix.mydomain.example port 50804.

Setting up the Greyfix server

Greyfix must be launched from a super-server like inetd. First you should add a line to the /etc/services file of greyfix.mydomain.example:

greyfix         50804/tcp       # Postfix greylisting daemon

The inetd configuration requires that you add a line to /etc/inetd.conf:

greyfix stream tcp nowait nobody /usr/local/sbin/greyfix greyfix -/ 24 -6 56

Remember to have inetd reload its configuration file (kill -1 $pid_of_inetd should do the trick).

If you have experience using xinetd or other super-server examples are welcome.

Configuring Postfix

Each MX must now be setup to query that particular Greyfix server. On mx1.mydomain.example, mx2.mydomain.example and mx3.mydomain.example use a Postfix /etc/postfix/main.cf with something like:

smtpd_recipient_restrictions = permit_mynetworks,
  reject_unauth_destination,
  check_policy_service inet:greyfix.mydomain.example:50804
greyfix.mydomain.example:50804_time_limit = 3600

Caveats

When you do this the Greyfix server becomes a single point of failure so you should carefully consider the pros and cons of such a setup.

You should protect the Greyfix service from access from unauthorized parties either putting it behind a firewall or enabling TCP Wrapper: Greyfix itself does not provide any access control.

TODO

Development

The source repository is hosted on Codeberg: https://codeberg.org/KMK/greyfix

Greyfix makes use of the GNU configuration framework to build: autoconf-2.71 and automake-1.16.5. When you first checkout the source, it is missing the configure script and some of its support files. You must first run

$ ./bootstrap

to get the GNU tools to copy them in your directory.

BUGS

Bugs are filed on Greyfix’s issue page. To report a bug see first check that it is not already present in the list. Then you can create a New issue.

Older versions

Note that version 0.3.8 fixes important bugs. Do not use earlier versions.