diff --git a/nftables/acl_security.pl b/nftables/acl_security.pl index 51b413480..c93ce1394 100644 --- a/nftables/acl_security.pl +++ b/nftables/acl_security.pl @@ -36,7 +36,7 @@ print ui_table_row( foreach my $a ( qw(view active create setup chains sets rules raw delete - apply bootup import clear quick quick_ip quick_port + apply bootup service import clear quick quick_ip quick_port quick_service quick_forward manual) ) { @@ -61,7 +61,7 @@ else { } foreach my $a ( qw(view active create setup chains sets rules raw delete - apply bootup import clear quick quick_ip quick_port + apply bootup service import clear quick quick_ip quick_port quick_service quick_forward manual) ) { diff --git a/nftables/defaultacl b/nftables/defaultacl index 721cfe05b..55a7b5416 100644 --- a/nftables/defaultacl +++ b/nftables/defaultacl @@ -10,6 +10,7 @@ raw=1 delete=1 apply=1 bootup=1 +service=1 import=1 clear=1 quick=1 diff --git a/nftables/index.cgi b/nftables/index.cgi index 7f7e3be8d..10e26987a 100755 --- a/nftables/index.cgi +++ b/nftables/index.cgi @@ -854,14 +854,11 @@ if ($partial) { print $rules_html; -my $init_support = foreign_check("init") && check_acl('bootup') && - nftables_service_status() ? 1 : 0; if ( @tables && (check_acl('active') || check_acl('setup') || - check_manual_acl() || - $init_support) + check_manual_acl()) ) { print ui_hr(); @@ -881,10 +878,35 @@ if ( $text{'index_edit_manual'}, $text{'index_edit_manualdesc'} ) if (check_manual_acl()); + print ui_buttons_end(); + } + +# Service controls, shown whether or not any rules are saved yet. Without +# them there is no way to see that the service is stopped or disabled, which +# is the normal state on a system that has never had a ruleset +my $svc = foreign_check("init") ? nftables_service_status() : 0; +my $can_service = $svc && check_acl('service'); +my $can_bootup = $svc && check_acl('bootup'); +if ($can_service || $can_bootup) { + print ui_hr(); + print ui_buttons_start(); + if ($can_service) { + if (nftables_service_running()) { + my $desc = $text{'index_stopdesc'}; + $desc .= " ".$text{'index_stopflush'} + if (nftables_service_stop_flushes()); + print ui_buttons_row("stop.cgi", $text{'index_stop'}, + $desc); + } + else { + print ui_buttons_row("start.cgi", $text{'index_start'}, + $text{'index_startdesc'}); + } + } print ui_buttons_row("bootup.cgi", $text{'index_bootup'}, $text{'index_bootupdesc'}, undef, ui_yesno_radio("boot", nftables_started_at_boot())) - if ($init_support); + if ($can_bootup); print ui_buttons_end(); } diff --git a/nftables/lang/en b/nftables/lang/en index fae9793d0..a85020699 100644 --- a/nftables/lang/en +++ b/nftables/lang/en @@ -93,6 +93,13 @@ quick_edup=An equivalent quick rule for $1 already exists. quick_failed=Failed to save and apply quick rule: $1 index_unapply=Revert Configuration index_unapplydesc=Click this button to reset the configuration listed above to the one that is currently active. +index_start=Start Firewall +index_startdesc=Load the saved nftables configuration into the kernel by starting the nftables service. +index_stop=Stop Firewall +index_stopdesc=Stop the nftables service. +index_stopflush=On this system stopping the service flushes the entire kernel ruleset, including tables belonging to other software. +start_err=Failed to start the nftables service +stop_err=Failed to stop the nftables service index_bootup=Activate at Boot index_bootupdesc=Change whether the system nftables service loads the saved rules at boot time or not. bootup_einit=This system does not support boot-time service management from Webmin. @@ -393,6 +400,7 @@ acl_raw=Edit raw rule text acl_delete=Delete tables, chains and sets acl_apply=Apply saved configuration acl_bootup=Enable firewall at boot +acl_service=Start and stop the firewall service acl_import=Import active tables acl_clear=Clear active tables acl_quick=Use quick controls diff --git a/nftables/nftables-lib.pl b/nftables/nftables-lib.pl index 7edfa4560..badebe85a 100644 --- a/nftables/nftables-lib.pl +++ b/nftables/nftables-lib.pl @@ -287,6 +287,59 @@ init::disable_at_boot(nftables_service_name()); undef($nftables_service_status_cache); } +# nftables_service_running() +# Returns true if the system nftables service has its ruleset loaded +sub nftables_service_running +{ +return 0 if (!nftables_service_status()); +foreign_require("init", "init-lib.pl"); +no warnings 'once'; +if (($init::init_mode || "") eq "systemd") { + return init::is_active_systemd( + init::action_unit(nftables_service_name())) ? 1 : 0; + } +my $file = init::action_filename(nftables_service_name()); +return 0 if (!$file || !-x $file); +return init::action_running($file) == 1 ? 1 : 0; +} + +# nftables_service_stop_flushes() +# Returns true if stopping the service flushes the whole kernel ruleset. Most +# distributions do exactly that, which takes out tables belonging to other +# software as well, so the admin is warned before doing it +sub nftables_service_stop_flushes +{ +my $unit_file = nftables_service_unit_file(); +return 0 if (!$unit_file); +my $data = read_file_contents($unit_file); +return 0 if (!$data); +foreach my $l (split(/\r?\n/, $data)) { + next if ($l !~ /^\s*ExecStop\s*=/); + return 1 if ($l =~ /flush\s+ruleset/); + } +return 0; +} + +# start_nftables_service() +# Starts the system nftables service, loading the saved ruleset +sub start_nftables_service +{ +foreign_require("init", "init-lib.pl"); +my ($ok, $err) = init::start_action(nftables_service_name()); +undef($nftables_service_status_cache); +return $ok ? undef : $err; +} + +# stop_nftables_service() +# Stops the system nftables service +sub stop_nftables_service +{ +foreign_require("init", "init-lib.pl"); +my ($ok, $err) = init::stop_action(nftables_service_name()); +undef($nftables_service_status_cache); +return $ok ? undef : $err; +} + # legacy_nftables_rules_files() # Returns the private rules files that releases before the switch to the # system nftables configuration wrote to diff --git a/nftables/start.cgi b/nftables/start.cgi new file mode 100755 index 000000000..22ce3b0c1 --- /dev/null +++ b/nftables/start.cgi @@ -0,0 +1,17 @@ +#!/usr/bin/perl +# start.cgi +# Start the system nftables service + +require './nftables-lib.pl'; ## no critic +use strict; +use warnings; +our (%text); +error_setup($text{'start_err'}); +assert_acl('service'); +nftables_service_status() || error($text{'bootup_eservice'}); + +my $err = start_nftables_service(); +error($err) if ($err); + +webmin_log("start"); +redirect("index.cgi"); diff --git a/nftables/stop.cgi b/nftables/stop.cgi new file mode 100755 index 000000000..49af43379 --- /dev/null +++ b/nftables/stop.cgi @@ -0,0 +1,17 @@ +#!/usr/bin/perl +# stop.cgi +# Stop the system nftables service + +require './nftables-lib.pl'; ## no critic +use strict; +use warnings; +our (%text); +error_setup($text{'stop_err'}); +assert_acl('service'); +nftables_service_status() || error($text{'bootup_eservice'}); + +my $err = stop_nftables_service(); +error($err) if ($err); + +webmin_log("stop"); +redirect("index.cgi");