* Finished (for now) anvil-manage-firewall. It's been added to anvil-daemon as well.

* Updated Log->entry() to accept 'print => [0|1]' to send a log message to STDOUT (minus prefix) to avoid tools that were repeatedly calling print and Log->entry back to back.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 6 years ago
parent 0ca80d0599
commit 5f77ff5885
  1. 4
      Anvil/Tools.pm
  2. 21
      Anvil/Tools/Log.pm
  3. 43
      Anvil/Tools/System.pm
  4. 9
      anvil.conf
  5. 35
      html/skins/alteeve/firewall.txt
  6. 66
      notes
  7. 13
      share/words.xml
  8. 56
      tools/anvil-daemon
  9. 244
      tools/anvil-manage-firewall

@ -793,6 +793,9 @@ sub _set_defaults
language => "en_CA",
log_date => 1,
log_file => "/var/log/anvil.log",
manage => {
firewall => 1,
},
password => {
algorithm => "sha512",
hash_count => 500000,
@ -935,6 +938,7 @@ sub _set_paths
'anvil-change-password' => "/usr/sbin/anvil-change-password",
'anvil-daemon' => "/usr/sbin/anvil-daemon",
'anvil-maintenance-mode' => "/usr/sbin/anvil-maintenance-mode",
'anvil-manage-firewall' => "/usr/sbin/anvil-manage-firewall",
'anvil-manage-power' => "/usr/sbin/anvil-manage-power",
'anvil-manage-striker-peers' => "/usr/sbin/anvil-manage-striker-peers",
'anvil-prep-database' => "/usr/sbin/anvil-prep-database",

@ -177,6 +177,12 @@ This is the highest log level, and it will generate a tremendous amount of log e
When set, the string is prepended to the log entry, after 'C<< file >> if set, and should be set to C<< __LINE__ >>. It is used to show where in 'C<< file >>' the log entry was made and can assist with debugging.
=head3 print (optional, default '0')
When set to '1', the log entry is also printed to STDOUT. The prefix (source file and timestamp) is NOT printed, and a newline is added to the end of the string.
B<< NOTE >>: This honours the log level. That is to say, it will only print the string to STDOUT if it also logs it to a file.
=head3 priority (optional)
This is an optional log priority (level) name. By default, the following priorities will be used based on the log level of the message.
@ -230,6 +236,7 @@ sub entry
my $level = defined $parameter->{level} ? $parameter->{level} : 2;
my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $facility = defined $parameter->{facility} ? $parameter->{facility} : $anvil->data->{defaults}{'log'}{facility};
my $print = defined $parameter->{'print'} ? $parameter->{'print'} : "";
my $priority = defined $parameter->{priority} ? $parameter->{priority} : "";
my $raw = defined $parameter->{raw} ? $parameter->{raw} : "";
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
@ -269,7 +276,8 @@ sub entry
}
# Log the file and line, if passed.
my $string = "";
my $string = "";
my $print_string = "";
if ($anvil->data->{sys}{log_date})
{
# Keep the debug level super high to avoid Get->date_and_time() going into an infinite loop.
@ -291,7 +299,8 @@ sub entry
# If I have a raw string, do no more processing.
if ($raw)
{
$string .= $raw;
$string .= $raw;
$print_string .= $raw;
}
elsif ($key)
{
@ -303,7 +312,8 @@ sub entry
variables => $variables,
});
#print $THIS_FILE." ".__LINE__."; [ Debug ] - message: [$message]\n";
$string .= $message;
$string .= $message;
$print_string .= $message;
}
# If the user set a log file, log to that. Otherwise, log via Log::Journald.
@ -359,6 +369,11 @@ sub entry
);
}
if ($print)
{
print $print_string."\n";
}
return(0);
}

