HPCメモ

HPC(High Performance Computing)に関連したりしなかったりすることのメモ書き

DDNSを捨てよIPアドレスを通知しよう

この記事は UdonTech Advent Calendar 2023の2日目の記事です。

   | \
   |∀ ゚) ダレモイナイ・・アナウメスルナラ イマノウチ
   |⊂
   |

一般のご家庭ではISP様の気分次第で*1割り当てられたIPアドレスが変わってしまうので、 外部からアクセスしたいんだけど、IPアドレスを知るためには同時に家庭内LANにも居ないといけないという ジレンマに悩まされていました。

解決策としてDDNS(Dynamic DNS)というのがこれまた古くから使われているんですが、年に数回外出先からsshで帰宅する程度にしか使わないので 名前を割り当てられるほどの大掛かりなサービス要らんのだけどなぁという気持ちもあって、なんとなくアレ使うの嫌なんですよね。

そもそも、一般に公開するサービスを提供するわけじゃないので誰にでも分かりやすいアドレスなんてものも不要だし、 むしろ名前でアクセスできると攻撃を受ける頻度が上がるだけじゃないだろうか(要出典)という気もします。

ところで、最近知ったのですが、一部のpublicなDNSに特殊なクエリを投げると自分の(DNS側から見た時の)IPアドレスを返してくれるそうです。

community.cisco.com

ということは、これを家庭内LANのマシン上で定期的に動かしつつ、IPアドレスが変わっていたら通知するシステムがあれば十分じゃないか!

ということで作りました :D

自分のIPアドレスを知る

これを実行するだけ。

dig myip.opendns.com @208.67.222.222 +short

+short オプションは、今回初めて知ったんですが便利ですね。 最初はsedでanswer sectionを抜き出して〜とかやってたんですが、このオプションをつければ単にIPアドレスだけ返してくれます。

通知する

そんなもんslackにDMで投げときゃ良いでしょ。 ってことで、こちらの記事を参考に incomming webhook を設定してcurlでポストします。

qiita.com

anton0825.hatenablog.com

デプロイメント

crontab -e

IPアドレスを調べてファイルに書き出し、前のIPアドレスとdiffとって違ってたらslackに投げるシェルスクリプトを作っておいて毎時5分くらいに動かします。

修正版

これで完成!!と思いきや、なんか定期的にIPアドレスが変わっていないのに通知が来る現象が起きました。

直前のIPアドレスとのdiffをとった時のログを見ると

1c1,4
< 192.168.0.1
---
> 
> ; <<>> DiG 9.10.6 <<>> myip.opendns.com @208.67.222.222 +short
> ;; global options: +cmd
> ;; connection timed out; no servers could be reached

あ、time outしとる・・・

というわけで、digが非ゼロで終了したら1分待ってリトライするようにして完成です(たぶん)((今のところ連続して同じIPアドレスが通知されたことは無いので、大丈夫そうだけど単にdigがエラーを返してないだけという可能性もあるので・・・)

需要は無さそうですが一応最終版のスクリプトを貼っておきます。

function onDetectNewGlobalIP(){
  #save difference 
  diff ${OUTPUTDIR}/globalIP.txt  ${OUTPUTDIR}/globalIP_new.txt

  #post to slack
  curl -X POST -H 'Content-type: application/json'\
   --data '{"text":"'$(cat ${OUTPUTDIR}/globalIP.txt)'"}' ${WEBHOOK_URL}
  rt=$?
  rm  ${OUTPUTDIR}/globalIP.txt 
  mv ${OUTPUTDIR}/globalIP_new.txt ${OUTPUTDIR}/globalIP.txt 
  return $rt
}

{
dig myip.opendns.com @208.67.222.222 +short  > ${OUTPUTDIR}/globalIP_new.txt
dig_rt=$?

error_count=0
while [ $dig_rt != 0 ]
do
  sleep 60
  dig myip.opendns.com @208.67.222.222 +short  > ${OUTPUTDIR}/globalIP_new.txt
  dig_rt=$?
  error_count=$(( ${error_count} + 1))
  if [ ${error_count} -gt  ${MAX_RETRY} ];then
    echo "dig failed more than ${MAX_RETRY} in a row"
    exit 1
  fi
done


if [ -f  ${OUTPUTDIR}/globalIP.txt ];then
  diff -q ${OUTPUTDIR}/globalIP_new.txt ${OUTPUTDIR}/globalIP.txt 
  if [ $? -ne 0 ];then
    echo 'global IP address is changed!'
  
    onDetectNewGlobalIP
  else
    echo 'global IP address is not changed!'
    rm ${OUTPUTDIR}/globalIP_new.txt 
  fi
else
  echo 'global IP address file does not exist'
  onDetectNewGlobalIP
fi

}  > ${OUTPUTDIR}/$(date +%Y%m%d-%H%M%S).log

OUTPUTDIR, WEBHOOK_URL, MAX_RETRYはそれぞれPC上のファイル置き場、slackに通知するためのwebhookのURL、MAX_RETRYはdigがエラーを返した時に リトライを行なう数です。

cronにこのスクリプトを仕込んで動かしつつ、cronで毎日1週間前のログを消すといった感じで運用していますが、今のところ快適に使えてます。

大前提としてルーターの設定などで外部からのsshアクセスは捌けるようにしておく必要がありますが、これで半年ほど使っていて特に問題なさそうです。

唯一の欠点は、淡々とIPアドレスが並んだslackの画面に往年の某掲示板を思い出してしまうところでしょうかね?

*1:嘘です、何かしらのアルゴリズムに基いて切り替わるはず