mirror of
https://github.com/webmin/webmin.git
synced 2026-08-03 06:40:38 +01:00
Add incremental ban time options to the Fail2Ban module
https://forum.virtualmin.com/t/fail2ban-bantime-increment-options-in-webmin/137675
This commit is contained in:
@@ -134,6 +134,60 @@ print &ui_table_row($text{'jail_bantime'},
|
||||
&ui_opt_textbox("bantime", $bantime, 6,
|
||||
$text{'default'}." (".$def_bantime.")"));
|
||||
|
||||
# Incremental banning options, supported since Fail2Ban 0.11.1
|
||||
if (&supports_bantime_increment()) {
|
||||
print &ui_table_hr();
|
||||
print &ui_table_span("<b>".$text{'jail_increment_header'}."</b><br>".
|
||||
&ui_note($text{'jail_increment_desc'}, 0));
|
||||
|
||||
my $def_increment = &canonical_fail2ban_boolean(
|
||||
&find_value("bantime.increment", $def));
|
||||
my $increment = &canonical_fail2ban_boolean(
|
||||
&find_value("bantime.increment", $jail));
|
||||
my $def_increment_text = $def_increment eq "true" ? $text{'yes'} :
|
||||
$text{'no'};
|
||||
print &ui_table_row($text{'jail_bantime_increment'},
|
||||
&ui_select("bantime_increment", $increment,
|
||||
[ [ "", $text{'default'}." (".$def_increment_text.")" ],
|
||||
[ "true", $text{'yes'} ],
|
||||
[ "false", $text{'no'} ] ], 1, 0, 1));
|
||||
|
||||
my $def_factor = &find_value("bantime.factor", $def) || 1;
|
||||
my $factor = &find_value("bantime.factor", $jail);
|
||||
print &ui_table_row($text{'jail_bantime_factor'},
|
||||
&ui_opt_textbox("bantime_factor", $factor, 8,
|
||||
$text{'default'}." (".&html_escape($def_factor).")"));
|
||||
|
||||
my $def_maxtime = &find_value("bantime.maxtime", $def);
|
||||
my $maxtime = &find_value("bantime.maxtime", $jail);
|
||||
my $def_maxtime_text = $def_maxtime ne "" ?
|
||||
&html_escape($def_maxtime) : $text{'jail_nolimit'};
|
||||
print &ui_table_row($text{'jail_bantime_maxtime'},
|
||||
&ui_opt_textbox("bantime_maxtime", $maxtime, 8,
|
||||
$text{'default'}." (".$def_maxtime_text.")"));
|
||||
|
||||
my $def_overalljails = &canonical_fail2ban_boolean(
|
||||
&find_value("bantime.overalljails", $def));
|
||||
my $overalljails = &canonical_fail2ban_boolean(
|
||||
&find_value("bantime.overalljails", $jail));
|
||||
my $def_overalljails_text = $def_overalljails eq "true" ?
|
||||
$text{'yes'} : $text{'no'};
|
||||
print &ui_table_row($text{'jail_bantime_overalljails'},
|
||||
&ui_select("bantime_overalljails", $overalljails,
|
||||
[ [ "", $text{'default'}." (".$def_overalljails_text.")" ],
|
||||
[ "true", $text{'yes'} ],
|
||||
[ "false", $text{'no'} ] ], 1, 0, 1));
|
||||
|
||||
my $def_rndtime = &find_value("bantime.rndtime", $def);
|
||||
my $rndtime = &find_value("bantime.rndtime", $jail);
|
||||
my $def_rndtime_text = $def_rndtime ne "" ?
|
||||
&html_escape($def_rndtime) : $text{'jail_none'};
|
||||
print &ui_table_row($text{'jail_bantime_rndtime'},
|
||||
&ui_opt_textbox("bantime_rndtime", $rndtime, 8,
|
||||
$text{'default'}." (".$def_rndtime_text.")"));
|
||||
print &ui_table_hr();
|
||||
}
|
||||
|
||||
# IPs to ignore
|
||||
my $def_ignoreip = &find_value("ignoreip", $def) || "127.0.0.1";
|
||||
my $ignoreip = &find_value("ignoreip", $jail);
|
||||
|
||||
@@ -40,6 +40,45 @@ $bantime = $def_bantime if (!defined($bantime) || $bantime eq "");
|
||||
print &ui_table_row($text{'jail_bantime'},
|
||||
&ui_textbox("bantime", $bantime, 5));
|
||||
|
||||
# Incremental banning options, supported since Fail2Ban 0.11.1
|
||||
if (&supports_bantime_increment()) {
|
||||
print &ui_table_hr();
|
||||
print &ui_table_span("<b>".$text{'jail_increment_header'}."</b><br>".
|
||||
&ui_note($text{'jail_increment_desc'}, 0));
|
||||
|
||||
my $increment = &canonical_fail2ban_boolean(
|
||||
&find_value("bantime.increment", $jail));
|
||||
print &ui_table_row($text{'jail_bantime_increment'},
|
||||
&ui_select("bantime_increment", $increment,
|
||||
[ [ "", $text{'default'}." (".$text{'no'}.")" ],
|
||||
[ "true", $text{'yes'} ],
|
||||
[ "false", $text{'no'} ] ], 1, 0, 1));
|
||||
|
||||
my $factor = &find_value("bantime.factor", $jail);
|
||||
print &ui_table_row($text{'jail_bantime_factor'},
|
||||
&ui_opt_textbox("bantime_factor", $factor, 8,
|
||||
$text{'default'}." (1)"));
|
||||
|
||||
my $maxtime = &find_value("bantime.maxtime", $jail);
|
||||
print &ui_table_row($text{'jail_bantime_maxtime'},
|
||||
&ui_opt_textbox("bantime_maxtime", $maxtime, 8,
|
||||
$text{'default'}." (".$text{'jail_nolimit'}.")"));
|
||||
|
||||
my $overalljails = &canonical_fail2ban_boolean(
|
||||
&find_value("bantime.overalljails", $jail));
|
||||
print &ui_table_row($text{'jail_bantime_overalljails'},
|
||||
&ui_select("bantime_overalljails", $overalljails,
|
||||
[ [ "", $text{'default'}." (".$text{'no'}.")" ],
|
||||
[ "true", $text{'yes'} ],
|
||||
[ "false", $text{'no'} ] ], 1, 0, 1));
|
||||
|
||||
my $rndtime = &find_value("bantime.rndtime", $jail);
|
||||
print &ui_table_row($text{'jail_bantime_rndtime'},
|
||||
&ui_opt_textbox("bantime_rndtime", $rndtime, 8,
|
||||
$text{'default'}." (".$text{'jail_none'}.")"));
|
||||
print &ui_table_hr();
|
||||
}
|
||||
|
||||
# IPs to ignore
|
||||
my $def_ignoreip = "127.0.0.1";
|
||||
my $ignoreip = &find_value("ignoreip", $jail);
|
||||
|
||||
@@ -671,6 +671,112 @@ my $out = &backquote_command("$config{'client_cmd'} -V 2>/dev/null </dev/null");
|
||||
return !$? && $out =~ /v?([0-9\.]+)/ ? $1 : undef;
|
||||
}
|
||||
|
||||
# supports_bantime_increment([version])
|
||||
# Returns 1 if the installed Fail2Ban version supports incremental ban times
|
||||
sub supports_bantime_increment
|
||||
{
|
||||
my ($version) = @_;
|
||||
$version = &get_fail2ban_version() if (!defined($version));
|
||||
return 0 if (!defined($version) || $version eq "");
|
||||
return &compare_version_numbers($version, "0.11.1") >= 0;
|
||||
}
|
||||
|
||||
# canonical_fail2ban_boolean(value)
|
||||
# Converts the boolean spellings accepted by Fail2Ban to true or false
|
||||
sub canonical_fail2ban_boolean
|
||||
{
|
||||
my ($value) = @_;
|
||||
return "" if (!defined($value) || $value eq "");
|
||||
return "true" if ($value =~ /^(1|on|true|yes)$/i);
|
||||
return "false" if ($value =~ /^(0|off|false|no)$/i);
|
||||
return $value;
|
||||
}
|
||||
|
||||
# valid_fail2ban_duration(value, [allow-zero])
|
||||
# Returns 1 for a safe subset of Fail2Ban duration expressions
|
||||
sub valid_fail2ban_duration
|
||||
{
|
||||
my ($value, $allow_zero) = @_;
|
||||
return 0 if (!defined($value) || $value eq "");
|
||||
my $number = qr/\d+(?:\.\d+)?/;
|
||||
my $unit = qr/(?:s|sec(?:ond)?s?|m|min(?:ute)?s?|h|hour(?:s)?|d|day(?:s)?|w|week(?:s)?|mo|mon|month(?:s)?|y|year(?:s)?)/i;
|
||||
return 0 if ($value !~ /^\s*(?:$number\s*$unit\s*)+$/ &&
|
||||
$value !~ /^\s*$number\s*$/);
|
||||
return 1 if ($allow_zero);
|
||||
my @numbers = $value =~ /(\d+(?:\.\d+)?)/g;
|
||||
return scalar(grep { $_ > 0 } @numbers) ? 1 : 0;
|
||||
}
|
||||
|
||||
# valid_positive_fail2ban_duration(value)
|
||||
# Returns 1 for a safe, positive Fail2Ban duration expression
|
||||
sub valid_positive_fail2ban_duration
|
||||
{
|
||||
return &valid_fail2ban_duration($_[0], 0);
|
||||
}
|
||||
|
||||
# valid_nonnegative_fail2ban_duration(value)
|
||||
# Returns 1 for a safe Fail2Ban duration expression, including zero
|
||||
sub valid_nonnegative_fail2ban_duration
|
||||
{
|
||||
return &valid_fail2ban_duration($_[0], 1);
|
||||
}
|
||||
|
||||
# valid_bantime_factor(value)
|
||||
# Returns 1 for a positive numeric incremental-ban growth factor
|
||||
sub valid_bantime_factor
|
||||
{
|
||||
my ($value) = @_;
|
||||
return defined($value) &&
|
||||
$value =~ /^(?:\d+(?:\.\d+)?|\.\d+)$/ && $value > 0;
|
||||
}
|
||||
|
||||
# validate_bantime_increment_inputs(&input)
|
||||
# Returns a language key for the first invalid incremental-ban option
|
||||
sub validate_bantime_increment_inputs
|
||||
{
|
||||
my ($input) = @_;
|
||||
foreach my $f ("bantime_increment", "bantime_overalljails") {
|
||||
my $value = $input->{$f};
|
||||
return "jail_e".$f if (defined($value) && $value ne "" &&
|
||||
$value !~ /^(true|false)$/);
|
||||
}
|
||||
if (!$input->{'bantime_factor_def'} &&
|
||||
!&valid_bantime_factor($input->{'bantime_factor'})) {
|
||||
return "jail_ebantime_factor";
|
||||
}
|
||||
if (!$input->{'bantime_maxtime_def'} &&
|
||||
!&valid_positive_fail2ban_duration($input->{'bantime_maxtime'})) {
|
||||
return "jail_ebantime_maxtime";
|
||||
}
|
||||
if (!$input->{'bantime_rndtime_def'} &&
|
||||
!&valid_nonnegative_fail2ban_duration($input->{'bantime_rndtime'})) {
|
||||
return "jail_ebantime_rndtime";
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# save_bantime_increment_options(&input, &jail)
|
||||
# Saves incremental-ban options using the standard jail directive handling
|
||||
sub save_bantime_increment_options
|
||||
{
|
||||
my ($input, $jail) = @_;
|
||||
&save_directive("bantime.increment",
|
||||
$input->{'bantime_increment'} eq "" ? undef :
|
||||
$input->{'bantime_increment'}, $jail);
|
||||
&save_directive("bantime.factor",
|
||||
$input->{'bantime_factor_def'} ? undef :
|
||||
$input->{'bantime_factor'}, $jail);
|
||||
&save_directive("bantime.maxtime",
|
||||
$input->{'bantime_maxtime_def'} ? undef :
|
||||
$input->{'bantime_maxtime'}, $jail);
|
||||
&save_directive("bantime.overalljails",
|
||||
$input->{'bantime_overalljails'} eq "" ? undef :
|
||||
$input->{'bantime_overalljails'}, $jail);
|
||||
&save_directive("bantime.rndtime",
|
||||
$input->{'bantime_rndtime_def'} ? undef :
|
||||
$input->{'bantime_rndtime'}, $jail);
|
||||
}
|
||||
|
||||
# Unblock given IP in given jail
|
||||
sub unblock_jailed_ip
|
||||
{
|
||||
|
||||
@@ -101,6 +101,14 @@ jail_others=Other parameters
|
||||
jail_maxretry=Matches before applying action
|
||||
jail_findtime=Max delay between matches
|
||||
jail_bantime=Time to ban IP for
|
||||
jail_increment_header=Incremental banning
|
||||
jail_increment_desc=Increase ban times for IP addresses that are banned repeatedly. Ban history comes from Fail2Ban's persistent database and is limited by the database purge age in Global Configuration.
|
||||
jail_bantime_increment=Increase repeat-offender ban times?
|
||||
jail_bantime_factor=Ban time growth factor
|
||||
jail_bantime_maxtime=Maximum increased ban time
|
||||
jail_bantime_overalljails=Use ban history from all jails?
|
||||
jail_bantime_rndtime=Maximum random additional ban time
|
||||
jail_nolimit=No limit
|
||||
jail_ignoreip=IP addresses to never ban
|
||||
jail_err=Failed to save jail
|
||||
jail_eclash=A jail with the same name already exists
|
||||
@@ -115,6 +123,12 @@ jail_elogpath=All log files must be absolute paths or patterns
|
||||
jail_emaxretry=Matches before applying action must be a number greater than zero
|
||||
jail_efindtime=Delay between matches must be a number greater than zero
|
||||
jail_ebantime=Time to ban an IP must be a number greater than zero
|
||||
jail_ebantime_increment=Invalid incremental banning selection
|
||||
jail_ebantime_factor=Ban time growth factor must be a number greater than zero
|
||||
jail_ebantime_maxtime=Maximum increased ban time must be a positive duration, such as 30m, 5h, 7d or 5w
|
||||
jail_ebantime_overalljails=Invalid all-jails ban history selection
|
||||
jail_ebantime_rndtime=Random additional ban time must be zero or a positive duration, such as 30m, 5h, 7d or 5w
|
||||
jail_eincrement_version=Incremental banning requires Fail2Ban version 0.11.1 or later
|
||||
jail_backend=Check for log file updates using
|
||||
jail_auto=Decide automatically
|
||||
jail_systemd=systemd
|
||||
@@ -235,4 +249,3 @@ status_err_unknownjail=Unknown jail
|
||||
syslog_logtarget=Fail2Ban action log
|
||||
|
||||
__norefs=1
|
||||
|
||||
|
||||
@@ -122,6 +122,19 @@ else {
|
||||
&error($text{'jail_eignoreip'});
|
||||
}
|
||||
|
||||
# Validate incremental banning options when submitted by a supported UI
|
||||
my @increment_fields = ("bantime_increment", "bantime_factor",
|
||||
"bantime_maxtime", "bantime_overalljails", "bantime_rndtime");
|
||||
my $increment_submitted = scalar(grep { exists($in{$_}) }
|
||||
@increment_fields);
|
||||
if ($increment_submitted && !&supports_bantime_increment()) {
|
||||
&error($text{'jail_eincrement_version'});
|
||||
}
|
||||
if ($increment_submitted) {
|
||||
my $err = &validate_bantime_increment_inputs(\%in);
|
||||
&error($text{$err}) if ($err);
|
||||
}
|
||||
|
||||
# Create new section or rename existing if needed
|
||||
&lock_all_config_files();
|
||||
if ($in{'new'}) {
|
||||
@@ -144,6 +157,8 @@ else {
|
||||
}
|
||||
&save_directive("ignoreip",
|
||||
@ignoreips ? join(" ", @ignoreips) : undef, $jail);
|
||||
&save_bantime_increment_options(\%in, $jail)
|
||||
if ($increment_submitted);
|
||||
|
||||
&unlock_all_config_files();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,18 @@ foreach my $ip (@ignoreips) {
|
||||
&error($text{'jail_eignoreip'});
|
||||
}
|
||||
|
||||
# Validate incremental banning options when submitted by a supported UI
|
||||
my @increment_fields = ("bantime_increment", "bantime_factor",
|
||||
"bantime_maxtime", "bantime_overalljails", "bantime_rndtime");
|
||||
my $increment_submitted = scalar(grep { exists($in{$_}) } @increment_fields);
|
||||
if ($increment_submitted && !&supports_bantime_increment()) {
|
||||
&error($text{'jail_eincrement_version'});
|
||||
}
|
||||
if ($increment_submitted) {
|
||||
my $err = &validate_bantime_increment_inputs(\%in);
|
||||
&error($text{$err}) if ($err);
|
||||
}
|
||||
|
||||
|
||||
# Update the jail
|
||||
&lock_all_config_files();
|
||||
@@ -47,6 +59,7 @@ foreach my $f ("maxretry", "findtime", "bantime") {
|
||||
$jail);
|
||||
&save_directive("banaction", $in{'banaction'} || undef, $jail);
|
||||
&save_directive("protocol", $in{'protocol'} || undef, $jail);
|
||||
&save_bantime_increment_options(\%in, $jail) if ($increment_submitted);
|
||||
&unlock_all_config_files();
|
||||
|
||||
&webmin_log("jaildef");
|
||||
|
||||
Reference in New Issue
Block a user