Entering Obscenely large numbers of items into Fortigate Firewalls with scripting

IT asked me to set up exceptions for over 1700 destinations for internet access for an important project. After verifying that there was minimal risk and compliance issues, I started working diligently until all 1700 addresses were properly entered into the firewall manually . . . And that’s a lie.
I didn’t manually enter a single address but instead did things the easy and repeatable way and hopefully after all of this, you’ll be able to do the same.
What’s nice about this request is that it came with an XML file that had all of the addresses. So each address was in its own XML attribute (IPv4,IPv6,URL). URLs were mixed with wildcard URLs in in the URL XML attributes as such:

<addresses>
<addresslist type=”URL”>
<address>*.adl.windows.com</address>
<address>random.address.com</address>
</addresslist>
<addresslist type=”IPv6″>
<address>2603:1027::/48</address>
<address>2603:1029:100::/48</address>
<address>2603:1037::/48</address>
</addresslist>
</addresses>

So this needed to be converted to a format that the firewall can understand. The firewalls in question here happened to be Fortigate firewalls so they have their own special syntactical language:

*****MAKE THESE RELATED AND SANITIZE********

config firewall address

edit “adobe”
set type wildcard-fqdn
set wildcard-fqdn “*.adobe.com”
next

edit “auth.gfx.ms”
set type fqdn
set fqdn “auth.gfx.ms”
next

edit “Octa17”
set subnet 54.235.64.102 255.255.255.255
next

end

So basically the XML file would need to be read to find the URL, IPv6m and IPv4 attributes, list all elements underneath, then spit out syntax the firewall will understand to get it to add those addresses.

#!/usr/bin/perl
use strict ;
use warnings ;
use XML::Simple ;
use Data::Dumper ;
my $xmlfile = ();
my $i = 1;
open(my $file,’>’,’address_import.conf’);
#####Sanitize these vars
#Set the address naming scheme. Whatever you put here will be
#added as an address name and a number will be appended to it.
my $addrname = “YourAddress_Name_Scheme_here”;
#Set VDOM that these will be added to
my $vdomname = “YourVdomNameHere”;

#configure the vdom
print $file “config vdom\n”;
print $file “edit $vdomname\n”;

#begin editing firewall addresses
print $file “config firewall address\n”;

#This does expect to see and #addresses.xml file to read in. See end notes for formatting reqs.
my $xmlfilelist = XMLin(‘addresses.xml’);
#print Dumper($xmlfilelist);

my ( $url_item ) = grep { $_->{type} eq ‘URL’ } @{$xmlfilelist->{addresslist}};

#print $url_item;
foreach my $address ( @{ $url_item->{ address } } ) {
#print “$address\n”;
if ($address =~ /\*/)
{
# print “asterisk $address\n”;
print $file ” edit \”$addrname$i\”\n”;
print $file ” set type wildcard-fqdn\n”;
print $file ” set wildcard-fqdn \”$address\”\n”;
print $file ” next\n”;
$i++;
}
else
{
# print “$address\n”;
print $file ” edit \”$addrname$i\”\n”;
print $file ” set type fqdn\n”;
print $file ” set fqdn \”$address\”\n”;
print $file ” next\n”;
$i++;
}

}

my ( $ipv4_item ) = grep { $_->{type} eq ‘IPv4’ } @{$xmlfilelist->{addresslist}};

#print $ipv4_item;
foreach my $address ( @{ $ipv4_item->{ address } } ) {
print $file ” edit \”$addrname$i\”\n”;
print $file ” set subnet \”$address\”\n”;
print $file ” next\n”;
$i++;
}

#move on to IPv6 if any
print $file “end\n”;

#need separate stanza for ipv6
#begin editing firewall addresses
print $file “config firewall address6\n”;
my ( $ipv6_item ) = grep { $_->{type} eq ‘IPv6’ } @{$xmlfilelist->{addresslist}};

foreach my $address ( @{ $ipv6_item->{ address } } ) {

print $file ” edit \”$addrname$i\”\n”;
print $file ” set ip6 $address\n”;
print $file ” next\n”;
$i++;
}

print $file “end\n”;

####XML format expected
#<addresses>
# <addresslist type=”[URL|IPv6|IPv4]”>
# <address>2603:1027::/48</address>
# <address>*.api.skype.com</address>
# <address>10.0.0.1/24</address>
# </addresslist>
#</addresses>
#any imported XML must have more than one sub element per element. For instance
#
#this
#<addresslist type=”IPv6″>
# <address>2603:1027::/48</address>
#</addresslist>
#
#errors out for some reason. Could be a problem with XML::Simple which is now
#Depricated
#
#Each element (URL, IPv6 etc) must be contiguous. This script won’t tolerate
#separate elements so all URL elements must be combined.

There was a good amount of fiddling with the XML file that had to be done ahead of time.  For instance, there were multiple duplicate entries (somewhere around 300-400) that were removed.  Also, although this can probably be improved in the script, there were various stanzas (Is that the right word) in the XML that were not consolidated, so IPv4 stanzas could be found intermingling in and around URL and IPv6 stanzas like crazy.  All that needed to be cleaned up ahead of time.  I ran out of time so I didn’t get to that, but I may try to fix it later if there is enough interest.  Also, the Fortigate UI does not like jamming 1700ish address elements into one address group.  I found it’s closer to at least a couple of hundred per-group.  Suggestions welcome!

Leave a Reply

Your email address will not be published. Required fields are marked *