Автоматическое заведение обратных зон на DNS сервере Juniper и Cisco

     Возникла необходимость автоматически заводить обратные зоны на DNS сервере получаемые из резервных копий конфигов  Juniper и Cisco. Для сбора конфигов в данном случае используем rancid. Предупреждая вопросы по поводу не эффективного написания скрипта. Он написан так, а не иначе, по причине чтобы уменьшить количество обновляемых одновременно записей на ДНС сервере. Длительность обновления нескольких сотен хостов суммарно не превышает 10 минут.

#!/usr/bin/perl
#
# Timur Nigamov  2014
#timur.nigamov@gmail.ru
#
use strict;
use warnings;
use File::Temp qw/tempfile/;
use subs qw/msg/;
##############################################
##############################################
##############################################
#
#CONFIGURATION SCRIPT
#
my $domain="in-addr.arpa";
my $rr_domain="jun.domain-internet.net";
my $router_db="/export/home/rancid/var/juniper/router.db";
my $conf_path="/export/home/rancid/var/juniper/configs/";
my $history_file="/var/db/ip2rr_jun.hst";
my $log_file = "/var/log/ip2rr-jun.log";
my $nsupdate="/usr/local/bin/nsupdate ";
#my $nsupdate="/usr/local/bin/nsupdate -d ";
#my $nsupdate="/bin/cat";
my $nsupdate_log="/var/log/nsupdate_jun.log";
my $rndc_key_file="/usr/local/etc/mit.key";
my $rndc_key_name = "mit-key";
my $ttl=86400;
my $send_update_to="dns0.domain.com";
my $verbose="4";
my $log_open="0";
##############################################