@ -1294,6 +1294,7 @@ sub check_firewall
### NOTE: We parse apart iptables-save instead of making frewall-cmd calls as this is a lot faster,
### if not ideal. May want to revisit how we do this later.
$anvil->data->{firewall}{default_zone} = "";
my $iptables = $anvil->System->call({shell_call => $shell_call});
foreach my $line (split/\n/, $iptables)
{
@ -1310,21 +1311,47 @@ sub check_firewall
next if $zone =~ /_log$/;
next if $zone =~ /_allow$/;
next if $zone =~ /_deny$/;
$anvil->data->{firewall}{zone}{$zone}{found} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "firewall::zone::${zone}::found" => $anvil->data->{firewall}{zone}{$zone}{found} }});
$anvil->data->{firewall}{zone}{$zone}{file} = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "firewall::zone::${zone}::found" => $anvil->data->{firewall}{zone}{$zone}{file} }});
}
if ($line =~ /-A INPUT_ZONES -i (\S+) -g IN_(.*)$/)
{
my $interface = $1;
my $zone = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
interface => $interface,
zone => $zone,
}});
$anvil->data->{firewall}{interface}{$interface}{zone} = $zone;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"firewall::interface::${interface}::zone" => $anvil->data->{firewall}{interface}{$interface}{zone},
}});
}
if ($line =~ /-A INPUT_ZONES -g IN_(.*)$/)
{
$anvil->data->{firewall}{default_zone} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"firewall::default_zone" => $anvil->data->{firewall}{default_zone},
}});
}
}
# Make sure, for each zone, we've got a zone file. We should, so we'll read it in.
foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{firewall}{zone}})
{
my $file = $anvil->data->{path}{directories}{firewalld_zones}."/".$zone.".xml";
$file =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file => $file }});
if (-e $file)
$anvil->data->{firewall}{zone}{$zone}{file} = $anvil->data->{path}{directories}{firewalld_zones}."/".$zone.".xml";
$anvil->data->{firewall}{zone}{$zone}{file} =~ s/\/\//\//g;
$anvil->data->{firewall}{zone}{$zone}{body} = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"firewall::zone::${zone}::file" => $anvil->data->{firewall}{zone}{$zone}{file},
}});
if (-e $anvil->data->{firewall}{zone}{$zone}{file})
{
my $zone = $anvil->Storage->read_file({file => $file});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { zone => $zone }});
$anvil->data->{firewall}{zone}{$zone}{body} = $anvil->Storage->read_file({file => $anvil->data->{firewall}{zone}{$zone}{file}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"firewall::zone::${zone}::body" => $anvil->data->{firewall}{zone}{$zone}{body},
}});
}
}

@ -163,3 +163,12 @@ install-manifest::refresh-packages = 1
# 604800 = Once per week
# 2419200 = Once per month (well, 4 weeks)
install-manifest::refresh-period = 86400
### System functions
# The machines used in the Anvil! are treated as appliances, and thus fully under our control. As such, much
# of the system is monitored, managed and auto-repaired. This can frustrate sysadmins. As such, an admin may
# use the 'system::*' options to retake control over some system behaviour.
# Setting this to '0' will disable auto-management of the firewall.
sys::manage::firewall = 1

@ -0,0 +1,35 @@
<!-- NOTE: These are used by 'anvil-manage-firewall', which is in turn called each minute by 'anvil-daemon'. -->
<!-- Changes to these templates should propagate within a minute or so. -->
<!-- start bcn_zone -->
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>#!variable!zone!#</short>
<description>#!variable!description!#</description>
<service name="ssh"/>
<service name="postgresql"/>
<service name="http"/>
<service name="https"/>
</zone>
<!-- end bcn_zone -->
<!-- start ifn_zone -->
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>#!variable!zone!#</short>
<description>#!variable!description!#</description>
<service name="ssh"/>
<service name="postgresql"/>
<service name="http"/>
<service name="https"/>
</zone>
<!-- end ifn_zone -->
<!-- start sn_zone -->
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>#!variable!zone!#</short>
<description>#!variable!description!#</description>
<service name="ssh"/>
</zone>
<!-- end sn_zone -->

66
notes

