]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blame - config/guardian/guardian.pl
Bump vdradmin package version.
[people/pmueller/ipfire-2.x.git] / config / guardian / guardian.pl
CommitLineData
7c4cc0d8 1#!/usr/bin/perl
6579aab2 2# based on V 1.7 guardian enhanced for IPFire and snort 2.8
7c4cc0d8
CS
3# Read the readme file for changes
4#
fb30e20c
CS
5# Enhanced for IPFire by IPFire Team
6# Added Portscan detection for non syslog system
7# Added SSH-Watch for SSH-Bruteforce Attacks
8# An suppected IP will be blocked on all interfaces
7c4cc0d8
CS
9
10$OS=`uname`;
11chomp $OS;
12print "OS shows $OS\n";
13
14require 'getopts.pl';
15
16&Getopts ('hc:d');
17if (defined($opt_h)) {
6579aab2
CS
18 print "Guardian v1.7 \n";
19 print "guardian.pl [-hd] <-c config>\n";
20 print " -h shows help\n";
21 print " -d run in debug mode (doesn't fork, output goes to STDOUT)\n";
22 print " -c specifiy a configuration file other than the default (/etc/guardian.conf)\n";
23 exit;
7c4cc0d8
CS
24}
25&load_conf;
26&sig_handler_setup;
27
28print "My ip address and interface are: $hostipaddr $interface\n";
29
30if ($hostipaddr !~ /\d+\.\d+\.\d+\.\d+/) {
6579aab2
CS
31 print "This ip address is bad : $hostipaddr\n";
32 die "I need a good host ipaddress\n";
7c4cc0d8
CS
33}
34
35$networkaddr = $hostipaddr;
36$networkaddr =~ s/\d+$/0/;
37$gatewayaddr = $hostipaddr;
38$gatewayaddr =~ s/\d+$/$hostgatewaybyte/;
39$broadcastaddr = $hostipaddr;
40$broadcastaddr =~ s/\d+$/255/;
41&build_ignore_hash;
42
43# This is the target hash. If a packet was destened to any of these, then the
44# sender of that packet will get denied, unless it is on the ignore list..
45
6579aab2
CS
46%targethash = ( "$networkaddr" => 1,
47 "$broadcastaddr" => 1,
48 "0" => 1, # This is what gets sent to &checkem if no
49 # destination was found.
50 "$hostipaddr" => 1);
7c4cc0d8 51
fb30e20c
CS
52%sshhash = ();
53
7c4cc0d8 54if ( -e $targetfile ) {
6579aab2 55 &load_targetfile;
7c4cc0d8
CS
56}
57
58if (!defined($opt_d)) {
6579aab2
CS
59 print "Becoming a daemon..\n";
60 &daemonize;
7c4cc0d8
CS
61} else { print "Running in debug mode..\n"; }
62
63open (ALERT, $alert_file) or die "can't open alert file: $alert_file: $!\n";
6579aab2 64seek (ALERT, 0, 2); # set the position to EOF.
7c4cc0d8
CS
65# this is the same as a tail -f :)
66$counter=0;
fb30e20c 67open (ALERT2, "/var/log/messages" ) or die "can't open /var/log/messages: $!\n";
6579aab2 68seek (ALERT2, 0, 2); # set the position to EOF.
fb30e20c 69# this is the same as a tail -f :)
fb30e20c 70
6579aab2
CS
71for (;;) {
72 sleep 1;
73 if (seek(ALERT,0,1)){
74 while (<ALERT>) {
75 chop;
76 if (defined($opt_d)) {
77 print "$_\n";
78 }
79 if (/\[\*\*\]\s+(.*)\s+\[\*\*\]/){
80 $type=$1;
81 }
82 if (/(\d+\.\d+\.\d+\.\d+):\d+ -\> (\d+\.\d+\.\d+\.\d+):\d+/) {
83 &checkem ($1, $2, $type);
84 }
85 if (/(\d+\.\d+\.\d+\.\d+)+ -\> (\d+\.\d+\.\d+\.\d+)+/) {
86 &checkem ($1, $2, $type);
87 }
88 }
89 }
90
91 sleep 1;
92 if (seek(ALERT2,0,1)){
93 while (<ALERT2>) {
94 chop;
95 if ($_=~/.*sshd.*Failed password for root from.*/) {
96 my @array=split(/ /,$_);&checkssh ($array[10], "possible SSH-Bruteforce Attack");}
97 }
98 }
99
100# Run this stuff every 30 seconds..
101 if ($counter == 30) {
102 &remove_blocks; # This might get moved elsewhere, depending on how much load
103 # it puts on the system..
104 &check_log_name;
105 $counter=0;
106 } else {
107 $counter=$counter+1;
108 }
7c4cc0d8
CS
109}
110
111sub check_log_name {
6579aab2
CS
112 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
113 $atime,$mtime,$ctime,$blksize,$blocks) = stat($alert_file);
114 if ($size < $previous_size) { # The filesize is smaller than last
115 close (ALERT); # we checked, so we need to reopen it
116 open (ALERT, "$alert_file"); # This should still work in our main while
117 $previous_size=$size; # loop (I hope)
118 write_log ("Log filename changed. Reopening $alert_file\n");
119 } else {
120 $previous_size=$size;
121 }
7c4cc0d8 122}
6579aab2 123
7c4cc0d8
CS
124
125sub checkem {
6579aab2
CS
126 my ($source, $dest,$type) = @_;
127 my $flag=0;
128
129 return 1 if ($source eq $hostipaddr);
130 # this should prevent is from nuking ourselves
131
132 return 1 if ($source eq $gatewayaddr); # or our gateway
133 if ($ignore{$source} == 1) { # check our ignore list..
134 &write_log("$source\t$type\n");
135 &write_log("Ignoring attack because $source is in my ignore list\n");
136 return 1;
137 }
138
139# if the offending packet was sent to us, the network, or the broadcast, then
140 if ($targethash{$dest} == 1) {
141 &ipchain ($source, $dest, $type);
142 }
143# you will see this if the destination was not in the $targethash, and the
144# packet was not ignored before the target check..
145 else {
146 &write_log ("Odd.. source = $source, dest = $dest - No action done.\n");
147 if (defined ($opt_d)) {
148 foreach $key (keys %targethash) {
149 &write_log ("targethash{$key} = $targethash{$key}\n");
150 }
151 }
152 }
7c4cc0d8
CS
153}
154
fb30e20c 155sub checkssh {
6579aab2
CS
156 my ($source,$type) = @_;
157 my $flag=0;
158
159 return 1 if ($source eq $hostipaddr);
160 # this should prevent is from nuking ourselves
161
162 return 1 if ($source eq $gatewayaddr); # or our gateway
163
164 if ($sshhash{$dest} eq "" ){
165 $sshhash{$dest} = 1;
166 }
167 if ($sshhash{$dest} >= 3 ) {
168 &write_log ("source = $source, count $sshhash{$dest} - blocking for ssh attack.\n");
169 &ipchain ($source, "", $type);
170 }
171# you will see this if the destination was not in the $sshhash, and the
172# packet was not ignored before the target check..
173 else {
174 &write_log ("Odd.. source = $source, ssh count only $sshhash{$dest} - No action done.\n");
175 if (defined ($opt_d)) {
176 foreach $key (keys %sshhash) {
177 &write_log ("sshhash{$key} = %sshhash{$key}\n");
178 }
179 }
180 $sshhash{$key} = $sshhash{$key}+1;
181 }
fb30e20c
CS
182}
183
6579aab2
CS
184sub ipchain {
185 my ($source, $dest, $type) = @_;
186 &write_log ("$source\t$type\n");
187 if ($hash{$source} eq "") {
188 &write_log ("Running '$blockpath $source'\n");
189 system ("$blockpath $source");
190 $hash{$source} = time() + $TimeLimit;
191 } else {
192# We have already blocked this one, but snort detected another attack. So
193# we should update the time blocked..
194 $hash{$source} = time() + $TimeLimit;
195 }
7c4cc0d8 196}
6579aab2 197
7c4cc0d8
CS
198sub build_ignore_hash {
199# This would cause is to ignore all broadcasts if it
200# got set.. However if unset, then the attacker could spoof the packet to make
201# it look like it came from the network, and a reply to the spoofed packet
6579aab2
CS
202# could be seen if the attacker were on the local network.
203# $ignore{$networkaddr}=1;
7c4cc0d8
CS
204
205# same thing as above, just with the broadcast instead of the network.
206# $ignore{$broadcastaddr}=1;
6579aab2
CS
207 my $count =0;
208 $ignore{$gatewayaddr}=1;
209 $ignore{$hostipaddr}=1;
210 if ($ignorefile ne "") {
211 open (IGNORE, $ignorefile);
212 while (<IGNORE>) {
213 chop;
214 next if (/\#/); #skip comments
215 next if (/^\s*$/); # and blank lines
216 $ignore{$_}=1;
217 $count++;
218 }
219 close (IGNORE);
220 print "Loaded $count addresses from $ignorefile\n";
221 } else {
222 print "No ignore file was loaded!\n";
223 }
7c4cc0d8
CS
224}
225
226sub load_conf {
6579aab2
CS
227 if ($opt_c eq "") {
228 $opt_c = "/etc/guardian.conf";
229 }
230
231 if (! -e $opt_c) {
232 die "Need a configuration file.. please use to the -c option to name a configuration file\n";
233 }
234
235 open (CONF, $opt_c) or die "Cannot read the config file $opt_c, $!\n";
236 while (<CONF>) {
237 chop;
238 next if (/^\s*$/); #skip blank lines
239 next if (/^#/); # skip comment lines
240 if (/LogFile\s+(.*)/) {
241 $logfile = $1;
242 }
243 if (/Interface\s+(.*)/) {
244 $interface = $1;
245 }
246 if (/AlertFile\s+(.*)/) {
247 $alert_file = $1;
248 }
249 if (/IgnoreFile\s+(.*)/) {
250 $ignorefile = $1;
251 }
252 if (/TargetFile\s+(.*)/) {
253 $targetfile = $1;
254 }
255 if (/TimeLimit\s+(.*)/) {
256 $TimeLimit = $1;
257 }
258 if (/HostIpAddr\s+(.*)/) {
259 $hostipaddr = $1;
260 }
261 if (/HostGatewayByte\s+(.*)/) {
262 $hostgatewaybyte = $1;
263 }
264 }
265
266 if ($interface eq "") {
267 die "Fatal! Interface is undefined.. Please define it in $opt_o with keyword Interface\n";
268 }
269 if ($alert_file eq "") {
270 print "Warning! AlertFile is undefined.. Assuming /var/log/snort.alert\n";
271 $alert_file="/var/log/snort.alert";
272 }
273 if ($hostipaddr eq "") {
274 print "Warning! HostIpAddr is undefined! Attempting to guess..\n";
275 $hostipaddr = &get_ip($interface);
276 print "Got it.. your HostIpAddr is $hostipaddr\n";
277 }
278 if ($ignorefile eq "") {
279 print "Warning! IgnoreFile is undefined.. going with default ignore list (hostname and gateway)!\n";
280 }
281 if ($hostgatewaybyte eq "") {
282 print "Warning! HostGatewayByte is undefined.. gateway will not be in ignore list!\n";
283 }
284 if ($logfile eq "") {
285 print "Warning! LogFile is undefined.. Assuming debug mode, output to STDOUT\n";
286 $opt_d = 1;
287 }
288 if (! -w $logfile) {
289 print "Warning! Logfile is not writeable! Engaging debug mode, output to STDOUT\n";
290 $opt_d = 1;
291 }
292
293 foreach $mypath (split (/:/, $ENV{PATH})) {
294 if (-x "$mypath/guardian_block.sh") {
295 $blockpath = "$mypath/guardian_block.sh";
296 }
297 if (-x "$mypath/guardian_unblock.sh") {
298 $unblockpath = "$mypath/guardian_unblock.sh";
299 }
300 }
301
302 if ($blockpath eq "") {
303 print "Error! Could not find guardian_block.sh. Please consult the README. \n";
304 exit;
305 }
306 if ($unblockpath eq "") {
307 print "Warning! Could not find guardian_unblock.sh. Guardian will not be\n";
308 print "able to remove blocked ip addresses. Please consult the README file\n";
309 }
310 if ($TimeLimit eq "") {
311 print "Warning! Time limit not defined. Defaulting to absurdly long time limit\n";
312 $TimeLimit = 999999999;
313 }
7c4cc0d8
CS
314}
315
7c4cc0d8 316sub write_log {
6579aab2
CS
317 my $message = $_[0];
318 my $date = localtime();
319 if (defined($opt_d)) { # we are in debug mode, and not daemonized
320 print STDOUT $message;
321 } else {
322 open (LOG, ">>$logfile");
323 print LOG $date.": ".$message;
324 close (LOG);
325 }
7c4cc0d8
CS
326}
327
7c4cc0d8 328sub daemonize {
6579aab2
CS
329 my ($home);
330 if (fork()) {
331# parent
332 exit(0);
333 } else {
334# child
335 &write_log ("Guardian process id $$\n");
336 $home = (getpwuid($>))[7] || die "No home directory!\n";
337 chdir($home); # go to my homedir
338 setpgrp(0,0); # become process leader
339 close(STDOUT);
340 close(STDIN);
341 close(STDERR);
342 print "Testing...\n";
343 }
344}
7c4cc0d8
CS
345
346sub get_ip {
6579aab2
CS
347 my ($interface) = $_[0];
348 my $ip;
349 open (IFCONFIG, "/bin/netstat -iee |grep $interface -A7 |");
350 while (<IFCONFIG>) {
351 if ($OS eq "FreeBSD") {
352 if (/inet (\d+\.\d+\.\d+\.\d+)/) {
353 $ip = $1;
354 }
355 }
356 if ($OS eq "Linux") {
357 if (/inet addr:(\d+\.\d+\.\d+\.\d+)/) {
358 $ip = $1;
359 }
360 }
361 }
362 close (IFCONFIG);
363
364 if ($ip eq "") { die "Couldn't figure out the ip address\n"; }
365 $ip;
366 }
7c4cc0d8
CS
367
368sub sig_handler_setup {
6579aab2
CS
369 $SIG{TERM} = \&clean_up_and_exit; # kill
370 $SIG{QUIT} = \&clean_up_and_exit; # kill -3
7c4cc0d8
CS
371# $SIG{HUP} = \&flush_and_reload; # kill -1
372}
373
374sub remove_blocks {
6579aab2
CS
375 my $source;
376 my $time = time();
377 foreach $source (keys %hash) {
378 if ($hash{$source} < $time) {
379 &call_unblock ($source, "expiring block of $source\n");
380 delete ($hash{$source});
381 }
382 }
7c4cc0d8
CS
383}
384
385sub call_unblock {
6579aab2
CS
386 my ($source, $message) = @_;
387 &write_log ("$message");
388 system ("$unblockpath $source");
7c4cc0d8
CS
389}
390
391sub clean_up_and_exit {
6579aab2
CS
392 my $source;
393 &write_log ("received kill sig.. shutting down\n");
394 foreach $source (keys %hash) {
395 &call_unblock ($source, "removing $source for shutdown\n");
396 }
397 exit;
7c4cc0d8
CS
398}
399
400sub load_targetfile {
6579aab2
CS
401 my $count = 0;
402 open (TARG, "$targetfile") or die "Cannot open $targetfile\n";
403 while (<TARG>) {
404 chop;
405 next if (/\#/); #skip comments
406 next if (/^\s*$/); # and blank lines
407 $targethash{$_}=1;
408 $count++;
409 }
410 close (TARG);
411 print "Loaded $count addresses from $targetfile\n";
7c4cc0d8 412}