This is the first attempt at enabling firewalld completely. There is a decent chance that problems exist, so it won't be a surprise if a few more commits are needed to this branch before things work.

* Added multiple new private methods to Network that help in managing the firewall.
* Updated Server->boot_server to manage the firewall after the server boots. Updated ->migrate_server to create a job, if a database connection exists, for the migration target to update it's firewall as soon after the server appears as possible.
* Updated ocf:server:alteeve to manage the firewall when called post-migration, in case there was no DB connection and the job above didn't run. Fixed a bug where the disk state wasn't being evaluated properly.
* Updated scan-server to check that the firewall is managed when a server state has changed.
* Updated anvil-daemon to run Network->manage_firewall on startup.
* Heavily reworked 'anvil-manage-server' to either just run 'Network->manage_firewall', or if passed '--server X', to wait for the server to appear for up to 1 minute, then to check that the firewall is managed (to capture servers being migrated to the host.)
* Removed firewall management from striker-prep-database.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 3 years ago
parent 9028611afb
commit bce9e2caaf
  1. 1
      Anvil/Tools.pm
  2. 2
      Anvil/Tools/DRBD.pm
  3. 1038
      Anvil/Tools/Network.pm
  4. 31
      Anvil/Tools/Server.pm
  5. 70
      ocf/alteeve/server
  6. 13
      scancore-agents/scan-server/scan-server
  7. 14
      share/words.xml
  8. 5
      tools/anvil-daemon
  9. 360
      tools/anvil-manage-firewall
  10. 4
      tools/anvil-migrate-server
  11. 6
      tools/anvil-provision-server
  12. 113
      tools/striker-prep-database

@ -1043,6 +1043,7 @@ sub _set_paths
pxe_grub => "/var/lib/tftpboot/grub.cfg", pxe_grub => "/var/lib/tftpboot/grub.cfg",
postfix_main => "/etc/postfix/main.cf", postfix_main => "/etc/postfix/main.cf",
postfix_relay_password => "/etc/postfix/relay_password", postfix_relay_password => "/etc/postfix/relay_password",
'qemu.conf' => "/etc/libvirt/qemu.conf",
ssh_config => "/etc/ssh/ssh_config", ssh_config => "/etc/ssh/ssh_config",
'type.striker' => "/etc/anvil/type.striker", 'type.striker' => "/etc/anvil/type.striker",
'type.dr' => "/etc/anvil/type.dr", 'type.dr' => "/etc/anvil/type.dr",

@ -2023,7 +2023,7 @@ sub manage_resource
} }
} }
# If we 'adjust'ed abovem this will likely complain that the backing disk already exists, and that's # If we 'adjust'ed above, this will likely complain that the backing disk already exists, and that's
# fine. # fine.
my $shell_call = $anvil->data->{path}{exe}{drbdadm}." ".$task." ".$resource; my $shell_call = $anvil->data->{path}{exe}{drbdadm}." ".$task." ".$resource;
my $output = ""; my $output = "";

File diff suppressed because it is too large Load Diff

