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

Опубликовано nekit - вс, 12/02/2012 - 13:12
Возникла однажды нужда в том, чтобы резервный DNS сервер знал о новых или удаленных зонах с основного сервера. Стандартными средствами BIND задачу не решить, поэтому пришлось нарисовать пару скриптов для ее решения. Первый скрипт парсит конфигурационный файл DNS сервера выуживая оттуда доменные имена, и пишет их в файл. Второй забирает этот файл по http (можно и по ftp), проверяет были ли добавлены/удалены домены, и если были изменения, то пишет новый конфигурационный файл и перезапускает BIND. Всвязи с написанием этого скрипта, я нарисовал третий скрипт (аналог второго), который создает отдельный конфигурационный файл для каждой зоны (так же учтите, что он не заставляет DNS сервер перечитывать конфиги). Далее приведен код скриптов и прилеплен архив с ними. Первый скрипт:
  1. #!/usr/bin/env perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. my $nc_name = "/path/to/named.conf.txt";    # Путь к конфигу днс сервера
  7. my $zl_name = "/path/to/test.txt";          # файл, который я буду забирать
  8. my $write_conf = 0;
  9. my @zones = ();
  10.  
  11. # Получаем список доменов
  12. # парсим строки вида zone "zone.name"
  13. open(FIN, '<', $nc_name) || die "error: $!\n";
  14. while (<FIN>){
  15.     chomp; $_ = lc;
  16.     if (/zone[ ]+"(([a-z1-9\-]+?\.)+?[a-z1-9\-]+)"/){
  17.         push(@zones, $1);
  18.     }
  19. }
  20. close(FIN);
  21.  
  22. # Проверяем были ли добавлены/удалены зоны
  23. if (open(FIN, '<', $zl_name)){
  24.     my @old_zones = ();
  25.  
  26.     while (<FIN>){
  27.         chomp; $_ = lc;
  28.         next unless /([a-z1-9\-]+?\.)+?[a-z1-9\-]+/;
  29.         push(@old_zones, $_);
  30.     }
  31.  
  32.     if ($#zones != $#old_zones){ $write_conf = 1; }
  33.     else {
  34.         foreach (@zones){
  35.             if (!($_ ~~ @old_zones)){
  36.                 $write_conf = 1;
  37.                 last;
  38.             }
  39.         }
  40.     }
  41.  
  42.     close(FIN);
  43. } else {
  44.     $write_conf = 1;
  45. }
  46.  
  47. # Пишем в файл (кот. второй скрипт будет забирать) найденные зоны
  48. if ($write_conf){
  49.     @zones = sort(@zones);
  50.     open(FOUT, '>', $zl_name) || die "error: $!\n";
  51.     foreach (@zones){ print(FOUT "$_\n"); }
  52.     close(FOUT);
  53. }
  54.  
  55. exit(0);
Второй скрипт:
  1. #!/usr/bin/env perl
  2.  
  3. use strict;
  4. use warnings;
  5. use File::Fetch;
  6.  
  7. # рабочая директория
  8. my $work_dir = '/tmp';
  9. # путь, где хранятся файлы зон
  10. my $szc_dir = '/etc/namedb/slave';
  11. # URL, откуда забирать список
  12. my $uri_zf = 'http://www.example.org/test.txt';
  13.  
  14. # имя конфигурационного файла
  15. my $config_file = 'dns.conf';
  16. # IP адреса авторитативных DNS серверов
  17. my $master_srv = 'ip; ip;';
  18. # шаблон зоны
  19. my $zone_template = 'zone "{zname}" { type slave; masters { {msrv} }; file "{fname}"; };';
  20. # команда управления DNS сервером
  21. my $rndc_bin = '/usr/sbin/rndc';
  22.  
  23. my $write_conf = 0;
  24. my @zones = ();
  25. my @deleted_zones = ();
  26.  
  27. # Качаем список доменов
  28. my $ff = File::Fetch->new(uri => $uri_zf);
  29. my $zl_file = $ff->fetch(to => $work_dir) || die 'error: ' . $ff->error;
  30.  
  31. open(FIN, '<', $zl_file) || die "error: can't open file $zl_file: $!\n";
  32. while (<FIN>){
  33.     chomp; $_ = lc;
  34.     next unless /([a-z1-9\-]+?\.)+?[a-z1-9\-]+/;
  35.     push(@zones, $_);
  36. }
  37. close(FIN);
  38.  
  39. unlink($zl_file);
  40.  
  41. # Определяем - были ли добавлены/удалены домены
  42. if (open(FIN, '<', $config_file)){
  43.     my @old_zones = ();
  44.  
  45.     while (<FIN>){
  46.         chomp; $_ = lc;
  47.         if (/zone[ ]+"(([a-z1-9\-]+?\.)+?[a-z1-9\-]+)"/){
  48.             push(@old_zones, $1);
  49.         }
  50.     }
  51.  
  52.     $write_conf = ($#zones != $#old_zones);
  53.     foreach (@old_zones){
  54.         if (!($_ ~~ @zones)){
  55.                 $write_conf = 1;
  56.                 push(@deleted_zones, "$szc_dir/$_.db");
  57.         }
  58.     }
  59.  
  60.     close(FIN);
  61. } else {
  62.     $write_conf = 1;
  63. }
  64.  
  65. # Если были добавлены/удалены домены, то пишем
  66. # конфигурационный файл, заставляем сервер перечитать
  67. # конфиги и удаляем не используемые файлы зон
  68. if ($write_conf){
  69.     my $buf = '';
  70.     my $rndc_args = "$rndc_bin reload > /dev/null 2>&1";
  71.  
  72.     open(FOUT, '>', $config_file) || die "error: can't open file $config_file: $!\n";
  73.     foreach (@zones){
  74.         $buf = $zone_template;
  75.         $buf =~ s/{zname}/$_/g;
  76.         $buf =~ s/{msrv}/$master_srv/g;
  77.         $buf =~ s/{fname}/$szc_dir\/$_.db/g;
  78.         print(FOUT "$buf\n");
  79.     }
  80.     close(FOUT);
  81.  
  82.     unlink(@deleted_zones) if ($#deleted_zones >= 0);
  83.     system($rndc_args) == 0 || die("command - $rndc_args failed: $?\n");
  84. }
  85.  
  86. exit(0);
Третий скрипт (полезен, если вам нужен отдельный конфигурационный файл для каждой зоны):
  1. #!/usr/bin/env perl
  2.  
  3. use strict;
  4. use warnings;
  5. use File::Fetch;
  6.  
  7. # рабочая директория
  8. my $work_dir = '/root/bind/tmp';
  9. # путь, где хранятся файлы зон
  10. my $szc_dir = '/etc/namedb/slave';
  11. # путь, куда будет писаться конфиг для зоны
  12. my $bind_incdir = '/etc/namedb/includes';
  13. # URL, откуда забирать список
  14. my $uri_zf = 'http://www.example.org/named.txt';
  15.  
  16. # IP адреса авторитативных DNS серверов
  17. my $master_srv = 'ip;';
  18. # шаблон зоны
  19. my $zone_template = 'zone "{zname}" {
  20.     type slave;
  21.     masters { {msrv} };
  22.     file "{fname}";
  23. };';
  24.  
  25.  
  26. my $write_conf = 0;
  27. my @zones = ();
  28. my @deleted_zones = ();
  29.  
  30. # Качаем список доменов
  31. my $ff = File::Fetch->new(uri => $uri_zf);
  32. my $zl_file = $ff->fetch(to => $work_dir) || die 'error: ' . $ff->error;
  33.  
  34. open(FIN, '<', $zl_file) || die "error: can't open file $zl_file: $!\n";
  35. while (<FIN>){
  36.     chomp; $_ = lc;
  37.     next unless /([a-z1-9\-]+?\.)+?[a-z1-9\-]+/;
  38.     push(@zones, $_);
  39. }
  40. close(FIN);
  41.  
  42. unlink($zl_file);
  43.  
  44. # Определяем - были ли добавлены/удалены домены
  45. if (open(FIN, '<', "$work_dir/dns_sync.txt")){
  46.     my @old_zones = ();
  47.  
  48.     while (<FIN>){ chomp; push(@old_zones, $_); }
  49.     close(FIN);
  50.  
  51.     $write_conf = ($#zones != $#old_zones);
  52.     foreach (@old_zones){
  53.         if (!($_ ~~ @zones)){
  54.                 $write_conf = 1;
  55.                 push(@deleted_zones, "$bind_incdir/$_.conf");
  56.                 push(@deleted_zones, "$szc_dir/$_.db");
  57.         }
  58.     }
  59. } else {
  60.     $write_conf = 1;
  61. }
  62.  
  63. # Если были добавлены/удалены домены, то пишем
  64. # конфигурационные файлы, удаляем не используемые
  65. # файлы зон и конфиги к ним
  66. if ($write_conf){
  67.     my $buf = '';
  68.     my @error_zones = ();
  69.  
  70.     open(FOUT_TMP, '>', "$work_dir/dns_sync.txt") || die "error: can't open file $work_dir/dns_sync.txt: $!\n";
  71.     foreach (@zones){
  72.         if (open(FOUT, '>', "$bind_incdir/$_.conf")){
  73.             $buf = $zone_template;
  74.             $buf =~ s/{zname}/$_/g;
  75.             $buf =~ s/{msrv}/$master_srv/g;
  76.             $buf =~ s/{fname}/$szc_dir\/$_.db/g;
  77.             print(FOUT "$buf\n");
  78.             close(FOUT);
  79.  
  80.             print(FOUT_TMP "$_\n");
  81.         } else {
  82.             push(@error_zones, "Can't open file $_.conf: $!\n");
  83.         }
  84.     }
  85.     close(FOUT_TMP);
  86.  
  87.     unlink(@deleted_zones) if ($#deleted_zones >= 0);
  88.     print(@error_zones) if ($#error_zones >= 0);
  89. }
  90.  
  91. exit(0);
Прикрепленные файлы