karma: general improvements
skip earlytalker checks for positive senders limit negative karma senders to 1 concurrent connection (hosts_allow) added karma::hook_pre_connection, to make hosts_allow change possible added karma score to log entries
This commit is contained in:
parent
60d3cda18e
commit
73f4759ae7
@ -163,6 +163,9 @@ sub connect_handler {
|
|||||||
return DECLINED unless $self->{_args}{'check-at'}{CONNECT};
|
return DECLINED unless $self->{_args}{'check-at'}{CONNECT};
|
||||||
return DECLINED if $self->is_immune();
|
return DECLINED if $self->is_immune();
|
||||||
|
|
||||||
|
my $karma = $self->connection->notes('karma_history');
|
||||||
|
return DECLINED if (defined $karma && $karma > 5);
|
||||||
|
|
||||||
$in->add(\*STDIN) or return DECLINED;
|
$in->add(\*STDIN) or return DECLINED;
|
||||||
if (! $in->can_read($self->{_args}{'wait'})) {
|
if (! $in->can_read($self->{_args}{'wait'})) {
|
||||||
return $self->log_and_pass();
|
return $self->log_and_pass();
|
||||||
|
@ -68,6 +68,7 @@ sub hook_pre_connection {
|
|||||||
|
|
||||||
my $remote = $args{remote_ip};
|
my $remote = $args{remote_ip};
|
||||||
my $max = $args{max_conn_ip};
|
my $max = $args{max_conn_ip};
|
||||||
|
my $karma = $self->connection->notes('karma_history');
|
||||||
|
|
||||||
if ( $max ) {
|
if ( $max ) {
|
||||||
my $num_conn = 1; # seed with current value
|
my $num_conn = 1; # seed with current value
|
||||||
@ -75,6 +76,7 @@ sub hook_pre_connection {
|
|||||||
foreach my $rip (@{$args{child_addrs}}) {
|
foreach my $rip (@{$args{child_addrs}}) {
|
||||||
++$num_conn if (defined $rip && $rip eq $raddr);
|
++$num_conn if (defined $rip && $rip eq $raddr);
|
||||||
}
|
}
|
||||||
|
$max = $self->karma_bump( $karma, $max ) if defined $karma;
|
||||||
if ($num_conn > $max ) {
|
if ($num_conn > $max ) {
|
||||||
my $err_mess = "too many connections from $remote";
|
my $err_mess = "too many connections from $remote";
|
||||||
$self->log(LOGINFO, "fail: $err_mess ($num_conn > $max)");
|
$self->log(LOGINFO, "fail: $err_mess ($num_conn > $max)");
|
||||||
@ -113,3 +115,12 @@ sub in_hosts_allow {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sub karma_bump {
|
||||||
|
my ($self, $karma, $max) = @_;
|
||||||
|
if ( $karma <= 0 ) {
|
||||||
|
$self->log(LOGINFO, "limiting max connects to 1 for negative karma ($karma)");
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
return $max;
|
||||||
|
};
|
||||||
|
@ -6,7 +6,7 @@ karma - reward nice and penalize naughty mail senders
|
|||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
Karma tracks sender history, providing the ability to deliver differing levels
|
Karma tracks sender history, allowing us to provide differing levels
|
||||||
of service to naughty, nice, and unknown senders.
|
of service to naughty, nice, and unknown senders.
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
@ -14,7 +14,7 @@ of service to naughty, nice, and unknown senders.
|
|||||||
Karma records the number of nice, naughty, and total connections from mail
|
Karma records the number of nice, naughty, and total connections from mail
|
||||||
senders. After sending a naughty message, if a sender has more naughty than
|
senders. After sending a naughty message, if a sender has more naughty than
|
||||||
nice connections, they are penalized for I<penalty_days>. Connections
|
nice connections, they are penalized for I<penalty_days>. Connections
|
||||||
from senders in the penalty box are tersely disconnected.
|
from senders in the penalty box are rejected per the settings in I<reject>.
|
||||||
|
|
||||||
Karma provides other plugins with a karma value they can use to be more
|
Karma provides other plugins with a karma value they can use to be more
|
||||||
lenient, strict, or skip processing entirely.
|
lenient, strict, or skip processing entirely.
|
||||||
@ -24,10 +24,9 @@ custom connection policies such as these two examples:
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
Hi there, well behaved sender. Please help yourself to TLS, AUTH, greater
|
Hi there, well behaved sender. Please help yourself to greater concurrency, multiple recipients, no delays, and other privileges.
|
||||||
concurrency, multiple recipients, no delays, and other privileges.
|
|
||||||
|
|
||||||
Hi there, naughty sender. Enjoy this poke in the eye with a sharp stick. Bye.
|
Hi there, naughty sender. You get a max concurrency of 1, and SMTP delays.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
@ -114,13 +113,7 @@ run before B<karma> for that to work.
|
|||||||
No attempt is made by this plugin to determine what karma is. It is up to
|
No attempt is made by this plugin to determine what karma is. It is up to
|
||||||
other plugins to make that determination and communicate it to this plugin by
|
other plugins to make that determination and communicate it to this plugin by
|
||||||
incrementing or decrementing the transaction note B<karma>. Raise it for good
|
incrementing or decrementing the transaction note B<karma>. Raise it for good
|
||||||
karma and lower it for bad karma. This is best done like so:
|
karma and lower it for bad karma. See B<USING KARMA IN OTHER PLUGINS>.
|
||||||
|
|
||||||
# only if karma plugin loaded
|
|
||||||
if ( defined $connection->notes('karma') ) {
|
|
||||||
$connection->notes('karma', $connection->notes('karma') - 1); # bad
|
|
||||||
$connection->notes('karma', $connection->notes('karma') + 1); # good
|
|
||||||
};
|
|
||||||
|
|
||||||
After the connection ends, B<karma> will record the result. Mail servers whose
|
After the connection ends, B<karma> will record the result. Mail servers whose
|
||||||
naughty connections exceed nice ones are sent to the penalty box. Servers in
|
naughty connections exceed nice ones are sent to the penalty box. Servers in
|
||||||
@ -133,7 +126,7 @@ an example connection from an IP in the penalty box:
|
|||||||
73122 (connect) earlytalker: pass: 64.185.226.65 said nothing spontaneous
|
73122 (connect) earlytalker: pass: 64.185.226.65 said nothing spontaneous
|
||||||
73122 (connect) relay: skip: no match
|
73122 (connect) relay: skip: no match
|
||||||
73122 (connect) karma: fail
|
73122 (connect) karma: fail
|
||||||
73122 550 You were naughty. You are penalized for 0.99 more days.
|
73122 550 You were naughty. You are cannot connect for 0.99 more days.
|
||||||
73122 click, disconnecting
|
73122 click, disconnecting
|
||||||
73122 (post-connection) connection_time: 1.048 s.
|
73122 (post-connection) connection_time: 1.048 s.
|
||||||
|
|
||||||
@ -211,12 +204,11 @@ karma_tool script.
|
|||||||
|
|
||||||
=head1 BUGS & LIMITATIONS
|
=head1 BUGS & LIMITATIONS
|
||||||
|
|
||||||
This plugin is reactionary. Like the FBI, it doesn't punish until
|
This plugin is reactionary. Like the FBI, it doesn't do anything until
|
||||||
after a crime has been committed. It an "abuse me once, shame on you,
|
after a crime has been committed.
|
||||||
abuse me twice, shame on me" policy.
|
|
||||||
|
|
||||||
There is little to be gained by listing servers that are already on DNS
|
There is little to be gained by listing servers that are already on DNS
|
||||||
blacklists, send to non-existent users, earlytalkers, etc. Those already have
|
blacklists, send to invalid users, earlytalkers, etc. Those already have
|
||||||
very lightweight tests.
|
very lightweight tests.
|
||||||
|
|
||||||
=head1 AUTHOR
|
=head1 AUTHOR
|
||||||
@ -255,6 +247,32 @@ sub register {
|
|||||||
$self->register_hook('disconnect', 'disconnect_handler');
|
$self->register_hook('disconnect', 'disconnect_handler');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub hook_pre_connection {
|
||||||
|
my ($self,$transaction,%args) = @_;
|
||||||
|
|
||||||
|
$self->connection->notes('karma_history', 0);
|
||||||
|
|
||||||
|
my $remote_ip = $args{remote_ip};
|
||||||
|
#my $max_conn = $args{max_conn_ip};
|
||||||
|
|
||||||
|
my $db = $self->get_db_location();
|
||||||
|
my $lock = $self->get_db_lock( $db ) or return DECLINED;
|
||||||
|
my $tied = $self->get_db_tie( $db, $lock ) or return DECLINED;
|
||||||
|
my $key = $self->get_db_key( $remote_ip ) or do {
|
||||||
|
$self->log( LOGINFO, "skip, unable to get DB key" );
|
||||||
|
return DECLINED;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( ! $tied->{$key} ) {
|
||||||
|
$self->log(LOGINFO, "pass, no record");
|
||||||
|
return $self->cleanup_and_return($tied, $lock );
|
||||||
|
};
|
||||||
|
|
||||||
|
my ($penalty_start_ts, $naughty, $nice, $connects) = $self->parse_value( $tied->{$key} );
|
||||||
|
$self->calc_karma($naughty, $nice);
|
||||||
|
return $self->cleanup_and_return($tied, $lock );
|
||||||
|
};
|
||||||
|
|
||||||
sub connect_handler {
|
sub connect_handler {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
@ -294,7 +312,7 @@ sub connect_handler {
|
|||||||
$self->cleanup_and_return($tied, $lock );
|
$self->cleanup_and_return($tied, $lock );
|
||||||
|
|
||||||
my $left = sprintf "%.2f", $self->{_args}{penalty_days} - $days_old;
|
my $left = sprintf "%.2f", $self->{_args}{penalty_days} - $days_old;
|
||||||
my $mess = "You were naughty. You are penalized for $left more days.";
|
my $mess = "You were naughty. You cannot connect for $left more days.";
|
||||||
|
|
||||||
return $self->get_reject( $mess, $karma );
|
return $self->get_reject( $mess, $karma );
|
||||||
}
|
}
|
||||||
@ -313,11 +331,11 @@ sub disconnect_handler {
|
|||||||
my $key = $self->get_db_key();
|
my $key = $self->get_db_key();
|
||||||
|
|
||||||
my ($penalty_start_ts, $naughty, $nice, $connects) = $self->parse_value( $tied->{$key} );
|
my ($penalty_start_ts, $naughty, $nice, $connects) = $self->parse_value( $tied->{$key} );
|
||||||
|
my $history = ($nice || 0) - $naughty;
|
||||||
|
|
||||||
if ( $karma < 0 ) {
|
if ( $karma < 0 ) {
|
||||||
$naughty++;
|
$history--;
|
||||||
my $negative_limit = 0 - $self->{_args}{negative};
|
my $negative_limit = 0 - $self->{_args}{negative};
|
||||||
my $history = ($nice || 0) - $naughty;
|
|
||||||
if ( $history <= $negative_limit ) {
|
if ( $history <= $negative_limit ) {
|
||||||
if ( $nice == 0 && $history < -5 ) {
|
if ( $nice == 0 && $history < -5 ) {
|
||||||
$self->log(LOGINFO, "penalty box bonus!");
|
$self->log(LOGINFO, "penalty box bonus!");
|
||||||
@ -326,15 +344,15 @@ sub disconnect_handler {
|
|||||||
else {
|
else {
|
||||||
$penalty_start_ts = sprintf "%s", time;
|
$penalty_start_ts = sprintf "%s", time;
|
||||||
};
|
};
|
||||||
$self->log(LOGINFO, "negative, sent to penalty box ($history)");
|
$self->log(LOGINFO, "negative, sent to penalty box (k: $karma, h: $history)");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self->log(LOGINFO, "negative");
|
$self->log(LOGINFO, "negative (k: $karma, h: $history)");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
elsif ($karma > 1) {
|
elsif ($karma > 1) {
|
||||||
$nice++;
|
$nice++;
|
||||||
$self->log(LOGINFO, "positive");
|
$self->log(LOGINFO, "positive (k: $karma, h: $history)");
|
||||||
}
|
}
|
||||||
|
|
||||||
$tied->{$key} = join(':', $penalty_start_ts, $naughty, $nice, ++$connects);
|
$tied->{$key} = join(':', $penalty_start_ts, $naughty, $nice, ++$connects);
|
||||||
@ -375,7 +393,11 @@ sub cleanup_and_return {
|
|||||||
|
|
||||||
sub get_db_key {
|
sub get_db_key {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $nip = Net::IP->new( $self->qp->connection->remote_ip ) or return;
|
my $ip = shift || $self->qp->connection->remote_ip;
|
||||||
|
my $nip = Net::IP->new( $ip ) or do {
|
||||||
|
$self->log(LOGERROR, "skip, unable to determine remote IP");
|
||||||
|
return;
|
||||||
|
};
|
||||||
return $nip->intip; # convert IP to an int
|
return $nip->intip; # convert IP to an int
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,10 +168,7 @@ sub data_post_handler {
|
|||||||
$self->log( LOGNOTICE, "fail, found virus $found" );
|
$self->log( LOGNOTICE, "fail, found virus $found" );
|
||||||
|
|
||||||
$self->connection->notes('naughty', 1); # see plugins/naughty
|
$self->connection->notes('naughty', 1); # see plugins/naughty
|
||||||
|
$self->adjust_karma( -1 );
|
||||||
if ( defined $self->connection->notes('karma') ) {
|
|
||||||
$self->connection->notes('karma', ($self->connection->notes('karma') - 1));
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( $self->{_args}{deny_viruses} ) {
|
if ( $self->{_args}{deny_viruses} ) {
|
||||||
return ( DENY, "Virus found: $found" );
|
return ( DENY, "Virus found: $found" );
|
||||||
|
Loading…
Reference in New Issue
Block a user