Автоматическая синхронизация зон между DNS серверами

Возникла однажды нужда в том, чтобы резервный DNS сервер знал о новых или удаленных зонах с основного сервера. Стандартными средствами BIND задачу не решить, поэтому пришлось нарисовать пару скриптов для ее решения. Первый скрипт парсит конфигурационный файл DNS сервера выуживая оттуда доменные имена, и пишет их в файл. Второй забирает этот файл по http (можно и по ftp), проверяет были ли добавлены/удалены домены, и если были изменения, то пишет новый конфигурационный файл и перезапускает BIND. Всвязи с написанием этого скрипта, я нарисовал третий скрипт (аналог второго), который создает отдельный конфигурационный файл для каждой зоны (так же учтите, что он не заставляет DNS сервер перечитывать конфиги).  Далее приведен код скриптов и прилеплен архив с ними.

Первый скрипт:

#!/usr/bin/env perl

use strict;
use warnings;

my $nc_name = "/path/to/named.conf.txt";    # Путь к конфигу днс сервера
my $zl_name = "/path/to/test.txt";          # файл, который я буду забирать
my $write_conf = 0;
my @zones = ();

# Получаем список доменов
# парсим строки вида zone "zone.name"
open(FIN, '<', $nc_name) || die "error: $!\n";
while (<FIN>){
    chomp; $_ = lc;
    if (/zone[ ]+"(([a-z1-9\-]+?\.)+?[a-z1-9\-]+)"/){
        push(@zones, $1);
    }
}
close(FIN);

