📑

DynamicDNS

2022/03/16に公開

DynamicDNS

IPアドレスがころころ変わるデバイスに外部からアクセスする場合に便利なのがDynamicDNSです。
そのデバイス自身がDynamicDNSでそのIPアドレスをDNSサーバに通知しておくと、
アクセスしたいデバイスはDNSサーバに問い合わせることでそのIPアドレスを知ることができます。
色々使い道はありますが自宅のサーバを外部に公開するような用途が主でしょう。

さて自力でDynamicDNSを使用するには以下のような準備が必要です。

  • 自由に使えるドメイン(またはサブドメイン)
  • そのドメインの権威DNSサーバの設定
  • デバイスが自身のIPアドレスを知る方法
  • デバイスがDynamicDNSでIPアドレスを権威DNSサーバに通知

権威DNSサーバの設定

まずは権威DNSサーバの設定について。
ここでは既にドメイン"sample.com"を所有していてそのドメインを管理する権威DNSサーバを運用中であるとします。
またDynamicDNSで更新するホスト名は特定のサブドメイン"ddns.sample.com"に属することとし、
更新対象のとあるデバイスのホスト名を"saturn.ddns.sample.com"とすることにします。
サブネット"ddns.sample.com"の権威DNSサーバが運用できるサーバ(Ubuntu 16.04+bind9)は既にあるものとします。

最初に"sample.com"を管理する権威DNSサーバに"ddns.sample.com"の管理を移譲する設定を行ないます。
移譲先は"ddns.sample.com"の権威DNSサーバでそれを"ns.sample.com"とするなら
bind9での標準では"sample.com"を管理する権威DNSサーバの適切なゾーンファイルの適切な位置に

ddns            IN   NS   ns.sample.com.

を追加するようなことになります。
もちろん"ns.sample.com"はDNS引き出来ることが前提です。
もしどこかのホスティングサービスを利用しているならそこの解説に従ってください。

次に"ddns.sample.com"の移譲先である"ns.sample.com"DNSサーバの設定です。
設定ファイル"/etc/bind/named.conf"に

zone "ddns.sample.com" {
        type master;
        file "/etc/bind/db.ddns.sample.com";
        allow-update { key ddns-sample-com.; };
};

を追加します。
またファイル"/etc/bind/db.ddns.sample.com"を作成し内容を以下のようにします。

$ORIGIN .
$TTL 604800
ddns.sample.com	IN SOA	ns.ddns.sample.com. admin.sample.com. (
				1         ; serial
				604800     ; refresh (1 week)
				86400      ; retry (1 day)
				2419200    ; expire (4 weeks)
				604800     ; minimum (1 week)
				)
			NS	ns.sample.com.

次いで

# rndc-confgen -r /dev/urandom -b 256

を実行するとメッセージが出力されるので、

# Use with the following in named.conf, adjusting the allow list as needed:

# End of named.conf

に挟まれた数行を設定ファイル"/etc/bind/named.conf"の先頭に追加します。
その際各行の先頭の'#'は削除してください。
追加するのはだいたい以下のようなものになるでしょう。

key "rndc-key" {
	algorithm hmac-md5;
	secret "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
};

controls {
	inet 127.0.0.1 port 953
		allow { 127.0.0.1; } keys { "rndc-key"; };
};

さらに

# dnssec-keygen -r /dev/urandom -a HMAC-SHA256 -b 128 -n HOST ddns-sample-com

を実行すると"Kddns-sample-com.+123+45678.key"と
"Kddns-sample-com.+123+45678.private"のような2つのファイル
(実際のファイルは数字部分が異なります)が得られるので、拡張子がprivateの方の中身の

Algorithm: 163 (HMAC_SHA256)
Key: xxxxxxxxxxxxxxxxxxxxxx== 

のような行を参考にして設定ファイル"/etc/bind/named.conf"の先の追加に続けて

key "ddns-sample-com." {
        algorithm hmac-sha256;
        secret "xxxxxxxxxxxxxxxxxxxxxx==";
};

のように追加します。その後