@ -200,6 +200,9 @@ sub boot_virsh
host => $anvil->data->{server}{location}{$server}{host_name}, host => $anvil->data->{server}{location}{$server}{host_name},
}}); }});
# Make sure the VNC port is open.
$anvil->Network->manage_firewall({debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::database::connections" => $anvil->data->{sys}{database}{connections} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::database::connections" => $anvil->data->{sys}{database}{connections} }});
if ($anvil->data->{sys}{database}{connections}) if ($anvil->data->{sys}{database}{connections})
{ {
@ -1102,6 +1105,34 @@ WHERE
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { migration_command => $migration_command }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { migration_command => $migration_command }});
# Register a job for the peer so it can update its firewall once the target is created.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"sys::database::connections" => $anvil->data->{sys}{database}{connections}
}});
if ($anvil->data->{sys}{database}{connections})
{
my $target_host_uuid = $anvil->Get->host_uuid_from_name({
debug => $debug,
host_name => $target,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { target_host_uuid => $target_host_uuid }});
if ($target_host_uuid)
{
my ($job_uuid) = $anvil->Database->insert_or_update_jobs({
file => $THIS_FILE,
line => __LINE__,
job_command => $anvil->data->{path}{exe}{'anvil-manage-firewall'}.$anvil->Log->switches,
job_data => "server=".$server,
job_name => "manage::firewall",
job_title => "job_0399",
job_description => "job_0400",
job_progress => 0,
job_host_uuid => $target_host_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { job_uuid => $job_uuid }});
}
}
# Call the migration now # Call the migration now
my ($output, $return_code) = $anvil->System->call({shell_call => $migration_command}); my ($output, $return_code) = $anvil->System->call({shell_call => $migration_command});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {

@ -260,7 +260,11 @@ if ($anvil->data->{switches}{migrate_to})
} }
elsif ($anvil->data->{switches}{migrate_from}) elsif ($anvil->data->{switches}{migrate_from})
{ {
# This is called after a migration is complete, so we're basically just doing a status check. # This is called after a migration is complete, so we're basically just doing a status check. In case
# the TCP port isn't open in the firewall yet though, we'll check that now.
$anvil->Network->manage_firewall();
# Now do the status check.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0529", variables => { server => $anvil->data->{environment}{OCF_RESKEY_name} }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0529", variables => { server => $anvil->data->{environment}{OCF_RESKEY_name} }});
server_status($anvil); server_status($anvil);
} }
@ -748,6 +752,9 @@ sub start_drbd_resource
} }
# If auto-promote isn't set, promote the resource. # If auto-promote isn't set, promote the resource.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"drbd::config::${local_host}::auto-promote" => $anvil->data->{drbd}{config}{$local_host}{'auto-promote'},
}});
if (not $anvil->data->{drbd}{config}{$local_host}{'auto-promote'}) if (not $anvil->data->{drbd}{config}{$local_host}{'auto-promote'})
{ {
foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}}) foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}})
@ -756,6 +763,7 @@ sub start_drbd_resource
server => $server, server => $server,
resource => $resource, resource => $resource,
}}); }});
# Make the local resource primary. # Make the local resource primary.
$anvil->DRBD->manage_resource({ $anvil->DRBD->manage_resource({
resource => $resource, resource => $resource,
@ -765,39 +773,42 @@ sub start_drbd_resource
} }
} }
### NOTE: We always check the peer now, in case it's resource is down and ours happens to be up.
# See if we're inconsistent and, if so, if we can connect our peers. # See if we're inconsistent and, if so, if we can connect our peers.
sleep 2; if (0)
$anvil->DRBD->get_status({debug => 3});
my $peer_startup_needed = 1;
foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}})
{ {
# Is the current resource up locally already? sleep 2;
my $role = defined $anvil->data->{drbd}{status}{$host}{resource}{$resource}{role} ? $anvil->data->{drbd}{status}{$host}{resource}{$resource}{role} : ""; $anvil->DRBD->get_status({debug => 3});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { my $peer_startup_needed = 1;
's1:resource' => $resource, foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}})
's2:role' => $role,
}});
# Check all volumes.
foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$host}{resource}{$resource}{devices}{volume}})
{ {
my $disk_state = defined $anvil->data->{drbd}{status}{$host}{resource}{$resource}{devices}{volume}{$volume}{'disk-state'} ? $anvil->data->{drbd}{status}{$host}{resource}{$resource}{devices}{volume}{$volume}{'disk-state'} : ""; # Is the current resource up locally already?
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disk_state => $disk_state }}); my $role = defined $anvil->data->{drbd}{status}{$host}{resource}{$resource}{role} ? $anvil->data->{drbd}{status}{$host}{resource}{$resource}{role} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:resource' => $resource,
's2:role' => $role,
}});
if ((lc($disk_state) eq "consistent") or # Check all volumes.
(lc($disk_state) eq "outdated") or foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$host}{resource}{$resource}{devices}{volume}})
(lc($disk_state) eq "failed") or
(not $disk_state))
{ {
# This will trigger trying to ssh into peer(s) and up'ing their resource. my $disk_state = defined $anvil->data->{drbd}{status}{$host}{resource}{$resource}{devices}{volume}{$volume}{'disk-state'} ? $anvil->data->{drbd}{status}{$host}{resource}{$resource}{devices}{volume}{$volume}{'disk-state'} : "";
$peer_startup_needed = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disk_state => $disk_state }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_startup_needed => $peer_startup_needed }});
last; if ((lc($disk_state) eq "consistent") or
(lc($disk_state) eq "outdated") or
(lc($disk_state) eq "failed") or
(not $disk_state))
{
# This will trigger trying to ssh into peer(s) and up'ing their resource.
$peer_startup_needed = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_startup_needed => $peer_startup_needed }});
last;
}
} }
} }
} }
### NOTE: We always check the peer now, in case it's resource is down and ours happens to be up.
# Do we need to start the resource on our peers? # Do we need to start the resource on our peers?
#$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_startup_needed => $peer_startup_needed }}); #$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_startup_needed => $peer_startup_needed }});
#if (not $peer_startup_needed) #if (not $peer_startup_needed)
@ -806,7 +817,7 @@ sub start_drbd_resource
# return(0); # return(0);
#} #}
# Start DRBD on the peer(s). # Start DRBD on the peer(s), if we can.
foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}}) foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}})
{ {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { resource => $resource }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { resource => $resource }});
@ -867,7 +878,7 @@ sub start_drbd_resource
my $all_resources_ok = 1; my $all_resources_ok = 1;
foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}}) foreach my $resource (sort {$a cmp $b} keys %{$anvil->data->{server}{$local_host}{$server}{resource}})
{ {
# This is set to '1' is either the volumes are UpToDate or Sync'ing. # This is set to '1' if either the volumes are UpToDate or Sync'ing.
$anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{ok} = 0; $anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{ok} = 0;
foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{devices}{volume}}) foreach my $volume (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{devices}{volume}})
{ {
@ -878,7 +889,7 @@ sub start_drbd_resource
$disk_state = "" if not defined $disk_state; $disk_state = "" if not defined $disk_state;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disk_state => $disk_state }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { disk_state => $disk_state }});
if ($disk_state ne "uptodate") if ($disk_state eq "uptodate")
{ {
$anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{devices}{volume}{$volume}{ok} = 1; $anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{devices}{volume}{$volume}{ok} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -892,7 +903,7 @@ sub start_drbd_resource
# See if we're a SyncTarget # See if we're a SyncTarget
foreach my $connection (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{connection}}) foreach my $connection (sort {$a cmp $b} keys %{$anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{connection}})
{ {
my $connection_state = $anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{connection}{'connection-state'}; my $connection_state = $anvil->data->{drbd}{status}{$local_host}{resource}{$resource}{connection}{$connection}{'connection-state'};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
connection => $connection, connection => $connection,
connection_state => $connection_state, connection_state => $connection_state,
@ -948,6 +959,7 @@ sub start_drbd_resource
} }
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { all_resources_ok => $all_resources_ok }});
if ($all_resources_ok) if ($all_resources_ok)
{ {
$waiting = 0; $waiting = 0;

@ -311,7 +311,7 @@ sub collect_data
* Server states; * Server states;
running - The domain is currently running on a CPU running - The domain is currently running on a CPU
idle - The domain is idle, and not running or runnable. This can be caused because the domain is waiting on IO (a traditional wait state) or has gone to sleep because there was nothing else for it to do. idle - The domain is idle, and not running or runnable. This can be caused because the domain is waiting on IO (a traditional wait state) or has gone to sleep because there was nothing else for it to do.
paused - The domain has been paused, usually occurring through the administrator running virsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor. paused - The domain has been paused. This can happen when a server is migrating to this host, or through the administrator running virsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor.
in shutdown - The domain is in the process of shutting down, i.e. the guest operating system has been notified and should be in the process of stopping its operations gracefully. in shutdown - The domain is in the process of shutting down, i.e. the guest operating system has been notified and should be in the process of stopping its operations gracefully.
shut off - The domain is not running. Usually this indicates the domain has been shut down completely, or has not been started. shut off - The domain is not running. Usually this indicates the domain has been shut down completely, or has not been started.
crashed - The domain has crashed, which is always a violent ending. Usually this state can only occur if the domain has been configured not to restart on crash. crashed - The domain has crashed, which is always a violent ending. Usually this state can only occur if the domain has been configured not to restart on crash.
@ -419,6 +419,9 @@ DELETED - Marks a server as no longer existing
}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { server_definition_uuid => $server_definition_uuid }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { server_definition_uuid => $server_definition_uuid }});
# Make sure the firewall is updated.
$anvil->Network->manage_firewall();
# Reload the servers. # Reload the servers.
$anvil->Database->get_servers(); $anvil->Database->get_servers();
$anvil->Database->get_server_definitions(); $anvil->Database->get_server_definitions();
@ -701,6 +704,14 @@ DELETED - Marks a server as no longer existing
server_boot_time => $server_boot_time." (".$anvil->Get->date_and_time({use_time => $server_boot_time}).")", server_boot_time => $server_boot_time." (".$anvil->Get->date_and_time({use_time => $server_boot_time}).")",
}}); }});
} }
# If the server was migrated or booted, update the firewall. With luck, this happened
# already, so this is a back-stop mainly.
if (($old_server_state eq "migrating") or ($old_server_state eq "shut off"))
{
# Make sure the firewall is updated.
$anvil->Network->manage_firewall();
}
} }
if ($server_boot_time ne $old_server_boot_time) if ($server_boot_time ne $old_server_boot_time)
{ {

@ -1320,6 +1320,11 @@ Note: This will connect the DR host until the disk(s) on DR are (all) UpToDate.
<key name="job_0396">Still sync'ing from: [#!variable!sync_source!#] at a rate of: [#!variable!sync_speed!#/sec]. Estimated time remaining is: [#!variable!time_to_sync!#].</key> <key name="job_0396">Still sync'ing from: [#!variable!sync_source!#] at a rate of: [#!variable!sync_speed!#/sec]. Estimated time remaining is: [#!variable!time_to_sync!#].</key>
<key name="job_0397">Sync'ed! Bringing the resource back down now.</key> <key name="job_0397">Sync'ed! Bringing the resource back down now.</key>
<key name="job_0398">Waiting for the connection to come up...</key> <key name="job_0398">Waiting for the connection to come up...</key>
<key name="job_0399">Manage Firewall</key>
<key name="job_0400">This will wait for the named server to appear, then update the firewall to ensure needed ports are open for access to the server's desktop.</key>
<key name="job_0401">Waiting until the server: [#!variable!server!#] appears.</key>
<key name="job_0402">[ Error ] - Timed out waiting for the server: [#!variable!server!#] to appear!</key>
<key name="job_0403">Waiting for the server: [#!variable!server!#] to appear. Will wait: [#!variable!time_left!#] more seconds.</key>
<!-- Log entries --> <!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key> <key name="log_0001">Starting: [#!variable!program!#].</key>
@ -2129,6 +2134,15 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0705">Forcing the dailing resync and checking to clear records in the history schema no longer in public schema.</key> <key name="log_0705">Forcing the dailing resync and checking to clear records in the history schema no longer in public schema.</key>
<key name="log_0706">Updating the OUI list will happen after the system has been up for at least an hour. You can force an update now by running 'striker-parse-oui --force' at the command line.</key> <key name="log_0706">Updating the OUI list will happen after the system has been up for at least an hour. You can force an update now by running 'striker-parse-oui --force' at the command line.</key>
<key name="log_0707">Updated: [#!data!path::configs::firewalld.conf!#] to disable 'AllowZoneDrifting'. See: https://firewalld.org/2020/01/allowzonedrifting</key> <key name="log_0707">Updated: [#!data!path::configs::firewalld.conf!#] to disable 'AllowZoneDrifting'. See: https://firewalld.org/2020/01/allowzonedrifting</key>
<key name="log_0708">Created the firewall zone: [#!variable!zone!#].</key>
<key name="log_0709">Added the interface: [#!variable!interface!#] to the firewall zone: [#!variable!zone!#].</key>
<key name="log_0710">Opening the firewall service: [#!variable!service!#] for the zone: [#!variable!zone!#]!</key>
<key name="log_0711">Closing the firewall service: [#!variable!service!#] for the zone: [#!variable!zone!#]!</key>
<key name="log_0712">Opening the firewall port: [#!variable!port!#/#!variable!protocol!#] for the zone: [#!variable!zone!#]!</key>
<key name="log_0713">Opening the firewall port range: [#!variable!port!#/#!variable!protocol!#] for the zone: [#!variable!zone!#]!</key>
<key name="log_0714">Closing the firewall port: [#!variable!port!#/#!variable!protocol!#] for the zone: [#!variable!zone!#]!</key>
<key name="log_0715">Closing the firewall port range: [#!variable!port!#/#!variable!protocol!#] for the zone: [#!variable!zone!#]!</key>
<key name="log_0716">Changes were made to the firewall, reloading now.</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. --> <!-- Messages for users (less technical than log entries), though sometimes used for logs, too. -->
<key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key> <key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>

@ -977,6 +977,9 @@ sub run_once
{ {
my ($anvil) = @_; my ($anvil) = @_;
# Make sure the firewall is configured.
$anvil->Network->manage_firewall();
# Check that the database is ready. # Check that the database is ready.
prep_database($anvil); prep_database($anvil);
@ -985,7 +988,7 @@ sub run_once
# Check the ssh stuff. # Check the ssh stuff.
# NOTE: This actually runs again in the minutes tasks, but needs to run on boot as well. # NOTE: This actually runs again in the minutes tasks, but needs to run on boot as well.
$anvil->System->check_ssh_keys({debug => 2}); $anvil->System->check_ssh_keys();
# Check setuid wrappers # Check setuid wrappers
check_setuid_wrappers($anvil); check_setuid_wrappers($anvil);

@ -9,9 +9,6 @@
# 2 = Failed to write or update a file. # 2 = Failed to write or update a file.
# #
# TODO: # TODO:
# - TEMP: During development, firewalling is disabled.
# - Add support for enabling/disabling MASQ'ing the BCN
# - Add support for listening for NTP queries based on /etc/chrony.conf's Server entries (map them to networks / zones).
# #
# # Allow routing/masq'ing through the IFN1 (provide net access to the BCN) # # Allow routing/masq'ing through the IFN1 (provide net access to the BCN)
# firewall-cmd --zone=IFN1 --add-masquerade # firewall-cmd --zone=IFN1 --add-masquerade
@ -51,32 +48,48 @@ if (not $anvil->data->{sys}{manage}{firewall})
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 3, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
# Read switches # Read switches
$anvil->data->{switches}{force} = ""; $anvil->data->{switches}{'job-uuid'} = "";
$anvil->data->{switches}{'y'} = ""; $anvil->data->{switches}{server} = "";
$anvil->Get->switches; $anvil->Get->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'switches::job-uuid' => $anvil->data->{switches}{'job-uuid'},
'switches::server' => $anvil->data->{switches}{server},
}});
# Enable and start the firewall, if needed # Log our start.
my $firewall_running = $anvil->Network->check_firewall({debug => 2}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0134"});
if (not $firewall_running)
{
# It must be disabled, exit
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0669"});
$anvil->nice_exit({exit_code => 0});
}
if (not $anvil->data->{switches}{force}) # If we've been passed a job UUID, pick up the details.
if ($anvil->data->{switches}{'job-uuid'})
{ {
$anvil->nice_exit({exit_code => 0}); $anvil->Job->clear();
} $anvil->Job->get_job_details();
$anvil->Job->update_progress({
progress => 1,
job_picked_up_by => $$,
job_picked_up_at => time,
message => "message_0134",
});
if ($anvil->data->{jobs}{job_data} =~ /server=(.*)$/)
{
$anvil->data->{switches}{server} = $1 if not $anvil->data->{switches}{server};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'switches::server' => $anvil->data->{switches}{server},
}});
wait_for_server($anvil);
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "message_0134"}); }
check_initial_setup($anvil);
# Restart, if needed. # This used to do all the work, but that's now moved to the method below. So all we do here now is call it.
if ($anvil->data->{firewall}{reload}) $anvil->Network->manage_firewall();
if ($anvil->data->{switches}{'job-uuid'})
{ {
restart_firewall($anvil); $anvil->Job->update_progress({
progress => 100,
message => "job_0281",
});
} }
# We're done # We're done
@ -87,284 +100,71 @@ $anvil->nice_exit({exit_code => 0});
# Private functions. # # Private functions. #
############################################################################################################# #############################################################################################################
sub check_initial_setup # This simple watches 'virsh list' until the named server appears.
sub wait_for_server
{ {
my ($anvil) = @_; ($anvil) = @_;
# See what we've found... We'll look at what 'check_firewall' finds later to know if any unused zones $anvil->Job->update_progress({
# need to be removed. progress => 25,
my $needed_zones = []; message => "job_0401,!!server!".$anvil->data->{switches}{server}."!!",
});
# This will get set if we need to restart the firewalld daemon. my $wait_until = time + 60;
$anvil->data->{firewall}{reload} = 0; my $waiting = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
# Get a list of networks. wait_until => $wait_until,
$anvil->Network->get_ips({debug => 3}); }});
while($waiting)
# Get the list of existing zones from iptables/firewalld.
$anvil->System->check_firewall({debug => 3});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "firewall::default_zone" => $anvil->data->{firewall}{default_zone} }});
my $internet_zone = "";
my $local_host = $anvil->Get->short_host_name();
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$local_host}{interface}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interface => $interface }});
if ($interface =~ /^((bcn|ifn|sn|mn)\d+)_/)
{
# We'll use the start of the string (network type) as the zone, though it should
# always be overridden by the ZONE="" variable in each interface's config.
my $zone = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { zone => $zone }});
if ((exists $anvil->data->{network}{$local_host}{interface}{$interface}{variable}{ZONE}) && ($anvil->data->{network}{$local_host}{interface}{$interface}{variable}{ZONE}))
{
$zone = $anvil->data->{network}{$local_host}{interface}{$interface}{variable}{ZONE};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { zone => $zone }});
}
push @{$needed_zones}, $zone;
$anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{ip} = $anvil->data->{network}{$local_host}{interface}{$interface}{ip};
$anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{subnet_mask} = $anvil->data->{network}{$local_host}{interface}{$interface}{subnet_mask};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"firewall::zone::${zone}::interface::${interface}::ip" => $anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{ip},
"firewall::zone::${zone}::interface::${interface}::subnet_mask" => $anvil->data->{firewall}{zone}{$zone}{interface}{$interface}{subnet_mask},
"network::${local_host}::interface::${interface}::default_gateway" => $anvil->data->{network}{$local_host}{interface}{$interface}{default_gateway},
}});
if ($anvil->data->{network}{$local_host}{interface}{$interface}{default_gateway})
{
$internet_zone = $zone;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { internet_zone => $internet_zone }});
if ((not $anvil->data->{firewall}{default_zone}) or ($anvil->data->{firewall}{default_zone} eq "public"))
{
$anvil->data->{firewall}{default_zone} = $zone;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "firewall::default_zone" => $anvil->data->{firewall}{default_zone} }});
}
}
}
}
# Process the list of existing zones from iptables/firewalld.
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"; my $shell_call = $anvil->data->{path}{exe}{virsh}." list --name";
my $user_file = $anvil->data->{path}{directories}{firewalld_zones_etc}."/".$zone.".xml"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { shell_call => $shell_call }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:zone" => $zone,
"s2:file" => $file,
"s3:user_file" => $user_file,
}});
### NOTE: This is probably overkill.
# 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 => {
"s1:zone" => $zone,
"s2:needed_zone" => $needed_zone,
}});
if ($needed_zone eq $zone)
{
$wanted = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { wanted => $wanted }});
last;
}
}
# Skip if this is a zone I don't care about.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { wanted => $wanted }});
next if not $wanted;
# Now, skip if the user-land file exists.
if (-e $user_file)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0143", variables => { zone => $zone, file => $user_file }});
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 }});
}
else
{
# This should never be hit, but it's a fail-safe in we're in a zone we don't manage.
next;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:template" => $template,
"s2:description" => $description,
}});
my $new_zone_body = $anvil->Template->get({debug => 3, file => "firewall.txt", show_name => 0, name => $template, variables => { my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
zone => $zone, $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
description => $description, 's1:output' => $output,
's2:return_code' => $return_code,
}}); }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_zone_body => $new_zone_body }});
# This is another fail safe, don't edit unless we have a new file body. foreach my $server (split/\n/, $output)
if (not $new_zone_body)
{ {
next; $server = $anvil->Words->clean_spaces({string => $server});
} next if not $server;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { server => $server }});
# If there isn't a body, see if the file exists. If it doesn't, create it. If it does, read it. if ($server eq $anvil->data->{switches}{server})
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 => 2, 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 => 2, list => { diff => $diff }});
if ($diff)
{ {
# Update it # Found it.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0136", variables => { zone => $zone, file => $file }}); $waiting = 0;
$update_file = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { waiting => $waiting }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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 => 2, list => { update_file => $update_file }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_file => $update_file }}); if ($waiting)
if ($update_file)
{ {
my $error = $anvil->Storage->write_file({ if (time > $wait_until)
file => $file,
body => $new_zone_body,
group => "root",
user => "root",
mode => "0644",
overwrite => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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 }}); # timed out
$anvil->nice_exit({exit_code => 2}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, priority => "err", key => "job_0402", variables => { server => $anvil->data->{switches}{server} }});
$anvil->Job->update_progress({
progress => 75,
message => "job_0402,!!server!".$anvil->data->{switches}{server}."!!",
});
} }
else else
{ {
# We need an immediate reload to pick up the new file. sleep 3;
restart_firewall($anvil); my $time_left = $wait_until - time;
} $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0403", variables => {
} server => $anvil->data->{switches}{server},
time_left => $time_left,
# 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 => 2, list => {
"s1:interface" => $interface,
"s2:in_zone" => $in_zone,
"s3:zone" => $zone,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_zone => $in_zone, 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,
}}); }});
$anvil->Job->update_progress({
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --zone=".$zone." --change-interface=".$interface." --permanent"; progress => 50,
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); message => "job_0403,!!server!".$anvil->data->{switches}{server}."!!,!!time_left!".$time_left."!!",
my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call}); });
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
$shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --zone=".$zone." --change-interface=".$interface;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
$anvil->data->{firewall}{reload} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "firewall::reload" => $anvil->data->{firewall}{reload} }});
} }
# 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 => 2, list => {
internet_zone => $internet_zone,
"firewall::default_zone" => $anvil->data->{firewall}{default_zone},
}});
if ($anvil->data->{firewall}{default_zone})
{
# What's the current default zone?
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --get-default-zone";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($default_zone, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { default_zone => $default_zone, return_code => $return_code }});
if ($default_zone ne $anvil->data->{firewall}{default_zone})
{
# Update.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0141", variables => { zone => $internet_zone }});
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --set-default-zone=".$anvil->data->{firewall}{default_zone};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 2, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
$anvil->data->{firewall}{reload} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "firewall::reload" => $anvil->data->{firewall}{reload} }});
}
}
# NOTE: We may want to do machine-specific stuff down the road.
my $type = $anvil->Get->host_type();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }});
return(0);
}
sub restart_firewall
{
my ($anvil) = @_;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0139"});
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --complete-reload";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "message_0140"});
$anvil->System->restart_daemon({debug => 3, daemon => "firewalld"});
$anvil->data->{firewall}{reload} = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "firewall::reload" => $anvil->data->{firewall}{reload} }});
return(0); return(0);
} }