@ -30,72 +30,6 @@ DOCS; -
====
### Create our zones (skip SN on striker)
# /etc/firewalld/zones/BCN1.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>BCN1</short>
<description>Back-Channel Network #1 - Used for all inter-machine communication in the Anvil!, as well as communication for foundation pack devices. Should be VLAN-isolated from the IFN and, thus, trusted.</description>
<service name="ssh"/>
<service name="postgresql"/>
<service name="http"/>
<service name="https"/>
</zone>
# /etc/firewalld/zones/IFN1.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>IFN1</short>
<description>Internet/Intranet-Facing Network #1 - Used for all client/user facing traffic. Likely connected to a semi-trusted network only.</description>
<service name="ssh"/>
<service name="postgresql"/>
<service name="http"/>
<service name="https"/>
</zone>
### NOTE: Only on nodes and DR, not on Striker
# /etc/firewalld/zones/SN1.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>SN1</short>
<description>Storage Network #1 - Used for DRBD communication between nodes and DR hosts. Should be VLAN-isolated from the IFN and, thus, trusted.</description>
<service name="ssh"/>
</zone>
# Put the interfaces under the appropriate zones.
firewall-cmd --zone=IFN1 --change-interface=ifn1_bond1
firewall-cmd --zone=BCN1 --change-interface=bcn1_bond1
# Set the IFN as the default zone (as that is what will most likely be edited by a user)
firewall-cmd --set-default-zone=IFN1
# Allow tftpd
firewall-cmd --zone=BCN1 --add-service=tftp --permanent
# Allow routing/masq'ing through the IFN1 (provide net access to the BCN)
firewall-cmd --zone=IFN1 --add-masquerade
# Check
firewall-cmd --zone=IFN1 --query-masquerade
[yes|no]
# Disable
# NOTE: Doesn't break existing connections
firewall-cmd --zone=IFN1 --remove-masquerade
### Example shell calls
# Firewall
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=postgresql
firewall-cmd --reload
firewall-cmd --state [running (rc: 0),not running (rc:252)]
firewall-cmd --zone=BCN1 --add-service=tftp
Ports we care about
Porto Number Used by Nets Description

