はじめに
IPv6 IPoE方式対応のBuffaloのルータ WXR-1751DHP2を購入し、我が家のインターネット回線をIPv6に変更して高速になった回線に喜んでいたら、事件は起こった。このルータ、何と起動して数日経つとWi-Fiアクセスポイントが使えなくなってしまうのである。何を言っているのか分からないと思うが、一番困惑しているのはうっかりこのルータを購入してしまった自分である。
参考にしたブログ記事[1]によると、何と半ば仕様であるようで、サポートに問合せしても埒が明かないので乗り換えたという報告が。これは安物買いの銭失いだろうか。しかし、安物とは言っても12,000円程度とどう考えても安物のルータの価格ではない。
ちなみに、決断が2週間遅ければNECのルータを選んでいたと思う。本当にタイミングが悪い。
モチベーション
しかし私は諦めない。
そのうち、再起動せざるを得ないということなら、先に再起動してしまえばいいのである。Buffaloのルータは、Webの設定画面に再起動ボタンが付いているので、これを自分や家族が寝ているうちに何とかして押すことを目指す。物理ボタンでもいいんだけど、ピタゴラスイッチを組んだらさすがに怒られそうなので今回は考慮しない。ちなみに、コードのテスト中にうっかりルータを何回か再起動させてしまい妻に怒られた。始末書ものである。
基本方針
毎日AM5:00にルータを再起動させる。バッチはPythonで記述し、動作環境はDockerコンテナ内に作成する。
バッチの全体の流れは以下のとおりである。
- ルータのWeb画面を開く
- ログイン情報を入力してログインボタンを押す
- 管理者としてログイン後、再起動ボタンを押す
- ルータ再起動
ログイン画面の突破
実装手順
Chromeの開発者モードを使ってデバッグを行うとログイン時のFormに入力した値が分かる。そこで、ログインに必要な値をバッチ内で生成、あるいは設定ファイルから読み込み、リクエストを作成してログインを試行すれば良い。しかし、問題はいきなりここで発生した。Formの中のInput要素にない、encrypted値の存在である。
暗号化
このように、HTMLの要素にない値が唐突に出てくる場面では、大半がJavaScriptの仕業であると考えられる。案の定、今回もSubmitを押した際にJavaScriptが走る仕様になっていた。
ところが、これを追いかけたところ、雲行きはどんどん怪しくなっていった。何と入力したパスワード文字列がRSAで暗号化されて、encryptedに格納されているのである。
BuffaloのルータのWeb画面はデフォルトでHTTP通信となっており、暗号化がされていない。恐らく、パスワードを保護するための暗号化だろう。こんなところに労力を割くならHTTPS化すればいいのに、大人の事情であろうか、何とも辛いものである。私も辛い。
挫折
RSAの暗号化はJavaScriptで書いてあったため、Pythonに移植しようと思ったが、PyCryptodomeで実装してもうまくいかず諦めた。たかだかルータのログイン画面なのになんでこんなに頑張ってしまったんだろう。ここまでに大体5時間かかった。
Selenium Web Driverの利用
暗号化部分が面倒なので、JavaScript部分はJavaScriptのまま動かしたいと考えていたところ、いっそのことChromeで動かしてしまえば良いではないかと思い当たり、Seleniumの導入を決意した。
Seleniumを入れたところで、全体の流れは基本方針のままである。最近はChrome Headlessの実装やPython wrapperなどが整っており、ログイン画面の突破と再起動ボタンを押下するまでは比較的簡単に実装することができた。はずだった。
ログイン画面の突破(再)
テストを繰り返す中で、何回かに一回しかログインができないことに気が付いた。しかも、ログインできない原因は「パスワードが違います」という画面に遷移するためであった。なんでやねん。
headlessモードを解除してデバッグしてみたら、過去にログインを試行した際のログイン名が自動入力されるようになっていた。取り急ぎ、設定値を入力する前に値をクリアすることで対応した。
コンテナ化
最新のlibrary/python:3.6であれば、chromium 66が利用できたため、これにChrome Web Driver 2.40とSelenium 3.11.0を利用した。コンテナであろうがおおよそ問題なく動く。
ただし、コンテナで動かす場合はChrome起動時のオプションに--disable-dev-shm-usageを付けた方がいいかもしれない。また、Cronで動かすときのdocker runのオプションは-itではなく、-iのみで動かす。
ここまででおおよそ10時間。前半の5時間を返して欲しいところではあるが、結果は上々である。
おわりに
Cronがdocker runするたびにBuffaloのルータが再起動するスクリプトができた。簡単なスクリプトだけの割にはイメージサイズが大きくなってしまい、少し悲しい。
コードはMITライセンスで以下に公開しましたので、ご参考までにどうぞ。大したことはしていないはず。
https://github.com/TK403/buffalo-rebooter