いずれは他人様のお宅に設置される予定のAndroid TV準拠ストリーミング・メディア・プレーヤAmigo 7xJPを外部からアクセスできるよう,ダイナミックDNSサービスを利用する。特にTermuxでインストールした,SSHやNode-Redのサービスにアクセスすることを想定。このお宅のLANルータに付与されるIPアドレスをダイナミックDNSサービスに反映させるのに既存Bashスクリプトを利用することにしたが予想外に手間がかかった。
なお,別途TeamViewerを介して画面共有・操作できるようにし,不便ながらも遠隔に文字入力できるようにしてあるがそちらはあくまでバックアップ手段。
ポートマッピング機能を利用すること前提で外部からルータにアクセスできるようダイナミックDNSサービスを利用する。深い意味はないがNo-IPを利用することにする(My No-IP)。IPアドレスを更新するクライアントが必要で,その機能を有するAndroidアプリは当然あると思った。実際あるのだが評価は概ね悪い。古くて最近のAndroidではエラーを起こす,手動でボタンを押さないと更新動作が起こらない…などなど。
No-IPは公式のLinuxクライントを用意しているが自分でコンパイルする必要がある。Termuxがデフォルトで対応しているリポジトリにLLVMは含まれているし,Its-Pointlessというコミュティ・リポジトリを利用するようにするとそこにGCCも含まれていることがわかっている。なのでコンパイルは可能だろうが,おそらく今後ともめったに使わないであろうツールチェインを一式インストールするのは無駄な気がするし,theonemule/no-ip: A shell script that works as Dynamic Update Client (DUC) for noip.comのREADMEでは,このツールの作者がその公式Linuxクライアントをどうしても正常動作させられなかった,としている。
この作者は代わりに使えるBashスクリプトを作成してくれているのでこれをありがたく使わせてもらう。 “Integrate with No-IP DDNS – API Information” にNo-IPのDNSレコードをHTTPのGETで外部から更新するAPIが説明してある。その部分を実現するのがスクリプト中の以下。
RESULT=$(wget -qO- $AUTHHEADER $USERAGENT $NOIPURL)
まずは変数を引用してないがために問題が起きていたので引用。加えてデバッグのため暫定的にqオプションを外す。
RESULT=$(wget -O- “$AUTHHEADER” “$USERAGENT” “$NOIPURL”)
しかしこれでは以下のエラーが起こる。
Resolving dynupdate.no-ip.com… 158.247.7.204
Connecting to dynupdate.no-ip.com|158.247.7.204|:443… connected.
HTTP request sent, awaiting response… 400 Bad Request
2021-08-13 22:46:11 ERROR 400: Bad Request.
“Bad Request” とだけ言われてもなぁ,とふと認証のためのヘッダを落としてみた。
RESULT=$(wget -O- “$USERAGENT” $NOIPURL)
そうすると以下のように通ってしまった!”Unauthorized” といわれつつ!それでええんかい!?
Resolving dynupdate.no-ip.com… 158.247.7.204
Connecting to dynupdate.no-ip.com|158.247.7.204|:443… connected.
HTTP request sent, awaiting response… 401 Unauthorized
Authentication selected: Basic realm=”No-IP DNS Update API”
Reusing existing connection to dynupdate.no-ip.com:443.
HTTP request sent, awaiting response… 200 OK
Length: unspecified [text/plain]
Saving to: ‘STDOUT’– [ <=> ] 20 –.-KB/s in 0s
2021-08-13 22:55:42 (324 KB/s) – written to stdout [20]
さて,今ヘッダ情報で認証をするかのように書いたが, “Integrate with No-IP DDNS – API Information” にあるように実はURLそのものにユーザ名,パスワードとも埋め込む形をとっており(すぐ下に具体的に),認証自身はそれでできるはずだ(URLが平文で流れるのは大変望ましくないが…)。さて,この文書には以下のように書かれている(斜体は私)。
Authorization: base64-encoded-auth-string should be the base64 encoding of username:password.
username:password: Username and password associated with the hostnames that are to be updated. No-IP uses an email address as the username.
スクリプトの実装ではユーザ名は電子メールアドレスではないものを使用している。仮にアカウントに結びついた電子メールに変えてみると,おそらくは “@” の扱いでwgetが処理できなくなる。 “@” は電子メールに含まれる上に,以下のようにURLの中にも現れるからだ。
http://username:password@dynupdate.no-ip.com/nic/update?hostname=mytest.example.com&myip=192.0.2.25
URLのユーザ名は電子メールではないIDの文字列で,Base64処理の対象は電子メールが期待されているのか…などとは思ったがとりあえず動いているようなので深追いしない。
なお,ログファイルは以下のような感じ:
Fri Aug 13 22:39:28 JST 2021 -- nochg xx.xx.xx.xx Fri Aug 13 22:45:51 JST 2021 -- Fri Aug 13 22:46:11 JST 2021 -- Fri Aug 13 22:55:42 JST 2021 -- good xx.xx.xx.xx Fri Aug 13 23:05:44 JST 2021 -- nochg xx.xx.xx.xx
“–” の右にあるのはwgetコマンドの返り値のはず。もし返り値に応じてどこかに通知を出すようなことをしたければ,① “nochg xx.xx.xx.xx” の場合,② “good xx.xx.xx.xx” の場合,③ “” の場合,の3つのケースに対応すればよい。⇐ 30日ごとにドメインネーム「契約」を更新しなくてはならないのだが,それを怠っていると,④ “nohost” となることがわかった。
theonemule/no-ip: A shell script that works as Dynamic Update Client (DUC) for noip.comではCronを使った定期実行なども考えられているが,単純にno-ip.shの定期実行オプションを利用することにした(10分間隔)。
“How do I Crontab on Termux.. : termux” にTermuxでのCronの使い方が。Termux:APIは termux-job-scheduler というスクリプトを含んでいて,これはAndroidのネイティブなジョブスケジューラを利用するものだとどこかで読んだんだがソースが見つからない。Termuxのwikiにも現時点で説明がなく,とりあえずヘルプを末尾に掲載しておく。
現在の ~/.termux/boot/start-servers.sh の内容は以下の通り:
#!/data/data/com.termux/files/usr/bin/sh node-red --node-args="--max-old-space-size=128" /data/data/com.termux/files/home/bin/no-ip.sh \ -c=/data/data/com.termux/files/home/.no-ip/no-ip.conf & sshd
Termuxはシングルユーザ環境で,標準的なUnixパスが利用できるわけでもないので(”Differences from Linux – Termux Wiki“),自分が直接手を入れたものは全てホームディレクトリ以下に置くことにした。その方がバックアップの面でもわかりやすいだろう。 Termuxは間違いなく便利なんだが,ちゃんとしたLinuxとはいろいろな点で違うので,それを常時意識せねばならず少し煩わしい。PRoot – Termux Wikiを利用すればより本格的なLinux体験ができるようだが,現時点ではそれがペイするかどうかわからない。
なお,Cronを利用しなかったのは,おそらくそのために期待されている方法はTermux-Servicesを介して利用することなのだが,それをブート時自動起動させる必要があり,いろいろヤヤコシクなりそうだったから。
Termux-job-schedulerのヘルプ:
~ $ termux-job-scheduler -h Usage: termux-job-scheduler [options] Schedule a script to run at specified intervals. -p/--pending list pending jobs and exit --cancel-all cancel all pending jobs and exit --cancel cancel given job-id and exit Options for scheduling: -s/--script path path to the script to be called --job-id int job id (will overwrite any previous job with the same id) --period-ms int schedule job approximately every period-ms milliseconds (default 0 mean s once). Note that since Android N, the minimum period is 900,000ms (15 minutes) . --network text run only when this type of network available (default any): any|unmeter ed|cellular|not_roaming|none --battery-not-low boolean run only when battery is not low, default true (at least Android O) --storage-not-low boolean run only when storage is not low, default false (at least Android O) --charging boolean run only when charging, default false --persisted boolean should the job survive reboots, default false --trigger-content-uri text (at least Android N) --trigger-content-flag int default 1, (at least Android N)
「TermuxからBashスクリプトでダイナミックDNSレコードを自動的に更新」への3件のフィードバック