2010年10月26日

ServersMan@VPS - CentOS5 - BINDでダイナミックDNSサーバー構築とPHPクライアント作成

業界最安値水準のVPSサービスDTIのServersMan@VPSを利用して個人的にサーバーを立ててみるシリーズの何回目だか忘れました。
業界最安値水準のVPSサービスDTIのServersMan@VPSの申し込みはこちら


9月にServersMan@VPS - CentOS5 - BINDでDNSサーバー構築(ダイナミックDNSも設定)という記事を書いてダイナミックDNSサーバーを構築したものの運用までの環境構築を記事にしていなかったのでそろそろ記録を残しておきます。

前回の記事ではサーバー側にHTTPアクセスでダイナミックDNS更新をおこなうPHPスクリプトを掲載しましたが、何の認証もなく固定のドメインで更新するだけのスクリプトだったので、これでは運用できませんね。ということで最低限の認証をする処理を追加したスクリプトに変更します。

単純にホスト名、ドメイン名を指定してパスワード認証したいので、URLにBASIC認証をかける方式でなくPOST入力を受け取って認証処理する方式にします。

サーバー側更新PHPスクリプト

<?php
/*
 * index.php
 */
// nsupdateコマンドのパス
$nsupdatePath    = '/usr/bin/nsupdate';
// アップデート認証ファイル保存フォルダ
$configDir       = '../data';
// 作業一時フォルダ
$updateTmpDir    = '/tmp/ddns';
// 更新対象ドメイン
$targetDomain    = null;
// 更新対象ホスト名
$targetHostName  = null;
// 更新用パスワード
$password        = null;

// 処理開始
mb_internal_encoding('UTF-8');
header('Content-Type: text/plain;charset=UTF-8');

