File Coverage

blib/lib/Firewall/PaloAlto.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Firewall::PaloAlto;
2 2     2   53583 use Moose;
  0            
  0            
3              
4             use 5.006;
5             use strict;
6             use warnings;
7              
8             use XML::Simple;
9             use LWP::UserAgent;
10             use Carp;
11             use Modern::Perl;
12             use Params::Validate qw(:all);
13              
14             use Data::Dumper;
15              
16             with 'Firewall::PaloAlto::Common';
17             use Firewall::PaloAlto::SystemInfo;
18             use Firewall::PaloAlto::Routes;
19              
20              
21             =head1 NAME
22              
23             Firewall::PaloAlto - Interact with a Palo Alto firewall's API through Perl.
24              
25             =head1 VERSION
26              
27             Version 0.03
28              
29             =cut
30              
31             our $VERSION = '0.04';
32              
33             =head1 SYNOPSIS
34              
35             The Firewall::PaloAlto module provides interfaces into the XML API of a Palo Alto firewall.
36              
37             use Firewall::PaloAlto;
38              
39             my $fw = Firewall::PaloAlto->new(host => 'pa.local', username => 'admin', password => 'admin');
40             $fw->connect();
41              
42             #Add a new virtual system
43             $fw->address('set', vsys_id => 6, 'display-name' => "Script_Tenant");
44              
45             #Add a virtual router to the chassis
46             $fw->virtual_router('set', vr_name => 'NEW_VR', interface => [ 'ae1.65', 'ae1.66' ]);
47              
48             #Add a new address - if the vsys is not specified it defaults to vsys1.
49             #This works for devices such as the VM series, which only have a vsys1.
50             $fw->address('set', name => 'Google_DNS', ip-netmask => '8.8.8.8/32');
51              
52             #Get the configuration for the newly created address:
53             my $address_config = $fw->address('get', name => 'Google_DNS');
54              
55             #Delete the newly created address
56             $fw->address('delete', name => 'Google_DNS);
57              
58             A list of functions that can be exported. You can delete this section
59             if you don't export anything, such as for a purely object-oriented module.
60              
61             =cut
62              
63             =head1 CLASS METHODS
64              
65             =head2 new(%parameters)
66              
67             The constructor generates a new object to interact with a specific firewall.
68              
69             The host, username and password parameters are mandatory.
70             If not specified, SSL is used, but it can be disabled using the argument ssl => 0
71              
72             Detailed debugging can be turned on using the debug => 1 argument. It is off by default.
73              
74             This method is inherited from the Firewall::PaloAlto::Common role.
75              
76             my $palo = Firewall::PaloAlto->new(host => 'paloalto.local', username => 'admin', password => 'admin', ssl => 0, debug => 1);
77              
78             B
79              
80             =over
81              
82             =item *
83             host - the hostname or IP of the firewall to connect to.
84              
85             =item *
86             username - a username to connect to the firewall.
87              
88             =item *
89             password - a password to connect to the firewall.
90              
91             =item *
92             ssl (optional, default: 1) - use SSL to connect to the firewall.
93              
94             =item *
95             debug (optional, default: 0) - print debugging messages.
96              
97             =back
98              
99             =cut
100              
101             =head1 CONFIGURATION METHODS
102              
103             =head2 connect()
104              
105             The connect functions connects to the Palo Alto, validates and saves the API key.
106             It has no parameters and is inherited from the Firewall::PaloAlto::Common role.
107              
108             $pa->connect();
109              
110             =cut
111              
112             =head2 commit(%parameters)
113              
114             The commit function commits the current candidate configuration to the Palo Alto firewall.
115              
116             $pa_firewall->commit(vsys_id => 10, poll => 10);
117              
118             B
119              
120             =over
121              
122             =item *
123             vsys_id (optional) - if supplied, performs a partial commit on the vsys specified. If not provided, performs a full commit on the device.
124              
125             =item *
126             poll (optional, default: 5 seconds) - defines the interval (seconds) that the method will poll the device to get the current status of the commit job.
127              
128             =back
129              
130             =cut
131              
132             sub commit {
133             my $self = shift;
134             my %args = @_;
135             my $requester = $self->_create_requester(type => 'commit');
136             my $operate = $self->_create_requester(type => 'op');
137             my $cmd = "";
138              
139             #If a poll interval is not defined, we default to 5 seconds.
140             $args{poll} //= 5;
141              
142             if (defined $args{vsys_id}) {
143             $cmd = "vsys$args{vsys_id}";
144             }
145              
146             my $response = $requester->(cmd => $cmd);
147              
148             my $job_result;
149              
150             do {
151             $job_result = $operate->(cmd => "$response->{result}->{job}");
152             $self->_debug_print((caller(0))[3], "JobID: $response->{result}->{job}, Status: $job_result->{result}->{job}->{result}, Progress: $job_result->{result}->{job}->{progress}",
153             sub { $job_result->{result}->{job}->{result} ne "FAILED" });
154              
155             sleep($args{poll});
156             } while ($job_result->{result}->{job}->{result} eq 'PEND');
157             }
158              
159              
160             =head2 vsys($action, %parameters)
161              
162             The vsys function creates a new virtual system.
163              
164             $pa->vsys('set', vsys_id => 4, display-name => "Tenant 4");
165              
166             B
167              
168             =over
169              
170             =item * $action - perform an action: ['get' | 'set' | 'delete']
171              
172             =item * vsys_id - the ID of the virtual system to perform the action on.
173              
174             =item * display-name - sets the display name of the virtual system.
175              
176             =back
177              
178             =cut
179              
180             sub vsys {
181             my $self = shift;
182             my $action = shift;
183             my (%args) = @_;
184              
185             #Validate our parameters.
186             validate(
187             @_, {
188             vsys_id => 1,
189             "display-name" => 1
190             }
191             );
192              
193             my $vsys_id = delete $args{vsys_id};
194             my $requester = $self->_create_requester(type => 'config', action => $action);
195              
196             my $elements = $self->_generate_elements(%args);
197              
198             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/vsys/entry[\@name='vsys$vsys_id']", element => $elements);
199             }
200              
201             =head2 vsys_import($action, %parameters);
202              
203             The vsys_import function imports interfaces and virtual-routers into a virtual system.
204              
205             $pa_firewall->vsys_import('set', vsys_id => 4, interface => [ ethernet1/1, ethernet1/2 ], virtual-router => [ default ]);
206              
207             B
208              
209             =over
210              
211             =item * $action - perform an action: ['get' | 'set' | 'delete']
212              
213             =item * vsys_id - the ID of the virtual system to perform the action on.
214              
215             =item * interface - an anonymous array of one or more interfaces to add to the virtual system.
216              
217             =item * virtual-router - an anonymous array of one or more virtual routers to add to the virtual system.
218              
219             =back
220              
221             =cut
222              
223             sub vsys_import {
224             my $self = shift;
225             my $action = shift;
226             my (%args) = @_;
227              
228             #validate(
229             # @_, {
230             # vsys_id => 1,
231             # interface => 0;
232             # "virtual-router" => 0,
233             # }
234             #);
235              
236             my $vsys_id = delete @args{vsys_id};
237              
238             my $requester = $self->_create_requester(type => 'config', action => $action);
239             my $elements = $self->_generate_elements(%args);
240              
241             #Add the interface or virtual router to a vsys
242             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/vsys/entry[\@name='vsys$vsys_id']/import/network", element => $elements);
243             }
244              
245             =head2 l3_subinterface($action, %parameters)
246              
247             This function creates a new layer 3 subinterface underneath a parent interface.
248              
249             $pa->l3_subinterface('set', parent => 'ethernet1/1', tag => 5, description => 'Tenant x untrust interface');
250              
251             B
252              
253             =over
254              
255              
256             =item * $action - perform an action: ['get' | 'set' | 'delete']
257              
258             =item * parent - the parent interface of the new subinterface
259              
260             =item * tag - the VLAN tag to use on the sub-interface. This is also used as the logical sub-interface identifier.
261              
262             =item * description - a description to add to the sub-interface.
263              
264             =back
265              
266             =cut
267              
268             sub l3_subinterface {
269             my $self = shift;
270             my ($action, %args) = @_;
271              
272             my $parent_interface = delete @args{'parent'};
273              
274             my $requester = $self->_create_requester(type => 'config', action => $action);
275             my $elements = $self->_generate_elements(%args);
276              
277             #Create the sub-interface
278             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/network/interface/aggregate-ethernet/entry[\@name='$parent_interface']/layer3/units/entry[\@name='$parent_interface.$args{tag}']",
279             element => $elements);
280              
281             }
282              
283              
284             =head2 virtual_router($action, %parameters)
285              
286             B
287              
288             =over
289              
290              
291             =item * $action - perform an action: ['get' | 'set' | 'delete']
292              
293             =item * vr_name - the name of the virtual router to perform the action on.
294              
295             =item * interface - an anonymous array of one or more interfaces to add to the virtual router.
296              
297             =back
298              
299             =cut
300              
301             sub virtual_router {
302             my $self = shift;
303             my ($action, %args) = @_;
304              
305             my $vr_name = delete @args{'vr_name'};
306              
307             my $requester = $self->_create_requester(type => 'config', action => $action);
308             my $elements = $self->_generate_elements(%args);
309            
310             #Create the virtual router
311             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/network/virtual-router/entry[\@name='$vr_name']",
312             element => $elements);
313             }
314              
315             =head2 zone($action, %parameters)
316              
317             B
318              
319             =over
320              
321             =item * $action - perform an action: ['get' | 'set' | 'delete']
322              
323             =item * vsys_id - the virtual system ID to which the zone is/should be a member of.
324              
325             =item * zone - the name of the zone to create/update/delete.
326              
327             =item * layer3 - an anonymous array of one or more interfaces to add to the zone.
328              
329             =back
330              
331             =cut
332              
333             sub zone {
334             my $self = shift;
335             my ($action, %args) = @_;
336             my $requester = $self->_create_requester(type => 'config', action => $action);
337              
338             my ($vsys_id, $zone) = delete @args{'vsys_id', 'zone'};
339             $vsys_id //= 1;
340              
341             my $elements = $self->_generate_elements(%args);
342              
343             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/vsys/entry[\@name='vsys$vsys_id']/zone/entry[\@name='$zone']/network",
344             element => $elements);
345             }
346              
347             =head2 ipv4_static_route($action, %parameters)
348              
349             B
350              
351             =over
352              
353             =item * $action - perform an action: ['get' | 'set' | 'delete']
354              
355             =item * vr_name - the name of the virtual router in which the static route resides.
356              
357             =item * route_name - the name of the route to perform the action on.
358              
359             =item * destination - the IPv4 destination of the route (IP/prefix)
360              
361             =item * nexthop - an anonymous hash specifying the next hop
362              
363             =item * ip-address - the next hop IP address
364              
365             =item * interface - the next hop interface
366              
367             =back
368              
369             =cut
370              
371             sub ipv4_static_route {
372             my $self = shift;
373             my ($action, %args) = @_;
374             my $requester = $self->_create_requester(type => 'config', action => $action);
375              
376             my ($vr_name, $route_name) = delete @args{'vr_name', 'route_name'};
377             $route_name = defined $route_name ? "\@name='$route_name'" : "'*'";
378              
379             my $elements = $self->_generate_elements(%args);
380              
381             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/network/virtual-router/entry[\@name='$vr_name']/routing-table/ip/static-route/entry[$route_name]",
382             element => $elements);
383             }
384              
385              
386             =head2 ipv6_static_route($action, %parameters)
387              
388             B
389              
390             =over
391              
392              
393             =item * $action - perform an action: ['get' | 'set' | 'delete']
394              
395             =item * vr_name - the name of the virtual router in which the static route resides.
396              
397             =item * route_name - the name of the route to perform the action on.
398              
399             =item * destination - the IPv6 destination of the route (IP/prefix)
400              
401             =item * nexthop - an anonymous hash specifying the next hop
402              
403             =item * ipv6-address - the next hop IPv6 address
404              
405             =item * interface - the next hop interface
406              
407             =back
408              
409             =cut
410              
411             sub ipv6_static_route {
412             my $self = shift;
413             my ($action, %args) = @_;
414             my $requester = $self->_create_requester(type => 'config', action => $action);
415              
416             my ($vr_name, $route_name) = delete @args{'vr_name', 'route_name'};
417             $route_name = defined $route_name ? "\@name='$route_name'" : "'*'";
418              
419             my $elements = $self->_generate_elements(%args);
420              
421             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/network/virtual-router/entry[\@name='$vr_name']/routing-table/ipv6/static-route/entry[$route_name]",
422             element => $elements);
423             }
424              
425             =head2 address($action, %parameters)
426              
427             B
428              
429             =over
430              
431             =item * vsys_id - the vsys ID in which the resides/shall reside.
432              
433             =item * name - the name of the address.
434              
435             =item * ip-netmask - the IP/netmask combination which defines the address.
436              
437             =item * ip-range - an IP address range (format: 'IPstart-IPend')
438              
439             =back
440              
441             =cut
442              
443             sub address {
444             my $self = shift;
445             my ($action, %args) = @_;
446             my $requester = $self->_create_requester(type => 'config', action => $action);
447              
448             #Keys to be extracted and deleted because they aren't part of the element
449             #delete returns the values that were deleted.
450             my ($vsys, $address) = delete @args{'vsys_id', 'name'};
451              
452             #If the vsys if not defined, we default to vsys1
453             $vsys //= "vsys1";
454              
455             $address = defined $address ? "\@name='$address'" : "'*'";
456              
457             my $elements = $self->_generate_elements(%args);
458              
459             return $requester->(xpath => "/config/devices/entry[\@name='localhost.localdomain']/vsys/entry[\@name=\'$vsys\']/address/entry[$address]", element => $elements);
460             }
461              
462              
463             =head2 address_group($action, %parameters)
464              
465             B
466              
467             =over
468              
469              
470             =item * $action - perform an action: ['get' | 'set' | 'delete']
471              
472             =item * vsys_id - the vsys ID in which the address group resides/shall reside.A
473              
474             =item * name - the name of the address group.
475              
476             =item * member - an anonymous array of one or more addresses. These can be either address entries created with address(), or explicit IP/netmasks (e.g. 9.8.8.8/32)
477              
478             =back
479              
480             =cut
481              
482             sub address_group {
483             my $self = shift;
484             my ($action, %args) = @_;
485             my $requester = $self->_create_requester(type => 'config', action => $action);
486              
487             #Keys to be extracted and deleted because they aren't part of the element
488             #delete returns the values that were deleted.
489             my ($vsys, $name) = delete @args{'vsys_id', 'name'};
490              
491             #If the vsys if not defined, we default to vsys1
492             $vsys //= "vsys1";
493              
494             $name = defined $name ? "\@name='$name'" : "'*'";
495              
496             my $elements = $self->_generate_elements(%args);
497              
498             return $requester->(xpath => "/config/devices/entry/vsys/entry[\@name=\'$vsys\']/address-group/entry[$name]", element => $elements);
499             }
500              
501              
502              
503             =head2 security_rule($action, %parameters)
504              
505             B
506              
507             =over
508              
509              
510             =item * $action - perform an action: ['get' | 'set' | 'delete']
511              
512             =item * vsys_id - the virtual system ID of the vsys in which the security rule resides/will reside. Defaults to 1 if not supplied.
513              
514             =item * name - the name of the rule.
515              
516             =item * from - the source zone, defaults to 'any' if not supplied.
517              
518             =item * to - the destination zone, defaults to 'any' if not supplied.
519              
520             =item * source - an anonymous array of source addresses - can be addresses, address groups or explicit IP/netmask entries. Defaults to 'any' if not supplied.
521              
522             =item * destination - an anonymous array of destination addresses - can be addresses, address groups or explicit IP/netmask entries. Defaults to 'any' if not supplied.
523              
524             =item * service - an anonymous array of one or more services. Defaults to 'application-default' if not supplied.
525              
526             =item * appplication - an anonymous array of one or more Palo Alto applications. Defaults to 'any' if not supplied.
527              
528             =item * source-user - an anonymous array of one or more Palo Alto source user mappings. Defaults to 'any' if not supplied.
529              
530             =item * hip-profile - an anonymous array of Host Information Profiles. defaults to 'any' if not supplied.
531              
532             =item * action - an action for the rule, either 'allow', 'deny' or 'drop'. Defaults to 'allow' if not supplied.
533              
534             =back
535              
536             =cut
537              
538              
539             sub security_rule {
540             my $self = shift;
541             my ($action, %args) = @_;
542             my $requester = $self->_create_requester(type => 'config', action => $action);
543              
544             #If the name isn't defined, default to all rules
545             my $rule_name = defined $args{name} ? "\@name='$args{name}'" : "'*'";
546             delete $args{name};
547            
548             #If the vsys if not defined, we default to vsys1
549             my $vsys = $args{vsys_id} // "vsys1";
550             delete $args{vsys_id};
551              
552             #If any of the following items aren't defined, we default to an anon array of type 'any'
553             my @default_any = qw(to from source destination application source-user hip-profiles);
554             for my $key (@default_any) {
555             $args{$key} //= ['any'];
556             }
557              
558             #If the service isn't defined, we default to 'application-default'
559             $args{service} //= ['application-default'];
560              
561             #If the action isn't defined, we defailt to 'allow'
562             $args{action} //= 'allow';
563              
564             my $elements = $self->_generate_elements(%args);
565              
566             return $requester->(xpath => "/config/devices/entry/vsys/entry[\@name=\'$vsys\']/rulebase/security/rules/entry[$rule_name]", element => $elements);
567             }
568              
569             =head1 OPERATIONAL METHODS
570              
571             =head2 get_system_info()
572              
573             Returns a HASHREF containing information about the system.
574              
575             =cut
576              
577             sub get_system_info {
578             my $self = shift;
579              
580             my $requester = $self->_create_requester(type => 'op');
581             my $result = $requester->(cmd => '');
582             return Firewall::PaloAlto::SystemInfo->new( $result );
583             }
584              
585             =head2 get_routing_table()
586              
587             Retrieves the routing table of a particular router. If no virtual router is specified, defaults to the 'default' virtual router.
588              
589             The return value is an object of type Firewall::PaloAlto::Routes.
590              
591             =cut
592              
593             sub get_routing_table {
594             my $self = shift;
595             my %args = @_;
596              
597             #Use the 'default' virtual router if none is defined.
598             $args{virtual_router} //= 'default';
599              
600             my $requester = $self->_create_requester(type => 'op');
601             my $result = $requester->(cmd => "$args{virtual_router}");
602              
603             return Firewall::PaloAlto::Routes->new( $result );
604             }
605              
606             =head1 AUTHOR
607              
608             Greg Foletta, C<< >>
609              
610             =head1 BUGS
611              
612             Please report any bugs or feature requests to C, or through
613             the web interface at L. I will be notified, and then you'll
614             automatically be notified of progress on your bug as I make changes.
615              
616              
617              
618             =head1 SUPPORT
619              
620             You can find documentation for this module with the perldoc command.
621              
622             perldoc Firewall::PaloAlto
623              
624              
625             You can also look for information at:
626              
627             =over 4
628              
629             =item * RT: CPAN's request tracker (report bugs here)
630              
631             L
632              
633             =item * AnnoCPAN: Annotated CPAN documentation
634              
635             L
636              
637             =item * CPAN Ratings
638              
639             L
640              
641             =item * Search CPAN
642              
643             L
644              
645             =back
646              
647              
648             =head1 ACKNOWLEDGEMENTS
649              
650              
651             =head1 LICENSE AND COPYRIGHT
652              
653             Copyright 2015 Greg Foletta.
654              
655             This program is free software; you can redistribute it and/or modify it
656             under the terms of the the Artistic License (2.0). You may obtain a
657             copy of the full license at:
658              
659             L
660              
661             Any use, modification, and distribution of the Standard or Modified
662             Versions is governed by this Artistic License. By using, modifying or
663             distributing the Package, you accept this license. Do not use, modify,
664             or distribute the Package, if you do not accept this license.
665              
666             If your Modified Version has been derived from a Modified Version made
667             by someone other than you, you are nevertheless required to ensure that
668             your Modified Version complies with the requirements of this license.
669              
670             This license does not grant you the right to use any trademark, service
671             mark, tradename, or logo of the Copyright Holder.
672              
673             This license includes the non-exclusive, worldwide, free-of-charge
674             patent license to make, have made, use, offer to sell, sell, import and
675             otherwise transfer the Package with respect to any patent claims
676             licensable by the Copyright Holder that are necessarily infringed by the
677             Package. If you institute patent litigation (including a cross-claim or
678             counterclaim) against any party alleging that the Package constitutes
679             direct or contributory patent infringement, then this Artistic License
680             to you shall terminate on the date that such litigation is filed.
681              
682             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
683             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
684             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
685             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
686             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
687             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
688             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
689             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
690              
691              
692             =cut
693              
694             1; # End of Firewall::PaloAlto