@ -7,6 +7,10 @@
# 0 = Normal exit. # 0 = Normal exit.
# 1 = Any problem that causes an early exit. # 1 = Any problem that causes an early exit.
# #
# NOTE: as per qemu.conf defaults, a maximum of 63 live migrations can happen at the same time. We need to
# to count how many servers there are and, if the number is over 63, update qemu.conf. The current
# range of ports available for live migration can be found here:
# - my ($migration_minimum, $migration_maximum) = $anvil->Network->_get_live_migration_ports();
use strict; use strict;
use warnings; use warnings;

@ -308,6 +308,9 @@ sub run_jobs
# Create the DRBD resource file # Create the DRBD resource file
create_resource_file($anvil); create_resource_file($anvil);
# Make sure the DRBD ports are open.
$anvil->Network->manage_firewall();
# Create the DRBD metadata, if needed. # Create the DRBD metadata, if needed.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::initialize_drbd' => $anvil->data->{job}{initialize_drbd} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::initialize_drbd' => $anvil->data->{job}{initialize_drbd} }});
create_md($anvil) if $anvil->data->{job}{initialize_drbd}; create_md($anvil) if $anvil->data->{job}{initialize_drbd};
@ -328,6 +331,9 @@ sub run_jobs
provision_server($anvil); provision_server($anvil);
} }
# Make sure the VNC port is open.
$anvil->Network->manage_firewall();
# Add the server to the cluster. # Add the server to the cluster.
add_server_to_cluster($anvil); add_server_to_cluster($anvil);

