yate: Update yate script to use an nftables set
[feed/telephony.git] / net / yate / files / banbrutes.pl
1 #!/usr/bin/perl
2
3 # This yate script will monitor authentication requests and update an
4 # nftables set with IP addresses of users who consistently fail to
5 # authenticate. The nftables set can then be used in OpenWrt's
6 # firewall configuration to block these IP addresses.
7 #
8 # The nftables set has to exist before launching yate.
9 #
10 # Here's an example configuration that creates an nftables set, where
11 # entries expire after 12 hours, and configures the OpenWrt firewall
12 # to drop packets where the source IP address is in the set. Put this
13 # in /etc/nftables.d/99-yate.nft:
14 #
15 # set yate_denylist {
16 # type ipv4_addr
17 # timeout 12h
18 # }
19 #
20 # chain yate_filter {
21 # type filter hook input priority -1; policy accept;
22 # ip saddr @yate_denylist counter drop comment "Drop packets from bad SIP clients"
23 # }
24 #
25 #
26 # To enable this script in yate, add it to the [scripts] section in
27 # /etc/yate/extmodule.conf.
28 #
29 # You can tweak how tolerant this script should be by modifying the
30 # constants below.
31
32 # A user's IP address will be added to the nftables set if there are
33 # more than MAX_AUTH_FAILURES consecutive authentication failures in
34 # MAX_AUTH_FAILURES_TIME_PERIOD seconds.
35 my $MAX_AUTH_FAILURES = 5;
36 my $MAX_AUTH_FAILURES_TIME_PERIOD = 3600; # seconds
37
38 # The name of the nftables table and set where IP addresses are added.
39 my $NFTABLES_TABLE = 'inet fw4';
40 my $NFTABLES_SET = 'yate_denylist';
41
42
43 use strict;
44 use warnings;
45 use lib '/usr/share/yate/scripts';
46 use Yate;
47
48 my %ip_auth_failures = ();
49
50 sub OnAuthenticationRequest($) {
51 my $yate = shift;
52
53 # Forget any expired failed authentications
54 foreach my $ip (keys(%ip_auth_failures)) {
55 my $failures = \@{$ip_auth_failures{$ip}};
56 while (@$failures &&
57 time() - @$failures[0] > $MAX_AUTH_FAILURES_TIME_PERIOD) {
58 shift(@$failures);
59 }
60
61 if (!@$failures) {
62 delete $ip_auth_failures{$ip};
63 }
64 }
65
66 my $remote_ip = $yate->param('ip_host');
67 my $remote_device = $yate->param('device') || '<unknown>';
68
69 if ($yate->header('processed') eq 'true') {
70 $yate->output("banbrutes: Successful authentication from $remote_ip");
71 delete $ip_auth_failures{$remote_ip};
72 return;
73 }
74
75 $yate->output("banbrutes: Failed authentication from $remote_ip");
76 push(@{$ip_auth_failures{$remote_ip}}, time());
77 if (scalar(@{$ip_auth_failures{$remote_ip}}) > $MAX_AUTH_FAILURES) {
78 $yate->output("banbrutes: Adding $remote_ip to nftables set $NFTABLES_SET (remote device: $remote_device)");
79 `nft add element $NFTABLES_TABLE $NFTABLES_SET { $remote_ip }`;
80 delete $ip_auth_failures{$remote_ip};
81 }
82 }
83
84 my $yate = new Yate();
85 $yate->install_watcher("user.auth", \&OnAuthenticationRequest);
86 $yate->listen();