pixbkup_1.1.txt

#!/usr/bin/perl
#===========================================================#
# Cisco PIX Config Backup Utility Script                    #
# Copyright 2004 Chris Sawall   csawall@hotmail.com         #
# http://tech.stlsawall.com                                 #
#                                                           #
my $written = "08/12/04";                                   #
my $lastupdated = "09/02/04";                               #
my $version = "1.1";                                        #
my $myemail = "sawall\@gmail.com";                          #
#===========================================================#
#===========================================================#
# COPYRIGHT NOTICE                                          #
# Copyright 2004 Chris Sawall  All Rights Reserved.         #
#                                                           #
# Cisco PIX Config Backup Utility Script (pixbkup.pl) may   #
# be used and modified free of charge by anyone so long as  #
# this copyright notice and the comments above remain       #
# intact.  By using this code you agree to indemnify        #
# Chris Sawall from any liability that might arise from     #
# its use.                                                  #
#                                                           #
# Selling the code for this program without prior written   #
# consent is expressly forbidden.  In other words,          #
# please ask first before you try and make money off of my  #
# program.                                                  #
#                                                           #
# Obtain permission before redistributing this software     #
# over the Internet or in any other medium. In all cases    #
# copyright and header must remain intact.                  #
#===========================================================#
#
# This main purpose of this script is to automate
# the process of backing up Cisco PIX configs.  It does so
# over a secure channel by using SSH.
#
#==========================================
# Directions on configuring the PIX
#
# Depending on whether or not you are using some type of
# Radius or TACACS solution, you may or may not need
# to create a user account on the PIX.  If you do, the
# following is the command to do so:
#
# username pixbkup password pixbkup privilege 5
#
# and the PIX config will look like:
# username pixbkup password /O5wMATnjhpdZAY9 encrypted privilege 5
#
# you will need to configure ssh:
# aaa authentication ssh console {RADIUS|TACACS|LOCAL}
#
# ensure that the device running the script can connect
# to the PIX:
# ssh 10.10.10.10 255.255.255.255 inside (or appropriate interface)
#
# tell the PIX to get authorization permissions locally
# aaa authorization command LOCAL
#
# set the privilege level to only allow the user to
# show the running configuration:
# privilege show level 5 command running-config
#
# set the enable password for level 5:
# enable password pixbkup level 5
#
#==========================================
# the Expect Perl module will need to be installed:
#
# [root@yoursystem root]# cpan
#
# cpan shell -- CPAN exploration and modules installation (v1.7601)
# ReadLine support available (try 'install Bundle::CPAN')
#
# cpan> install Expect
#
#==========================================
# Change log
#
# 8/24/04 - added section to look for existing config files and MD5
#           hashes.  if the hashes match, then a new backup config
#           is not stored.
#
# 8/25/04 - added section to see which hosts a config could not be
#           gotten from.  put host into array and attempt again
#           after first pass at all hosts.  if fails a 2nd time,
#           it will send email to admin notifying of failure
#
# 9/02/04 - worked out the bugs of actually getting the above
#           two items to work properly.
#==========================================

use File::stat;
use Expect;
use strict;
$|++;

my $debug = 1;
# set admin email addrs separated by a space
my $fwemail = "FWADMIN\@YOURDOMAIN.COM";
my $sendmailprog = '/usr/sbin/sendmail -t';
my $compareprog = "/usr/bin/cmp";
# set the username and password of the backup acct
my $user = "pixbkup";
my $pass = "pixbkup";
# set this parameter if your ssh client expect you
# to accept connecting to a new ssh host, my testing
# was with fedora and openssh
# 1 to enable; 0 to disable
my $autoaccept = 0;
# automatically delete older config files
my $deleteoldconfigs = 1;
my $keeplogfiletime = 2;
# define what privilege level is defined on the pix
my $enlvl = "5";
# tell script if you will be using a privilege level
# or if you are just going to use the default enable
# password to login (not suggested).
# set to 1 to use defined privilege level.
my $userprivset = 1;
# set "real" enable password if necessary (not suggested)
my $enpass = "PASSWORD";
# define command to display current configuration
# not the "exclude passw".  this is to ensure that the
# real enable password is not stored with the configuration
# i believe the pix version has to be at least 6.3 or higher
my $getcfgcmd = "sh run | exclude passw";
# define the file containing a list of firewalls to connect
# to and backup config.  hosts beginning with a pound sign (#)
# will be ignored.
my $hostfile = "firewall.list";
# define temp log file to store data.  deleted at end of script
my $logfile = "/tmp/xlog.txt";
# set the location to store config files.  be sure to lock
# down access to this folder.
my $configdir = "/logs/pixconfigs";
my $pixbkuplog = "/logs/pixbkup.log";
# choose whether or not to create md5 hashes of configurations
# setting to 1 will create hash
my $createmd5 = 1;
# define binary locations to do md5 hashes on config files
my $opensslexe = "/usr/bin/openssl";
my $md5sumexe = "/usr/bin/md5sum";
# define with binary to execute
my $usemd5prog = "md5sum"; # can be "md5sum" or "openssl"
# define whether or not to review for existing files with matching
# md5 hashes.  if the same, do not get/keep new file.
# set to 1 to enable and 0 to disable
my $removeifsame = 1;
# set various variables
my (@checkhosts, @hosts, $host, $expect, $result, $grabdata, $sumsmatch);
my ($sec,$min,$hour,$mday,$mon,$year,$currenttime,$pixcfgfile,$pixmd5file,$sum,$error);
my ($sumsmatch, @splitsum, $md5only, $numdownhosts, @downhosts, $beenhere, $checkdowns);
my ($origpixcfg, $foundend, $rmdnhost, $delresults, $cmpcfgs);
my $starttag = "---- Start config for $host ----";
my $endtag = "---- Stop config for $host ----";