// 入力確認処理
if( isset($_POST['domain']) ) {
    $targetDomain = stripslashes($_POST['domain']);
    $targetDomain = mb_convert_encoding($targetDomain,'UTF-8','auto');
    $targetDomain = mb_convert_kana(trim($targetDomain),'a');
}
if( isset($_POST['host']) ) {
    $targetHostName = stripslashes($_POST['host']);
    $targetHostName = mb_convert_encoding($targetHostName,'UTF-8','auto');
    $targetHostName = mb_convert_kana(trim($targetHostName),'a');
}
if( isset($_POST['pass']) ) {
    $password = stripslashes($_POST['pass']);
    $password = mb_convert_encoding($password,'UTF-8','auto');
    $password = mb_convert_kana(trim($password),'a');
}
// 妥当性検査(簡易)
if( is_null($targetDomain) || strlen($targetDomain) == 0
|| strpos($targetDomain,DIRECTORY_SEPARATOR) !== false ) {
    echo "INVALID ACCESS";
    die;
} else if( is_null($targetHostName) || strlen($targetHostName) == 0
|| strpos($targetHostName,DIRECTORY_SEPARATOR) !== false ) {
    echo "INVALID ACCESS";
    die;
} else if( is_null($password) || strlen($password) == 0 ) {
    echo "INVALID ACCESS";
    die;
} else {
    $targetConfFile = $configDir.DIRECTORY_SEPARATOR.$targetDomain;
    if( !file_exists($targetConfFile) ) {
        echo "THAT DOMAIN IS NOT REGISTED";
        die;
    } else {
        $lines = file($targetConfFile,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        // 1行目はcrypt暗号化されたパスワード
        $registedPassword = array_shift($lines);
        if( $registedPassword != crypt($password,$registedPassword) ) {
            echo "INVALID PASSWORD";
            die;
        }
        // 登録済みホスト確認
        if( !in_array($targetHostName,$lines) ) {
            echo "INVALID HOST NAME";
            die;
        }
    }
}
// 更新処理
$updateUqdn    = $targetHostName.'.'.$targetDomain;
$tmpFilePath   = $updateTmpDir.DIRECTORY_SEPARATOR.$updateUqdn.'.txt';
// 削除コマンド実行
$updateText    = "update delete ".$updateUqdn."¥n¥n";
$fileHandle    = @fopen($tmpFilePath,'w');
if( $fileHandle ) {
    fwrite($fileHandle,$updateText);
    fclose($fileHandle);
}
exec($nsupdatePath.' '.$tmpFilePath);
// 現在アクセスされているアクセス元IPで更新
$updateText    = "update add ".$updateUqdn." 3600 IN A "
    .$_SERVER['REMOTE_ADDR']."¥n¥n";
$fileHandle    = @fopen($tmpFilePath,'w');
if( $fileHandle ) {
    fwrite($fileHandle,$updateText);
    fclose($fileHandle);
}
exec($nsupdatePath.' '.$tmpFilePath);
@unlink($tmpFilePath);
echo 'OK';
exit;
?>

クライアント用更新PHPスクリプト

クライアント用のPHPスクリプトはcron設定などで実行することにします。Windowsサーバーをご利用の方は使いづらいですね。

<?php
/*
 * ddnsupdate.php
 */
// DDNS更新用URL
$ddnsUpdateUrl  = 'http://ddns.example.com/';
// DDNS更新ドメイン名
$ddnsDomainName = 'example.com';
// DDNS更新用パスワード
$ddnsPassword   = 'hogehoge';
// ホスト名設定配列
$ddnsHostArray  = array('private');

// 処理開始
// プロトコル分割
list( $protocol, $accessString ) = explode('//',$ddnsUpdateUrl);
// ホスト名とURI分割
$updateHostName = null;
$updateHostPort = null;
$updateUri      = null;
if( strpos($accessString,'/') !== false ) {
    $pos = strpos($accessString,'/');
    $updateHostName = substr($accessString,0,$pos);
    $updateUri      = substr($accessString,$pos);
}
if( strlen($updateHostName) == 0 ) {
    echo "invalid configuration!\n";
    die;
}
if( strpos($updateHostName,':') !== false ) {
    list($updateHostName,$updateHostPort) = explode(':',$updateHostName);
}
if( strlen($updateUri) == 0 ) {
    $updateUri = '/';
}
// ホスト名からIPアドレスを取得
$address	= gethostbyname($updateHostName);
if( preg_match('/^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}/',$address) == 0 ) {
    echo "Target Host is not found!\n";
    die;
}
if( preg_match('/https/',$protocol) > 0 ) {
    $address = 'ssl://'.$address;
}
if( is_null($updateHostPort) ) {
    if( preg_match('/https/',$protocol) > 0 ) {
        $updateHostPort = 443;
    } else {
        $updateHostPort = 80;
    }
}
// ホストごとに更新
foreach( $ddnsHostArray as $hostName ) {
    // POSTデータ文字列
    $postData = urlencode('domain').'='.urlencode($ddnsDomainName)
    .'&'.urlencode('pass').'='.urlencode($ddnsPassword)
    .'&'.urlencode('host').'='.urlencode($hostName);
    // リクエスト文字列の生成
    $requestStrings = "POST {$updateUri} HTTP/1.1\r\n"
    ."Accept: */*\r\n"
    ."Host: {$updateHostName}\r\n"
    ."Content-Type: application/x-www-form-urlencoded\r\n"
    ."Content-Length: ".strlen($postData)."\r\n\r\n"
    .$postData."\r\n\r\n";
    // ソケットオープン
    $errorNumber  = '';
    $errorMessage = '';
    if( $socket = @fsockopen( $address, $updateHostPort, $errorNumber, $errorMessage, 60 ) ){
        if( @socket_set_timeout( $socket, 60 ) ) {
            // リクエスト送信
            fwrite( $socket, $requestStrings );
            // レスポンス文字列受信
            $responseText	= '';
            while (!feof($socket)) {
                $responseText .= fgets($socket, 128);
            }
            echo $responseText."\nOK\n".$hostName."\n";
        } else {
            echo "Can't set connection timeout: update ".$hostName."\n";
        }
        @fclose($socket);
    } else {
        echo "Can't set connection timeout: update ".$hostName."\n";
    }
}
exit;
?>
サーバー・クライアント共に設置して冒頭の設定部分をご自分の環境に併せてphpで実行すれば更新されるはず。エラーがあったら教えてください。
/usr/bin/php ddnsupdate.php


最後にクライアント側でこのスクリプトが定期実行されるようにcronに登録しておけば完了です。
crontab -e
毎時30分に実行するようにするには下記のように編集すれば良いです。crontabの編集が分からない方は別途お調べください。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=''
HOME=/
30 * * * * /usr/bin/php ddnsupdate.php > /dev/null 2>&1


haruchaco at 23:50│Comments(0)TrackBack(0)サーバー | CentOS

トラックバックURL

コメントする

名前
URL
 
  絵文字