* 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. 15
      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. 52
      tools/anvil-daemon
  9. 244
      tools/anvil-manage-firewall

@ -793,6 +793,9 @@ sub _set_defaults
language => "en_CA", language => "en_CA",
log_date => 1, log_date => 1,
log_file => "/var/log/anvil.log", log_file => "/var/log/anvil.log",
manage => {
firewall => 1,
},
password => { password => {
algorithm => "sha512", algorithm => "sha512",
hash_count => 500000, hash_count => 500000,
@ -935,6 +938,7 @@ sub _set_paths
'anvil-change-password' => "/usr/sbin/anvil-change-password", 'anvil-change-password' => "/usr/sbin/anvil-change-password",
'anvil-daemon' => "/usr/sbin/anvil-daemon", 'anvil-daemon' => "/usr/sbin/anvil-daemon",
'anvil-maintenance-mode' => "/usr/sbin/anvil-maintenance-mode", '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-power' => "/usr/sbin/anvil-manage-power",
'anvil-manage-striker-peers' => "/usr/sbin/anvil-manage-striker-peers", 'anvil-manage-striker-peers' => "/usr/sbin/anvil-manage-striker-peers",
'anvil-prep-database' => "/usr/sbin/anvil-prep-database", '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. 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) =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. 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 $level = defined $parameter->{level} ? $parameter->{level} : 2;
my $line = defined $parameter->{line} ? $parameter->{line} : ""; my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $facility = defined $parameter->{facility} ? $parameter->{facility} : $anvil->data->{defaults}{'log'}{facility}; 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 $priority = defined $parameter->{priority} ? $parameter->{priority} : "";
my $raw = defined $parameter->{raw} ? $parameter->{raw} : ""; my $raw = defined $parameter->{raw} ? $parameter->{raw} : "";
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0; my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
@ -270,6 +277,7 @@ sub entry
# Log the file and line, if passed. # Log the file and line, if passed.
my $string = ""; my $string = "";
my $print_string = "";
if ($anvil->data->{sys}{log_date}) if ($anvil->data->{sys}{log_date})
{ {
# Keep the debug level super high to avoid Get->date_and_time() going into an infinite loop. # Keep the debug level super high to avoid Get->date_and_time() going into an infinite loop.
@ -292,6 +300,7 @@ sub entry
if ($raw) if ($raw)
{ {
$string .= $raw; $string .= $raw;
$print_string .= $raw;
} }
elsif ($key) elsif ($key)
{ {
@ -304,6 +313,7 @@ sub entry
}); });
#print $THIS_FILE." ".__LINE__."; [ Debug ] - message: [$message]\n"; #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. # 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); 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, ### 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. ### 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}); my $iptables = $anvil->System->call({shell_call => $shell_call});
foreach my $line (split/\n/, $iptables) foreach my $line (split/\n/, $iptables)
{ {
@ -1310,21 +1311,47 @@ sub check_firewall
next if $zone =~ /_log$/; next if $zone =~ /_log$/;
next if $zone =~ /_allow$/; next if $zone =~ /_allow$/;
next if $zone =~ /_deny$/; next if $zone =~ /_deny$/;
$anvil->data->{firewall}{zone}{$zone}{found} = 1; $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}{found} }}); $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. # 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}}) foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{firewall}{zone}})
{ {
my $file = $anvil->data->{path}{directories}{firewalld_zones}."/".$zone.".xml"; $anvil->data->{firewall}{zone}{$zone}{file} = $anvil->data->{path}{directories}{firewalld_zones}."/".$zone.".xml";
$file =~ s/\/\//\//g; $anvil->data->{firewall}{zone}{$zone}{file} =~ s/\/\//\//g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { file => $file }}); $anvil->data->{firewall}{zone}{$zone}{body} = "";
if (-e $file) $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->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 => { zone => $zone }}); $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 # 604800 = Once per week
# 2419200 = Once per month (well, 4 weeks) # 2419200 = Once per month (well, 4 weeks)
install-manifest::refresh-period = 86400 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 Ports we care about
Porto Number Used by Nets Description 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_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_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_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 --> <!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key> <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_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_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_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. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>

