App-PaloAlto-PolicyVerify

 view release on metacpan or  search on metacpan

lib/App/PaloAlto/PolicyVerify.pm  view on Meta::CPAN

use strict;
use warnings;
use 5.010;

package App::PaloAlto::PolicyVerify;
$App::PaloAlto::PolicyVerify::VERSION = '0.0.2';

# PODNAME
# ABSTRACT: Test firewall rules using log files.

use Device::Firewall::PaloAlto;
use Getopt::Long qw(GetOptionsFromArray);
use Text::CSV;

sub new {
    my $class = shift;
    my %obj;

# Set default arguments. Paloa Alto defaults are set by the Device::Firewall::PaloAlto module.
    my %arguments = (
        vr      => 'default',
        vsys    => 1,
        sepchar => ',',
        fields  => '0,1,2,3,4',
        @_
    );

    # Extract and check the fields
    $obj{fields} = [ split( ',', $arguments{fields} ) ];
    if ( ( my $nfields = @{ $obj{fields} } ) != 5 ) {
        die
"'fields' argument has $nfields comma-separated values; needs to be 5 - e.g. '2,4,7,8,9'";
    }

    # Set up the firewall object
    my %pa_args = %arguments{qw(uri username password insecure)};
    $pa_args{verify_hostname} = delete $pa_args{insecure};
    $obj{vr}                  = $arguments{vr};

    $obj{fw} = Device::Firewall::PaloAlto->new(%pa_args)->auth()
      or die $obj{fw}->error;

    # Set up the CSV object
    $obj{csv} =
      Text::CSV->new( { binary => 1, sep_char => $arguments{sepchar} } )
      or die Text::CSV->error_diag();

    # Open the logfile;
    open( $obj{fh}, '<:encoding(utf8)', $arguments{logfile} )
      or die "Could not open file '$arguments{logfile}'";

    return bless \%obj, $class;
}

sub sep {
    my $self = shift;
    my ($sep_char) = @_;

    $self->{csv}->sep_char($sep_char);

    return $self;
}

sub logfile {
    my $self     = shift;
    my $filepath = shift;

    open( $self->{fh}, '<:encoding(utf8)', $filepath )
      or die "Could not open file '$filepath'";

    return $self;
}

sub fields {
    my $self           = shift;
    my %column_numbers = @_;

    die "Five fields are required" unless ( keys %column_numbers == 5 );

    $self->{fields} =
      [ @column_numbers{qw(src_ip dst_ip src_port dst_port protocol)} ];

    return $self;
}

use constant {
    SRC_IP   => 0,
    DST_IP   => 1,
    SRC_PORT => 2,
    DST_PORT => 3,
    PROTO    => 4
};

{

    # Cache for src/dst IP, dst port, and proto flows.
    my %run_cache;

lib/App/PaloAlto/PolicyVerify.pm  view on Meta::CPAN

    sub ip_to_zone {
        my $self = shift;
        my ($ip) = @_;

        # Find the egress interface from the FIB
        # We check if the entry exists because we want to know about
        # undefined routes that don't exist.
        my $fib_entry;
        if ( exists $fib_cache{$ip} ) {
            $fib_entry = $fib_cache{$ip};
        }
        else {
            $fib_entry = $fib_cache{$ip} = $self->{fw}->test->fib_lookup(
                ip             => $ip,
                virtual_router => $self->{vr}
            );
        }
        warn "No valid route for IP '$ip', skipping..." and return
          unless $fib_entry;

# FIXME: we're diving straight into the Device::Firewall::PaloAlto::Test::FIB's
# internal structure. Once its interface is better defined we'll go through that.
        my $fib_interface = $fib_entry->{entries}[0]{interface};

        # Find the zone the interface is tethered in
        $interfaces //= $self->{fw}->op->interfaces;
        warn $interfaces->error and return unless $interfaces;

        my $interface = $interfaces->interface($fib_interface);
        warn $interface->error unless $interface;

        my $zone = $interface->zone;
        warn
"Interface '$fib_interface' does not appear to be in a zone, skipping..."
          and return
          unless $zone;

        return $zone;
    }

}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::PaloAlto::PolicyVerify - Test firewall rules using log files.

=head1 VERSION

version 0.0.2

=head1 SYNOPSIS

This is the supporting module for the L<pa_policy_verify> application.

=head1 DESCRIPTION

This module contains the methods used by the L<pa_policy_verify> application.
It takes in information allowing it to connect to a Palo Alto firewall, and a logfile containing
flows - source/destination IP & ports, and a protocol.

It then runs each flow in the log against the security rulebase currently installed on the Palo Alto firewall 
and returns a result. The result contains:

=over 2

=item Which rule the flow would have hit

=item The action the rule takes

=item The index of the rule.

=back

The main use case is when migrating from a different firewall to the Palo Alto. It allows for the 
qualification of the migrated rulebase prior to the cutover of production flows.

=head1 METHODS

=head2 new

    my $fw_tester = App::PaloAlto::PolicyVerify->new(
        uri => 'https://pa.localdomain',
        username => 'admin',
        password => 'redacted',
        insecure => 0,
        vr => 'default',
        vsys => 1,
        logfile => '/home/user/logs.csv',
        sepchar => ',',
        fields => '0,1,2,3,4'
    );

Contructs the object. Each argument maps to a command line switch in L<pa_policy_verify>. Please refer to its
documentation for information and default values.

The only argument without a default is C<logfile>.

=head2 sepchar

    $fw_tester->sepchar(';');

Sets the separating character between the fields in the logfile.

=head2 logfile 

    $fw_tester->logfile('/home/user/logfile.csv');

Sets the logfile containing flow information that will be run against the firewall.

=head2 fields

    $fw_tester->fields(
        src_ip => 3,
        dst_ip => 4,
        src_port => 8,
        dst_port => 9,
        protocol => 20
    );

Sets the column number in the logfile where each of the 5-tuple flow information resides.

=head2 run

    $fw_tester->run();

Runs the flows contained in the logfile against the firewall.

=head1 AUTHOR

Greg Foletta <greg@foletta.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2019 by Greg Foletta.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut



( run in 0.485 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )