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;