Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages


home | help
SKEM(8)			FreeBSD	System Manager's Manual		       SKEM(8)

     skem -- State KEeping Milter

     skem -h
     skem -v
     skem [-d] [-C directory] [-u uid |	user] -b | -w ip.add.res.s1
	  [ip.add.res.s2 ... ip.add.res.sN]
     skem [-d] [-C directory] [-u uid |	user] [-g seconds] [-B seconds]
	  [-l seconds] [-p pid-filename] [-L]
     skem [-d] [-C directory] [-u uid |	user] [-g seconds] [-B seconds]
	  [-l seconds] [-p pid-filename] [-2] [-4] [-S spamword] [-s pattern]
	  [-r reject-string] socket

     The skem utility is a sendmail milter, that checks	and maintains a	list
     of	whitelisted, temporary banned, and permanently blacklisted IP-ad-
     dresses.  How you obtain the entries is up	to you,	but the	included
     logwatcher	module provides	one possibility.

     The list is stored	in a directory (see the	-C option), each entry being a
     file (usually -- zero sized) or a symlink (usually	-- a "broken" one).
     Such entries are stored efficiently (within the directory itself) and the
     directories are searched using the	hash tables on modern file systems. At
     the same time, they can be	listed,	added, and removed with	the simple
     ls(1), touch(1) and rm(1).

     The current release considers the following attributes of each entry:

     mtime   The time of creation

     mode    Setuid means the entry is "white-"	and setgid -- "blacklisted".

     ctime   The time, this blacklisted	entry last tried to contact us.	Keep-
	     ing this record allows for	periodic purging of blacklist of the
	     IPs not seen in a while, to keep the list from growing indefi-

     This milter does not itself filter	spam, instead it memorizes the ver-
     dicts issued by your other	anti-spam defenses to reduce the system	load
     and resource consumption by temporarily rejecting the relays suspected of
     spamming (banned) and, optionally,	by permanently rejecting the relays
     "convicted" of spamming (blacklisted).

     The idea is to stem the spam from real spam sources, while	reducing the
     ill effects of false-positives to merely delaying,	rather than rejecting
     future messages.

     The following options are available:

     -2	     Do	the check twice	-- at the beginning (connection) and at	the
	     end of headers. This is to	allow a	ban, issued by other milters
	     (and/or our own logwatcher, if enabled) based on the headers ar-
	     riving from a previously not banned source, to have effect.

	     For example, if you have spamtrap-addresses, which	trigger	black-
	     listing, an e-mail	coming from a previously unseen	IP may contain
	     them mixed	with regular addresses.	Without	this option, skem will
	     let the message through, even if subsequent transmissions from
	     this address will be banned. With this option, even the first e-
	     mail will be rejected. Only use if	you employ such	detectors and
	     if	they don't have	the capability to reject the whole message
	     themselves, otherwise it is just a	waste.

     -4	     Use only if sendmail supports closing connection at the conn-
	     time. At the time of this writing (8.12.x being the latest	ver-
	     sion), sendmail must be rebuilt with the -D_FFR_MILTER_421	flag
	     (a	C define). This	lets us	issue temporary	rejections right at
	     the connection time and make sendmail close the connection	on the
	     spot, saving resources. If	the sendmail does not support this,
	     however (and by default it	does not), any attempts	by skem	to do
	     this will be interpreted as its own temporary failures by

	     Without this option, skem will work with any sendmail issuing the
	     temporary bans at the helo-time.  The permanent bans are always
	     acted upon	at the conn-time.

     -B	seconds
	     This specifies the	amount of time a blacklist entry is allowed to
	     exist in the list since it	was last triggered. This is to avoid
	     wasting storage on	IPs, that have once offended us, but are not
	     doing this	any more. The default value is 3 weeks (1814400	sec-

     -C	directory
	     This is the database directory. If	not specified, "." is assumed.
	     skem will chdir(3)	into this directory, and -- if started by root
	     --	will also chroot(3) there.

     -L	     This option will cause skem to run	in the cleanup mode only. If
	     the -l options was	not specified prior, skem will make one
	     cleanup run and exit. Otherwise, it will be doing the cleanups
	     periodically, but without becoming	a milter.

     -S	spamword
	     Tells the logwatcher to treat the occurrence of this spamword in
	     addition to the reject (see -r below) and pattern (see -s below)
	     as	a sign of definite spam. This will cause the logwatcher	to is-
	     sue a permanent (setgid), rather than a normal temporary ban.
	     Use carefully. Without it logwatcher will only create temporary

     -b	ip1 [ip2 ... ipN]
	     Blacklist the specified IPs and exit. If an entry already exists,
	     it	is skipped without change of status.

     -d	     Allow logging at the LOG_DEBUG level. Without this, skem only
	     logs messages up to the LOG_INFO level.

     -g	seconds
	     Expire the	temporary bans after this many seconds.	 The default
	     is	an hour	and a half (1800 seconds). Keep	it below 4 hours, or
	     some legitimate mail, that	might be delayed on the	temporarily
	     banned server may generate	a warning to the sender. Such warnings
	     are remarkably confusing to the laymen and	women, who can't, for
	     some reason, distinguish them from	real bounces --	even though
	     TO	RESEND..."

     -h	     Print out an the help screen and exit with	code 0.

     -l	seconds
	     Perform the cleanup this often. If	not specified, no cleanup will
	     be	performed at all (in which case, you may wish to rebuild skem
	     with the -DSKEM_NO_CLEANUP	define). During	cleanups, the expired
	     temporary bans are	removed, as well as the	permanent bans,	which
	     were not triggered	for the	last 3 weeks (but see the -B option

     -p	filename
	     Write our process ID into the file	specified.

     -r	reject-string
	     Tells logwatcher what to look for in the log messages. The	de-
	     fault is "reject=5".

     -s	pattern
	     This turns	on the logwatcher thread, which	reads log entries from
	     stdin and applies the pattern to those of them, that contain the
	     reject-string.  The pattern is expected to	be either a "-"	(dash)
	     to	use the	default	pattern, or an extended	regular	expression
	     with exactly one parenthesized expression -- the IP address of
	     the relay.

     -u	username | uid
	     Switch identity to	the specified username or (numeric) uid.  skem
	     will refuse to become a milter as root, so	this argument is
	     mandatory,	unless we are starting as a non-root already.

     -v	     Print the version string(s) and exit.

     -w	ip1 [ip2 ... ipN]
	     Whitelist the specified IPs and exit. If an entry already exists,
	     it	is skipped without change of status. Whitelists	are never re-
	     moved by the cleanup. If you have lower priority Mail eXchangers
	     for your domain, or other legitimate machines, which frequently
	     forward mail (ham and spam) to you, you should whitelist them.

     The cleanup thread	cleans up the directory	off the	expired	temporary bans
     and the long-unseen permanent bans. See the description of	-l -L -B op-
     tions above for details. You can have the skem-milter do that, or,	if
     that's how	you like it, use the -L	option launching skem from cron(8).
     (Not that I can see a single reason to do it that way in normal opera-

     Note, that	the expired entries are	always removed by the milter thread
     itself, when triggered -- whether or not the cleanup thread is activated.

     To	avoid building the cleanup functionality, be sure to NOT use the
     -DSKEM_CLEANUP define.

     The current milter	architecture does not provide a	way for	milters	to
     learn the fate of each message. They can not request to be	notified, when
     a message is rejected by one of the many different	mechanisms (such as
     another milter, non-existent domain, spamtrap, etc.). The only way	to get
     this information is by watching the log files. You	can cook something up
     with awk(1) / perl(1) / python(1) / Tcl(n)	/ whatever your	poison -- all,
     your logwatcher needs to do, is place entries in the directory, where
     skem is looking for them -- with correct mode.

     skem by default includes its own logwatcher thread, which is reasonably
     useful (but see the Security Considerations section below). If used, it
     expects stdin to contain the arriving log messages. Those of the lines,
     that contain the reject string (see the -r	options	above),	are examined
     closer with IP-address pattern as provided	by the -s option. If the
     string yielded by the pattern's parenthesized expression looks like an IP
     address, the address is banned. If	the spamword is	specified (by -S op-
     tion) and the string contains it as well, the ban is permanent. Other-
     wise, it is temporary. No ban is created if the IP	is already listed as

     To	avoid building the logwatcher functionality, be	sure to	NOT use	the
     -DSKEM_LOGWATCHER define.

Security Considerations
     Although believed to be secure anyway, skem will refuse to	become a mil-
     ter as root insisting on the alternative username to be passed with the
     -u	switch.	If started as root, it will also chroot(3) itself into the
     database directory	prior to dropping root privileges.

     However, using the	logwatcher may expose you to denial of service attacks
     if	your installation logs network-obtained	data. For example, MIMEDefang
     milter logs the subjects of the messages it analyzes. If a	subject	con-
     tains the reject string and matches the IP	pattern, the IP	may be banned
     --	any IP,	that an	attacker puts into subject.

     The FreeBSD example below does not	have this problem, but the
     syslog.conf(5) syntax on other systems may	not be as powerful.

     Even on FreeBSD, a	local user can use logger(1) to	pretend	to be
     sendmail.	This is, however, intentional. It gives	the users the power to
     request bans through logwatcher directly by something like

     logger -t sendmail	-p mail.notice "Please,	reject=55x relay=[]"

     But if you	can not	trust local users, you,	probably, can not rely on log-
     watching anyway.

     Here is the "live"	syslog.conf(5) entry on	my server

     mail.notice,   |exec /home/mi/skem/skem -C /var/db/skem -S spammer -2 -s - -g 12000 -u mi	-l 6000	-4 -B 604800 -d	/var/db/skem/skem.sock

     This has a	disadvantage of	being shut down	every time the syslogd(8) is
     HUP-ed, and not restarted until sendmail(8) has something to say (like
     complain about a missing skem-milter).

     A different way would be to log into a named pipe (see mkfifo(1)) with
     skem reading from the pipe. However, this is likely to hang the syslogd
     should skem ever go down (which is	very unlikely, but still).

     No	configuration files are	considered by skem -- all settings are given
     on	command	line.

     When started as root, skem	will chroot to the directory specified with
     the -C option (or to "."),	so be sure the socket and the optional
     pid-filename specify paths	underneath it. For convenience,	skem will try
     to	automatically adjust the paths specified so that they are correct af-
     ter the chroot-ing.

     The skem executable and the manual	page should be installed according to
     your OS standards and customs (probably, under /usr/local/sbin and
     /usr/local/man/man8). To tell sendmail about the milter, please, refer to
     the sendmail's libmilter documentation (in	libmilter/README).

Future enhancements being considered
     Depending on user interest	(if any), the following	features might be im-

     grey list
	     delay IPs,	not yet	listed,	or listed less than a given number of
	     seconds ago

     accept syslog messages
	     enhance the logwatcher module to accept the syslog	messages di-
	     rectly through an inet or a domain	socket

     interpret the values of the links
	     Although reading a	file is	unpleasant, the	readlink(2) is a fast
	     way to obtain information.	 skem can use the targets of the sym-
	     link-entries for various "interesting" purposes. For example,
	     logwatcher	can record the perceived reasons for adding an entry,

	     ln	-s "caught in spam-trap"

	     This is fun, but wasteful,	because	all the	information is in the
	     maillogs too, one just needs to fgrep(1) for a particular IP-ad-
	     dress to find out the reasons, it was added to the	list.

     logger(1),	rm(1), touch(1), sendmail(8), syslogd(8), umask(1)

     Mikhail Teterin <>

FreeBSD	13.0			 July 29, 2021			  FreeBSD 13.0

NAME | SYNOPSIS | DESCRIPTION | CLEANUP | LOGWATCHER | Security Considerations | EXAMPLES | FILES | INSTALL | Future enhancements being considered | SEE ALSO | AUTHORS

Want to link to this manual page? Use this URL:

home | help