NAME
Mailmunge - A milter-based email filtering tool.
INTRODUCTION
Mailmunge is an email filtering tool that uses the Milter library to interface with either Sendmail or Postfix. The milter interface then uses its own protocol to interface with a Perl program; this permits you to write your mail filtering policies in Perl rather than C. Additionally, the Perl worker processes are single-threaded (unlike the multithreaded milter library) which greatly simplifies the coding.
THE 10KM VIEW TO GETTING STARTED
Here's what you'll need to get up and running.
Build and install Mailmunge.
Set configuration variables in /etc/default/mailmunge
Configure Sendmail or Postfix to communicate with the mailmunge Milter.
Write your Perl filter in /etc/mailmunge/mailmunge-filter.pl
Restart your MTA and Mailmunge
Profit!
ARCHITECTURE
Mailmunge consists of four components:
-
mailmunge
is a multi-threaded C program that interfaces with Postfix or Sendmail via the Milter protocol. -
mailmunge-multiplexor
talks tomailmunge
via a socket.mailmunge-multiplexor
manages a pool of worker processes that actually perform the filtering. The Perl filter
The Perl filter is a Perl program that you write. Instances of this program are managed by
mailmunge-multiplexor
; your filter communicates withmailmunge-multiplexor
via its STDIN and STDOUT descriptors.Mailmunge ships with Perl libraries that hide the protocol details from you, allowing to write your filters without knowing the details of the communication protocol. These Perl libraries offer many convenience functions for writing email filtering policies.
-
mm-mx-ctrl
talks tomailmunge-multiplexor
over the same socket used bymailmunge
and lets you obtain statistics about worker processes and askmailmunge-multiplexor
to terminate and restart worker processes gracefully (in a way that does not interfere with ongoing mail filtering.)
OVERVIEW OF MILTER
The Milter library and protocol permit an external program to modify the responses to various SMTP commands. They also permit an external program to modify the message header and/or body before it is delivered.
The milter library features a number of callbacks that are invoked at various stages in the SMTP session. These callbacks can modify the MTA's response to the SMTP commands.
IMPORTANT MAILMUNGE PERL MODULES
Mailmunge has a number of Perl modules. The most important ones are described below.
Mailmunge::Filter
Mailmunge::Filter is the base class for writing filtering policies. To write policies, derive a class from Mailmunge::Filter
and override the callback methods (described in "CALLBACKS") to implement your policy.
Your filter file should be called /etc/mailmunge/mailmunge-filter.pl. It will typically look something like this:
package MyFilter;
use strict;
use warnings;
use base qw(Mailmunge::Filter);
# Add methods and callbacks as required
my $filter = MyFilter->new(); # Instantiate the filter
$filter->run(); # and run the filter
1;
Of course, if your filter gets complicated, you can split it out as one or more Perl module files, and then the filter program could look like this:
use strict;
use warnings;
use Mailmunge::Filter::MyComplicatedFilter;
my $filter = Mailmunge::Filter::MyComplicatedFilter->new();
$filter->run();
1;
Writing your filter as a proper Perl module is good practice; it makes it easier to write unit tests for your filter.
Mailmunge::Context
A Mailmunge::Context object holds the context for the current email message. For example, it holds the sender, recipients, message subject, and a MIME::Entity
object representing the message itself.
Not all pieces of context are available in every callback; the documentation for each callback function will specify which pieces of context are available for that callback.
Mailmunge::Response
A Mailmunge::Response object represents the desired response to an SMTP command. This includes the response code (for example, "250"), the response DSN (for example, "2.1.0") and the response text.
CALLBACKS
Callbacks are Mailmunge::Filter
methods invoked by Mailmunge at various points in the SMTP session. They are passed two arguments: The usual $self
argument that is the Mailmunge::Filter
object, and a $ctx
argument that is a Mailmunge::Context
object.
Most callbacks must return a Mailmunge::Response
object. However, the message-oriented callbacks filter_message
and filter_wrapup
don't return anything special (and their return values are ignored.)
$filter->filter_relay($ctx)
This callback is called when an SMTP client connects to the MTA. The following $ctx
fields are available:
$ctx->hostip IP address of connecting host
$ctx->hostname Hostname of the connecting host
$ctx->client_port Client TCP port
$ctx->my_ip Server's IP address
$ctx->my_port Server's TCP port
$ctx->qid Queue ID (Note: May be NOQUEUE if queue ID not available)
The function must return an Mailmunge::Response object instructing the MTA how to handle the connection attempt.
For example, if you wish to reject connections from 192.168.44.2, you could use:
sub filter_relay {
my ($self, $ctx) = @_;
if ($ctx->hostip eq '192.168.44.2') {
return Mailmunge::Response->REJECT(message => 'Your IP is banned.');
}
return Mailmunge::Response->CONTINUE();
}
$filter->filter_helo($ctx)
This callback is called after the SMTP client issues its EHLO or HELO command. The following $ctx
fields are available:
$ctx->hostip IP address of connecting host
$ctx->hostname Hostname of the connecting host
$ctx->helo The argument to the EHLO/HELO command
$ctx->client_port Client TCP port
$ctx->my_ip Server's IP address
$ctx->my_port Server's TCP port
$ctx->qid Queue ID (Note: May be NOQUEUE if queue ID not available)
The function must return an Mailmunge::Response object instructing the MTA how to respond to the HELO/EHLO
For example, if you wish to reject connections from a host that uses your domain (example.com) in its HELO, you could use:
sub filter_helo {
my ($self, $ctx) = @_;
if ($ctx->helo =~ /\.example\.com$/i)
return Mailmunge::Response->REJECT(message => "You aren't one of us!");
}
return Mailmunge::Response->CONTINUE();
}
$filter->filter_sender($ctx)
This callback is called when an SMTP client issues a MAIL From: command. The following $ctx
fields are available:
$ctx->sender Envelope sender address
$ctx->hostip IP address of connecting host
$ctx->hostname Hostname of the connecting host
$ctx->helo The argument to the EHLO/HELO command
$ctx->qid Queue ID (Note: May be NOQUEUE if queue ID not available)
$ctx->esmtp_args Arrayref of ESMTP arguments to MAIL From:
The function must return an Mailmunge::Response object instructing the MTA how to handle the MAIL From: command.
Here is a simple example:
sub filter_sender {
my ($self, $ctx) = @_;
if ($ctx->sender eq '<spammer@nogood.example>') {
return Mailmunge::Response->REJECT(message => "We don't like spammers");
}
return Mailmunge::Response::CONTINUE();
}
$filter->filter_recipient($ctx)
This callback is called when an SMTP client issues a RCPT To: command. The following $ctx
fields are available:
$ctx->recipients An arrayref consisting of a single recipient
$ctx->sender Envelope sender address
$ctx->hostip IP address of connecting host
$ctx->hostname Hostname of the connecting host
$ctx->first_recip The recipient from the I<first> RCPT To: command
$ctx->helo The argument to the EHLO/HELO command
$ctx->cwd The current working directory
$ctx->qid Queue ID
$ctx->rcpt_mailer The ${rcpt_mailer} macro value for this recipient
$ctx->rcpt_host The ${rcpt_host} macro value for this recipient
$ctx->rcpt_addr The ${rcpt_addr} macro value for this recipient
$ctx->esmtp_args Arrayref of ESMTP arguments to MAIL From:
The function must return an Mailmunge::Response object instructing the MTA how to handle the RCPT To: command.
Here is a simple example:
sub filter_recipient {
my ($self, $ctx) = @_;
if ($ctx->recipients->[0] eq '<moved@example.org>') {
return Mailmunge::Response->REJECT(message => 'Moved; try <newaddress@example.com>');
}
return Mailmunge::Response::CONTINUE();
}
$filter->filter_message($ctx)
This callback is called when the filter should scan a message body. See "filter_message" in Mailmunge::Filter for more details.
$filter->filter_wrapup($ctx)
This callback is called when the filter has finished scanning a message body, but wants one last chance to modify the message headers or disposition. See "filter_wrapup" in Mailmunge::Filter for more details.
SEE ALSO
mailmunge, mailmunge-multiplexor, mm-mx-ctrl, mailmunge-protocol, Mailmunge::Filter, Mailmunge::Context, Mailmunge::Response, Mailmunge::Constants
AUTHOR
Dianne Skoll <dianne@skollsoft.com>
LICENSE
This code is licensed under the terms of the GNU General Public License, version 2.
Copyright © 2024 Skoll Software Consulting