@ -541,117 +541,8 @@ sub configure_firewall
{ {
my ($anvil) = @_; my ($anvil) = @_;
# What zones are there? # All the firewall management is now in the method below.
my $in_zone = ""; $anvil->Network->manage_firewall();
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --get-active-zones";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, debug => 2, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
# If the return code was 252, firewalld isn't running.
if ($return_code eq "252")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0669"});
return(0);
}
foreach my $line (split/\n/, $output)
{
if ($line =~ /^\S/)
{
$in_zone = $line;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_zone => $in_zone }});
$anvil->data->{firewall}{zone}{$in_zone}{active} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"firewall::zone::${in_zone}::active" => $anvil->data->{firewall}{zone}{$in_zone}{active},
}});
}
elsif ($line =~ /^\s+interfaces: (.*)$/)
{
my $interfaces = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { interfaces => $interfaces }});
$anvil->data->{firewall}{zone}{$in_zone}{interfaces} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"firewall::zone::${in_zone}::interfaces" => $anvil->data->{firewall}{zone}{$in_zone}{interfaces},
}});
}
}
my $reload = 0;
foreach my $zone (sort {$a cmp $b} keys %{$anvil->data->{firewall}{zone}})
{
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --permanent --info-zone=".$zone;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, debug => 2, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
foreach my $line (split/\n/, $output)
{
if ($line =~ /^\s+services: (.*)$/)
{
my $services = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { services => $services }});
foreach my $service (split/ /, $services)
{
$anvil->data->{firewall}{zone}{$in_zone}{service}{$service} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"firewall::zone::${in_zone}::service::${service}" => $anvil->data->{firewall}{zone}{$in_zone}{service}{$service},
}});
}
}
}
# Is postgres open?
if ((not exists $anvil->data->{firewall}{zone}{$in_zone}{service}{postgresql}) or (not $anvil->data->{firewall}{zone}{$in_zone}{service}{postgresql}))
{
### TODO: Switch this to Network->manage_firewall().
# Enable it.
my $service = "postgresql";
$reload = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
service => $service,
reload => $reload,
}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0667", variables => {
zone => $zone,
service => $service,
}});
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --zone=".$zone." --permanent --add-service=".$service;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, debug => 2, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
}
}
# Reload the firewall?
if ($reload)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0139"});
my $shell_call = $anvil->data->{path}{exe}{'firewall-cmd'}." --reload";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call, debug => 2, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
}
return(0); return(0);
} }

Loading…
Cancel
Save