dynamic static dns

Сам себе DynDNS


Если по каким-то причинам вы не можете или не хотите использовать один из публичных Dynamic сервисов и у вас есть свой домен, то данная заметка может оказаться вам полезной.

Будем считать, что у нас уже есть настроенный DNS-сервер , который обслуживает зону example.com и веб-сервер с поддержкой , который будет использоваться для обновления A-записей в DNS.

Прежде всего необходимо создать ключ для динамического обновления dns-записей.

 
dnssec-keygen -a HMAC-MD5 -b 512 -r /dev/urandom -n USER dyndns
Kdyndns.+157+50370
ll Kdyndns.+157+50370.*
-rw------- 1 root root 113 May  3 17:17 Kdyndns.+157+50370.key
-rw------- 1 root root 229 May  3 17:17 Kdyndns.+157+50370.private

В созданном файле Kdyndns.+157+50370.private нас интересует только одна строка, которая начинается с Key:

Private-key-format: v1.3
Algorithm: 157 (HMAC_MD5)
Key: PaLaoO01EvheJqQ5AfOJ9yZoumGATKhFeMdcfWFzA+VGCzURzBfhee0X4Ad54Tss7BANs562LOTQpCM52j6YGQ==
Bits: AAA=
Created: 20150503131756
Publish: 20150503131756
Activate: 20150503131756

Ключ нужно добавить в файл конфигурации bind, например /etc/named.conf, и разрешить для него обновления целевой зоны.

key "dyndns" {
algorithm hmac-md5;
secret "PaLaoO01EvheJqQ5AfOJ9yZoumGATKhFeMdcfWFzA+VGCzURzBfhee0X4Ad54Tss7BANs562LOTQpCM52j6YGQ==";
};
zone "example.com" {
type master;
file "/var/named/example.com";
allow-update { key "dyndns"; };
};

Так же понадобится секретный хеш для обновления DNS посредством http-запроса, который можно сгенерировать, например с помощью openssl так:

openssl rand -hex 24
701cc7069159e9547def51b9f9c423a5660730ee3a44cdf6

Мой пример php-скрипта для обновления DNS:

 
// configuration of users and domains
$udns = array(
'user' => array(
'home' => '701cc7069159e9547def51b9f9c423a5660730ee3a44cdf6'
)
);
// main domain for dynamic DNS
$dyndns = "example.com";
$dnskey = "PaLaoO01EvheJqQ5AfOJ9yZoumGATKhFeMdcfWFzA+VGCzURzBfhee0X4Ad54Tss7BANs562LOTQpCM52j6YGQ==";
// short sanity check for given IP
function checkip($ip) {
$iptupel = explode(".", $ip);
foreach ($iptupel as $value) {
if ($value < 0 || $value > 255)
return false;
}
return true;
}
// check request data
function checkreq(&$value, $default = false) {
return isset($value) ? $value : $default;
}
// retrieve IP
$ip = $_SERVER['REMOTE_ADDR'];
// check for given user
$user = checkreq($_REQUEST['u']);
// check for given domain
$subdomain = checkreq($_REQUEST['d']);
// check for given secret
$secret = checkreq($_REQUEST['s']);
// check for needed variables
if($subdomain && $ip && $user && $secret) {
// short sanity check for given IP
if(preg_match("/^(\d{1,3}\.){3}\d{1,3}$/", $ip) && checkip($ip) &&
$ip != "0.0.0.0" && $ip != "255.255.255.255") {
// short sanity check for given domain
if(isset($udns[$user][$subdomain]) && $udns[$user][$subdomain] == $secret) {
// check for some ip
if(gethostbyname($subdomain.".".$dyndns) == $ip) {
echo "IP address $ip not changing for domain $subdomain";
}
else {
// shell escape all values
$subdomain = escapeshellcmd($subdomain);
$ip = escapeshellcmd($ip);
// prepare command
$data = '<<EOF
zone $dyndns
update delete $subdomain.$dyndns A
update add $subdomain.$dyndns 60 A $ip
send
EOF';
// run DNS update
exec("/usr/bin/nsupdate -y dyndns:$dnskey $data", $cmdout, $ret);
// check whether DNS update was successful
if ($ret != 0) {
echo "Changing DNS for $subdomain.$dyndns to $ip failed with code $ret";
}
}
}
else {
echo "Domain $subdomain for $user from $ip with $subdomain was wrong";
}
}
}
else {
echo "DDNS change for $user from $ip with $subdomain failed because of missing values";
}

Запрос скрипта со стороны клиента будет выглядеть так (безопаснее использовать https):

wget https://dyn.rascal.su/?u=user&d=home&s=701cc7069159e9547def51b9f9c423a5660730ee3a44cdf6