SPAMPD(8)

๐Ÿ’– SPAMPD โ€” Spam Proxy Daemon ๐Ÿ’–

okay bestie, gather round, because this is spampd from section 8 of the manual (that's the sysadmin/daemon section, iykyk), and she is a lot in the best way ๐ŸŒธ


โœจ NAME โœจ

SpamPD โ€” the Spam Proxy Daemon. That's it, that's the name, she's iconic ๐Ÿ’…

โœจ VERSION โœจ

This is the documentation for SpamPD version 2.62. (Yes, it's giving vintage. She's still slaying.)

โœจ DESCRIPTION โœจ

So spampd is an SMTP/LMTP proxy that marks (or tags) spam using SpamAssassin (<http://www.SpamAssassin.org/>). The way it works is so polite: the proxy is designed to be totally transparent to both the sending and the receiving mail servers, and at no point does it take responsibility for the message itself fr fr. So if something goes wrong inside spampd (or inside SpamAssassin), the mail servers just disconnect, and the sending server is still the one responsible for retrying the message for as long as it's configured to do so. Not our problem, we're just the proxy ๐Ÿ’…

spampd uses SpamAssassin to modify (tag) relayed messages based on their spam score, which means all SA settings apply โ€” that's all described in the SA documentation, go read it. By default spampd will only tell SA to tag a message if it exceeds the spam threshold score. BUT ๐Ÿซถ if you want it to rewrite every single message passing through, just add the --tagall option (and check SA's docs for how non-spam messages get tagged).

spampd logs all aspects of its operation to syslog(8), using the mail syslog facility. Organized queen.

The latest version can be found at <https://github.com/mpaperno/spampd> โœจ

โœจ SYNOPSIS โœจ

spampd [ options ]

Options, babes:

  • --config <filename> โ€” Load options from file(s).
  • --host <host>[:<port>] โ€” Hostname/IP and optional port to listen on.
  • --port <n> โ€” Port to listen on (alternate syntax to above).
  • --socket <socketpath> โ€” UNIX socket to listen on.
  • --socket-perms <mode> โ€” The octal mode to set on the UNIX socket.
  • --relayhost <hst>[:<prt>] โ€” Host and optional port to relay mail to.
  • --relayport <n> โ€” Port to relay to (alternate syntax to above).
  • --relaysocket <sockpath> โ€” UNIX socket to relay to.
  • --min-servers | -mns <n> โ€” The minimum number of servers to keep running.
  • --min-spare | -mnsp <n> โ€” The minimum number of servers to have waiting.
  • --max-spare | -mxsp <n> โ€” The maximum number of servers to have waiting.
  • --max-servers | -mxs <n> โ€” The maximum number of child servers to start.
  • --maxrequests or -r <n> โ€” Maximum requests that each child can process.
  • --childtimeout <n> โ€” Time out children after this many seconds.
  • --satimeout <n> โ€” Time out SpamAssassin after this many seconds.
  • --child-name-template [s] โ€” Template for formatting child process name.
  • --pid or -p <filename> โ€” Store the daemon's process ID in this file.
  • --user or -u <user> โ€” Specifies the user that the daemon runs as.
  • --group or -g <group> โ€” Specifies the group that the daemon runs as.
  • --[no]detach โ€” Detach from the console daemonize (default).
  • --[no]setsid โ€” Completely detach from stderr with setsid().
  • --maxsize n โ€” Maximum size of mail to scan (in KB).
  • --dose โ€” (D)ie (o)n (s)pamAssassin (e)rrors.
  • --tagall โ€” Tag all messages with SA headers, not just spam.
  • --set-envelope-headers โ€” Set X-Envelope-From and X-Envelope-To headers.
  • --set-envelope-from โ€” Set X-Envelope-From header only.
  • --local-only or -L โ€” Turn off all SA network-based tests.
  • --homedir <path> โ€” Use the specified directory as SA home.
  • --saconfig <filename> โ€” Use the file for SA "user_prefs" configuration.
  • --logfile or -o <dest> โ€” Destination for logs (syslog|stderr|<filename>).
  • --logsock or -ls <sock> โ€” Allows specifying the syslog socket type.
  • --logident or -li <name> โ€” Specify syslog identity name.
  • --logfacility or -lf <nm> โ€” Specify syslog facility (log name).
  • --log-rules-hit or -rh โ€” Log the names of each matched SA test per mail.
  • --debug or -d [<areas>] โ€” Controls extra debug logging.
  • --help | -h | -? [txt] โ€” Show basic command-line usage.
  • -hh | -?? [txt] โ€” Show short option descriptions (this text).
  • -hhh | -??? [txt] โ€” Show usage summary and full option descriptions.
  • --man [html|txt] โ€” Show full docs a man page or HTML/plain text.
  • --show defaults|<thing> โ€” Print default option values or <thing> and exit.
  • --version โ€” Print version information and exit.

Compatibility with previous SpamPD versions:

  • --children or -c <n> โ€” Same as --max-servers | -mxs (since v2.60).

Deprecated since SpamAssassin v3:

  • --auto-whitelist or -aw โ€” Use the SA global auto-whitelist feature.
โš ๏ธ heads up bestie, that last one is deprecated โ€” she's giving "still here but don't get attached" ๐Ÿ’€

โœจ REQUIRES โœจ

Perl modules, periodt:

  • Mail::SpamAssassin
  • Net::Server (>= v0.89, v2.009+ recommended) with PreForkSimple and/or PreFork submodules.
  • IO::File
  • Time::HiRes
  • IO::Socket::IP (if using TCP/IP sockets)
  • IO::Socket::UNIX (if using UNIX sockets)

โœจ OPERATION โœจ

spampd is meant to operate as an S/LMTP mail proxy which passes each message through SpamAssassin for analysis. Okay but here's the thing โ€” spampd does not do anything other than check for spam, so it is not suitable as an anti-relay system. No cap. It's meant to work in conjunction with your regular mail system. Typically you'd pipe any messages you wanted scanned through spampd after initial acceptance by your MX host. This is especially useful for using Postfix's (<http://www.postfix.org>) advanced content filtering mechanism, although it's certainly not limited to that application.

Please re-read the second sentence in the above paragraph. Like, actually re-read it. You should NOT enable spampd to listen on a public interface (IP address) unless you know exactly what you're doing! It is very easy to set up an open relay this way. ๐Ÿ˜ญ

Here are some simple examples (square brackets in the "diagrams" indicate physical machines):

Running between firewall/gateway and internal mail server

The firewall/gateway MTA would be configured to forward all of its mail to the port that spampd listens on, and spampd would relay its messages to port 25 of your internal server. spampd could either run on its own host (and listen on any port) or it could run on either mail server (and listen on any port except port 25).

Internet ->
[ MX gateway (@inter.net.host:25) -> spampd (@localhost:2025) ] ->
[ Internal mail (@private.host.ip:25) ]

Using Postfix advanced content filtering

Please see the FILTER_README that came with the Postfix distribution. You need to have a version of Postfix which supports this (ideally v.2 and up).

Internet -> [ Postfix (@inter.net.host:25)  ->
              spampd (@localhost:10025)     ->
              Postfix (@localhost:10026)  ] -> final delivery

Note that these examples only show incoming mail delivery. Since it is often unnecessary to scan mail coming from your network, it may be desirable to set up a separate outbound route which bypasses spampd entirely. (Sneaky, but valid ๐ŸŒธ)

๐ŸŒˆ Scalable Mode ๐ŸŒˆ

Since v2.60 spampd can optionally run in "scalable mode", which dynamically adjusts the number of child servers that can process requests simultaneously. She's โœจflexibleโœจ. This is activated automatically if the "--min-servers" option is specifically set to be lower than "--max-servers".

Historically SpamPD inherited from the module Net::Server::PreForkSimple, which only allows for a static number of child servers to be running at once. This new option essentially allows for inheriting from Net::Server::PreFork, which features dynamic allocation of child servers, with some tunable parameters. (The reason PreFork wasn't used to begin with is because older versions of it didn't seem to work... it was an old TODO to try again later. Relatable, honestly. ๐Ÿ™ˆ)

Here is what the Net::Server::PreFork documentation has to say (option names changed to match spampd):

"This personality binds to one or more ports and then forks "--min-servers" child process. The server will make sure that at any given time there are "--min-spare" servers available to receive a client request, up to "--max-servers". Each of these children will process up to "--maxrequests" client connections. This type is good for a heavily hit site, and should scale well for most applications."

Some experimentation and tuning will likely be needed to get the best performance vs. efficiency. Keep in mind that a SIGHUP sent to the parent process will reload configuration files and restart child servers gracefully (handy for tuning a busy site ๐Ÿ‘‘).

See the documentation for "--min-servers", "--max-servers", "--min-spare", and "--max-spare" options, and also the section about "Other Net::Server Options" for tuning parameters and links to further documentation.


๐Ÿ’พ INSTALLATION AND CONFIGURATION ๐Ÿ’พ

okay bestie, so you can just run spampd straight from the command prompt if you wanna โ€” it's genuinely perfect for testing stuff. But for the long haul? You're gonna want to park it somewhere cute like /usr/bin or /usr/local/bin and have it fire up at system startup. For example, on a Red Hat-style Linux system you can use a script in /etc/rc.d/init.d to start spampd (there's a sample script available in the spampd code repository at https://github.com/mpaperno/spampd/tree/master/misc ๐Ÿ’…).

spampd also ships as a package for a lot of Linux distributions, including Debian and its derivatives (Ubuntu, etc). Honestly this is usually the easiest/best way to install and configure spampd since the package should already handle all your system-specific stuff like setting it up and running as a daemon and all that โœจ. That said โ€” heads up ๐Ÿ™ˆ โ€” packages might not offer the latest version of spampd. If you want a good reference for which packages exist and what versions they carry, hit up https://repology.org/project/spampd/versions.

spampd is also used in the turnkey Mail-in-a-Box project (https://mailinabox.email/), which includes Postfix as the main MTA and Dovecot as the local delivery agent with LMTP protocol. And even if you don't need the whole turnkey solution, it's lowkey informative to peruse the MIAB setup files (https://github.com/mail-in-a-box/mailinabox/tree/master/setup) and configuration files (https://github.com/mail-in-a-box/mailinabox/tree/master/conf) for reference ๐ŸŒธ.

All spampd options have reasonable defaults, especially for a Postfix-centric installation. You might wanna specify the --max-servers option if you have an especially beefy or weak server box, because spampd is a memory-hungry program (it's giving โœจmain character energyโœจ but also it eats RAM). Check the Options for details on this and all other parameters.

To show default values for all options, run spampd --show defaults.

Since v2.61 spampd injects a _SPAMPDVERSION_ "template tag" macro at message processing time. You can find that template tag documented over at https://spamassassin.apache.org/doc/Mail_SpamAssassin_Conf.html#TEMPLATE-TAGS, and it can be used in an add_header SA config file directive, for example:

add_header all Filter-Version SpamAssassin _VERSION_ (_SUBVERSION_, Rules: _RULESVERSION_) / SpamPD _SPAMPDVERSION_
๐Ÿ“ข Note that spampd replaces spamd from the SpamAssassin distribution in function. You do not need to run spamd in order for spampd to work. This has apparently been the source of some confusion, so now you know ๐Ÿ’–.

๐Ÿ“ฎ Postfix-specific Notes ๐Ÿ“ฎ

Here's a typical setup for Postfix "advanced" content filtering as described in the FILTER_README that came with the Postfix distribution (which you really need to read, bestie, I'm not even joking):

/etc/postfix/master.cf:

smtp             inet  n  -  y  -  -   smtpd
  -o content_filter=smtp:localhost:10025
  -o myhostname=mx.example.com

localhost:10026  inet  n  -  n  -  10  smtpd
  -o content_filter=
  -o myhostname=mx-int.example.com

The first entry is the main public-facing MTA which uses localhost:10025 as the content filter for all mail. The second entry receives mail from the content filter and does final delivery. Both smtpd instances use the same Postfix main.cf file. spampd is the process that listens on localhost:10025 and then connects to the Postfix listener on localhost:10026. Note that the myhostname options must be different between the two instances, otherwise Postfix will think it's talking to itself and abort sending ๐Ÿ˜ญ.

For the above example you can simply start spampd like this:

spampd --host=localhost:10025 --relayhost=localhost:10026

FILTER_README from the Postfix distro has more details and examples of various setups, including how to skip the content filter for outbound mail ๐Ÿ‘‘

Another tip for Postfix when you're deciding what timeout values to use for the --childtimout and --satimeout options is this command:

# postconf | grep timeout

This will return a list of useful timeout settings and their values. For explanations see the relevant man page (smtp, smtpd, lmtp). By default spampd is set up for the default Postfix timeout values ๐Ÿ’….

The following guide has some more specific setup instructions: Integrating SpamAssassin into Postfix using spampd (https://wiki.apache.org/spamassassin/IntegratePostfixViaSpampd).


โฌ†๏ธ UPGRADING โฌ†๏ธ

Always consult the changelog.txt file which should be included in the spampd repository/distribution. no cap.

If you're upgrading from a version prior to 2.2, please note that the --add-sc-header option is no longer supported ๐Ÿ™…. Use SA's built-in header manipulation features instead (as of SA v2.6).

Upgrading from version 1 simply involves replacing the spampd program file with the latest one. Note that the dead-letters folder is no longer being used and the --dead-letters option is no longer needed (though no errors are thrown if it's present, so it's giving harmless ex). Check the OPTIONS list below for a full list of new and deprecated options. Also be sure to check out the change log ๐Ÿ“.

Since v2.60 spampd has a new "Scalable Mode" feature which varies the number of running child servers based on demand. This is disabled by default. The option previously known as --children (or -c) is now called --max-servers (or -mxs), but the old style is still accepted (iconic, honestly). See descriptions of the max-servers and min-servers options for details.

Also note that v2.60 added the ability to use a CONFIGURATION FILE for specifying all options โœจ.


๐Ÿ› ๏ธ USAGE ๐Ÿ› ๏ธ

spampd [
  [ --config | --cfg | --config-file | --cfg-file [<filename>] ][...]

  [ --host <host>[:<port>]      | --socket <path> --socket-perms <mode> ]
  [ --relayhost <host>[:<port>] | --relaysocket <path>                  ]

  [--min-servers | -mns  <n>] [--saconfig  <file>] [--user  | -u <user>  ]
  [--min-spare   | -mnsp <n>] [--satimeout <n>   ] [--group | -g <group> ]
  [--max-spare   | -mxsp <n>] [--dose            ] [--pid   | -p <file>  ]
  [--max-servers | -mxs  <n>] [--maxsize   <n>   ] [--[no]detach         ]
  [--maxrequests | -r    <n>] [--local-only | -L ] [--[no]setsid         ]
  [--childtimeout        <n>] [--tagall     | -a ] [--log-rules-hit | -rh]
  [ --child-name-template | -cnt [<template>] ]    [--homedir <path>     ]
  [ [--set-envelope-headers | -seh] | [--set-envelope-from | -sef] ]

  [ --logfile | -o (syslog|stderr|<filename>) ][...]
  [ --logsock | -ls <socketpath>    ]  [ --logident    | -li <name> ]
  [ --debug   | -d [<area,...>|1|0] ]  [ --logfacility | -lf <name> ]
  [ --show ( all | (defaults, config, version, argv, start, self) ) ][...]
]
spampd --version
spampd [--help | -?] | -?? [txt] | -??? [txt] | [-???? | --man [html|txt]]

Options are case-insensitive, bestie, we're chill like that. = can be used as a name/value separator instead of a space (--name=value). A - or -- prefix can be used for all options. The shortest unique option name can be used. All options must be listed individually (no "bundling" โ€” sorry, no group hug). All boolean options can take an optional argument of 1 or 0, or can be negated by adding a no- prefix in front of the name. And an option specified on the command line overrides the same option loaded from config file(s) ๐Ÿ’….


โš™๏ธ OPTIONS โš™๏ธ

Please be sure to also read the general information about specifying option arguments in the above USAGE section, okay? Don't skip it ๐Ÿ™ˆ

To view default values for all options, run spampd --show defaults.


  • --config or -cfg or --config-file or --cfg-file <filename> โ€” "new in v2.60" โ€” Load options from one or more configuration file(s). This option can be specified multiple times ๐Ÿ”. The filename can also be a list of files separated by a : (colon). If multiple files specify the same option, the last one loaded will take precedence. Also, any options specified on the actual command line will take precedence (regardless of where they appear relative to the --config option). And here's the kicker: --config can only be specified on the command line โ€” you cannot use it within another configuration file ๐Ÿšซ.

See the CONFIGURATION FILE section for more details.

  • --host (<ip>|<hostname>)[:<port>] โ€” Specifies what hostname/IP and port spampd listens on. By default, it listens on 127.0.0.1 (localhost) on port 10025.

As of v2.60 this option can also handle IPv6 addresses in the form of --host n:n:n or, with port, --host [n:n:n]:port (the square brackets are optional in both forms but recommended in the latter case ๐Ÿ’–).

Note that the port specified this way implicitly overrides the --port option.

โš ๏ธ Important! You should NOT enable spampd to listen on a public interface (IP address) unless you know exactly what you're doing! Like, actually exactly. This is the part where you read twice and click once, bestie ๐Ÿ˜ญ
  • --port <n> โ€” Specifies what port spampd listens on. This is an alternate to using the above --host=ip:port notation. Note that a port specified in the --host option will override this one (the host one wins, sorry port, it's giving second-place energy).
  • --socket <socketpath> โ€” Specifies what UNIX socket spampd listens on. If this is specified, --host and --port are ignored (they simply don't get a turn ๐Ÿ’…).
  • --socket-perms <mode> โ€” The file mode for the created UNIX socket (see --socket) in octal format, e.g. 700 to specify access only for the user spampd is run as.
  • --relayhost (<ip>|<hostname>)[:<port>] โ€” Specifies the hostname/IP to which spampd will relay all messages. Defaults to 127.0.0.1 (localhost) on port 25.

As of v2.60 this option can also handle IPv6 addresses in the form of --relayhost n:n:n or, with port, --relayhost [n:n:n]:port (the square brackets are optional in both forms but recommended in the latter case โœจ).

Note that the port specified this way implicitly overrides the --relayport option.

  • --relayport <n> โ€” Specifies what port spampd will relay to. This is an alternate to using the above --relayhost=ip:port notation. Note that a port specified in the --relayhost option will override this one.
  • --relaysocket <socketpath> โ€” Specifies what UNIX socket spampd will relay to. If this is specified, --relayhost and --relayport will be ignored ๐Ÿซถ.
  • --user or -u <username>
  • --group or -g <groupname> โ€” Specifies the user and/or group that the proxy will run as. Default is mail/mail ๐Ÿ‘‘
  • --children or -c <n>
  • --max-servers or -mxs <n> โ€” "new in v2.60" โ€” Number of child servers to start and maintain (where n > 0). Each child will process up to --maxrequests (below) before exiting and being replaced by another child. Keep this number low on systems w/out a lot of memory ๐Ÿง . Note that there is always a parent process running, so if you specify 5 children you will actually have 6 spampd processes running. yes, math, we love her ๐Ÿ’€

Note: if you ALSO set --min-servers, and it's less than --max-servers? congrats bestie, the server flips into "Scalable Mode" and this option literally changes meaning ๐Ÿฆ‹. In scalable mode the number of actually-running servers bounces around between --min-servers and --max-servers, depending on demand.

You might wanna set your origination mail server to cap concurrent connections to spampd so it matches this setting (for Postfix that's the xxxx_destination_concurrency_limit setting, where xxxx is the transport being used, usually smtp or lmtp) ๐Ÿ’….

See also the --min-servers, --min-spare, and --max-spare options.

  • --min-servers, -mns <n> "new in v2.60" โ€” the minimum number of child servers to start and maintain, where n > 0 โœจ.

Note: if this option is set and it's less than the --max-servers option, then the server runs in "Scalable Mode". By default this option is undefined, meaning spampd runs only a set number of servers, the ones you specified in --max-servers. In scalable mode, the number of actual running servers will fluctuate between --min-servers and --max-servers, based on demand ๐ŸŒŠ.

See also the --max-servers, --min-spare, and --max-spare options.

  • --min-spare, -mnsp <n> "new in v2.60" โ€” the minimum number of servers to have waiting for requests. Real talk: don't set your minimum and maximum numbers too close to each other, or the server will fork and kill children way too often ๐Ÿ˜ญ (copied straight from Net::Server::PreFork).

Note: this option only gets used when running in "Scalable Mode". See the --min-servers and --max-servers options ๐Ÿ‘€.

  • --max-spare, -mxsp <n> "new in v2.60" โ€” the maximum number of servers to have waiting for requests (also copied from Net::Server::PreFork).

Note: this one is also only used when running in "Scalable Mode". See the --min-servers and --max-servers options ๐Ÿ’–.

  • --maxrequests, -mr, -r <n> โ€” spampd works by forking child servers to handle each message, right? Well, the maxrequests parameter says how many requests will be handled before the child exits. Here's the tea ๐Ÿซ–: a child never gives back memory, so one big chonky message can leave it super bloated, and the only way to reclaim that memory is for the child to exit. Periodt.
  • --childtimeout <n> โ€” how many seconds you allow each child server before it times out a transaction โฐ. In an S/LMTP transaction the timer gets reset for every command. This timeout includes the time it'd take to send the message data, so don't make it too short, bestie. Note that it's more likely the origination or destination mail servers will timeout first, and that's totally fine โ€” this is just a "sane" failsafe ๐Ÿ’….
  • --satimeout <n> โ€” the number of seconds allowed for processing a message with SpamAssassin (that's feeding it the message, analyzing it, and adding the headers/report if necessary) ๐ŸŽ€.

This should be less than your origination and destination servers' timeout settings for the DATA command. (For Postfix, that's set in (smtp|lmtp)_data_done_timeout and smtpd_timeout.) If a timeout happens while processing the message, the problem gets logged and the message is passed on anyway (w/out spam tagging, obviously ๐Ÿ™ˆ). To fail the message with a temp 450 error instead, see the --dose (die-on-sa-errors) option, below.

  • --child-name-template, -cnt [<template>] "new in v2.61" โ€” the template for formatting the child process name ๐Ÿ“›. Use a blank string (just the argument name without a value) to leave the child process name unchanged (it'll be the same as the parent command line).

The template uses printf() style formatting, but with named parameter placeholders. For example (wrapped for clarity):

%base_name: child #%child_count(%child_status)
[req %req_count/%req_max, time lst/avg/ttl %(req_time_last).4f/%(req_time_avg).4f/%(req_time_ttl).4f,
ham/spm %req_ham/%req_spam, rules v%sa_rls_ver)]'

Would produce something like:

spampd: child #4(D) [req 8/30, time lst/avg/ttl 0.0222/0.0256/0.2045, ham/spm 3/5, rules v1891891]

Parameters are specified like: "Value of %(my_name)s is %(my_float_value).4f", with names in parenthesis followed by a standard printf() style formatting specifier ("s" is default), or simply as "Value of %my_name is %my_value" with the default format being a string (works for numerics also). Keep in mind that any actual % characters need to be escaped as %% โš ๏ธ. Formatting warnings will be logged as "sprintf" errors (most likely a parameter was misspelled).

The following variables are available ๐Ÿ‘‡:

  • base_name โ€” # Base script name, eg. "spampd"
  • spampd_ver โ€” # SpamPD version, eg. "2.61"
  • perl_ver โ€” # Perl version, eg. "5.28.1"
  • ns_ver โ€” # Net::Server version, eg. "2.009"
  • ns_typ โ€” # Net::Server type, "PreFork" or "PreForkSimple"
  • ns_typ_acr โ€” # Net::Server type acronym, "PF" or "PFS"
  • sa_ver โ€” # SpamAassassin version, eg. "3.4.2"
  • sa_rls_ver โ€” # SpamAassassin rules update version, eg. "1891891" or "(unknown)"
  • child_count โ€” # total number of children launched so far (current child number)
  • child_status โ€” # child status, "C" for connected, or "D" for disconnected
  • req_count โ€” # number of requests child has processed so far
  • req_max โ€” # maximum child requests before exit
  • req_time_last โ€” # [s] time to process the last message
  • req_time_ttl โ€” # [s] total processing time for this child
  • req_time_avg โ€” # [s] average processing time for this child (req_time_ttl / req_count)
  • req_ham โ€” # count of ham messages scored by child
  • req_spam โ€” # count of spam messages scored by child
  • --pid, -p <filename> โ€” names a file where spampd will write its process ID, so it's easy to kill later ๐Ÿ”ช. The directory holding this file has to be writable by the spampd user, no cap.
  • --logfile, -o (syslog|stderr|<filename>) "new in v2.60" โ€” which logging method to use ๐Ÿ“. It can be one or more of:
    • "syslog": use the system's syslogd (via Sys::Syslog). This is the default setting ๐Ÿ’ค.
    • "stderr": send all logging to stderr (if you're running in background mode these may still end up in the default system log).
    • "filename": use the specified file (the location must be accessible to the user spampd is running as). This can also be a device handle, eg: "/dev/tty0" or even "/dev/null" to disable logging entirely ๐Ÿ•ณ๏ธ.

This option may be specified multiple times. You may also specify multiple destinations by separating them with a ":" (colon): "--logfile stderr:/var/log/spampd.log".

Simultaneous logging to "syslog", "stderr", and one "filename" is possible โœจ. At this time only one log file can be used at a time (if several are specified, then the last one takes precedence).

  • --logsock, -ls <type> "new in v2.20" "updated in v2.60" โ€” the syslog socket to use if --logfile is set to syslog ๐Ÿงฆ.

"Since v2.60:"

The type can be any of the socket types or logging mechanisms as accepted by the subroutine Sys::Syslog::setlogsock(). Depending on the version of Sys::Syslog and the underlying operating system, one of the following values (or their subset) can be used:

native, tcp, udp, inet, unix, stream, pipe, console, eventlog (Win32 only)

The default behavior since spampd v2.60 is to let Sys::Syslog pick the default syslog socket ๐ŸŽฒ. This is the recommended usage for Sys::Syslog (since v0.15), which chooses thusly:

The default is to try native, tcp, udp, unix, pipe, stream, console. Under systems with the
Win32 API, eventlog will be added as the first mechanism to try if Win32::EventLog is available.

For more information please consult the Sys::Syslog documentation at https://metacpan.org/pod/Sys::Syslog ๐Ÿ“š.

To preserve backwards-compatibility, the default on HP-UX and SunOS (Solaris) systems is "inet".

"Prior to v2.60:"

The default was "unix" โ€” except on HP-UX and SunOS (Solaris) systems, where it's "inet".

  • --logident, -li <name> "new in v2.60" โ€” the syslog identity name to use ๐Ÿท๏ธ. This may also be used in log files written directly (w/out syslog).
  • --logfacility, -lf <name> "new in v2.60" โ€” the syslog facility name to use. This is typically the name of the system-wide log file to be written to ๐Ÿ“‚.
  • --[no]detach [0|1] "new in v2.20" โ€” tells spampd to detach from the console and fork into the background ("daemonize") ๐Ÿ‘ป. Using --nodetach can be useful for running under the control of some daemon management tools, or for testing from a command line.
  • --[no]setsid [0|1] "new in v2.51" โ€” if --setsid is specified, then spampd will fork after the bind method to release itself from the command line, and then run the POSIX::setsid() command to truly daemonize ๐ŸŒ™. Only used if --nodetach isn't specified.
  • --maxsize <n> โ€” the maximum message size to send to SpamAssassin, in KBytes ๐Ÿ˜. Messages over this size are not scanned at all, and an appropriate message gets logged saying so. The size includes headers and attachments (if any).
  • --dose [0|1] โ€” acronym for (d)ie (o)n (s)pamAssassin (e)rrors ๐Ÿ’€. When it's disabled and spampd hits a problem processing the message through SpamAssassin (timeout or other error), it will still pass the mail on to the destination server. When it's enabled, the mail is instead rejected with a temporary error (code 450, which means the origination server should keep retrying to send it) ๐Ÿ”. See the related --satimeout option, above.
  • --tagall, -a [0|1] โ€” tells spampd to have SpamAssassin add headers to all scanned mail, not just spam ๐Ÿท๏ธ. Otherwise spampd will only rewrite messages which exceed the spam threshold score (as defined in the SA settings). Note that for this option to work as of SA-2.50, the always_add_report and/or always_add_headers settings in your SpamAssassin local.cf need to be set to 1/true โš ๏ธ.
  • --log-rules-hit, -rh [0|1] โ€” logs the names of each SpamAssassin rule which matched the message being processed ๐Ÿ“‹. This list is returned by SA.
  • --set-envelope-headers, -seh [0|1] "new in v2.30" โ€” turns ON the adding of X-Envelope-To and X-Envelope-From headers to the mail being scanned, before it gets handed off to SpamAssassin. the whole idea? it helps SA deal with any blacklist/whitelist to/from directives based on the actual sender/recipients instead of the possibly bogus envelope headers. BUT ๐Ÿ’€ this potentially exposes the list of ALL recipients of that mail (even the BCC'd ones). so like, usage of this option is discouraged, bestie, fr.
โš ๏ธ NOTE: even though spampd tries to prevent this leakage by removing the X-Envelope-To header after scanning, SpamAssassin itself might add headers that report recipient(s) listed in X-Envelope-To. so it's not a full fix, just so you know.
  • --set-envelope-from, -sef [0|1] "new in v2.30" โ€” same as the option above but only enables the addition of the X-Envelope-From header. for those that don't feel comfortable with the possible information exposure of X-Envelope-To. ๐Ÿซถ and heads up โ€” the above option overrides this one.
  • --local-only, -L [0|1] โ€” turns off ALL SA network-based tests (DNS, Razor, etc). no network, no problem โœจ
  • --homedir <directory> "new in v2.40" โ€” uses the specified directory as the home directory for the spamassassin process. things like the auto-whitelist and other plugin (razor/pyzor) files get written to here. a good place for this is in the same place your bayes_path SA config setting points to (if any). make sure this directory is accessible to the user that spampd is running as ๐Ÿ”‘

thanks to Alexander Wirt for this fix. (slay, Alexander)

  • --saconfig <filename> โ€” uses the specified file for SpamAssassin configuration options in addition to the default local.cf file. any options specified here will override the same option from local.cf. so it's giving main character energy, and local.cf is just the supporting cast โœจ
  • --debug, -d [<area,...>|1|0] "(updated in v2.60)" โ€” turns on SpamAssassin debug messages which print to the system mail log (same log as spampd will log to). also turns on more verbose logging of what spampd is doing (new in v2). also increases log level of Net::Server to 4 (debug), adding yet more info (but not too much) (new in v2.2).

"New in v2.60:"

setting the value to 1 (one) is the same as using no parameter (eg. simply -d). the value of 0 (zero) disables debug logging.

the area list is passed on directly to SpamAssassin and controls logging facilities. if no areas are listed (and debug logging is enabled), all debugging information is printed (this is equivalent to passing "all" as the area). diagnostic output can also be enabled for each area individually; area is the area of the code to instrument. for example, to produce diagnostic output on bayes, learn, and dns, use:

-d bayes,learn,dns

you can also disable specific areas with the "no" prefix:

-d all,norules,nobayes

to show only spampd debug messages (none from SpamAssassin), use:

-d spampd

for more information about which areas (aka channels or facilities) are available, please see the documentation at:

SpamAssassin Wiki::DebugChannels http://wiki.apache.org/spamassassin/DebugChannels

Mail::SpamAssassin::Logger::add_facilities() https://spamassassin.apache.org/doc/Mail_Spam_Assassin_Logger.html#METHODS

  • --show <thing>[,<thing>[,...]] "new in v2.60" โ€” meant primarily for debugging configuration settings (or code), this will print some information to the console and then exit. ๐Ÿฆ‹

<thing> may be one or more of:

  • "defaults": show default values for all options, in a format suitable for a config file.
  • "config": shows option values after processing all given command-line arguments, including anything loaded from config file(s).
  • "start": shows the final configuration arguments after processing any config file(s).
  • "version": same as the --version switch but runs after parsing all options and shows the actual Net::Server type which would be used (PreFork or PreForkSimple).
  • "argv": shows anything remaining on command line (@ARGV) after processing all known arguments (this will be passed onto Net::Server).
  • "self": dumps the whole SpamPD object, including all settings. Trรฉs geek. ๐Ÿ’…
  • "all": prints all of the above.

multiple "thing"s may be specified by using the --show option multiple times, or separating the items with a comma: "--show config,start,argv".

note that all thing options besides "defaults" and "config" require the Perl module Data::Dumper installed. ๐Ÿ‘‘
  • --version "new in v2.52" โ€” prints version information about SpamPD, Net::Server, SpamAssassin, and Perl. the receipts, basically ๐Ÿงพ
  • --help, -h, -? [txt]
  • --hh, -?? [txt]
  • --hhh, -??? [txt]
  • --man, -hhhh, -???? [html|txt] โ€” prints increasingly verbose usage information. by default help is displayed in "terminal" (groff) format with some text styling applied. if you want to use "less" as a pager, provide it with the -R switch, eg.:
spampd --??? | less -R

alternatively you can request plain-text format with the optional "txt" value.

--man displays the full documentation, optionally in "html" or plain text "txt" formats (default is to use actual "man" format/display). the HTML version is saved to a temp file and an attempt is made to open it in the default system browser (it is better if the browser is already opened). if available, the optional Perl module HTML::Display is used to (try to) open a browser. ๐ŸŒˆ


๐ŸŒธ Other Net::Server Options ๐ŸŒธ

Net::Server supports some other options which spampd doesn't accept directly. for example there are access control options, child process tuning, and a few more (see below). such options can be passed through to Net::Server (and subtypes) by specifying them at the end of the spampd command line (or in a configuration file) following two dashes -- by themselves (this is a common convention for passing options onto another program). as an example, it may look something like this:

spampd --host 10.0.0.1 -port 10025 -- --cidr_allow 10.0.0.0/24

the --cidr_allow after the -- is passed onto Net::Server. if the -- were not there, you would get an error from spampd about an unknown option. ๐Ÿ™ˆ

to specify Net::Server options in a configuration file, place them after two dashes (--) on a line by themselves. see "CONFIGURATION FILE" for an example.

this only makes sense with the few options not directly controlled by/through spampd. as of Net::Server v2.009 the list is:

reverse_lookups, allow, deny, cidr_allow, cidr_deny, chroot, ipv, conf_file,
serialize, lock_file, check_for_dead, max_dequeue, check_for_dequeue

if running in "Scalable Mode" then these settings from Net::Server::PreFork can also be very relevant to performance tuning:

check_for_waiting, check_for_spawn, min_child_ttl

keep in mind that the Net::Server types inherit from each other: "PreFork" inherits from "PreForkSimple" which inherits from "Net::Server" itself. which means all the options are also inherited. generational wealth, but for flags ๐Ÿ’œ

see the Net::Server(3) https://https://metacpan.org/pod/Net::Server#DEFAULT-ARGUMENTS-FOR-Net::Server, Net::Server::PreForkSimple(3) https://metacpan.org/pod/Net::Server::PreForkSimple#COMMAND-LINE-ARGUMENTS, and Net::Server::PreFork(3) https://metacpan.org/pod/Net::Server::PreFork#COMMAND-LINE-ARGUMENTS documentation for details.


๐Ÿ’€ Deprecated Options ๐Ÿ’€

the following options are no longer used but still accepted for backwards compatibility with prevoius spampd versions:

  • --dead-letters
  • --heloname
  • --stop-at-threshold
  • --add-sc-header
  • --hostname
  • --auto-whitelist, -aw "deprecated with SpamAssassin v3+" โ€” this option is no longer relevant with SA version 3.0 and above, which controls auto whitelist use via config file settings. do not use it unless you must use an older SA version. an error will be generated if attempting to use this option with SA 3.0 or above. ๐Ÿ˜ญ

for SA version < 3.0, turns on the SpamAssassin global whitelist feature. see the SA docs. note that per-user whitelists are not available.

NOTE: DBBasedAddrList is used as the storage mechanism. if you wish to use a different mechanism (such as SQLBasedAddrList), the spampd code will need to be modified in 2 instances (search the source for DBBasedAddrList). lowkey cursed but okay ๐Ÿซ 

โœจ CONFIGURATION FILE โœจ

since v2.60 spampd allows for the use of a configuration file to load server parameters. one or more files can be specified on the command line (see --config option for more details on syntax). the format of a configuration file is simple key/value pairs. comments (starting with # or ;) and blank lines are ignored. the option names are exactly as they appear above in the "OPTIONS" section. they can be listed with or w/out the -/-- prefixes. key/value separator can be one or more of space, tab, or = (equal) sign. ๐ŸŽ€

multiple configuration files can be loaded, with the latter ones being able to override options loaded earlier. any options specified on the command line will take precedence over options from file(s). configuration file(s) are reloaded during a HUP-induced restart (see "SIGNALS"), making it possible to adjust settings dynamically on a running server. live config edits, main character behavior ๐Ÿ’…

you may also provide "passthrough" options directly to Net::Server by putting them after a -- on a line by itself (this is just like using the lonesome -- on a command line; see "Other Net::Server Options").

note that one cannot use the --config option to load a file from within another file. a config file can only be specified on the command line. no config-ception, sorry bestie ๐Ÿ˜”

use the spampd --show defaults > spampd.config command to generate a sample configuration file showing all default values. the example below demonstrates various valid syntax for the file.

# Sample configuration file for SpamPD.

# Double dashes
--user    spampd

# Single dash and = separator with spaces
-pid = /var/run/spampd/spampd.pid

# No dashes required, equals separator no spaces
homedir=/var/cache/spampd

# No dashes, space separator
host  127.0.0.1

# Disabled option (after comment character)
#port  10025

# Boolean values can be set/unset a number of ways:
tagall      1
local-only  0
set-envelope-from
no-log-rules-hit

# Passthrough arguments for Net::Server[::PreFork[Simple]] could go here.
# Be sure to also uncomment the "--" if using any.
# --
# cidr_allow      127.0.0.1/32

๐ŸŒˆโœจ๐Ÿ’–๐Ÿฆ‹๐ŸŒธ

โœจ SIGNALS โœจ

Okay bestie, this is how you talk to the daemon while it's running. Send the master process a signal and watch it move. ๐Ÿ’…

HUP โ€” "updated in v2.60"

Sending HUP to the master process will restart all the children gracefully (meaning the currently running requests will shut down once the request is complete).

"Since v2.60":

SpamAssassin configuration IS reloaded on HUP. Any children currently in the middle of a transaction will finish with the previous SA config and then exit. A new set of children, using the new config, is spawned immediately upon HUP and will serve any new requests.

In a similar manner, spampd will also reload its own settings from any configuration file(s) specified on the original command line with --config option (see "OPTIONS" and "CONFIGURATION FILE").

"Since v2.52": Children were restarted but SpamAssassin configuration was not reloaded.

"Prior to v2.52": HUP would try to restart the server with all default settings (usually failing). ๐Ÿ’€ imagine.

TTIN, TTOU

Sending TTIN signal to the master process will dynamically increase the number of children by one, and TTOU signal will decrease it by one. No restart needed, she just grows. ๐Ÿฆ‹

INT, TERM

Sending INT or TERM signal to the master process will kill all the children immediately and shut down the daemon. Zero chill, no waiting, just gone. ๐Ÿ’€

QUIT

Sending QUIT signal to the master process will perform a graceful shutdown, waiting for all children to finish processing any current transactions and then shutting down the parent process. This is the polite one. ๐ŸŒธ


๐ŸŒˆ EXAMPLES ๐ŸŒˆ

Running between firewall/gateway and internal mail server

spampd listens on port 10025 on the same host as the internal mail server.

spampd --host=192.168.1.10

Same as above but spampd runs on port 10025 of the same host as the firewall/gateway and passes messages on to the internal mail server on another host.

spampd --relayhost=192.168.1.10

Using Postfix advanced content filtering example and disable SA network checks

spampd --port=10025 --relayhost=127.0.0.1:10026 --local-only

Using UNIX sockets instead if INET ports

spampd listens on the UNIX socket /var/run/spampd.socket with persmissions 700 instead of a TCP port:

spampd --socket /var/run/spampd.socket --socket-perms 700

spampd will relay mail to /var/run/dovecot/lmtp instead of a TCP port:

spampd --relaysocket /var/run/dovecot/lmtp
Remember that the user spampd runs as needs to have read AND write permissions on the relaysocket! ๐Ÿซถ

๐Ÿ‘‘ CREDITS ๐Ÿ‘‘

spampd is written and maintained by Maxim Paperno <MPaperno@WorldDesign.com>. The open source code repository is located at <https://github.com/mpaperno/spampd/>. See <http://www.WorldDesign.com/index.cfm/rd/mta/spampd.htm> for historical info. ๐Ÿ“œ

spampd v2 uses two Perl modules (MSDW::SMTP::Client and MSDW::SMTP::Server) by Bennett Todd and Copyright (C) 2001 Morgan Stanley Dean Witter. These are distributed under the GNU GPL (see module code for more details). Both modules have been slightly modified from the originals and are included in this file under new names (SpamPD::Client and SpamPD::Server, respectively).

Also thanks to Bennett Todd for the example smtpproxy script which helped create this version of spampd. See <http://bent.latency.net/smtpprox/> (seems to be down ๐Ÿ˜ญ) or <https://github.com/jnorell/smtpprox>.

spampd v1 was based on code by Dave Carrigan named assassind. Trace amounts of his code or documentation may still remain. Thanks to him for the original inspiration and code. <https://openshut.net/>. ๐ŸŽ€

Also thanks to spamd (included with SpamAssassin) and amavisd-new (<http://www.ijs.si/software/amavisd/>) for some tricks.

Various people have contributed patches, bug reports, and ideas, all of whom I would like to thank. I have tried to include credits in code comments and in the change log, as appropriate.

Code Contributors (in order of appearance):

  • Kurt Andersen
  • Roland Koeckel
  • Urban Petry
  • Sven Mueller

See also: <https://github.com/mpaperno/spampd/graphs/contributors/> โ€” the whole squad, iconic. ๐ŸŒŸ


spampd is Copyright (c) Maxim Paperno; All Rights Reserved.

Portions are Copyright (c) 2001 Morgan Stanley Dean Witter as mentioned above in the Credits section.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.


๐Ÿ› BUGS ๐Ÿ›

Use GitHub issue tracking: <https://github.com/mpaperno/spampd/issues> โ€” tell her what's wrong, she can take it, she's a big girl. ๐Ÿ’–


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€


and that's the whole page, bestie!! ๐ŸŒˆโœจ This one's straight from perl v5.40.1, dated 2026-06-13 โ€” so you know it's fresh, no cap. Go filter some mail, you absolute legend. ๐Ÿ’– ๐ŸŽ€ ๐Ÿ‘‘ ๐Ÿ’… ๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from