LoginSignup
9
12

More than 5 years have passed since last update.

Linux上に自前DDNSのクライアントを構築する

Last updated at Posted at 2017-08-22

はじめに

  • そのまま使えるコードはありません
    • 実際の環境にばらつきがあると思われるため…
  • 方式がいろいろ考えられるため、ほんの一例
  • 異常系や例外は未考慮…

今更DDNS

DDNSサービスはいろいろあるが、自分のルーターにはDDNSサービスがバンドルされていない。
DDNSを自前でやるにも、方式がいろいろありそう。
どうやるのが良いのだろう。

前提

  • サーバーは、BIND9(RFC2136に対応していれば、何でもよさそう)
  • クライアントは、Ubuntu 16.04(systemdが動けば、何でもよさそう)
  • ルーターは、SEIL/x86(IPアドレスを取得する方式によっては、何でもよさそう)
  • ホスト名をつける対象は、PPPoE接続のグローバルIPv4アドレス

コンセプト

  • なるべくOSやルーターに依存しない、汎用的な方法を探る
  • なるべくDDNS専用のサーバープロセスなどを立てない
  • つまり、手抜き

DDNSクライアント

DDNSクライアントの機能は、ざっくりこんな感じと想像。

  1. 外部のグローバルIPアドレスを得る
  2. そのIPアドレスをDNSサーバーに送る
  3. DNSレコードを書き換える

このうち、2.と3.は基本的にnsupdateでできるはず。
問題は1.。ルーターがSEIL/x86なので、ルーターにバンドルされたDDNSサービスが無い。
DDNSサービスに応じたクライアントはいろいろあるが、サービスにある程度依存していると思われる1
今回はサーバーがただのBIND9なので、それに応じたクライアントを考えてみる。

主な凡例

  • 203.0.113.1: 外部のグローバルIPアドレス
  • 192.168.0.1: ルーターのプライベートIPアドレス
  • hoge.dip.example.org: 外部のグローバルIPアドレスに対応したホスト名
  • 203.0.113.254: DNSサーバーアドレス

外部のグローバルIPアドレスを得る方法

ルーターから取得する

ルーターに定期的にログインする

  • メリット: ルーターはPPPoEで得たIPアドレスを確実に知っている
  • デメリット: ポーリングのために定期的にログインする(そしてlogも荒れる)

SEILなら、show status ppp pppoe0で表示できるのは知っていた。
SNMP非対応のRT57iを使っていた時にお世話になった、telnetで得た情報をMRTG化する記事を思い出した。
SEILにsshでコマンド送れるのかな…と思って実行したら、できてしまった。

$ ssh -i ~/.ssh/gw.local user@192.168.0.1 'show status ppp pppoe0'
7ffeffff: Interface: pppoe0
7ffeffff:   LCP state: opened
7ffeffff:   IPCP state: opened
7ffeffff:   IPv6CP state: initial
7ffeffff:   BCP state: initial
7ffeffff:   LCP negotiated options:
7ffeffff:   none
7ffeffff:   IPCP negotiated options:
7ffeffff:   address 203.0.113.1
7ffeffff:   primary dns address 198.51.100.1
7ffeffff:   secondary dns address 198.51.100.2
7ffeffff:   IPv6CP negotiated options:
7ffeffff:   none
7ffeffff:   BCP negotiated options:
7ffeffff:   none
7ffeffff:   keepalive: 30 seconds interval
00000000: success
7ffe0000: exit(0).
$ ssh -i ~/.ssh/gw.local user@192.168.0.1 'show status ppp pppoe0' | sed -nr 's/^.*\s\s+address\s(.*)\s*$/\1/p'
203.0.113.1

sshの鍵をパスフレーズ無しで作ると、シェルスクリプトからも情報が取得できそうだ。sedの正規表現は適当。
ただ、色々謎がある。

  • 7ffeffff: って?
  • show logerror telnet telnetd: sppp__ioctl:40: socket: Operation not permittedが残る

同じことをRTX1200にやると、Received disconnect from 192.168.0.1 port 22:2: Channel request 'exec' is not supportedというエラー。VyOSではInvalid command: [show]というエラー。
sshでなくtelnet+expectならうまく行く可能性もあるが、どちらにせよ無理やり感が残る。

SNMPで取得する

  • メリット: ルーターの情報を取得する一般的な方法
  • デメリット: 自分の知識不足

RT57iでは、SNMPが使えないのでtelnetで情報を得ていたのだった。
SEILならSNMPが使える。
そこで、snmpwalk。

$ sudo apt install snmp
:
$ snmpwalk -v 2c -c HogeHoge 192.168.0.1 | grep 203.0.113.1
:
iso.3.6.1.2.1.4.21.1.7.0.0.0.0 = IpAddress: 203.0.113.1
:
$ snmpget -v 2c -c HogeHoge 192.168.0.1 iso.3.6.1.2.1.4.21.1.7.0.0.0.0 | sed -nr 's/.*IpAddress: (.*)\s*$/\1/p'
203.0.113.1

アドレスが取れる。トラップを使えば割り込みにできるかもしれない。
ただ、ここで取得できた情報は、何を示しているのか。こちらを見ながらMIBを入れてみた後、

$ snmptranslate iso.3.6.1.2.1.4.21.1.7.0.0.0.0
:
RFC1213-MIB::ipRouteNextHop.0.0.0.0

すると、ipRouteNextHopとのこと2。インターフェースのアドレスというわけではなさそうだ。
PPPoEが接続できなかった場合は、どうなるのか…3