# Проверяем были ли добавлены/удалены зоны
if (open(FIN, '<', $zl_name)){
    my @old_zones = ();

    while (<FIN>){
        chomp; $_ = lc;
        next unless /([a-z1-9\-]+?\.)+?[a-z1-9\-]+/;
        push(@old_zones, $_);
    }

    if ($#zones != $#old_zones){ $write_conf = 1; }
    else {
        foreach (@zones){
            if (!($_ ~~ @old_zones)){
                $write_conf = 1;
                last;
            }
        }
    }

    close(FIN);
} else {
    $write_conf = 1;
}

# Пишем в файл (кот. второй скрипт будет забирать) найденные зоны
if ($write_conf){
    @zones = sort(@zones);
    open(FOUT, '>', $zl_name) || die "error: $!\n";
    foreach (@zones){ print(FOUT "$_\n"); }
    close(FOUT);
}

exit(0);


Второй скрипт:

#!/usr/bin/env perl

use strict;
use warnings;
use File::Fetch;

# рабочая директория
my $work_dir = '/tmp';
# путь, где хранятся файлы зон
my $szc_dir = '/etc/namedb/slave';
# URL, откуда забирать список
my $uri_zf = 'http://www.example.org/test.txt';

# имя конфигурационного файла
my $config_file = 'dns.conf';
# IP адреса авторитативных DNS серверов
my $master_srv = 'ip; ip;';
# шаблон зоны
my $zone_template = 'zone "{zname}" { type slave; masters { {msrv} }; file "{fname}"; };';
# команда управления DNS сервером
my $rndc_bin = '/usr/sbin/rndc';

my $write_conf = 0;
my @zones = ();
my @deleted_zones = ();

# Качаем список доменов
my $ff = File::Fetch->new(uri => $uri_zf);
my $zl_file = $ff->fetch(to => $work_dir) || die 'error: ' . $ff->error;

open(FIN, '<', $zl_file) || die "error: can't open file $zl_file: $!\n";
while (<FIN>){
    chomp; $_ = lc;
    next unless /([a-z1-9\-]+?\.)+?[a-z1-9\-]+/;
    push(@zones, $_);
}
close(FIN);

unlink($zl_file);

# Определяем - были ли добавлены/удалены домены
if (open(FIN, '<', $config_file)){
    my @old_zones = ();

    while (<FIN>){
        chomp; $_ = lc;
        if (/zone[ ]+"(([a-z1-9\-]+?\.)+?[a-z1-9\-]+)"/){
            push(@old_zones, $1);
        }
    }

    $write_conf = ($#zones != $#old_zones);
    foreach (@old_zones){
        if (!($_ ~~ @zones)){
                $write_conf = 1;
                push(@deleted_zones, "$szc_dir/$_.db");
        }
    }

    close(FIN);
} else {
    $write_conf = 1;
}

# Если были добавлены/удалены домены, то пишем
# конфигурационный файл, заставляем сервер перечитать
# конфиги и удаляем не используемые файлы зон
if ($write_conf){
    my $buf = '';
    my $rndc_args = "$rndc_bin reload > /dev/null 2>&1";

    open(FOUT, '>', $config_file) || die "error: can't open file $config_file: $!\n";
    foreach (@zones){
        $buf = $zone_template;
        $buf =~ s/{zname}/$_/g;
        $buf =~ s/{msrv}/$master_srv/g;
        $buf =~ s/{fname}/$szc_dir\/$_.db/g;
        print(FOUT "$buf\n");
    }
    close(FOUT);

    unlink(@deleted_zones) if ($#deleted_zones >= 0);
    system($rndc_args) == 0 || die("command - $rndc_args failed: $?\n");
}

exit(0);


Третий скрипт (полезен, если вам нужен отдельный конфигурационный файл для каждой зоны):

#!/usr/bin/env perl

use strict;
use warnings;
use File::Fetch;

# рабочая директория
my $work_dir = '/root/bind/tmp';
# путь, где хранятся файлы зон
my $szc_dir = '/etc/namedb/slave';
# путь, куда будет писаться конфиг для зоны
my $bind_incdir = '/etc/namedb/includes';
# URL, откуда забирать список
my $uri_zf = 'http://www.example.org/named.txt';

# IP адреса авторитативных DNS серверов
my $master_srv = 'ip;';
# шаблон зоны
my $zone_template = 'zone "{zname}" {
    type slave;
    masters { {msrv} };
    file "{fname}";
};';


my $write_conf = 0;
my @zones = ();
my @deleted_zones = ();

# Качаем список доменов
my $ff = File::Fetch->new(uri => $uri_zf);
my $zl_file = $ff->fetch(to => $work_dir) || die 'error: ' . $ff->error;

open(FIN, '<', $zl_file) || die "error: can't open file $zl_file: $!\n";
while (<FIN>){
    chomp; $_ = lc;
    next unless /([a-z1-9\-]+?\.)+?[a-z1-9\-]+/;
    push(@zones, $_);
}
close(FIN);

unlink($zl_file);

# Определяем - были ли добавлены/удалены домены
if (open(FIN, '<', "$work_dir/dns_sync.txt")){
    my @old_zones = ();

    while (<FIN>){ chomp; push(@old_zones, $_); }
    close(FIN);

    $write_conf = ($#zones != $#old_zones);
    foreach (@old_zones){
        if (!($_ ~~ @zones)){
                $write_conf = 1;
                push(@deleted_zones, "$bind_incdir/$_.conf");
                push(@deleted_zones, "$szc_dir/$_.db");
        }
    }
} else {
    $write_conf = 1;
}

# Если были добавлены/удалены домены, то пишем
# конфигурационные файлы, удаляем не используемые
# файлы зон и конфиги к ним
if ($write_conf){
    my $buf = '';
    my @error_zones = ();

    open(FOUT_TMP, '>', "$work_dir/dns_sync.txt") || die "error: can't open file $work_dir/dns_sync.txt: $!\n";
    foreach (@zones){
        if (open(FOUT, '>', "$bind_incdir/$_.conf")){
            $buf = $zone_template;
            $buf =~ s/{zname}/$_/g;
            $buf =~ s/{msrv}/$master_srv/g;
            $buf =~ s/{fname}/$szc_dir\/$_.db/g;
            print(FOUT "$buf\n");
            close(FOUT);

            print(FOUT_TMP "$_\n");
        } else {
            push(@error_zones, "Can't open file $_.conf: $!\n");
        }
    }
    close(FOUT_TMP);

    unlink(@deleted_zones) if ($#deleted_zones >= 0);
    print(@error_zones) if ($#error_zones >= 0);
}

exit(0);
Прикрепленные файлы
Яндекс.Метрика