Его задача - реализовать аналог DDNS-updates для зон bind, хранящихся в ldap. Сам bind, насколько мне известно, такого пока не умеет.
Очень полезно для MDS (Mandriva DIrectory Service), если хочется резольвить имена узлов с динамически выданным ip адресом.
Оригинал скрипта взят отсюда: http://bind9-ldap.bayour.com/
В текущем варианте скрипт работоспособен, но хочется сделать его универсальным и опакетить.
Текущие его недостатки:
1) Редактирование переменных в самом скрипте, скрипт так и попортить недолго
Нужно вынести переменные в отдельный конфиг и поместить его например в /etc/dhcp2ldap.conf
Необъявленным в конфиге переменным присваивать значеня по умолчанию.
При отсутствии конфига завершать работу.
Нужно предусмотреть в качестве параметров коммандной строки указание своего пути для конфига.
2) В текущем варианте он тупо читает все записи в dhcpd.leases и оформляет их в ldap в forward и reverse зонах, при чем при обновлении записи он сначала удаляет ее, а потом заново записывает.
Это приводит к удалению и повторному добавлению записей об узлах, адреса которых закреплены в dhcpd по mac-адресу. В MDS к таким записям можно добавлять cname, которые потом пропадут при добавлении.
Нужно будет контролировать кем созданы записи и не трогать записи, созданные из веб-интерфейса MDS.
3) Этот недостаток выходит из второго. Читаются все записи в dhcpd.leases, в том числе и устаревшие, что приводит к тому, что зона в DNS заполняется ненужными устаревшими данными.
Веб-интерфейс MDS в содержимом зоны dhcp выводит только актуальные записи, я разберусь как он фильтрует их, а потом нужно будет реализовать такой же миханизм фильтрации в скрипте.
Наверняка недостатки 2 и 3 устранятся одной общей для них проверкой.
4) В изначальном варианте скрипт обслуживал подсеть с маской /16, я его подправил для работы с маской подсети /24
В идеале маску подсети нужно читать из конфига.
Сам скрипт (работоспособен с dns и dhcp - сервером, настроеным для MDS):
Spoiler
Код: Выделить всё
#!/usr/bin/perl
$CONFIGFILE="/etc/dhcp2ldap.conf";
####################################################################
# Edit These for Your Domain #
####################################################################
# dhcpd.leases file location
# Default:
# $LEASES = "/var/lib/dhcpd/dhcpd.leases"
$LEASES = "/var/lib/dhcpd/dhcpd.leases";
# DC Base
# Default:
# $BASE="dc=localhost,dc=localdomain";
$BASE="dc=localhost,dc=localdomain";
# Domain Name
# Default:
# $DOMAIN = "localhost.localdomain";
$DOMAIN = "localhost.localdomain";
# Netmask
# Valid values is "8", "16" or "24"
# Default:
# $NETMASK = "24"
$NETMASK = "24"
# Reverse zone Name
# Default:
# $REVERSE = "1.168.192.in-addr.arpa";
$REVERSE = "1.168.192.in-addr.arpa";
# Full forward zone base in LDAP
# Default:
# $FORWARD_BASE = ",ou=$DOMAIN,ou=$DOMAIN,ou=dns,$BASE";
$FORWARD_BASE = ",ou=$DOMAIN,ou=$DOMAIN,ou=dns,$BASE";
# Full reverse zone base in LDAP
# Default:
# $REVERSE_BASE = ",ou=$REVERSE,ou=$DOMAIN,ou=dns,$BASE";
$REVERSE_BASE = ",ou=$REVERSE,ou=$DOMAIN,ou=dns,$BASE";
# Login to bind to LDAP
# Default:
# $LOGIN="manager";
$LOGIN="manager";
# Password for $LOGIN
# Default:
# $PASSWORD = "secret";
$PASSWORD = "secret";
# LDAP hostname
# Default:
# $LDAP_HOST = 'localhost';
$LDAP_HOST = 'localhost';
# The description see below
# Default:
# $UPDATE_TIME = 30; # In Seconds
$UPDATE_TIME = 30; # In Seconds
# Set this to positive if you would like dhcp2ldap
# to periodically re-read the entire leases file
# even if the time stamp hasn't changed
#
# It will perform the auto-verify after
# ($AUTO_VERIFY * $UPDATE_TIME) seconds
#
# This is probably a good idea unless your parse times
# are incredibly high
$AUTO_VERIFY = 0;
####################################################################
# Do Not Edit Below This Line #
####################################################################
use IO::Socket;
use Net::LDAP;
$USER = "cn=$LOGIN,$BASE";
print "Initializing LDAP Connection...";
$ldap = Net::LDAP->new($LDAP_HOST) or die "$@";
$ldap->bind($USER, password => $PASSWORD) or die "$!\n";
print "Done\n";
$params = shift;
if($params eq "-d"){
daemonize();
}
if($params eq "-h"){
usage();
exit;
}
while(1){
if(changed($LEASES)){
parse($LEASES);
do_stuff();
}
sleep $UPDATE_TIME;
}
sub changed{
my $file = shift;
my $curstat = (stat($file))[9];
if($AUTO_VERIFY){
$check_count++;
}
if($oldstat != $curstat || (($check_count == $AUTO_VERIFY) && $AUTO_VERIFY)){
$oldstat = $curstat;
$check_count = 0;
print "Timestamp change or AUTO_VERIFY triggered...\n";
return 1;
}
else{
return 0;
}
}
sub daemonize{
chdir '/';
umask 0;
open STDIN, '/dev/null';
open STDOUT, '/dev/null';
open STDERR, '/dev/null';
if(fork()){
exit;
}
else{
setsid;
return;
}
}
sub parse{
my $ip,$hostname;
open IN, shift or die "Could not open leases file: $!\n";
while($_ = <IN>){
if(/^#/){
next;
}
chomp $_;
if(/lease/){
/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/;
$ip = $&;
while(!(/\}/)){
$_ = <IN>;
if(/client-hostname/){
/\".*\"/;
$_ = $&;
/\w+\S*\w+/;
$_ = $&;
$hostname = lc;
$hosts{$hostname} = $ip;
}
}
}
$line = "";
};
close IN;
}
sub do_stuff{
my $readd, $host, $ip, $hostname;
while(($host,$ip) = each(%hosts)){
$hostname = $host . "." . $DOMAIN . ".";
$lookup = "";
eval{$lookup = inet_ntoa((gethostbyname("$host.$DOMAIN"))[4]); };
($first,$second,$third,$fourth) = split(/\./,$ip);
if($lookup eq $ip){
next;
}
elsif($lookup ne $ip){
print "Removing inaccurate records for $host...\n";
$result = $ldap->delete("relativeDomainName=$host" . $FORWARD_BASE);
$result->code && warn "failed to remove entry: ", $result->error ;
$result = $ldap->delete("relativeDomainName=$fourth" . $REVERSE_BASE);
$result->code && warn "failed to remove entry: ", $result->error ;
$readd = 1;
}
if(!$lookup || $readd){
print "Adding entry for $host at $ip...\n";
$result = $ldap->add( "relativeDomainName=$host" . $FORWARD_BASE,
attr => [
'relativeDomainName'=> $host,
'objectClass'=> ['top','dNSZone'],
'dNSTTL' => '7200',
'zoneName' => $DOMAIN,
'aRecord' => $ip
]
);
$result->code && warn "failed to add entry: ", $result->error ;
$result = $ldap->delete("relativeDomainName=$fourth" . $REVERSE_BASE);
$result->code && warn "failed to remove entry: ", $result->error ;
$result = $ldap->add( "relativeDomainName=$fourth" . $REVERSE_BASE,
attr => [
'relativeDomainName'=> "$fourth",
'objectClass'=> ['top','dNSZone'],
'dNSTTL' => '7200',
'zoneName' => $REVERSE,
'pTRRecord' => $hostname
]
);
$result->code && warn "failed to add entry: ", $result->error ;
$readd = 0;
}
}
}
sub usage{
print <<EOF;
dhcp2ldapd v1.1: Dynamic DNS Updates for the Bind9 LDAP backend
Copyright 2005 Travis Groth <travis\@netfoo.org> under the GNU GPL
Usage:
dhcp2ldapd [-d | -h]
-d runs dhcp2ldap in daemon mode
-h displays this help message
-c <path-to-configfile>
Please edit the config variables before running!
EOF
}
#############################################################################
#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#############################################################################Описание сервиса для управления скриптом:
Spoiler
Код: Выделить всё
[Unit]
Description=dhcp2ldap
After=dhcpd.service
After=named.service
After=ldap.service
[Service]
EnvironmentFile=-/etc/dhcp2ldap.conf
PIDFile=/var/run/dhcp2ldap.pid
ExecStart=/usr/sbin/dhcp2ldap
[Install]
WantedBy=multi-user.targetСоответственно скрипт тогда должен лежать в /usr/sbin, а описание сервиса в /usr/lib/systemd/system