@ -4,8 +4,8 @@
# nodes. # nodes.
# #
# Exit codes; # Exit codes;
# 0 = Normal exit # 0 = Normal exit or md5sum of this program changed and it exited to reload.
# 1 = md5sum of this program changed. Exited to reload. # 1 =
# 2 = Unable to connect to any database, even after trying to initialize the local system. # 2 = Unable to connect to any database, even after trying to initialize the local system.
# #
# TODO: # 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 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). # 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}{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->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 => { $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}, "s2:timing::repo_update_interval" => $anvil->data->{timing}{repo_update_interval},
"s3:now_time" => $now_time, "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}, "s5:timing::next_repo_check" => $anvil->data->{timing}{next_repo_check},
}}); }});
@ -168,30 +168,35 @@ sub handle_periodic_tasks
my ($anvil) = @_; my ($anvil) = @_;
my $now_time = time; my $now_time = time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"s1:now_time" => $now_time, "s1:now_time" => $now_time,
"s2:timing::next_sum_check" => $anvil->data->{timing}{next_sum_check}, "s2:timing::next_minute_check" => $anvil->data->{timing}{next_minute_check},
"s3:timing::next_repo_check" => $anvil->data->{timing}{next_repo_check}, "s3:timing::next_repo_check" => $anvil->data->{timing}{next_repo_check},
}}); }});
# Time to check if the files on disk have changed? # Time to run once per minute tasks.
if ($now_time >= $anvil->data->{timing}{next_sum_check}) 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)) 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->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. # Mark that we want to check the database config next time.
$check_if_database_is_configured = 1; $check_if_database_is_configured = 1;
# Update the next check time. # Update the next check time.
$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->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:timing::sum_check_interval" => $anvil->data->{timing}{sum_check_interval}, "s1:timing::minute_checks" => $anvil->data->{timing}{minute_checks},
"s2:timing::next_sum_check" => $anvil->data->{timing}{next_sum_check}, "s2:timing::next_minute_check" => $anvil->data->{timing}{next_minute_check},
}}); }});
} }
@ -301,6 +306,18 @@ sub run_once
return(0); 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) # This handles tasks that need to run on boot (if any)
sub boot_time_tasks sub boot_time_tasks
{ {
@ -379,6 +396,9 @@ AND
} }
} }
# Check the firewall needs to be updated.
check_firewall($anvil);
return(0); return(0);
} }

@ -6,13 +6,28 @@
# #
# Exit codes; # Exit codes;
# 0 = Normal exit. # 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 strict;
use warnings; use warnings;
use Anvil::Tools; use Anvil::Tools;
use Data::Dumper; use Data::Dumper;
use Text::Diff;
# Disable buffering # Disable buffering
@ -28,12 +43,20 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1});
$anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}}); $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 # Read switches
$anvil->data->{switches}{'y'} = ""; $anvil->data->{switches}{'y'} = "";
$anvil->Get->switches; $anvil->Get->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 2, level => 3, key => "message_0134"});
check_initial_setup($anvil); check_initial_setup($anvil);
# We're done # We're done
@ -48,6 +71,13 @@ sub check_initial_setup
{ {
my ($anvil) = @_; 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. # Get a list of networks.
$anvil->System->get_ips(); $anvil->System->get_ips();
my $internet_zone = ""; my $internet_zone = "";
@ -64,12 +94,13 @@ sub check_initial_setup
$zone = $anvil->data->{sys}{network}{interface}{$interface}{variable}{ZONE}; $zone = $anvil->data->{sys}{network}{interface}{$interface}{variable}{ZONE};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { zone => $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->{firewall}{zone}{$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}{subnet} = $anvil->data->{sys}{network}{interface}{$interface}{subnet};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"zones::${zone}::interface::${interface}::ip" => $anvil->data->{zones}{$zone}{interface}{$interface}{ip}, "firewall::zone::${zone}::interface::${interface}::ip" => $anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{ip},
"zones::${zone}::interface::${interface}::subnet" => $anvil->data->{zones}{$zone}{interface}{$interface}{subnet}, "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 => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
@ -83,23 +114,204 @@ sub check_initial_setup
} }
} }
# See what we've found... # Get the list of existing zones from iptables/firewalld.
foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{zones}}) $anvil->System->check_firewall({debug => 3});
foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{firewall}{zone}})
{
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 => { zone => $zone }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{zones}{$zone}{interface}}) "s1:zone" => $zone,
"s2:needed_zone" => $needed_zone,
}});
if ($needed_zone eq $zone)
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }}); $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"});
}
return(0);
}
# Reload the firewall as we change zones.
sub reload_firewall
{
my ($anvil) = @_;
# Get the list of existing zones. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0139"});
my $firewall = $anvil->System->check_firewall({debug => 2}); my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'firewall-cmd'}." --reload"});
print Dumper $firewall; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output }});
# What am I? $anvil->data->{firewall}{reload} = 1;
my $type = $anvil->System->get_host_type();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }});
return(0); return(0);
} }

Loading…
Cancel
Save