NextcloudやTinyTiny-RSSなどを自宅サーバに配置し、@niftyのDDNS経由でアクセスしていましたが、ある日突然接続できなくなり、WebDAVへのアクセスも「network is unreachable」な状態になりました。
いくつか試して解決したのでメモしておきます。
pingしてみる
pingにもIPv4とIPv6のオプションがありますので対象によって変更します。
今回のトラブルではIPアドレス指定だと応答が返ってきますが、ホスト名だと返ってきませんのDNSくさいです。
Windowsのコマンドプロンプト
IPv4
オプションなし
ping sakue9000.com
「-4」オプションでIPv4を強制できます。
ping -4 sakue9000.com
IPv6
同様に「-6」オプションでIPv6を強制できます。
ping -6 sakue9000.com
Linuxのコンソール
IPv4
オプションなし
ping sakue9000.com
IPv4を強制
ping -4 sakue9000.com
IPv6
ping6コマンド
ping6 sakue9000.com
pingの「-6」オプション
ping -6 sakue9000.com
IPアドレスで直接アクセス
IPv4の場合
IPv4の場合はホスト名をIPアドレスに置き換えてブラウザでアクセスするだけです。
以下のようなURLだった場合、
https://sakue9000.com/nextcloud
以下のようにアクセスするだけです
https://123.123.123.123/nextcloud
IPv6の場合
IPv6の場合はIPアドレスそのままだとエラーになるので、 [] で囲います。
https://sakue9000.com/nextcloud
IPv6のアドレスが以下だった場合(例は適当です)
123a:123a:123a:123a
以下のように括弧で囲ってやります。
https://[123a:123a:123a:123a]/nextcloud
今回は上記でアクセスすることができたので、DNS関係ですね。
nslookupで確認
IPv4の場合
IPv4はオプションなしでOKです。
nslookup sakue9000.com
問い合わせるDNSサーバを指定する場合は対話モードで
nslookup server 8.8.8.8 sakue9000.com
ここで気になったのが、IPv6の環境なのになぜかIPv4のIPアドレスも返ってきた事です。
IPv6導入前の古い設定が残ったままなのでしょうか???
IPv6の場合
IPv4はAレコードですが、IPv6はAAAAレコードを参照したいので対話モードで実行します。
nslookup set type=AAAA sakue9000.com
DNSサーバを変更する場合
nslookup set type=AAAA server 8.8.8.8 sakue9000.com
IPv4のアドレスが先程返ってきましたが、上記でIPv6のアドレスを取得してみると、自宅サーバのIPv6アドレスがちゃんと返ってきています。
何故かIPv4とIPv6のアドレスの両方が設定されてる状態になっていました。
@niftyのDDNS設定ページは、現在設定されているIPアドレスなどが確認できないため、かなり困りました。
自動更新スクリプトの修正
現在の環境は「IPv6接続オプション」を追加してIPv4とIPv6が共存している状態です。
自動更新スクリプトはwgetで各種通信を行っていますが、IPv4もしくはIPv6を強制することもできるため、今回はIPv4に強制しました。
ここでは修正した箇所だけ抜粋し、スクリプト全体は次の項にします。
修正箇所1
以下のようにwgetでIPアドレス取得ページへアクセスしています。
$wget = `wget --secure-protocol=auto -O - -q $atniftyddns?$conf_ipaddr`;
IPアドレス取得ページが利用できなくなっていたため、自前でIPアドレスだけをtext/plainで返すcgiを配置し、そこへIPv4でアクセスして取得します。
「–inet4-only」の部分でIPv4を強制していますが、IPv6に強制する場合は「–inet6-only」としてください。
$wget = `wget --inet4-only --secure-protocol=auto -O - -q http://sakue.com/ipchk/ipchk-simple.cgi`;
修正箇所2
こちらは実際に更新を行うための通信をしている部分です。
$wget = `wget --secure-protocol=auto -O - -q --http-user=$niftyid --http-password=$niftypasswd $atniftyddns?$update_ip`;
同様に「–inet4-only」としてIPv4を強制を強制します。
$wget = `wget --inet4-only --secure-protocol=auto --no-check-certificate -O- -v --http-user=$niftyid --http-passwd=$niftypasswd $atniftyddns` ;
修正箇所3
ここはIPv4とIPv6のIPアドレスを取り出している部分です。
2箇所ありますので接続プロトコルに合わせて修正します。
今回はIPv4を強制したので元のスクリプトのままですが、IPv6の場合は以下の例のように取り出すよう修正します。
IPv4
$wget =~/[0-9]+(\.[0-9]+){3}/;
IPv6
$wget =~/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/;
@nifty自動更新スクリプト
元のスクリプト(niftyddns.cgi)を拝借したのは下のサイト様です。
ありがとうございます!
ipcheck.cgi(IPアドレスを返す)
以下はアクセスするとtext/plainでIPアドレスを返すスクリプトで、自宅サーバのグローバルIPを取得したいので、パーミッションを「0755」にして外部のレンタルサーバなどに設置しておきます。
IPv4/IPv6のどちらでもOKです。
#!/usr/bin/perl print "Content-type: text/plain\n\n"; print <<"EOM"; $ENV{'REMOTE_ADDR'} EOM exit;
設置したら実際にブラウザでアクセスして、画面にIPアドレスが表示されることを確認してください。
niftyddns.cgi(DDNSのIP更新)
ここで紹介する更新用スクリプトは、IPv4もしくはIPv6のどちらかを強制して取得するよう修正したものです。
パーミッションは「0755」にして実行できるようにしておきます。
IPv4を強制
wgetの「–inet4-only」を付けたくらいだったと思います。
#!/usr/bin/perl -w use strict; use Encode; #------------------------------------------------------------------------------# # グローバル変数 ## ユーザ毎にカスタマイズが必要な変数 my $niftyid = "AAAAAAAA"; # @niftyのユーザID my $niftypasswd = "BBBBBBBBBBBBBBBBBB"; # @niftyのパスワード my $basedir = "/home/sakue/ddns"; # このスクリプトをインストールするディレクトリ my $datadir = "$basedir/atniftyddns"; # このスクリプトで使うファイル(IPアドレスの保存ファイル、wgetの出力結果等)を保存するディレクトリ my $addrfile = "$datadir/prev_ip.txt"; # 前回のIPアドレスを保存するファイル名 my $updatetime = "$datadir/update.txt"; # 前回、IPアドレス更新を実行した時間(time関数値)を保存するファイル名 my $resultfile = "$datadir/result.txt"; # スクリプトの結果(IPアドレスに変更が無かったか、あるいはIPアドレス更新処理をしたか)を記録するファイル名 my $interval = 7; # IPアドレス変更が無い場合でもこの変数で指定された日にち(デフォルト7日)が経過したら更新処理を行う ## カスタマイズ不要な変数 #my $atniftyddns = "http://domain.nifty.com/domain/DdnsIpChangeConfirm.do"; # 今後、@nifty DDNSサービスで変更がなければこのままでOK my $atniftyddns = "https://domain.nifty.com/cp/ddns/DdnsIpChangeAdd.do?change_type=ip"; my $conf_ipaddr = "DDNS_CHANGE_IP"; # 今後、@nifty DDNSサービスで変更がなければこのままでOK my $update_ip = "DDNS_UPDATE_IP_ADDRESS"; # 今後、@nifty DDNSサービスで変更がなければこのままでOK my $wget; my $current_ip; # 現在のIPアドレス my $previous_ip; # 前回のIPアドレス my $change = 1; # 前回のIPアドレスと現在のIPアドレスが一致していない場合、または、初めてこのスクリプトを実行する場合に1にセット my $updated; # $updatetimeファイルに保存されているIPアドレス更新実行時間 #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # スクリプトで使うファイルの保存ディレクトリが無い場合にディレクトリを作成 if( !-d $datadir ) { mkdir( $datadir, 0755 ) || die "ディレクトリ生成失敗 : $!"; } #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # @nifty DDNSのIPアドレス更新ページをwgetでアクセスし、現在のIPアドレスを確認 $wget = `wget --inet4-only --secure-protocol=auto -O - -q https://sakue/ipcheck.cgi`; $wget =~/[0-9]+(\.[0-9]+){3}/; $current_ip = $&; $current_ip = $wget; #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # 前回のIPアドレスと現在のIPアドレスが一致するか比較し、一致していない場合には # IPアドレスが変更したとして$changeフラグをセット ## 前回のIPアドレスを保存しているファイルが存在している場合は現在のIPアドレスと ## 一致するか確認 if ( -e $addrfile ) { open( FH, "<$addrfile" ); $previous_ip = <FH>; $previous_ip =~ s/\s//g; $previous_ip =~ s/\r\n//g; $previous_ip =~ s/\r//g; $previous_ip =~ s/\n//g; close( FH ); if ( $current_ip eq $previous_ip ) { $change = 0; } else { unlink $addrfile; open( FH, ">addrfile" ); print FH $current_ip; close( FH ); } } ## 前回のIPアドレスを保存しているファイルが存在しない場合は現在のIPアドレスを ## 保存 else { open( FH, ">$addrfile" ); print FH $current_ip; close( FH ); } #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # 最終更新時から$intervalで指定した日数経過している場合は、IPアドレスに変更が # なくても更新処理を実行する($changeフラグをセットする) if ( -e $updatetime ) { open( FH, "<$updatetime" ); $updated = <FH>; close( FH ); if ( $updated <= time - ( $interval * 24 * 3600 ) ) { $change = 1; unlink $updatetime; open( FH, ">$updatetime" ); print FH time; close FH; } } else { $change = 1; open( FH, ">$updatetime" ); print FH time; close FH; } #------------------------------------------------------------------------------# # $changeフラグがセットしてある場合、IPアドレス更新を実行し、IPアドレス保存ファ # イルに保存しているIPアドレスを現在のIPアドレスに更新 if ( $change ) { if ( -e $resultfile ) { unlink( $resultfile ); } $wget = `wget --inet4-only --secure-protocol=auto --no-check-certificate -O- -v --http-user=$niftyid --http-passwd=$niftypasswd $atniftyddns` ; # IPv4 $wget =~/[0-9]+(\.[0-9]+){3}/; $current_ip = $&; my $remain = $'; $remain =~/ /; $remain = $`; Encode::from_to($remain, 'shiftjis', 'utf-8'); open( FH, ">$resultfile" ); print FH $current_ip . $remain . "\n"; close( FH ); } else { unlink( $resultfile ); open( FH, ">$resultfile" ); print FH "IPアドレス($current_ip)は変更されていません。\n"; close( FH ); }
IPv6を強制
wgetの「–inet6-only」オプションの付加と、IPアドレス取り出し部分の正規表現が異なる程度です。
#!/usr/bin/perl -w use strict; use Encode; #------------------------------------------------------------------------------# # グローバル変数 ## ユーザ毎にカスタマイズが必要な変数 my $niftyid = "AAAAAAAA"; # @niftyのユーザID my $niftypasswd = "BBBBBBBBBBBBBBBBBB"; # @niftyのパスワード my $basedir = "/home/sakue/ddns"; # このスクリプトをインストールするディレクトリ my $datadir = "$basedir/atniftyddns"; # このスクリプトで使うファイル(IPアドレスの保存ファイル、wgetの出力結果等)を保存するディレクトリ my $addrfile = "$datadir/prev_ip.txt"; # 前回のIPアドレスを保存するファイル名 my $updatetime = "$datadir/update.txt"; # 前回、IPアドレス更新を実行した時間(time関数値)を保存するファイル名 my $resultfile = "$datadir/result.txt"; # スクリプトの結果(IPアドレスに変更が無かったか、あるいはIPアドレス更新処理をしたか)を記録するファイル名 my $interval = 7; # IPアドレス変更が無い場合でもこの変数で指定された日にち(デフォルト7日)が経過したら更新処理を行う ## カスタマイズ不要な変数 #my $atniftyddns = "http://domain.nifty.com/domain/DdnsIpChangeConfirm.do"; # 今後、@nifty DDNSサービスで変更がなければこのままでOK my $atniftyddns = "https://domain.nifty.com/cp/ddns/DdnsIpChangeAdd.do?change_type=ip"; my $conf_ipaddr = "DDNS_CHANGE_IP"; # 今後、@nifty DDNSサービスで変更がなければこのままでOK my $update_ip = "DDNS_UPDATE_IP_ADDRESS"; # 今後、@nifty DDNSサービスで変更がなければこのままでOK my $wget; my $current_ip; # 現在のIPアドレス my $previous_ip; # 前回のIPアドレス my $change = 1; # 前回のIPアドレスと現在のIPアドレスが一致していない場合、または、初めてこのスクリプトを実行する場合に1にセット my $updated; # $updatetimeファイルに保存されているIPアドレス更新実行時間 #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # スクリプトで使うファイルの保存ディレクトリが無い場合にディレクトリを作成 if( !-d $datadir ) { mkdir( $datadir, 0755 ) || die "ディレクトリ生成失敗 : $!"; } #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # @nifty DDNSのIPアドレス更新ページをwgetでアクセスし、現在のIPアドレスを確認 $wget = `wget --inet6-only --secure-protocol=auto -O - -q https://sakue/ipcheck.cgi`; $wget =~/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/; $current_ip = $&; $current_ip = $wget; #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # 前回のIPアドレスと現在のIPアドレスが一致するか比較し、一致していない場合には # IPアドレスが変更したとして$changeフラグをセット ## 前回のIPアドレスを保存しているファイルが存在している場合は現在のIPアドレスと ## 一致するか確認 if ( -e $addrfile ) { open( FH, "<$addrfile" ); $previous_ip = <FH>; $previous_ip =~ s/\s//g; $previous_ip =~ s/\r\n//g; $previous_ip =~ s/\r//g; $previous_ip =~ s/\n//g; close( FH ); if ( $current_ip eq $previous_ip ) { $change = 0; } else { unlink $addrfile; open( FH, ">addrfile" ); print FH $current_ip; close( FH ); } } ## 前回のIPアドレスを保存しているファイルが存在しない場合は現在のIPアドレスを ## 保存 else { open( FH, ">$addrfile" ); print FH $current_ip; close( FH ); } #------------------------------------------------------------------------------# #------------------------------------------------------------------------------# # 最終更新時から$intervalで指定した日数経過している場合は、IPアドレスに変更が # なくても更新処理を実行する($changeフラグをセットする) if ( -e $updatetime ) { open( FH, "<$updatetime" ); $updated = <FH>; close( FH ); if ( $updated <= time - ( $interval * 24 * 3600 ) ) { $change = 1; unlink $updatetime; open( FH, ">$updatetime" ); print FH time; close FH; } } else { $change = 1; open( FH, ">$updatetime" ); print FH time; close FH; } #------------------------------------------------------------------------------# # $changeフラグがセットしてある場合、IPアドレス更新を実行し、IPアドレス保存ファ # イルに保存しているIPアドレスを現在のIPアドレスに更新 if ( $change ) { if ( -e $resultfile ) { unlink( $resultfile ); } $wget = `wget --inet6-only --secure-protocol=auto --no-check-certificate -O- -v --http-user=$niftyid --http-passwd=$niftypasswd $atniftyddns` ; # IPv6 $wget =~/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/; $current_ip = $&; my $remain = $'; $remain =~/ /; $remain = $`; Encode::from_to($remain, 'shiftjis', 'utf-8'); open( FH, ">$resultfile" ); print FH $current_ip . $remain . "\n"; close( FH ); } else { unlink( $resultfile ); open( FH, ">$resultfile" ); print FH "IPアドレス($current_ip)は変更されていません。\n"; close( FH ); }
修正が必ず必要な箇所(IPv4/IPv6共通)
8行目付近
“AAAAAAAA” の””内文字列を@niftyのユーザーIDに書き換えます(例:my $niftyid = “niftyID”;)
my $niftyid = "AAAAAAAA"; # @niftyのユーザID
“BBBBBBBBBBBBBBBBBB”の””内文字列を@niftyのパスワードに書き換えます(例:my $niftypasswd = “password”;)
my $niftypasswd = "BBBBBBBBBBBBBBBBBB"; # @niftyのパスワード
更新用スクリプトを配置するディレクトリを指定します。
例えば以下の例は /home/sakue/ddns/niftyddns.cgi に配置する場合の指定方法です。
my $basedir = "/home/sakue/ddns"; # このスクリプトをインストールするディレクトリ
38行目付近
wgetで自宅サーバから外部のIPアドレス取得スクリプトにアクセスする部分です。
https://sakue/ipcheck.cgi となっていますが、存在しないので ipcheck.cgi を設置したURLに書き換えます。
# @nifty DDNSのIPアドレス更新ページをwgetでアクセスし、現在のIPアドレスを確認 $wget = `wget --inet6-only --secure-protocol=auto -O - -q https://sakue/ipcheck.cgi`;
cronへ登録
/etc/crontab に以下のように追記します(10分おきに更新する例です)
# DDNSのIPアドレス更新(@niftyDDNS) 0,10,20,30,40,50 * * * * root /home/sakue/ddns/niftyddns.cgi
おわりに
@niftyのDDNSは情報があまり公開されていないので詳細は不明ですが、nslookupでIPv6のアドレスが返ってくる状態なのに、見覚えのないIPv4のアドレスも返ってきていたのが原因でした。
ソフトごとに正常にアクセスできるかどうかが、まちまちだったのはIPv4でアクセスしたか、IPv6でアクセスしたかの違いだったようです。
IPv4でアクセスしたソフトは見知らぬIPアドレスを参照しようとしたのでアクセスできなかったという具合です。
管理画面に「オフラインにする」というボタンがあり、実行すると設定がクリアされるのかどうか不明ですが、上記修正前に一度「オフラインにする」ボタンを押しておきました。
せめて、「現在の設定状態の確認」「設定のクリア」の2つさえできるようになっていれば、今回のように悩まずに済んだので、今後改善されると嬉しいなと思います。
コメントフォーム