File Coverage

blib/lib/Mail/MtPolicyd/Plugin/Honeypot.pm
Criterion Covered Total %
statement 9 57 15.7
branch 0 20 0.0
condition 0 6 0.0
subroutine 3 10 30.0
pod 1 7 14.2
total 13 100 13.0


line stmt bran cond sub pod time code
1             package Mail::MtPolicyd::Plugin::Honeypot;
2              
3 1     1   1127 use Moose;
  1         1  
  1         6  
4 1     1   4167 use namespace::autoclean;
  1         3  
  1         8  
5              
6             our $VERSION = '2.01'; # VERSION
7             # ABSTRACT: mtpolicyd plugin for creating an honeypot
8              
9             extends 'Mail::MtPolicyd::Plugin';
10             with 'Mail::MtPolicyd::Plugin::Role::Scoring';
11             with 'Mail::MtPolicyd::Plugin::Role::UserConfig' => {
12             'uc_attributes' => [ 'enabled' ],
13             };
14             with 'Mail::MtPolicyd::Plugin::Role::PluginChain';
15              
16 1     1   104 use Mail::MtPolicyd::Plugin::Result;
  1         1  
  1         598  
17              
18              
19             has 'enabled' => ( is => 'rw', isa => 'Str', default => 'on' );
20              
21             has 'score' => ( is => 'rw', isa => 'Maybe[Num]' );
22             has 'mode' => ( is => 'rw', isa => 'Str', default => 'reject');
23              
24             has 'recipients' => ( is => 'rw', isa => 'Str', default => '' );
25             has 'recipients_re' => ( is => 'rw', isa => 'Str', default => '' );
26              
27             has _recipients => ( is => 'ro', isa => 'ArrayRef', lazy => 1,
28             default => sub {
29             my $self = shift;
30             return [ split(/\s*,\s*/, $self->recipients) ];
31             },
32             );
33              
34             has _recipients_re => ( is => 'ro', isa => 'ArrayRef', lazy => 1,
35             default => sub {
36             my $self = shift;
37             return [ split(/\s*,\s*/, $self->recipients_re) ];
38             },
39             );
40              
41             has 'reject_message' => ( is => 'rw', isa => 'Str', default => 'trapped by honeypod' );
42              
43             has 'expire' => ( is => 'rw', isa => 'Int', default => 60*60*2 );
44              
45             sub run {
46 0     0 1   my ( $self, $r ) = @_;
47 0           my $ip = $r->attr('client_address');
48 0           my $recipient = $r->attr('recipient');
49 0           my $session = $r->session;
50              
51 0           my $enabled = $self->get_uc( $session, 'enabled' );
52 0 0         if( $enabled eq 'off' ) {
53 0           return;
54             }
55              
56 0 0         if( $self->is_in_honeypot( $r, $ip ) ) {
57 0           return $self->trapped_action;
58             }
59 0 0         if( $self->is_honeypot_recipient( $recipient ) ) {
60 0           $self->add_to_honeypot( $r, $ip );
61 0           return $self->trapped_action;
62             }
63              
64 0           return;
65             }
66              
67             sub trapped_action {
68 0     0 0   my ( $self, $r ) = @_;
69              
70 0 0         if( $self->mode eq 'reject' ) {
71 0           return( Mail::MtPolicyd::Plugin::Result->new(
72             action => 'reject '.$self->reject_message,
73             abort => 1,
74             ) );
75             }
76 0 0 0       if( defined $self->score && ! $r->is_already_done($self->name.'-score') ) {
77 0           $self->add_score($r, $self->name => $self->score);
78             }
79 0 0         if( defined $self->chain ) {
80 0           my $chain_result = $self->chain->run( $r );
81 0           return( @{$chain_result->plugin_results} );
  0            
82             }
83 0           return;
84             }
85              
86             sub is_honeypot_recipient {
87 0     0 0   my ( $self, $recipient ) = @_;
88              
89 0 0 0       if( $self->is_in_recipients( $recipient )
90             || $self->is_in_recipients_re( $recipient ) ) {
91 0           return(1);
92             }
93              
94 0           return(0);
95             }
96              
97             sub is_in_recipients {
98 0     0 0   my ( $self, $recipient ) = @_;
99              
100 0 0         if( grep { $_ eq $recipient } @{$self->_recipients} ) {
  0            
  0            
101 0           return(1);
102             }
103              
104 0           return(0);
105             }
106              
107             sub is_in_recipients_re {
108 0     0 0   my ( $self, $recipient ) = @_;
109              
110 0 0         if( grep { $recipient =~ /$_/ } @{$self->_recipients_re} ) {
  0            
  0            
111 0           return(1);
112             }
113              
114 0           return(0);
115             }
116              
117             sub is_in_honeypot {
118 0     0 0   my ( $self, $r, $ip ) = @_;
119 0           my $key = join(",", $self->name, $ip );
120 0 0         if( my $ticket = $r->server->memcached->get( $key ) ) {
121 0           return( 1 );
122             }
123 0           return;
124             }
125              
126             sub add_to_honeypot {
127 0     0 0   my ( $self, $r, $ip ) = @_;
128 0           my $key = join(",", $self->name, $ip );
129 0           $r->server->memcached->set( $key, '1', $self->expire );
130 0           return;
131             }
132              
133             __PACKAGE__->meta->make_immutable;
134              
135             1;
136              
137             __END__
138              
139             =pod
140              
141             =encoding UTF-8
142              
143             =head1 NAME
144              
145             Mail::MtPolicyd::Plugin::Honeypot - mtpolicyd plugin for creating an honeypot
146              
147             =head1 VERSION
148              
149             version 2.01
150              
151             =head1 DESCRIPTION
152              
153             The Honeypot plugin creates an honeypot to trap IPs sending to unused recipient addresses.
154              
155             The plugin requires that you define unused recipient addresses as honeypots.
156             These addresses can be specified by the recipients and recipients_re parameters.
157              
158             Each time an IP tries to send an mail to one of these honeypots the message will be
159             reject if mode is 'reject' and an scoring is applied.
160             The IP is also added to a temporary IP blacklist till an timeout is reached (parameter expire).
161             All IPs on this blacklist will also be rejected if mode is 'reject' and scoring is applied.
162              
163             =head1 EXAMPLE
164              
165             <Plugin honeypot>
166             module = "Honeypot"
167             recipients = "bob@company.com,joe@company.com"
168             recipients_re = "^(tic|tric|trac)@(gmail|googlemail)\.de$"
169             </Plugin>
170              
171             =head1 PARAMETERS
172              
173             =over
174              
175             =item (uc_)enabled (default: on)
176              
177             Enable/disable this check.
178              
179             =item score (default: empty)
180              
181             Apply an score to this message if it is send to an honeypot address or it has been
182             added to the honeypot before by sending an mail to an honeypot.
183              
184             =item mode (default: reject)
185              
186             The default is to return an reject.
187              
188             Change to 'passive' if you just want scoring.
189              
190             =item recipients (default: '')
191              
192             A comma separated list of recipients to use as honeypots.
193              
194             =item recipients_re (default: '')
195              
196             A comma separated list of regular expression to match against the
197             recipient to use them as honeypots.
198              
199             =item reject_message (default: 'trapped by honeypod')
200              
201             A string to return with the reject action.
202              
203             =item expire (default: 7200 (2h))
204              
205             Time in seconds till the client_ip is removed from the honeypot.
206              
207             =item Plugin (default: empty)
208              
209             Execute this plugins when the condition matched.
210              
211             =back
212              
213             =head1 AUTHOR
214              
215             Markus Benning <ich@markusbenning.de>
216              
217             =head1 COPYRIGHT AND LICENSE
218              
219             This software is Copyright (c) 2014 by Markus Benning <ich@markusbenning.de>.
220              
221             This is free software, licensed under:
222              
223             The GNU General Public License, Version 2, June 1991
224              
225             =cut