@ -232,6 +232,17 @@ About to try to download aproximately: [#!variable!packages!#] packages needed t
<key name="message_0128">The attempt to enable the 'Install Target' function failed! Please check the logs for details.</key>
<key name="message_0129">[ Error ] - The comps.xml file: [#!variable!comps_xml!#] was found, but something failed when we tried to copy it to: [#!variable!target!#].</key>
<key name="message_0130">Updated repository data.</key>
<key name="message_0131">Back-Channel Network ##!variable!number!# - Used for all inter-machine communication in the Anvil!, as well as communication for foundation pack devices. Should be VLAN-isolated from the IFN and, thus, trusted.</key>
<key name="message_0132">Storage Network ##!variable!number!# - Used for DRBD communication between nodes and DR hosts. Should be VLAN-isolated from the IFN and, thus, trusted.</key>
<key name="message_0133">Internet/Intranet-Facing Network ##!variable!number!# - Used for all client/user facing traffic. Likely connected to a semi-trusted network only.</key>
<key name="message_0134">Updating / configuring the firewall.</key>
<key name="message_0135">Found an unneeded zone: [#!variable!zone!#], it will be removed.</key>
<key name="message_0136">The zone: [#!variable!zone!#] file: [#!variable!file!#] needs to be updated.</key>
<key name="message_0137">The zone: [#!variable!zone!#] file: [#!variable!file!#] doesn't exist, it will now be created.</key>
<key name="message_0138">The interface: [#!variable!interface!#] will be added to the zone: [#!variable!zone!#].</key>
<key name="message_0139">Reloading the firewall...</key>
<key name="message_0140">Restarting the firewall...</key>
<key name="message_0141">Changing the default zone to: [#!variable!zone!#].</key>
<!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key>
@ -513,6 +524,8 @@ The body of the file: [#!variable!file!#] does not match the new body. The file
<key name="log_0239">'Install Target' job: [#!data!switches::job-uuid!#] picked up.</key>
<key name="log_0240">'Install Target' job: [#!data!switches::job-uuid!#] aborted, system not yet configured.</key>
<key name="log_0241">Package list loaded.</key>
<key name="log_0242">The firewall zone: [#!variable!zone!#] is not needed. The zone file: [#!variable!file!#] has been backed up to: [#!variable!backup!#] and will now be removed.</key>
<key name="log_0243">[ Error ] - Failed to delete the file: [#!variable!file!#].</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>

@ -4,8 +4,8 @@
# nodes.
#
# Exit codes;
# 0 = Normal exit
# 1 = md5sum of this program changed. Exited to reload.
# 0 = Normal exit or md5sum of this program changed and it exited to reload.
# 1 =
# 2 = Unable to connect to any database, even after trying to initialize the local system.
#
# TODO:
@ -100,15 +100,15 @@ my $now_time = time;
# Once a minute, we'll check the md5sums and see if we should restart.
# Once a day, we'll refresh an Install Target's RPM repository (has no effect on non-Striker dashboards).
$anvil->data->{timing}{sum_check_interval} = 60;
$anvil->data->{timing}{minute_checks} = 60;
$anvil->data->{timing}{repo_update_interval} = 86400;
$anvil->data->{timing}{next_sum_check} = $now_time + $anvil->data->{timing}{sum_check_interval};
$anvil->data->{timing}{next_minute_check} = $now_time + $anvil->data->{timing}{minute_checks};
$anvil->data->{timing}{next_repo_check} = $now_time; # We want to run on daemon startup
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => {
"s1:timing::sum_check_interval" => $anvil->data->{timing}{sum_check_interval},
"s1:timing::minute_checks" => $anvil->data->{timing}{minute_checks},
"s2:timing::repo_update_interval" => $anvil->data->{timing}{repo_update_interval},
"s3:now_time" => $now_time,
"s4:timing::next_sum_check" => $anvil->data->{timing}{next_sum_check},
"s4:timing::next_minute_check" => $anvil->data->{timing}{next_minute_check},
"s5:timing::next_repo_check" => $anvil->data->{timing}{next_repo_check},
}});
@ -168,30 +168,35 @@ sub handle_periodic_tasks
my ($anvil) = @_;
my $now_time = time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:now_time" => $now_time,
"s2:timing::next_sum_check" => $anvil->data->{timing}{next_sum_check},
"s3:timing::next_repo_check" => $anvil->data->{timing}{next_repo_check},
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:now_time" => $now_time,
"s2:timing::next_minute_check" => $anvil->data->{timing}{next_minute_check},
"s3:timing::next_repo_check" => $anvil->data->{timing}{next_repo_check},
}});
# Time to check if the files on disk have changed?
if ($now_time >= $anvil->data->{timing}{next_sum_check})
# Time to run once per minute tasks.
if ($now_time >= $anvil->data->{timing}{next_minute_check})
{
# Even if it is time to check, don't if a job is running.
# Check the firewall needs to be updated.
check_firewall($anvil);
# Check if the files on disk have changed. Even if it is time to check, don't if a job is
# running.
if ((not $anvil->data->{timing}{jobs_running}) && ($anvil->Storage->check_md5sums))
{
# NOTE: We exit with '0' to prevent systemctl from showing a scary red message.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "warn", key => "message_0014"});
$anvil->nice_exit({code => 1});
$anvil->nice_exit({code => 0});
}
# Mark that we want to check the database config next time.
$check_if_database_is_configured = 1;
# Update the next check time.
$anvil->data->{timing}{next_sum_check} = $now_time + $anvil->data->{timing}{sum_check_interval};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:timing::sum_check_interval" => $anvil->data->{timing}{sum_check_interval},
"s2:timing::next_sum_check" => $anvil->data->{timing}{next_sum_check},
$anvil->data->{timing}{next_minute_check} = $now_time + $anvil->data->{timing}{minute_checks};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:timing::minute_checks" => $anvil->data->{timing}{minute_checks},
"s2:timing::next_minute_check" => $anvil->data->{timing}{next_minute_check},
}});
}
@ -301,6 +306,18 @@ sub run_once
return(0);
}
# Configure/update the firewall.
sub check_firewall
{
my ($anvil) = @_;
# Check the firewall needs to be updated.
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'anvil-manage-firewall'}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
return(0);
}
# This handles tasks that need to run on boot (if any)
sub boot_time_tasks
{
@ -379,6 +396,9 @@ AND
}
}
# Check the firewall needs to be updated.
check_firewall($anvil);
return(0);
}

