Если по каким-то причинам вы не можете или не хотите использовать один из публичных Dynamic DNS сервисов и у вас есть свой домен, то данная заметка может оказаться вам полезной.
Будем считать, что у нас уже есть настроенный DNS-сервер Bind, который обслуживает зону example.com и веб-сервер с поддержкой PHP, который будет использоваться для обновления 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