#input $filename - config file name
#return @reverse - array with names interface and ip
#
sub conf2data
{
my $data="";
my @iface_unit="";
my $ifunit="";my $hostname="";my $undef="";
my $ifcount="0";
my @ifaces ="";
my $filename="";
$filename=shift;
open (DATA, "<", $filename);
my @conf= <DATA>;
close (DATA);
my $i=0;
my $ifc="0";
my $ifstart="";
foreach (@conf)
{
    if ( m/host-name/)
        {
        (undef,$hostname)=split (' ', $_);
        ($hostname,undef)=split (';',$hostname);
        }
    if ( m/^interfaces /)
        {    
        $ifstart=$i;
        $i++;
        while ($i < $#conf)
        {
        $data=$conf[$i];
            if ($data=~ m/^}/){ $ifc=1;last;}
            $data=~ s/{//;
            $data=~ s/^\s+//;
            $data=~ s/;//g;
            if ($data =~ m/^description.*|.*inactive.*|.*unnumbered-address.*/)
            {next;}
            if ( $data=~ m/xe-|irb |ge-|fxp|et|lo0|^ae/ )
            {
            #print "ifunit=$data\n";
            $data=~ s/;//g;
            $ifunit = $data;
            push (@ifaces,$ifunit);
            ++$ifcount;
            ++$i;
            }
        ++$i;
        }
        }
if ($ifc > 0){last;};
++$i;
}
$data="";
$ifc=$i;
for (my $j=$ifstart; $j<=$ifc;$j++)
{
$data=$conf[$j];
$data=~ s/^\s+//;
$data=~ s/{//;
$data=~ s/\;//g;
    if ($data=~m/^unit|address|^xe-\d+|^irb |^ge-|^et|^fxp|^lo0|^ae/)
    {
#       print "****IF=$data\n";
        $data=~ s/^\s+//;
        $data=~ s/\s+$//;
        $data=~ s/\;//g;
        push (@iface_unit,$data);
    }
}
push(@iface_unit,"end");
#print "*****Interface unit**************\n@iface_unit\n*******end***********\n";
$i="";
my $ii="0";
my $host="";
my $if="";
my $unit_id="";
my $addr="";
my $got_unit="0";
my @reverse="";
my $j="0";
my $end_if="";
foreach $if (@ifaces)
{
$i=0;
$host="";
chomp($if);
$if=~ s/\s+//;
if ($if ne "")
 {
 #print "**IF none zero -> $if\n";
#    foreach $data (@iface_unit)
    for ($j=$ii; $j <=$#iface_unit;$j++)
    {
    $data=$iface_unit[$j];
    if ($data eq "") {next;}
#    print "****Data -> $data\n";
    if ($data=~ m/end/)
        {
        last;}
    if ($data=~ m/xe|irb|et|ge-|fxp|lo0|^ae/)
        {
        if ($i == "1") {$ii=$j; last;}
        if ($data=~m/$if/)    
            {
            $i="1";
#           print "**Found IF  -> $if -> Data $data\n ";
            next;    
            }
        }
    if ($data=~m/unit/)
        {
        #$i=0;
#       print "**Found unit  -> $if -> Data $data\n ";
            (undef,$unit_id)=split(" ",$data);
             next;
        }
    if($data=~m/address/)
        {
        #$i=0;
#       print "**Found address  -> $if -> Data $data\n ";
        (undef,$addr)=split(" ",$data); ($addr,undef)=split("/",$addr);
        next if is_private_ip($addr);#skip private ip address
        if ($unit_id eq ""){ $host=$hostname." ".$if." ".$addr."\n";}
        else {$host=$hostname." ".$if.".".$unit_id." ".$addr."\n";}
#       print "++++$host\n";
        push (@reverse,$host);
        }
 
    }
 }
#++$j;
}
#print "@reverse\n";
return @reverse;
}
####################################################################################
sub ip2dns {

my ($ip, $int, $rtr, $city) = @_;
my @ip_full=();
my $domain="in-addr.arpa";

if ($ip=~/:/) {
# Count additional zero bytes we should insert to ipv6 address
my @ip6array = (split /:/, $ip);
my $add = 8 - $#ip6array;
$domain="ip6.arpa";

# Add leading zeros to partially filled sections
for (my $i=0; $i<=$#ip6array; $i++){
$ip6array[$i] =~ s/^(\S)$/000$1/;
$ip6array[$i] =~ s/^(\S\S)$/00$1/;
$ip6array[$i] =~ s/^(\S\S\S)$/0$1/;

# Build new array containing ipv6 full address
push (@ip_full, split (//, $ip6array[$i]));
# Find :: and insert there proper number of zeros
if (not $ip6array[$i]){
for (my $q=1; $q<=$add; $q++) {
push (@ip_full, 0, 0, 0, 0);
}
}
}
}
else { @ip_full=split /\./, $ip };

# Reverse array containing ipv6 full address and put it to scalar
my $todns = join('.', reverse @ip_full) . ".$domain";
# Print result
return "$todns.\t$rtr-$int.$city.stream-internet.net.";
}
###################################################################################
sub is_private_ip($) {
$_=shift;
my $private=0;
$private=1 if (/^10\./
or /^172\.1[6-9]\./ or /^172\.2[0-9]\./ or /^172\.3[01]\./
or /^192\.168\./
);
return $private;
}
####################################################################################
sub rndc_secret($$) {
# usage: <secret> = rndc_secret(RNDC_KEY_FILE, RNDC_KEY_NAME)
#
my ($key_file, $key_name)=@_;
my $key_found  = 0;
my $key_secret = '';
return 0 if not $key_file;
return 0 if not $key_name;
if (not -f $key_file) {
die "Rndc-key file '$key_file' not found.\n";
}

open FILE, '<', $key_file
or die "Can't open file '$key_file': $!\n";
while (<FILE>) {
if (not $key_found) {
next if not /\bkey\s+"([^"]+)"/;
$key_found=1 if "$key_name" eq "$1";
}
if ($key_found) {
$key_secret=$1 if /\bsecret\s+"([^"]+)"/;
}
last if $key_secret;

}
close FILE or die "Can't close file '$rndc_key_file': $!\n";

die "Error. Section key '$key_name' not found.\n"   if not $key_found;
die "Error. Secret not found (key: '$key_name').\n" if not $key_secret;

return $key_secret;
}
####################################################################################
sub msg($@) {
    my $level = shift;
    return 0 if $level > $verbose;

    my $now = localtime();
    if ((not $log_open) and (-f $log_file)) {
#    if (($log_open) and (-f $log_file)) {
        open LOG, '>>', $log_file
            or die "Can't open file '$log_file': $!\n";
        $log_open = 1;  
    }
    if ($log_open) {
        # print in file
        printf LOG "[%s] %s\n", $now, $_ foreach @_;
    }
    else {
        # print in STDOUT
        printf "[%s] %s\n", $now, $_ foreach @_;
    }      
}
###################################################################################
sub updatedns()
{
my $verbose="4";my $log_open="0";my @rtr_db="";my @addr="";my $city="";
my $router="";my $hostname="";my $ipaddr="";my $data="";my $int="";
my $ptr="";my @changes="";my $hist1="";my %history;
if (open (HISTORY,"<", $history_file))
{  while(<HISTORY>) { chomp; $history{$_} = 0; };
close HISTORY;
};
$data=$_[0]; print "Data--->$data\n"; if ($data eq"") {next;}
if ($data ne "")
{ ($router,$hostname,$int,$ipaddr)=split(" ",$data);
    if ($router         =~ s/(\S+)\.(\S+)$/$1/) { $city = $2; }
    else {$city = "msk";}
        if (($int=~ m/xe|irb|ge-|et|lo0|ae/) and (is_private_ip($ipaddr)!= 1))
            { $int=~s/\/+/-/g; $ptr=ip2dns($ipaddr,$int,$router,$city);  # check in history
                if (exists $history{$ptr}) { msg 5, "$ipaddr found in history file"; }
                else { my ($arpa, $name)=split /\s+/, $ptr; msg 5, "update $arpa";
                 push @changes,"prereq yxrrset $arpa IN PTR\n",
                "update delete $arpa IN PTR\n",
                "send\n","update add $arpa $ttl IN PTR $name\n",
                "send\n";
                };
            $history{$ptr}=1;
            }
}
while (my ($rr, $flag)=each %history) {
if ($flag == 0) {my ($arpa, $name)=split /\s+/, $rr;
msg 5, "delete old record '$arpa' from dns server";
#push @changes, "update delete $arpa IN PTR $name\n\n";
delete $history{$rr};
}
}
msg 3, scalar(@changes)." changes made";
print "****Changes**************\n@changes***************\n";
if (@changes) {
    msg 4, '# start ns-update procedure'; msg 5, "get dns-key '$rndc_key_name' from file '$rndc_key_file'";
    my $rndc_key_secret=rndc_secret($rndc_key_file, $rndc_key_name);
    my ($tmp, $tmpfile)=tempfile('ip2rr-jun.XXXXXX');
    print $tmp "server $send_update_to\n" if $send_update_to;
    print $tmp "key $rndc_key_name $rndc_key_secret\n" if $rndc_key_secret;
    msg 5, 'dump @changes to tmpfile: ' . $tmpfile;
    print $tmp $_ foreach (@changes);
    close $tmp;
    my $cmd = "$nsupdate $tmpfile";
    $cmd .= " >> $nsupdate_log 2>&1" if -w $nsupdate_log;
    msg 5, "system($cmd)";
    system($cmd);
    msg 5, "delete tmpfile: $tmpfile";
    unlink($tmpfile);
    msg 5, "save history to $history_file";
     $hist1=$hist1.$_."\n" foreach (keys %history);
};
return ($hist1);
}
####################################################################################
#main
#
my @rtr_db="";my @addr="";my $city="";my $router="";
my $hostname="";my $ipaddr="";my $data="";my $int="";
my $ptr="";my @changes="";my @history=();my $rez="";
my @zone=""; @addr="";
open (RDB,$router_db) || die "Can't open routers db file";
while (<RDB>)
    {  next if /^#/ or /:down/ or (not /cisco/ and not /juniper/);
    chomp;
    (my $r, undef,undef)=split(/:/);
    push (@rtr_db,$r); }
    close RDB;
foreach $router (@rtr_db)
{
    if ($router ne "")
    {
    @zone=conf2data($conf_path.$router);
        foreach $data (@zone){
        $data = $router." ".$data;
        $rez=&updatedns ($data); ;
        push (@history,$rez);
        }    
    }
}
open HISTORY, ">", $history_file or die "Can't write file '$history_file': $!";
print HISTORY "@history";
close HISTORY or die "Can't save file '$history_file': $!";


Аналогично скрипт для маршрутизаторов cisco

#!/usr/bin/perl
#
# Nigamov Timur 2013
#timur.nigamov@gmail.ru
#
use strict;
use warnings;
use File::Temp qw/tempfile/;
use subs qw/msg/;
##############################################
##############################################
##############################################
#
#CONFIGURATION SCRIPT
my $ranciddir     = "/export/home/rancid/var";
my $domain="in-addr.arpa";
my $rr_domain="stream-internet.net";
my $history_file="/var/db/ip2rr_cisco.hst";
my $log_file = "/var/log/ip2rr-cisco.log";
my $nsupdate="/usr/local/bin/nsupdate ";
#my $nsupdate="/usr/local/bin/nsupdate -d ";
#my $nsupdate="/bin/cat";
my $nsupdate_log="/var/log/nsupdate_cisco.log";
my $rndc_key_file="/usr/local/etc/mtu.key";
my $rndc_key_name = "mit-key";
my $ttl=86400;
my $send_update_to="dns0.domain.com";
my $verbose="4";
my $log_open="0";
##############################################
#input $filename - config file name
#return @reverse - array with names interface and ip
#
sub conf2data
{
my @reverse;
my $section_open =0;my $ip="";
my $data=""; my @iface_unit="";
my $host ="";
my $int=""; my $hostname="";my $undef="";
my $ifcount="0"; my @ifaces ="";my $filename="";
$filename=shift;
open (DATA, "<", $filename);
my @conf= <DATA>;
close (DATA);
my $i=0;
my $ifc="0";
my $ifstart="";
foreach (@conf)
{
    chomp;
    if ( m/^hostname/)
{
(undef,$hostname)=split (' ', $_);
next;
}
    if (!$section_open and /^interface\s+(\S+)\s*/i)
        {# Make short names for interfaces
        $_=lc($1); s/[\/\:\.]/\./g;
        s/tengigabitethernet/te/ or s/HundredGigE/hu/ or s/hundredgige/hu/ or s/TenGigE/te/ or s/gigabitethernet/ge/ or s/GigabitEthernet/ge/ or s/tunnel/tun/
        or s/fastethernet/fa/ or s/ethernet/e/  or s/serial/se/ or s/loopback/lo/ or s/port-channel/po/ or s/bundle-ether/be/ or s/Bundle-Ether/be/
        or s/BE/be/ or s/vlan/vl/;
        $int=$_;  $section_open = 1; msg 5, "open section (\$int: $int). file $filename: $.";
        }
    elsif ($section_open and /\s+ipv6 address (2a02:\S+)/i) {
        $ip=lc($1); $ip=~s/\/\d{1,3}//; # delete mask

        $host=$hostname." ".$int." ".$ip."\n";
push (@reverse,$host);
#        msg 4, "%-20s\t%-30s (%s)", "$ip", "$host", "file $filename: $.";
        }
    elsif ($section_open and /\s+ip address (\d+\.\d+\.\d+\.\d+)/) {
        $ip=$1;  next if is_private_ip($ip); # skip private ip address
$host=$hostname." ".$int." ".$ip."\n";
push (@reverse,$host);
#        msg 4, "%-20s\t%-30s (%s)", "$ip", "$host", "file $filename: $.";
        }
    elsif ($section_open and /\s+ipv4 address (\d+\.\d+\.\d+\.\d+)/) {
        $ip=$1; next if is_private_ip($ip); # skip private ip address
$host=$hostname." ".$int." ".$ip."\n";
push (@reverse,$host);
#        msg 4, "%-20s\t%-30s (%s)", "$ip", "$host", "file $filename: $.";
        }
    elsif ($int and /^\S/) {
        msg 5, "close section (\$int: $int). file $filename: $." if $int;
        $section_open = 0; $int = '';
        }
}
print "################Reverse#######################\n";
print "@reverse\n";
print "#############################################\n";
return @reverse;
}
####################################################################################
sub ip2dns {
my ($ip, $int, $rtr, $city) = @_;
my @ip_full=();
my $domain="in-addr.arpa";

if ($ip=~/:/) {
# Count additional zero bytes we should insert to ipv6 address
my @ip6array = (split /:/, $ip);
my $add = 8 - $#ip6array;
$domain="ip6.arpa";

# Add leading zeros to partially filled sections
for (my $i=0; $i<=$#ip6array; $i++){
$ip6array[$i] =~ s/^(\S)$/000$1/;
$ip6array[$i] =~ s/^(\S\S)$/00$1/;
$ip6array[$i] =~ s/^(\S\S\S)$/0$1/;

# Build new array containing ipv6 full address
push (@ip_full, split (//, $ip6array[$i]));
# Find :: and insert there proper number of zeros
if (not $ip6array[$i]){
for (my $q=1; $q<=$add; $q++) {
push (@ip_full, 0, 0, 0, 0);
}
}
}
}
else { @ip_full=split /\./, $ip };

# Reverse array containing ipv6 full address and put it to scalar
my $todns = join('.', reverse @ip_full) . ".$domain";
# Print result
return "$todns.\t$rtr-$int.$city.stream-internet.net.";
}
###################################################################################
sub is_private_ip($) {
$_=shift;
my $private=0;
$private=1 if (/^10\./
or /^172\.1[6-9]\./ or /^172\.2[0-9]\./ or /^172\.3[01]\./
or /^192\.168\./
);
return $private;
}
####################################################################################
sub rndc_secret($$) {
# usage: <secret> = rndc_secret(RNDC_KEY_FILE, RNDC_KEY_NAME)
#
my ($key_file, $key_name)=@_;
my $key_found  = 0;
my $key_secret = '';
return 0 if not $key_file;
return 0 if not $key_name;
if (not -f $key_file) {
die "Rndc-key file '$key_file' not found.\n";
}

open FILE, '<', $key_file
or die "Can't open file '$key_file': $!\n";
while (<FILE>) {
if (not $key_found) {
next if not /\bkey\s+"([^"]+)"/;
$key_found=1 if "$key_name" eq "$1";
}
if ($key_found) {
$key_secret=$1 if /\bsecret\s+"([^"]+)"/;
}
last if $key_secret;

}
close FILE or die "Can't close file '$rndc_key_file': $!\n";