@ -6,13 +6,28 @@
#
# Exit codes;
# 0 = Normal exit.
# 1 = Failed to unlink an unneeded file.
# 2 = Failed to write or update a file.
#
# TODO:
# - Add support for enabling/disabling MASQ'ing the BCN
#
# # Allow routing/masq'ing through the IFN1 (provide net access to the BCN)
# firewall-cmd --zone=IFN1 --add-masquerade
# # Check
# firewall-cmd --zone=IFN1 --query-masquerade
# #[yes|no]
# # Disable
# # NOTE: Doesn't break existing connections
# firewall-cmd --zone=IFN1 --remove-masquerade
#
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use Text::Diff;
# Disable buffering
@ -28,12 +43,20 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1});
$anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
# If the user has disabled auto-management of the firewall, exit.
if (not $anvil->data->{sys}{manage}{firewall})
{
# Do nothing.
$anvil->nice_exit({exit_code => 0});
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
# Read switches
$anvil->data->{switches}{'y'} = "";
$anvil->Get->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 2, level => 3, key => "message_0134"});
check_initial_setup($anvil);
# We're done
@ -48,6 +71,13 @@ sub check_initial_setup
{
my ($anvil) = @_;
# See what we've found... We'll look at what 'check_firewall' finds later to know if any unused zones
# need to be removed.
my $needed_zones = [];
# This will get set if we need to restart the firewalld daemon.
$anvil->data->{firewall}{reload} = 0;
# Get a list of networks.
$anvil->System->get_ips();
my $internet_zone = "";
@ -64,12 +94,13 @@ sub check_initial_setup
$zone = $anvil->data->{sys}{network}{interface}{$interface}{variable}{ZONE};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { zone => $zone }});
}
push @{$needed_zones}, $zone;
$anvil->data->{zones}{$zone}{interface}{$interface}{ip} = $anvil->data->{sys}{network}{interface}{$interface}{ip};
$anvil->data->{zones}{$zone}{interface}{$interface}{subnet} = $anvil->data->{sys}{network}{interface}{$interface}{subnet};
$anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{ip} = $anvil->data->{sys}{network}{interface}{$interface}{ip};
$anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{subnet} = $anvil->data->{sys}{network}{interface}{$interface}{subnet};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"zones::${zone}::interface::${interface}::ip" => $anvil->data->{zones}{$zone}{interface}{$interface}{ip},
"zones::${zone}::interface::${interface}::subnet" => $anvil->data->{zones}{$zone}{interface}{$interface}{subnet},
"firewall::zone::${zone}::interface::${interface}::ip" => $anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{ip},
"firewall::zone::${zone}::interface::${interface}::subnet" => $anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{subnet},
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
@ -83,23 +114,204 @@ sub check_initial_setup
}
}
# See what we've found...
foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{zones}})
# Get the list of existing zones from iptables/firewalld.
$anvil->System->check_firewall({debug => 3});
foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{firewall}{zone}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { zone => $zone }});
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{zones}{$zone}{interface}})
my $file = exists $anvil->data->{firewall}{zone}{$zone}{file} ? $anvil->data->{firewall}{zone}{$zone}{file} : $anvil->data->{path}{directories}{firewalld_zones}."/".$zone.".xml";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:zone" => $zone,
"s2:file" => $file,
}});
# Is this a zone I want/need?
my $wanted = 0;
foreach my $needed_zone (sort {$a cmp $b} @{$needed_zones})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:zone" => $zone,
"s2:needed_zone" => $needed_zone,
}});
if ($needed_zone eq $zone)
{
$wanted = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { wanted => $wanted }});
last;
}
}
# Remove the file if needed, and then skip this zone if we don't care about it.
if (not $wanted)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "message_0135", variables => { zone => $zone }});
if (-e $file)
{
# Archive and delete it.
my $backup_file = $anvil->Storage->backup({file => $file });
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, secure => 0, key => "log_0242", variables => {
zone => $zone,
file => $file,
backup => $backup_file,
}});
unlink $file;
if (-e $file)
{
# Failed to unlink the unneeed zone file.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0243", variables => { file => $file }});
$anvil->nice_exit({exit_code => 1});
}
}
delete $anvil->data->{firewall}{zone}{$zone};
reload_firewall($anvil);
next;
}
# Create or update the zone file, if needed.
my $template = "";
my $description = "";
if ($zone =~ /bcn(\d+)/i)
{
my $number = $1;
$template = "bcn_zone";
$description = $anvil->Words->string({key => "message_0131", variables => { number => $number }});
}
elsif ($zone =~ /sn(\d+)/i)
{
my $number = $1;
$template = "sn_zone";
$description = $anvil->Words->string({key => "message_0132", variables => { number => $number }});
}
elsif ($zone =~ /ifn(\d+)/i)
{
my $number = $1;
$template = "ifn_zone";
$description = $anvil->Words->string({key => "message_0133", variables => { number => $number }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:template" => $template,
"s2:description" => $description,
}});
my $new_zone_body = $anvil->Template->get({file => "firewall.txt", show_name => 0, name => $template, variables => {
zone => $zone,
description => $description,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { new_zone_body => $new_zone_body }});
# If there isn't a body, see if the file exists. If it doesn't, create it. If it does, read it.
my $update_file = 0;
my $old_zone_body = exists $anvil->data->{firewall}{zone}{$zone}{body} ? $anvil->data->{firewall}{zone}{$zone}{body} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { old_zone_body => $old_zone_body }});
if (-e $file)
{
# Has it changed?
my $diff = diff \$old_zone_body, \$new_zone_body, { STYLE => 'Unified' };
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { diff => $diff }});
if ($diff)
{
# Update it
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0136", variables => { zone => $zone, file => $file }});
$update_file = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { update_file => $update_file }});
}
}
else
{
# Create it
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0137", variables => { zone => $zone, file => $file }});
$update_file = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { update_file => $update_file }});
}
if ($update_file)
{
my $error = $anvil->Storage->write_file({
file => $file,
body => $new_zone_body,
group => "root",
user => "root",
mode => "0644",
overwrite => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { error => $error }});
if ($error)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0043", variables => { file => $file }});
$anvil->nice_exit({exit_code => 2});
}
reload_firewall($anvil);
}
# Make sure the appropriate interfaces are in this zone.
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{firewall}{zone}{$zone}{interface}})
{
my $in_zone = exists $anvil->data->{firewall}{interface}{$interface}{zone} ? $anvil->data->{firewall}{interface}{$interface}{zone} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:interface" => $interface,
"s2:in_zone" => $in_zone,
"s3:zone" => $zone,
}});
if ((not $in_zone) or ($zone ne $in_zone))
{
# Add it
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0138", variables => {
interface => $interface,
zone => $zone,
}});
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'firewall-cmd'}." --zone=".$zone." --change-interface=".$interface});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
reload_firewall($anvil);
}
# Delete it so we know this one has been processed.
delete $anvil->data->{firewall}{interface}{$interface};
}
}
# Do we need to update the default zone?
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
internet_zone => $internet_zone,
"firewall::default_zone" => $anvil->data->{firewall}{default_zone},
}});
if (($internet_zone) && ($anvil->data->{firewall}{default_zone}) && ($anvil->data->{firewall}{default_zone} ne $internet_zone))
{
# Yup
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "message_0141", variables => { zone => $internet_zone }});
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'firewall-cmd'}." --set-default-zone=".$internet_zone});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
}
# NOTE: We may want to do machine-specific stuff down the road.
#my $type = $anvil->System->get_host_type();
#$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { type => $type }});
# Restart, if needed.
if ($anvil->data->{firewall}{reload})
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, key => "message_0140"});
$anvil->System->restart_daemon({debug => 2, daemon => "firewalld"});
}
# Get the list of existing zones.
my $firewall = $anvil->System->check_firewall({debug => 2});
print Dumper $firewall;
return(0);
}
# Reload the firewall as we change zones.
sub reload_firewall
{
my ($anvil) = @_;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0139"});
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'firewall-cmd'}." --reload"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
# What am I?
my $type = $anvil->System->get_host_type();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }});
$anvil->data->{firewall}{reload} = 1;
return(0);
}

Loading…
Cancel
Save