#=========================================
# set current time and day var
#=========================================
($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
$year+=1900;
$mon+=1;
if($mon < 10) {$mon = 0 . $mon;}
if($mday < 10) {$mday = 0 . $mday;}
if($sec < 10) {$sec = 0 . $sec;}
if($min < 10) {$min = 0 . $min;}
if($hour < 10) {$hour = 0 . $hour;}
$currenttime = "$mon$mday$year.$hour$min$sec";
if($debug) {print "###Show Current Time ###\n"; print "Time => $currenttime\n";}

if (-e $hostfile) {open(FWFILE,"$hostfile") || die "Failed to read config\n";}
if($debug)
 {
 print "Current hostfile is $hostfile\n";
 my $numhosts = `cat $hostfile | wc -l`;
 print "Total number of hosts is $numhosts\n";
 }
$checkdowns = 0;
@hosts = <FWFILE>;
close(FWFILE);
&main;

sub main # --- Begin MAIN subroutine
{
if(!$checkdowns) {@checkhosts = @hosts;}
if($checkdowns) {@checkhosts = @downhosts;}

foreach (@checkhosts)
#while ($host = <FWFILE>)
{
 chomp();
 $host = $_;
 undef $numdownhosts;
 # --- Set out filename for PIX config
 if($debug) {print "\n*****************************\nCurrent host is $host\n";}
 # --- Commented hosts will be ignored
 if($host =~ /^#/){if($debug) {print "Host commented out: $host\n";} next;}
 $pixcfgfile = "$host.$currenttime.cfg";
 $pixmd5file = "$host.$currenttime.md5";
 if($debug) {print "Current PIX CFG file is $host.$currenttime.cfg\n";}
 if($debug) {print "Current PIX MD5 file is $host.$currenttime.md5\n";}
 # --- Start using Expect to connect to PIX
 $expect=Expect->spawn("ssh -l $user $host");
 # --- Don't display data to screen ---
 $expect->log_stdout(0);
 # --- Start log file and truncate any existing log file ---
 $expect->log_file($logfile, "w");
 # --- Log into pix and write config ---
 $result=$expect->expect(5,'assword:');
 if ($result == 1) { print $expect "$pass\n";}
 if (($autoaccept) && ($result == 0))
  {
  if($debug) {print "Auto Accepting SSH Fingerprint from $host\n";}
  print $expect "yes\n";
  $result=$expect->expect(5,'assword:');
  if ($result == 1) { print $expect "$pass\n";}
  }
 else
  {
  $result=$expect->expect(5,'assword:');
  if ($result == 1) { print $expect "$pass\n";}
  }
 $result=$expect->expect(5,'>');
 if($userprivset)
  {
  if($debug) {print "User PRIV Set - Using Level $enlvl\n";}
  if ($result == 1) { print $expect "en $enlvl\n";}
  $result=$expect->expect(1,'assword:');
  if ($result == 1) { print $expect "$pass\n";}
  }
 else
  {
  if($debug) {print "User PRIV *NOT* Set - Using Default Enable Level 15\n";}
  if ($result == 1) { print $expect "en\n";}
  $result=$expect->expect(1,'assword:');
  if ($result == 1) { print $expect "$enpass\n";}
  }
 $result=$expect->expect(1,'#');
 if ($result == 1) { print $expect "pager 0\n";}
 $result=$expect->expect(1,'#');
 $expect->print_log_file("\n$starttag\n");
 if ($result == 1) { print $expect "$getcfgcmd\n";}
 $result=$expect->expect(1,'#');
 if ($result == 1) { print $expect "pager 25\n";}
 $result=$expect->expect(1,'#');
 $expect->print_log_file("\n$endtag\n");
 if ($result == 1) { print $expect "exit\n";}
 $error=$expect->before();
 # --- write current config to file ---
 if (-e "$configdir\/$pixcfgfile")
  {
  unlink("$configdir\/$pixcfgfile");
  unlink("$configdir\/$pixmd5file");
  if($debug) {print "Config file ($configdir/$pixcfgfile) exists, removing...\n";}
  }
 open(PIXCFG, ">>$configdir/$pixcfgfile");
 open(PIXLOG, ">>$pixbkuplog");
 open(FIXFILE, "$logfile");
 $foundend = 0;
 while(<FIXFILE>)
 {
  chomp();
  tr/\r//d;
  if(/$starttag/) {$grabdata = 1; if($debug) {print "Found Start tag for $host\n";}}
  if(/$endtag/) {$grabdata = 0; if($debug) {print "Found End tag for $host\n";}}
  if(/^cryptochecksum/i) {$foundend = 1; if($debug) {print "Found End of Config file for $host - foundend = $foundend - grabdata = $grabdata\n"; print "\tEnd line found is ==> $_\n";}}
  if($grabdata)
  {
   if($_=~/$starttag/ || /$getcfgcmd/ || /^\s+/) {next;}
   if($debug) {if(/^cryptochecksum/i) {print "\tIn Loop - Line still there? ==> $_\n";}}
   print PIXCFG "$_\n";
  }
 }
 close(PIXCFG);
 close(FIXFILE);

if(!$foundend)
 {
 if($debug) {print "Did not get config from $host - Did not find end of config\n";}
 unlink("$configdir\/$pixcfgfile");
 if($beenhere < 2) {push(@downhosts, $host);}
 next;
 }
# --- create md5 hash file if necessary
if($createmd5)
 {
 if($debug) {print "Creating MD5 Hash\n";}
 if($usemd5prog =~ /openssl/)
  {
  if($debug) {print "Using OpenSSL...\n";}
  $sum = `$opensslexe md5 $configdir/$pixcfgfile`;
  }
 if($usemd5prog =~ /md5sum/)
  {
  if($debug) {print "Using MD5sum...\n";}
  $sum = `$md5sumexe $configdir/$pixcfgfile`;
  if($debug) {print "New Sum ==> $sum\n";}
  @splitsum = split ' ', $sum;
  $md5only = $splitsum[0];
  if($debug) {print "Sum only ==> $md5only\n";}
  }
 if($removeifsame)
  {
  &comparemd5s;
  $cmpcfgs = `$compareprog $origpixcfg $configdir/$pixcfgfile`;
  print PIXLOG "$cmpcfgs\n";
  if(($debug) && ($cmpcfgs)) {print "File Compare ==> $cmpcfgs\n";}
  }
 if(($debug) && (!$removeifsame)) {print "Not checking for existing MD5 files...\n";}
 if($sumsmatch)
  {
  if($debug) {print "The MD5 Sum matches existing file.  Moving on...\n";}
  if($beenhere >= 1)
   {
   &shiftout;
   #$rmdnhost = shift(@downhosts);
   #if($debug) {print "\"Shifting\" out downhost $rmdnhost.\n";}
   }
  unlink("$configdir\/$pixcfgfile");
  unlink $logfile;
  if($debug) {print "Unlinked $logfile.\n";}
  next;
  }
 else
  {
  if($debug) {print "Creating new file.\n";}
  print PIXLOG "$currenttime - Got new config for $host\n";
  open(MD5File,">>$configdir/$pixmd5file");
  print MD5File "$sum\n";
  close(MD5File);
  unlink $logfile;
  if($debug) {print "Unlinked $logfile.\n";}
  if($beenhere >= 1) {&shiftout;}
  }
 }
} # --- END MAIN WHILE LOOP OF FWFILE
if($debug) {print "\nTEMP TEMP -> downhosts = @downhosts\n";}
$numdownhosts = @downhosts;
if($debug) {print "TEMP TEMP -> numdownhosts = $numdownhosts\n";}
if($debug) {print "TEMP TEMP -> 1st in downhosts = $downhosts[0]\n";}
} # --- END MAIN SUBROUTINE

if(($numdownhosts >= 1) && ($beenhere < 2))
 {
 if($debug) {print "\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";}
 if($debug) {print "Was unable to connect to: @downhosts\n";}
 if($debug) {print "Going to try one more time.\n";}
 if($debug) {print "!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";}
 $checkdowns = 1;
 $beenhere = 1;
 if($beenhere == 1) {$beenhere ++; &main;} # --- Only try this once
 }

if(($numdownhosts >= 1) && ($beenhere >= 2))
 {
 if($debug) {print "\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";}
 if($debug) {print "numdownhosts = $numdownhosts\n";}
 if($debug) {print "Still unable to connect to: @downhosts\nSending emails to $fwemail\n";}
 if($debug) {print "!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";}
 print PIXLOG "$currenttime - Could not get config from: @downhosts\n";
 open(MAIL,"|$sendmailprog");
# NOTE - the following can not be indented
print MAIL <<END_EMAIL;
From: pixbkup\@ameren.com
To: $fwemail
Subject: Failures on PIX Config Backup

Failed to get the configs from the following hosts:
@downhosts

Current time: $currenttime

END_EMAIL
 close MAIL;
 }

if($deleteoldconfigs)
 {
 if($debug) {print "Auto set to delete log files older $keeplogfiletime day(s)...\n";}
 #$delresults = `find $configdir/* -ctime +$keeplogfiletime  -exec rm -f {} \\; 2> /dev/null`;
 $delresults = `find $configdir/* -ctime +$keeplogfiletime  -exec rm -f {} \\;`;
 print "Deletion results ==> $delresults\n";
 }

sub shiftout
 {
 $numdownhosts = @downhosts;
 for (my $x=0; $x < $numdownhosts; $x++)
  {
  if($host =~ $downhosts[$x])
   {
   delete $downhosts[$x];
   if($debug) {print "\"Shifting\" out downhost $host.\n";}
   }
  }
 }

sub comparemd5s # --- Begin comparemd5s loop
 {
 if(!$host) {if($debug) {print "Host variable is blank! Exiting.\n";} exit;}
 my ($origmd5sum, @md5data, @tmpsplit, @multimd5s, $nummd5s, $newestmd5, $existmd5name, $existmd5file);
 if($debug) {print "Checking hash for $host against existing files.\n";}

 opendir(LDIR,$configdir);
 my @listofmd5s = readdir(LDIR);
 closedir(LDIR);

 foreach(@listofmd5s)
  {
  if($_ =~ /$host\S+\.md5/)
   {
   push(@multimd5s, $_);
   }
  }
 $nummd5s = @multimd5s;
 if($debug) {print "Found $nummd5s file(s) for $host.\n";}
 if($nummd5s >= 1)
  {
  sort(@multimd5s);
  $newestmd5 = pop(@multimd5s);
  }
 if(($newestmd5 =~ /\S+/) || ($nummd5s == 0))
  {
  if($nummd5s != 0)
   {
   $existmd5name = $newestmd5;
   $existmd5file = "$configdir/$newestmd5";
   if($debug) {print "Found an existing MD5 Sum file.\n";}
   if($debug) {print "Existing MD5 file is ==> $existmd5file\n";}
   open(GETMD5, "$existmd5file");
   @md5data = <GETMD5>;
   close(GETMD5);
   if($debug) {print "Current data from file is ==> @md5data\n";}
   @tmpsplit = split (' ', shift(@md5data));
   #$origmd5sum = shift(@tmpsplit);
   $origmd5sum = $tmpsplit[0];
   $origpixcfg = $tmpsplit[1];
   if($debug) {print "Existing MD5 Sum is $origmd5sum\n";}
   }
  if($origmd5sum =~ $md5only)
   {
   if($debug) {print "Sums Match - Setting to true\n";}
   $sumsmatch=1;
   }
  else
   {
   if($debug)
    {
    if($nummd5s == 0)
     {
     print "No existing MD5 Sums\n";
     }
    else
     {
     print "Sums DON'T Match - Setting to false\n";
     }
    }
   $sumsmatch=0;
   }
  }
 else
  {
  if($debug) {print "No existing MD5 file found for $host\n";}
  }
 } # --- End comparemd5s subroutine

if($debug) {print ".........End of Script...........\n";}
close(PIXLOG);
exit;