# chmod g+w /etc/bind/
# /etc/init.d/bind9 restart

を実行すれば完了です。

IPアドレスを知る方法

自分のIPアドレスを知る方法について。
Linuxが走っているデバイスが自分自身のIPアドレスを知りたければ"ifconfig"コマンドで事足ります。
NIC(ネットワークインターフェイス)が複数あったり、1つのNICに複数のIPアドレスが設定してあったり、
はたまたブリッジしてあったりと複雑なケースは色々思いつきますが、
ごく一般的には

$ ifconfig eth0 | sed -e '/inet /!d' | head -n 1 | awk '{print $2}'

でIPv4アドレスが、

$ ifconfig eth0 | sed -e '/inet6/!d' -e '/fe80/d' | head -n 1 | awk '{print $2}'

でIPv6アドレスが得られます。
ただIPv4の場合だとNAT配下にあるデバイスではこれで知れるのはプライベートアドレスであり、
レジデンシャルルーターにただ1つ設定されたグローバルIPアドレスはわかりません。
またIPv6では一般に複数のIPアドレスが振られどのIPアドレスが送信元に設定されるのかは
経路情報と共に考える必要があったりして面倒。
ということでこれら問題を解決するために外部サーバーの力を借ります。

外部に自由に使えるウェブサーバ(Apache+PHP)を持っているなら、
そこにファイル"ip.php"を以下の内容で作成しウェブブラウザからアクセス可能にします。

<?php
        $ip=$_SERVER["HTTP_X_FORWARDED_FOR"];
        if(strlen($ip)==0)
                $ip=$_SERVER["REMOTE_ADDR"];
        echo $ip;
?>

これのURLが"https://<サーバ名>/ip.php"とすれば、

$ wget -4 -O - https://<サーバ名>/ip.php 2> /dev/null

でアクセス元のIPv4のグローバルIPアドレスが取得できます。
IPv6の場合は

$ wget -6 -O - https://<サーバ名>/ip.php 2> /dev/null

でいけます。
これをシェルスクリプト上で処理してDNSサーバに通知すればDynamicDNSが利用できます。

DynamicDNSでIPアドレスを通知

DNSサーバにIPアドレスを通知する方法について。
前節で取得したIPアドレスをDNSサーバに通知します。
Linux上ではnsupdateコマンドで対話的に行なえます。
実行は以下のような感じになります。

$ nsupdate -k <プライベートキーファイル> 
> server <通知先の権威DNSサーバ>.
> update delete <対象ホスト名(FQDN)>.
> update add <対象ホスト名(FQDN)>. <寿命(秒)> A <IPv4アドレス>
> update add <対象ホスト名(FQDN)>. <寿命(秒)> AAAA <IPv6アドレス>
> send

ここで<プライベートキーファイル>とは前々節で作成した"K*.*.private"っぽいファイルのことです。
前々節の記述を元に具体的に例示するなら、

$ nsupdate -k Kddns-sample-com.+123+45678.private
> server ns.sample.com
> update delete eagle.ddns.sample.com.
> update add eagle.ddns.sample.com. 3600 A xxx.xxx.xxx.xxx
> update add eagle.ddns.sample.com. 3600 AAAA xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
> send

な感じになります。
これをシェルスクリプト内に埋め込む等非対話式で処理するなら

echo -e "server <通知先の権威DNSサーバ>\nupdate delete <対象ホスト名(FQDN)>.\nupdate add <対象ホスト名(FQDN)>. <寿命(秒)> A <IPv4アドレス>\nupdate add <対象ホスト名(FQDN)>. <寿命(秒)> AAAA <IPv6アドレス>\nsend" | nsupdate -k <プライベートキーファイル> 

のようにすれば可能です。
ここで注意すべきは<寿命>。
権威DNSサーバはキャッシュDNSサーバから問い合わせを受けたときに合わせてこの<​寿命>を通知しますが、
キャッシュDNSサーバはこの​<寿命>が尽きるまでこの情報を保持して再度問い合わせることはありません。

Discussion