2015年4月4日

お名前.comのDNSレコードをRaspberry PiからCasperJSで自動更新する

去年までここのブログのドメイン管理はvalue-domainで行っていたのですが、つい最近同じGMOグループのお名前.comにドメイン移管しました。

ただ、value-domainではダイナミックDNS機能が標準で提供されており、自宅のIPアドレスをRaspberry Piからcurlコマンドで自動更新できていたのですが、お名前.comではその機能が提供されていないことに移管してから気づき、手動で更新する羽目に。。

よくよく調べると、WindowsであればDiCEなどで自動更新できるみたいですが、DNSレコード更新のためだけにWindows機を常時起動しておくわけにもいかず、できれば今まで通りRaspberry Piから自動更新できる方法を模索していました。
(DiCEはLinux版もあるみたいですが、Raspbery Piで動くものは無いようでした)

そこで今回は、ヘッドレスブラウザのPhantomJSを使った自動テストフレームワークであるCasperJSを使って、お名前.comの管理ページからDNSレコードを変更するまでの操作をスクリプト化し、無理やり自動更新するようにしてみました。

注意:本方法を使った場合、設定によっては先方のサーバに思わぬ負荷を与えてしまうことがあります。ご利用にあたっては自己責任でお願い致します。
  1. Raspberry Pi(raspbian)にPhantomJSをインストール
    apt-getではインストールできないため、こちらのビルド版を/usr/local/bin以下にコピーして利用
  2. 続けてCasperJSをインストール
    公式サイトにある1.1.0-beta3のzipファイルをダウンロードし、/usr/local/casperjsに展開後、PATH環境変数に/usr/local/casperjs/binを追加
  3. 以下スクリプトをonamaedns.jsの名前でコピー
    var casper = require('casper').create({
     pageSettings: {
      userAgent: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)'
     }, 
     logLevel: 'info', 
     waitTimeout: 30000, 
     verbose: true
    });
    casper.on('remote.message', function(msg) {
     this.log(msg, 'info');
    });
    var new_ip = null;
    
    function exit_usage() {
     casper.echo('usage: casperjs --ssl-protocol=any --ignore-ssl-errors=true onamaedns.js [options]');
     casper.echo('options:');
     casper.echo('  --userid    onamae.com id');
     casper.echo('  --passwd    onamae.com password');
     casper.echo('  --domain    update domain');
     casper.echo('  --host      update hostname');
     casper.exit();
    }
    var options = casper.cli.options;
    if (!options['userid'] || !options['passwd'] || !options['domain'] || !options['host']) {
     exit_usage();
    }
    
    casper.start();
    
    casper.thenOpen('http://whatismyip.akamai.com/', function() {
     new_ip = this.fetchText('body');
     this.log('new_ip=' + new_ip, 'info');
    });
    
    casper.thenOpen('https://www.onamae.com/domain/navi/domain.html', function() {
     this.evaluate(function(options) {
      document.querySelector('input[name="username"]').value = options['userid'];
      document.querySelector('input[name="password"]').value = options['passwd'];
      document.querySelector('input[name="login"]').click();
     }, options);
    });
    
    casper.waitForSelector('a[href*="domain/navi/menu/domain"]', function() {
     this.click('a[href*="domain/navi/menu/domain"]');
    });
    
    casper.waitForSelector('a[href*="domain/navi/dns_manage"]', function() {
     this.click('a[href*="domain/navi/dns_manage"]');
    });
    
    casper.waitForSelector('input[type="radio"][value="' + options['domain'] + '"]', function() {
     this.click('input[type="radio"][value="' + options['domain'] + '"]');
    });
    
    casper.waitForSelector('a.btn07.idSubmitexternal > span', function() {
     this.click('a.btn07.idSubmitexternal > span');
    });
    
    casper.waitForSelector('a[title*="domain/navi/dns_controll/input"]', function() {
     this.click('a[title*="domain/navi/dns_controll/input"]');
    });
    
    casper.waitForSelector('#dns_controll_inputForm', function() {
     var ret = this.evaluate(function(new_ip, options) {
      var num = document.querySelector('input[value="' + options['host'] + '.' + options['domain'] + '"]').getAttribute('id').replace('hostNameUsed', '');
      var old_ip = document.querySelector('#hdd_add_recvalue_a_used' + num).value;
      if (new_ip == old_ip) {
       return false
      }
    
      document.querySelector('#hdd_add_recvalue_a_used' + num).value = new_ip;
      document.querySelector('#idSubmit').click();
      return true;
     }, new_ip, options);
     if (ret === false) {
      this.die('same ip adress');
     }
    });
    
    casper.then(function() {
     this.click('#idSubmit');
    });
    
    casper.waitForSelector('a[href*="submitDnsconfirmForm"] > span', function() {
     this.click('a[href*="submitDnsconfirmForm()"]');
    });
    
    casper.waitForUrl(/domain\/navi\/dns_controll\/result/, function() {
     this.exit();
    });
    
    casper.run();
    
  4. お名前.comの管理ページにログインして、本スクリプトで更新するDNSレコードを登録しておく
  5. スクリプトを実行してみて動作テスト(問題なければcronに登録)
    casperjs --ssl-protocol=any --ignore-ssl-errors=true onamaedns.js
ハマった所としては、
  • PhantomJSのバージョンが古いため、(お名前.comを含む)POODLE対策を行ったサーバにHTTPS接続できない
    →casperjs --ssl-protocol=any(またはtlsv1) --ignore-ssl-errors=trueをつける
  • たまにタイムアウトで失敗する
    →Raspberry Pi(特に初期のやつ)だと重い処理らしく、waitForSelector()で応答を待つようにした
なところで、一応1ヶ月前くらいから毎朝3時に無線LANルーターの再起動とともに実行するよう仕掛けていますが、無事に成功しているようです。
(その度にお名前.comから更新完了の通知メールが届くのが難点ですが。。)