外部サーバーから取得する

  • メリット: 簡単そう
  • デメリット: 外部のサーバーに依存することになる

外側のグローバルIPアドレスは、インターネットにあるwebサーバーなどにアクセスすると、ログに残る。
ならば、webサーバーに教えてもらえばよいのでは。

こちらの記事によると、こういったサービスがいくつかあるようだ。
試しにifconfig.coを使ってみた

$ curl ifconfig.co
203.0.113.1

外部のサービスに依存したくない場合、自分のwebサーバーにアドレスを返すページを置いておいて、curlで取得する手もある。
PHPでは、<?php echo $_SERVER["REMOTE_ADDR"]; ?>などで、返すことができるようだ。
ただ、この方法でも、webサーバーは必要となる。
とりあえず、この方式にするかな。

IPアドレスを送ってDNSレコードを書き換える

nsupdateで更新するにしても、外部のグローバルIPアドレスを取得した後、多少ロジックが必要だ。
アドレスの変化をポーリングする場合、例えば

  1. 今の外部のグローバルIPアドレスを取得する
  2. 今までのIPアドレスを取得する
  3. 両者が異なっていたら、nsupdateする

といったことを定期的に動かす必要がある。
定期的に動かすには、サービスにする必要がある。手抜きしたい…。

cron

最初に思った。もちろん使える。

systemd

systemdでも、サービスを簡単に作れるのでは。
探すと、こちらに詳しい記事が。
--userで、ユーザーモードで動かせて、enableもできる。
起動n分後に実行などもできそうなので、SEIL/x86の仮想マシンが起動して、PPPoEでアドレスを取得完了後、といったタイミングも狙えそう。
これならシェルスクリプトを定期実行できそうなので、クライアント全体はシェルスクリプトでで実現しようかな。

シェルスクリプトからのnsupdate

検索して出てくるnsupdateの例は、インタラクティブモードで、プロンプトからコマンドを入力しているものが多い。
nsupdateのマニュアルを見ると、インタラクティブモードでなく、標準入力やファイルでもコマンドを渡せる模様。
コマンドのテンプレートを用意しておき、アドレスをsedで置き換えて標準入力から渡してみる。

このマニュアルによると、アドレスの変更という操作は存在せず、一旦deleteしてからaddする必要がある模様。

設置ファイル

~/.config/systemd/user/dip.service
[Unit]
Description=Dynamic IP update service

[Service]
Type=simple
ExecStart=/bin/sh /home/hogehoge/bin/dip.sh

[Install]
WantedBy=default.target
~/.config/systemd/user/dip.timer
[Unit]
Description=Dynamic IP update service

[Timer]
OnBootSec=3min
OnUnitActiveSec=30min
Unit=dip.service

[Install]
WantedBy=timers.target
/home/hogehoge/bin/dip.sh
#!/bin/sh
#PPP_IP=`ssh -i /home/hogehoge/.ssh/192.168.0.1 user@192.168.0.1 'show status ppp pppoe0' | sed -nr 's/^.*\s\s+address\s(.*)\s*$/\1/p'`
#PPP_IP=`snmpget -v 2c -c HogeHoge 192.168.0.1 iso.3.6.1.2.1.4.21.1.7.0.0.0.0 | sed -nr 's/.*IpAddress: (.*)\s*$/\1/p'`
#PPP_IP=`curl -s ifconfig.co`
PPP_IP=`curl -s http://www.example.org/~hogehoge/remote-addr.php`
DNS_IP=`dig @203.0.113.254 hoge.dip.example.org A | sed -nr 's/^hoge\.dip\.example\.org.*IN\tA\t(.*)\s*$/\1/p'`
if [ -n "$PPP_IP" -a \( -z "$DNS_IP" -o "$PPP_IP" != "$DNS_IP" \) ]; then
    cat /home/hogehoge/bin/nsupdate.txt | sed "s/NEW_IP/$PPP_IP/" | nsupdate -k /home/hogehoge/.ssh/Khoge.dip.example.org.+xxx+yyyyy.key
fi
/home/hogehoge/bin/nsupdate.txt
server 203.0.113.254
update delete hoge.dip.example.org A
update add hoge.dip.example.org 600 A NEW_IP
send
remote-addr.php
<?php echo $_SERVER["REMOTE_ADDR"]; ?>

サービス開始

# ファイル読み込み
$ systemctl --user daemon-reload
# ファイル読み込み状況確認
$ systemctl --user list-unit-files
# 実行
$ systemctl --user start dip.service
$ systemctl --user start dip.timer
# 実行状況確認
$ systemctl --user status dip.service
$ systemctl --user status dip.service
# 有効化
$ systemctl --user enable dip.service
$ systemctl --user enable dip.timer

感想

結局ごちゃごちゃしてしまった…
GnuDIPとか入れたほうがよかったかしら…
でも色々勉強になった。

宿題他

  • 旧IPアドレス取得は、毎回DNS検索という手抜き
  • PPPoEが接続できない間は、何が起こるんでしょうね…(未確認)
  • shの条件式があやしい
  • sed -rってMacOSでは使えないのね…

  1. 各サービス向けに作られたクライアントの中には、汎用的に使えるものもあり、それらを使うという方向もあるが、今回は勉強がてらクライアントも自前の方向で。 

  2. 蛇足だが、ipRouteNextHopで検索すると、snmptranslate的なことをwebでできるところがいろいろ見つかった。 

  3. 自宅はOSPFのマルチホーム環境だが、別の出口のアドレスが取得された。 

9
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12