die "Error. Section key '$key_name' not found.\n"   if not $key_found;
die "Error. Secret not found (key: '$key_name').\n" if not $key_secret;

return $key_secret;
}
####################################################################################
sub msg($@) {
    my $level = shift;
    return 0 if $level > $verbose;

    my $now = localtime();
    if ((not $log_open) and (-f $log_file)) {
#    if (($log_open) and (-f $log_file)) {
        open LOG, '>>', $log_file
            or die "Can't open file '$log_file': $!\n";
        $log_open = 1;    
    }
    if ($log_open) {
        # print in file
        printf LOG "[%s] %s\n", $now, $_ foreach @_;
    }
    else {
        # print in STDOUT
        printf "[%s] %s\n", $now, $_ foreach @_;
    }      
}
###################################################################################
sub updatedns()
{
my $verbose="4";my $log_open="0";my @rtr_db="";my @addr="";my $city="";
my $router="";my $hostname="";my $ipaddr="";my $data="";my $int="";
my $ptr="";my @changes="";my $hist1="";my %history;
if (open (HISTORY,"<", $history_file))
{  while(<HISTORY>) { chomp; $history{$_} = 0; };
close HISTORY;
};
$data=$_[0]; print "Data--->$data\n"; if ($data eq"") {next;}
if ($data ne "")
{ ($router,$hostname,$int,$ipaddr)=split(" ",$data);
    if ($router =~ s/(\S+)\.(\S+)$/$1/) { $city = $2; }
    else {$city = "msk";}  
    if (is_private_ip($ipaddr)!= 1)
    { $int=~s/\/+/-/g; $ptr=ip2dns($ipaddr,$int,$router,$city);  # check in history
    if (exists $history{$ptr}) { msg 5, "$ipaddr found in history file"; }
else { my ($arpa, $name)=split /\s+/, $ptr; msg 5, "update $arpa";
push @changes,"prereq yxrrset $arpa IN PTR\n",
"update delete $arpa IN PTR\n",
"send\n","update add $arpa $ttl IN PTR $name\n",
"send\n";
};
   $history{$ptr}=1;
   }
}
while (my ($rr, $flag)=each %history) {
if ($flag == 0) {my ($arpa, $name)=split /\s+/, $rr;
msg 5, "delete old record '$arpa' from dns server";
#push @changes, "update delete $arpa IN PTR $name\n\n";
delete $history{$rr};
}
}
msg 3, scalar(@changes)." changes made";
print "****Changes**************\n@changes***************\n";
if (@changes) {
    msg 4, '# start ns-update procedure'; msg 5, "get dns-key '$rndc_key_name' from file '$rndc_key_file'";
    my $rndc_key_secret=rndc_secret($rndc_key_file, $rndc_key_name);
    my ($tmp, $tmpfile)=tempfile('ip2rr-cisco.XXXXXX');
    print $tmp "server $send_update_to\n" if $send_update_to;
    print $tmp "key $rndc_key_name $rndc_key_secret\n" if $rndc_key_secret;
    msg 5, 'dump @changes to tmpfile: ' . $tmpfile;
    print $tmp $_ foreach (@changes);
    close $tmp;
    my $cmd = "$nsupdate $tmpfile";
    $cmd .= " >> $nsupdate_log 2>&1" if -w $nsupdate_log;
    msg 5, "system($cmd)";
    system($cmd);
    msg 5, "delete tmpfile: $tmpfile";
    unlink($tmpfile);
    msg 5, "save history to $history_file";
     $hist1=$hist1.$_."\n" foreach (keys %history);
};
return ($hist1);
}
####################################################################################
#main
#
my $rtr_file="";my $r;
my @addr="";my $city="";my $router="";
my $hostname="";my $ipaddr="";my $data="";my $int="";
my $ptr="";my @changes="";my @history=();my $rez="";
my @zone=""; @addr="";my @routers = ();
foreach my $in_filename (glob("$ranciddir/*/router.db"))
{
  open (RDB, $in_filename) or die "can't read $in_filename";
  $in_filename =~ s/\/router\.db//;
    while (<RDB>) {
next if /^#/ or /:down/ or not /cisco/;
chomp;($r, undef, undef) = split (/:/);
$rtr_file = "$in_filename/configs/$r";
push (@routers,$rtr_file);
}
 close RDB;
}
#rint "@routers\n";  
foreach $router (@routers)
{
    if ($router ne "")
    {
    @zone=conf2data($router);
    foreach $data (@zone){
$router=reverse ($router);
($r,undef)=split "/",$router;
$r=reverse ($r);
$router=reverse($router);
        $data = $r." ".$data;
$rez=&updatedns ($data); ;
push (@history,$rez);
}
    }
}
open HISTORY, ">", $history_file or die "Can't write file '$history_file': $!";
print HISTORY "@history";
close HISTORY or die "Can't save file '$history_file': $!";










Комментариев нет:

Отправить комментарий