| #!/usr/bin/perl -w |
| ############################################################################### |
| # $Id$ |
| ############################################################################### |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| ############################################################################### |
| |
| =head1 NAME |
| |
| VCL::Module::OS::Linux::firewall::ufw.pm |
| |
| =head1 DESCRIPTION |
| |
| This module provides support for configuring ufw-based firewalls. |
| |
| =cut |
| |
| ############################################################################### |
| package VCL::Module::OS::Linux::firewall::ufw; |
| |
| # Specify the lib path using FindBin |
| use FindBin; |
| use lib "$FindBin::Bin/../../../../.."; |
| |
| # Configure inheritance |
| use base qw(VCL::Module::OS::Linux::firewall::iptables); |
| |
| # Specify the version of this module |
| our $VERSION = '2.5'; |
| |
| our @ISA; |
| |
| # Specify the version of Perl to use |
| use 5.008000; |
| |
| use strict; |
| use warnings; |
| use diagnostics; |
| |
| use VCL::utils; |
| |
| ############################################################################### |
| |
| =head1 OBJECT METHODS |
| |
| =cut |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 initialize |
| |
| Parameters : none |
| Returns : boolean |
| Description : Returns true if the ufw and iptables-save commands exist on the |
| computer. Returns false if the command does not exist. |
| |
| =cut |
| |
| sub initialize { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $arguments = shift || {}; |
| |
| my $computer_name = $self->data->get_computer_hostname(); |
| |
| notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object to control $computer_name"); |
| |
| if (!$self->os->command_exists('ufw')) { |
| notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not initialized to control $computer_name, ufw command does not exist"); |
| return 0; |
| } |
| elsif (!$self->os->command_exists('iptables-save')) { |
| notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not initialized to control $computer_name, iptables-save command does not exist"); |
| return 0; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized to control $computer_name"); |
| return 1; |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 save_configuration |
| |
| Parameters : none |
| Returns : boolean |
| Description : Executes iptables-save and writes all lines containing 'vcl-' to |
| the end of /etc/ufw/after.rules. A comment beginning with |
| "DISCLAIMER" along with user instructions is added before the |
| section added by VCL. If this exists prior to the execution of |
| this subroutine, the "DISCLAIMER" line and everything underneath |
| it are first removed. |
| |
| "ufw enable" is executed after the .rules file is modified. This |
| reloads the configuration and ensures the firewall is enabled. |
| |
| =cut |
| |
| sub save_configuration { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $computer_name = $self->data->get_computer_short_name(); |
| |
| my $rules_file_path = '/etc/ufw/after.rules'; |
| |
| # Make a backup copy of after.rules |
| my $timestamp = makedatestring(); |
| my $file_timestamp = $timestamp; |
| $file_timestamp =~ s/:+/-/g; |
| $file_timestamp =~ s/\s+/_/g; |
| my $backup_rules_file_path = "/tmp/ufw-after.rules.$file_timestamp"; |
| $self->os->copy_file($rules_file_path, $backup_rules_file_path); |
| |
| my @original_lines = $self->os->get_file_contents($rules_file_path); |
| if (!@original_lines) { |
| notify($ERRORS{'WARNING'}, 0, "failed to save ufw firewall configuration on $computer_name, contents of $rules_file_path could not be retrieved"); |
| return; |
| } |
| |
| my @updated_lines; |
| for my $original_line (@original_lines) { |
| if ($original_line !~ /#.*(VCL|DISCLAIMER)/) { |
| push @updated_lines, $original_line; |
| } |
| else { |
| # Ignore all lines after the first line containing '# VCL' is found |
| last; |
| } |
| } |
| |
| # Call iptables-save |
| # All lines added by vcl should contain a 'vcl-' |
| my $command = "iptables-save"; |
| my ($exit_status, $output) = $self->os->execute($command); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve iptables rules containing 'vcl-' from $computer_name: $command"); |
| return; |
| } |
| elsif ($exit_status ne '0') { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve iptables rules containing 'vcl-' from $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output)); |
| return 0; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "retrieved iptables rules containing 'vcl-' from $computer_name on $computer_name:\n" . join("\n", @$output)); |
| } |
| |
| # Note: Do not use "WARNING" in the message or else it will show up in vcld.log, creating noise when searching for WARNING messages |
| push @updated_lines, <<EOF; |
| |
| # DISCLAIMER: The remainder of this file has been automatically configured by VCL ($timestamp) |
| # Do not modify this line or any lines below |
| # Custom firewall configuration lines may be added to this file but must be located above this line |
| EOF |
| |
| my @vcl_lines; |
| my $current_table; |
| my $last_table_written = ''; |
| LINE: for my $line (@$output) { |
| # Find lines that specify a table name: |
| # *nat |
| # *filter |
| if ($line =~ /^\s*\*(.+)$/) { |
| $current_table = $1; |
| } |
| elsif ($line =~ /(vcl|$PROCESSNAME)-/) { |
| if ($last_table_written ne $current_table) { |
| if ($last_table_written) { |
| push @vcl_lines, "COMMIT\n"; |
| } |
| |
| push @vcl_lines, "*$current_table"; |
| $last_table_written = $current_table; |
| } |
| |
| push @vcl_lines, $line; |
| } |
| } |
| push @vcl_lines, 'COMMIT'; |
| my $vcl_string = join("\n", @vcl_lines); |
| |
| push @updated_lines, @vcl_lines; |
| my $updated_string = join("\n", @updated_lines); |
| |
| if ($self->os->create_text_file($rules_file_path, $updated_string)) { |
| notify($ERRORS{'OK'}, 0, "added VCL-specific lines to $rules_file_path on $computer_name:\n$vcl_string"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to save ufw firewall configuration on $computer_name, $rules_file_path could not be updated"); |
| return; |
| } |
| |
| return $self->enable(); |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| =head2 enable |
| |
| Parameters : none |
| Returns : boolean |
| Description : Calls "ufw --force enable" to reload the ufw firewall and enable |
| it on boot. |
| |
| =cut |
| |
| sub enable { |
| my $self = shift; |
| if (ref($self) !~ /VCL::Module/i) { |
| notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); |
| return 0; |
| } |
| |
| my $computer_name = $self->data->get_computer_short_name(); |
| |
| my $command = 'ufw --force enable'; |
| my ($exit_status, $output) = $self->os->execute($command, 1); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command on $computer_name: $command"); |
| return; |
| } |
| elsif ($exit_status ne '0') { |
| notify($ERRORS{'WARNING'}, 0, "failed to enable ufw on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output)); |
| return 0; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "enabled ufw on $computer_name"); |
| return 1; |
| } |
| } |
| |
| #////////////////////////////////////////////////////////////////////////////// |
| |
| 1; |
| __END__ |
| |
| =head1 SEE ALSO |
| |
| L<http://cwiki.apache.org/VCL/> |
| |
| =cut |