diff --git a/fail2ban/images/status.gif b/fail2ban/images/status.gif new file mode 100644 index 000000000..f6a34a259 Binary files /dev/null and b/fail2ban/images/status.gif differ diff --git a/fail2ban/index.cgi b/fail2ban/index.cgi index e6ffbfe76..a5b065858 100755 --- a/fail2ban/index.cgi +++ b/fail2ban/index.cgi @@ -28,14 +28,14 @@ if ($err) { # Show category icons my @links = ( "list_filters.cgi", "list_actions.cgi", - "list_jails.cgi", "edit_config.cgi", - "edit_manual.cgi", ); + "list_jails.cgi", "list_status.cgi", + "edit_config.cgi", "edit_manual.cgi", ); my @titles = ( $text{'filters_title'}, $text{'actions_title'}, - $text{'jails_title'}, $text{'config_title'}, - $text{'manual_title'}, ); + $text{'jails_title'}, $text{'status_title'}, + $text{'config_title'}, $text{'manual_title'}, ); my @icons = ( "images/filters.gif", "images/actions.gif", - "images/jails.gif", "images/config.gif", - "images/manual.gif", ); + "images/jails.gif", "images/status.gif", + "images/config.gif", "images/manual.gif", ); &icons_table(\@links, \@titles, \@icons, 5); # Show start / stop buttons diff --git a/fail2ban/list_status.cgi b/fail2ban/list_status.cgi new file mode 100755 index 000000000..3543934b3 --- /dev/null +++ b/fail2ban/list_status.cgi @@ -0,0 +1,102 @@ +#!/usr/local/bin/perl +# Show a list of all defined actions + +use strict; +use warnings; +require './fail2ban-lib.pl'; +our (%in, %text, %config); + +&ui_print_header(undef, $text{'status_title2'}, ""); + +# Check if firewalld is used +&foreign_require('firewalld', 'install_check.pl'); +my $is_firewalld = &firewalld::is_installed(); + +my $out = &backquote_logged("$config{'client_cmd'} status 2>&1 &1 '; + my $nbsp = ' '; + my $ipslimit = sub { + my ($ips, $limit) = @_; + $limit ||= 15; + # Limit sanity check + $limit = 1 if ($limit < 1); + my $ipscount = () = $ips =~ /$br/g; + if ($ipscount > $limit) { + my @ips = split($br, $ips); + @ips = @ips[0 .. $limit]; + $ips = join($br, @ips); + $ips .= "$br$nbsp".&text('list_rules_plus_more', $ipscount-$limit).""; + } + return $ips; + }; + my $jips; + my $noval; + &open_execute_command($fh, $jcmd, 1); + while(<$fh>) { + if (/-\s+(.*):\s*(.*)/) { + my $col = $1; + my $val = $2; + $col = lc($col); + $col =~ s/\s/_/g; + if ($col !~ /journal_matches/) { + push(@head, "
\n"; -print &ui_form_end(); - -# Show allowed ports and services in this zone -my @links = ( &ui_link("edit_port.cgi?new=1&zone=".&urlize($zone->{'name'}), - $text{'index_padd'}), - &ui_link("edit_serv.cgi?new=1&zone=".&urlize($zone->{'name'}), - $text{'index_sadd'}), - &ui_link("edit_forward.cgi?new=1&zone=".&urlize($zone->{'name'}), - $text{'index_fadd'}), - ); -if (@{$zone->{'services'}} || @{$zone->{'ports'}}) { - my @tds = ( "width=5" ); - unshift(@links, &select_all_link("d", 1), - &select_invert_link("d", 1)); - print &ui_form_start("delete_rules.cgi", "post"); - print &ui_hidden("zone", $zone->{'name'}); - print &ui_links_row(\@links); - print &ui_columns_start([ "", $text{'index_type'}, $text{'index_port'}, - $text{'index_proto'} ], 100, 0, \@tds); - foreach my $s (@{$zone->{'services'}}) { - my $url = "edit_serv.cgi?id=".&urlize($s). - "&zone=".&urlize($zone->{'name'}); - my $sportsprotos = &list_firewalld_service_desc($s); - my $sport = $sportsprotos->{'ports'}; - my $sprotocols = $sportsprotos->{'protocols'}; - $sport = " ($sport)" if ($sport); - print &ui_checked_columns_row([ - &ui_link($url, $text{'index_tservice'}), - &ui_link($url, "$s$sport"), - $sprotocols || "", - ], \@tds, "d", "service/".$s); - } - foreach my $p (@{$zone->{'ports'}}) { - my $url = "edit_port.cgi?id=".&urlize($p). - "&zone=".&urlize($zone->{'name'}); - my ($port, $proto) = split(/\//, $p); - print &ui_checked_columns_row([ - &ui_link($url, $text{'index_tport'}), - &ui_link($url, $port), - uc($proto), - ], \@tds, "d", "port/".$p); - } - foreach my $f (@{$zone->{'forward-ports'}}) { - my ($port, $proto, $dstport, $dstaddr) = - &parse_firewalld_forward($f); - my $p = join("/", $port, $proto, $dstport, $dstaddr); - my $url = "edit_forward.cgi?id=".&urlize($p). - "&zone=".&urlize($zone->{'name'}); - print &ui_checked_columns_row([ - &ui_link($url, $text{'index_tforward'}), - &ui_link($url, $port), - &ui_link($url, uc($proto)), - ], \@tds, "d", "forward/".$p); - } - print &ui_columns_end(); - print &ui_links_row(\@links); - print &ui_form_end([ [ undef, $text{'index_delete'} ] ]); - } -else { - print "$text{'index_none'}
\n";
- print &ui_links_row(\@links);
- }
-
-if ($azone) {
- # Show interfaces for this zone
- print &ui_form_start("save_ifaces.cgi");
- print &ui_hidden("zone", $zone->{'name'});
- print "
$text{'index_ifaces'} \n";
- my %zifcs = map { $_, 1 } &unique(@{$azone->{'interfaces'}},
- @{$zone->{'interfaces'}});
- print &ui_radio("iface_def", %zifcs ? 0 : 1,
- [ [ 1, $text{'index_ifaces_def'} ],
- [ 0, $text{'index_ifaces_sel'} ] ]),"\n";
- foreach my $i (&list_system_interfaces()) {
- print &ui_checkbox("iface", $i, $i, $zifcs{$i}),"\n";
- }
- print &ui_submit($text{'save'});
- print &ui_form_end();
- }
-
-# Show start/apply buttons
-print &ui_hr();
-print &ui_buttons_start();
+# Is FirewallD running if not, show start button
my $ok = &is_firewalld_running();
if ($ok) {
- print &ui_buttons_row("restart.cgi", $text{'index_restart'},
- $text{'index_restartdesc'},
+ # Get rules and zones
+ @zones = &list_firewalld_zones();
+ @zones || &error($text{'index_ezones'});
+ if ($in{'zone'}) {
+ ($zone) = grep { $_->{'name'} eq $in{'zone'} } @zones;
+ }
+ else {
+ ($zone) = grep { $_->{'default'} } @zones;
+ }
+ $zone ||= $zones[0];
+ my ($azone);
+ eval {
+ local $main::error_must_die = 1;
+ my @azones = &list_firewalld_zones(1);
+ ($azone) = grep { $_->{'name'} eq $zone->{'name'} } @azones;
+ };
+
+ # Show zone selector
+ print &ui_form_start("index.cgi");
+ print "$text{'index_zone'} ",
+ &ui_select("zone", $zone->{'name'},
+ [ map { [ $_->{'name'},
+ $_->{'name'}.($_->{'default'} ? ' (default)' : '') ]}
+ @zones ], 1, 0, 0, 0,
+ "onChange='form.submit()'")," ",
+ &ui_submit($text{'index_zonedef'}, "defzone")," ",
+ &ui_submit($text{'index_zonedel'}, "delzone")," ",
+ &ui_submit($text{'index_zoneadd'}, "addzone")," ",
+ "
\n"; + print &ui_form_end(); + + # Show allowed ports and services in this zone + my @links = ( &ui_link("edit_port.cgi?new=1&zone=".&urlize($zone->{'name'}), + $text{'index_padd'}), + &ui_link("edit_serv.cgi?new=1&zone=".&urlize($zone->{'name'}), + $text{'index_sadd'}), + &ui_link("edit_forward.cgi?new=1&zone=".&urlize($zone->{'name'}), + $text{'index_fadd'}), + ); + if (@{$zone->{'services'}} || @{$zone->{'ports'}}) { + my @tds = ( "width=5" ); + unshift(@links, &select_all_link("d", 1), + &select_invert_link("d", 1)); + print &ui_form_start("delete_rules.cgi", "post"); + print &ui_hidden("zone", $zone->{'name'}); + print &ui_links_row(\@links); + print &ui_columns_start([ "", $text{'index_type'}, $text{'index_port'}, + $text{'index_proto'} ], 100, 0, \@tds); + foreach my $s (@{$zone->{'services'}}) { + my $url = "edit_serv.cgi?id=".&urlize($s). + "&zone=".&urlize($zone->{'name'}); + my $sportsprotos = &list_firewalld_service_desc($s); + my $sport = $sportsprotos->{'ports'}; + my $sprotocols = $sportsprotos->{'protocols'}; + $sport = " ($sport)" if ($sport); + print &ui_checked_columns_row([ + &ui_link($url, $text{'index_tservice'}), + &ui_link($url, "$s$sport"), + $sprotocols || "", + ], \@tds, "d", "service/".$s); + } + foreach my $p (@{$zone->{'ports'}}) { + my $url = "edit_port.cgi?id=".&urlize($p). + "&zone=".&urlize($zone->{'name'}); + my ($port, $proto) = split(/\//, $p); + print &ui_checked_columns_row([ + &ui_link($url, $text{'index_tport'}), + &ui_link($url, $port), + uc($proto), + ], \@tds, "d", "port/".$p); + } + foreach my $f (@{$zone->{'forward-ports'}}) { + my ($port, $proto, $dstport, $dstaddr) = + &parse_firewalld_forward($f); + my $p = join("/", $port, $proto, $dstport, $dstaddr); + my $url = "edit_forward.cgi?id=".&urlize($p). + "&zone=".&urlize($zone->{'name'}); + print &ui_checked_columns_row([ + &ui_link($url, $text{'index_tforward'}), + &ui_link($url, $port), + &ui_link($url, uc($proto)), + ], \@tds, "d", "forward/".$p); + } + print &ui_columns_end(); + print &ui_links_row(\@links); + print &ui_form_end([ [ undef, $text{'index_delete'} ] ]); + } + else { + print "$text{'index_none'}
\n";
+ print &ui_links_row(\@links);
+ }
+
+ if ($azone) {
+ # Show interfaces for this zone
+ print &ui_form_start("save_ifaces.cgi");
+ print &ui_hidden("zone", $zone->{'name'});
+ print "
$text{'index_ifaces'} \n";
+ my %zifcs = map { $_, 1 } &unique(@{$azone->{'interfaces'}},
+ @{$zone->{'interfaces'}});
+ print &ui_radio("iface_def", %zifcs ? 0 : 1,
+ [ [ 1, $text{'index_ifaces_def'} ],
+ [ 0, $text{'index_ifaces_sel'} ] ]),"\n";
+ foreach my $i (&list_system_interfaces()) {
+ print &ui_checkbox("iface", $i, $i, $zifcs{$i}),"\n";
+ }
+ print &ui_submit($text{'index_ifaces_apply'});
+ print &ui_form_end();
+ }
+
+ print &ui_hr();
+
+ # Show start/apply buttons
+ print &ui_buttons_start();
+ print &ui_buttons_row("list_rules.cgi", $text{'index_listrules'},
+ &text("index_listrules_restartdesc",
+ "".$zone->{'name'}.""),
+ [ [ "zone", $zone->{'name'} ] ]);
+ print &ui_buttons_row("restart.cgi", $text{'index_restart_firewalld'},
+ $text{'index_restart_firewallddesc'},
[ [ "zone", $zone->{'name'} ] ]);
print &ui_buttons_row("stop.cgi", $text{'index_stop'},
$text{'index_stopdesc'},
[ [ "zone", $zone->{'name'} ] ]);
- }
-else {
+}
+
+# Show Start and disable/enabled at boot button
+if (!$ok) {
+ print &ui_buttons_start();
print &ui_buttons_row("start.cgi", $text{'index_start'},
$text{'index_startdesc'},
[ [ "zone", $zone->{'name'} ] ]);
diff --git a/firewalld/list_rules.cgi b/firewalld/list_rules.cgi
new file mode 100755
index 000000000..a97f98fb2
--- /dev/null
+++ b/firewalld/list_rules.cgi
@@ -0,0 +1,182 @@
+#!/usr/local/bin/perl
+# List FirewallD rich and direct rules
+
+use strict;
+use warnings;
+require './firewalld-lib.pl';
+our (%in, %text, %config);
+&ReadParse();
+my $dzone = $in{'zone'};
+if (!$dzone) {
+ my $zone = &get_default_zone();
+ $dzone = $zone->{'name'};
+ }
+&ui_print_header(&text('list_rules_title_sub', "".&html_escape($dzone).""), $text{'list_rules_title'}, "");
+
+my $head;
+my @head = (undef, $text{'list_rules_type'});
+my $tdc = "style=\"text-align: center\"";
+my @links = ( &select_all_link("rules"),
+ &select_invert_link("rules") );
+
+# Check rich rules first
+my $fh = 'rrules';
+my $rcmd = "$config{'firewall_cmd'} --list-rich-rules --zone=$dzone";
+&open_execute_command($fh, "$rcmd 2>&1 ) {
+ my @body;
+ if ($_ =~ /\S+/) {
+ push(@body, $text{'list_rules_type_rich'});
+
+ # Get protocol
+ if (/family=["'](ipv\d)["']/) {
+ push(@head, $text{'list_rules_protocol'});
+ push(@body, $1 =~ /ipv6/i ? "IPv6" : "IPv4");
+ }
+
+ # Get address
+ if (/address=["'](.*?)["']/) {
+ push(@head, $text{'list_rules_ip'});
+ push(@body, "$1 ");
+ }
+
+ # Get origin
+ if (/\s+(source|destination)\s+/) {
+ push(@head, $text{'list_rules_origin'});
+ push(@body, $1 eq 'source' ? 'Input' : 'Output');
+ }
+
+ # Get action
+ if (/(accept|reject|drop|mark$)/i) {
+ push(@head, $text{'list_rules_action'});
+ push(@body, ucfirst($1));
+ }
+
+ # Add full rule
+ push(@head, $text{'list_rules_rule'});
+ push(@body, "$_");
+
+ # Print start
+ if (!$head++) {
+ print &ui_form_start("save_rules.cgi", "post");
+ print &ui_hidden("zone", $dzone);
+ print &ui_links_row(\@links);
+ print &ui_columns_start(\@head);
+ }
+ print &ui_checked_columns_row(\@body, [ 'width=5', $tdc, $tdc, undef, $tdc, $tdc, undef ], "rules", $_);
+ }
+ }
+close($fh);
+
+# Check direct rules
+my $fh2 = 'drules';
+my $dcmd = "$config{'firewall_cmd'} --direct --get-all-rules";
+&open_execute_command($fh2, "$dcmd 2>&1 ) {
+ my @body;
+ if ($_ =~ /\S+/) {
+ my $ndash = "–";
+ my $br = "
";
+ my $nbsp = " ";
+ my $ips = $ndash;
+ my $candelete = 1;
+ my $ipslimit = sub {
+ my ($ips, $limit) = @_;
+ $limit ||= 15;
+ # Limit sanity check and adjustment
+ $limit = 1 if ($limit < 1);
+ $limit -= 1;
+ my $ipscount = () = $ips =~ /$br/g;
+ if ($ipscount > $limit) {
+ my @ips = split($br, $ips);
+ @ips = @ips[0 .. $limit];
+ $ips = join($br, @ips);
+ $ips .= "$br$nbsp".&text('list_rules_plus_more', $ipscount-$limit)."";
+ }
+ return $ips;
+ };
+ # Extract IPs from match sets
+ if (/set\s+\-\-match-set\s+(.*?)\s+/) {
+ my $ipset_name = $1;
+ my $ipset_cmd = &has_command($config{'firewall_ipset'} || 'ipset');
+ my $ipset_cmd_out = &backquote_logged("$ipset_cmd list ".quotemeta($ipset_name)." 2>&1 0) {
+ my @ipset_cmd_out_lines = split(/\n/, $ipset_cmd_out);
+ my @ips = map { $_ =~ /^([0-9\.\:a-f\/]+)/i } @ipset_cmd_out_lines;
+ $ips = join("$nbsp$nbsp$br", @ips);
+ }
+ }
+ }
+ # Rules with match sets must not be controlled here
+ $candelete = 0;
+ }
+
+ # Standard direct rules
+ else {
+ # Extract IPs from the rule,
+ # considering comma separated
+ my @ips = ($_ =~ /-[sd]\s+([0-9\.\:a-f,\/]+)/gi);
+ $ips = join("$nbsp$nbsp$br", @ips);
+ $ips =~ s/\s*,\s*/$nbsp$nbsp$br/g;
+ $ips ||= $ndash;
+ }
+
+ # Trim the number of IPs to allow at max 10
+ $ips = &$ipslimit($ips);
+
+ # Add type name
+ push(@body, $text{'list_rules_type_direct'});
+
+ # Get protocol
+ if (/(ipv\d)/) {
+ push(@head, $text{'list_rules_protocol'});
+ push(@body, $1 =~ /ipv6/i ? "IPv6" : "IPv4");
+ }
+
+ # Get address
+ if (/address=["'](.*?)["']/) {
+ }
+ push(@head, $text{'list_rules_ip'});
+ push(@body, $ips);
+
+ # Get origin
+ if (/(INPUT|OUTPUT)/) {
+ push(@head, $text{'list_rules_origin'});
+ push(@body, ucfirst(lc($1)));
+ }
+
+ # Get action
+ if (/(ACCEPT|REJECT|DROP|MARK$)/) {
+ push(@head, $text{'list_rules_action'});
+ push(@body, ucfirst(lc($1)));
+ }
+
+ # Add full rule
+ push(@head, $text{'list_rules_rule'});
+ push(@body, "$_");
+
+ # Print start
+ if (!$head++) {
+ print &ui_form_start("save_rules.cgi", "post");
+ print &ui_hidden("zone", $dzone);
+ print &ui_links_row(\@links);
+ print &ui_columns_start(\@head);
+ }
+ print &ui_checked_columns_row(\@body, [ 'width=5', $tdc, $tdc, undef, $tdc, $tdc, undef ], "rules", $_, undef, !$candelete);
+ }
+ }
+close($fh2);
+
+
+if ($head) {
+ print &ui_columns_end();
+ print &ui_links_row(\@links);
+ print &ui_form_end([ [ 'remove', $text{'list_rules_delete'} ] ] );
+ }
+else {
+ print "There are no existing direct or rich firewall rules to display."
+ }
+
+&ui_print_footer("index.cgi?zone=".&urlize($dzone), $text{'index_return'});
diff --git a/firewalld/save_rules.cgi b/firewalld/save_rules.cgi
new file mode 100755
index 000000000..055857060
--- /dev/null
+++ b/firewalld/save_rules.cgi
@@ -0,0 +1,26 @@
+#!/usr/local/bin/perl
+# Delete multiple ports or services
+
+use strict;
+use warnings;
+require './firewalld-lib.pl';
+our (%in, %text);
+&error_setup($text{'delete_err'});
+&ReadParse();
+my @rules = split(/\0/, $in{'rules'});
+@rules || &error($text{'delete_enone'});
+
+my @zones = &list_firewalld_zones();
+my ($zone) = grep { $_->{'name'} eq $in{'zone'} } @zones;
+$zone || &error($text{'port_ezone'});
+
+if ($in{'remove'}) {
+ foreach my $rule (@rules) {
+ my $rrfunc = \&{"remove_" . ($rule =~ /^(ipv4|ipv6|eb)/ ? 'direct' : 'rich') . "_rule"};
+ my $rmerr = &$rrfunc($rule, $zone);
+ &error(&text('delete_edel', $rule, $rmerr)) if ($rmerr);
+ }
+ }
+
+&webmin_log("save", "rules", scalar(@rules));
+&redirect("list_rules.cgi?zone=".&urlize($zone->{'name'}));