UniFi OSコンソールのAPIを叩いてみる

Programming

操作画面がクリーンで高機能なUniFi製品ですが、(非公認ではあるものの)様々なAPIを備えているようです。うまく活用すれば、コマンドを定期実行したり、状態を定期的に収集して既に運用しているログ監視ツールと連携したりすることもできそうです。この記事ではその一端をご紹介します。

スポンサーリンク

きっかけ

UniFiのWebインタフェースには、回線のスピードテストを測定する機能があります。

それを利用して自宅で使用しているフレッツ光クロスの実効速度を何度か測ったのですが、時間帯によって速度に変化があることがわかりました。

その傾向をより正確に掴みたいなと思ったのですが、Webインタフェースでは一日に一回指定した時間にスピードテストを自動で実行してくれるものの、それ以上の頻度で実施するには手動でスピードテストを実行する必要があります。

これを自動化できないかというのが今回のきっかけです。UniFiのことだから外から叩けるAPIがあったりするのではないか?という淡い期待から探し始めました。

Community Wikiに非公式APIの情報が記載されている

Ubiquitiの製品は、特に海外においてユーザコミュニティが充実していて、その代表がこちらのWikiです。

Just a moment...

ハードウェアの詳細スペックなど、オフィシャルサイト以上の情報が集まっていて非常に興味深いのですが、ソフトウェアのセクションを眺めると、期待通りUniFi ControllerのAPI一覧が載っているページが見つかりました。

これを使って定期的にスピードテストを実行させてみようと思います。

スポンサーリンク

定期的にスピードテストを実行させる

まずはワンショットでスピードテストを実行させる

上記のAPI一覧を見ると、APIは以下のように使うようです(cURLの例)。

# authenticate and save the cookie contents in local file cookie.txt with switch '-c'
curl -k -X POST --data '{"username": "<USERNAME>", "password": "<PASSWORD>"}' --header 'Content-Type: application/json' -c cookie.txt https://<ADDRESS>/api/auth/login
# pass the local file cookie.txt with switch '-b'
curl -k -X GET -b cookie.txt https://<ADDRESS>/proxy/network/api/s/default/self
  • <USERNAME>
  • <PASSWORD>
  • <ADDRESS>

は適切な値にします。

二つ目をスピードテストに切り替えると以下になります。

# authenticate and save the cookie contents in local file cookie.txt with switch '-c'
curl -k -X POST --data '{"username": "<USERNAME>", "password": "<PASSWORD>"}' --header 'Content-Type: application/json' -c cookie.txt https://<ADDRESS>/api/auth/login
# pass the local file cookie.txt with switch '-b'
curl -k -X POST --data '{"cmd": "speedtest"}' -b cookie.txt  --header 'Content-Type: application/json' https://<ADDRESS>/proxy/network/api/s/default/cmd/devmgr

…これでシンプルに動くかと思ったのですが、結果は…

{"message":"Invalid CSRF Token","level":"debug"}

CSRF Tokenが無効と言われてしまいました。

CSRF Tokenはおそらく一つ目のレスポンスに入っているはずなのでヘッダを出力して確かめてみます。

curl -k -X POST --dump-header - --data '{"username": "<USERNAME>", "password": "<PASSWORD>"}' --header 'Content-Type: application/json' -c cookie.txt https://<ADDRESS>/api/auth/login
HTTP/2 200 
vary: Origin
x-dns-prefetch-control: off
expect-ct: max-age=0
x-frame-options: SAMEORIGIN
strict-transport-security: max-age=15552000; includeSubDomains
x-download-options: noopen
x-content-type-options: nosniff
x-permitted-cross-domain-policies: none
referrer-policy: no-referrer
x-xss-protection: 0
accept-ranges: bytes
content-type: application/json; charset=utf-8
x-csrf-token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
x-response-time: 171ms
set-cookie: TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; path=/; samesite=none; secure; httponly
content-length: 7213
date: Fri, 12 May 2023 10:19:38 GMT

x-csrf-tokenが入ってますね。それでは、これを二つ目のリクエストにつっこんでみます。動作確認なんで、ひとまずgrepで適当に…。

#!/bin/bash

token=`curl -k -X POST --dump-header - --data '{"username": "<USERNAME>", "password": "<PASSWORD>"}' --header 'Content-Type: application/json' -c cookie.txt https://<ADDRESS>/api/auth/login | grep x-csrf-token`
curl -k -X POST --data '{"cmd": "speedtest"}' -b cookie.txt --header 'Content-Type: application/json' --header "$token" https://<ADDRESS>/proxy/network/api/s/default/cmd/devmgr 

これを実行すると

{"meta":{"rc":"ok"},"data":[]}

okが返ってきて、実際にスピードテストが実行されました!めでたい。

スクリプトを定期実行させる

あとは、スクリプトファイルにして適当な場所に置き、cronで定期実行されるようにすればOKです。

/etc/cron.d/の下に適当な名前でファイルを置き、

MAILTO=""
0 */3 * * * root /root/speedtest.sh

3時間ごとに定期実行させるようにしました。

このファイルは再起動すると消えてしまうようですが(追記:再起動しても残っていました)、ひとまずはこれで数日様子を見てみます。

結果

Webインタフェースを確かめると、きちんと3時間ごとにスピードテストが実行されているようです。めでたい。

ただし、長期間続けるとサーバ側に負荷がかかり迷惑をかけてしまうので、長期にわたってやり続けるのは避けた方が良いです。念の為。

スポンサーリンク

スピードテスト結果を取得するAPIは?

上記のスクリーンショットはWebインタフェースのものですが、せっかくなのでスピードテストの結果をバルクで取得できると嬉しいです(よね?)。

そこで改めてWikiを確認してみると…該当しそうなAPIはありません。最新のデータを取得することはできるのですが。Wikiは全ての情報が網羅的に載せられているわけではないようです。うーん。

巷にあるライブラリのコードを調べる

WikiにはAPIの情報が載っているだけですが、GitHubなどにはそれらを各言語でラップしたライブラリがいくつか存在しています。

それらをチラ見していたところ、PHPのライブラリにそれっぽい関数があることに気づきました。

GitHub - Art-of-WiFi/UniFi-API-client: A PHP API client class to interact with Ubiquiti's UniFi Controller API
A PHP API client class to interact with Ubiquiti's UniFi Controller API - GitHub - Art-of-WiFi/UniFi-API-client: A PHP API client class to interact with Ubiquiti's UniFi Controller API
public function stat_speedtest_results($start = null, $end = null)
    {
        $end     = empty($end) ? time() * 1000 : intval($end);
        $start   = empty($start) ? $end - (24 * 3600 * 1000) : intval($start);
        $payload = ['attrs' => ['xput_download', 'xput_upload', 'latency', 'time'], 'start' => $start, 'end' => $end];
        return $this->fetch_results('/api/s/' . $this->site . '/stat/report/archive.speedtest', $payload);
    }

PHPはあまり詳しくないですが、

  • attrsに受け取りたい情報を指定し、
  • さらにtimestamp(ミリ秒)の範囲を指定して
  • /api/s/default/stat/report/archive.speedtestを叩いている

ようです。では、これをcURLで叩いてみましょう。

#!/bin/bash

token=`curl -k -X POST --dump-header - --data '{"username": "<USERNAME>", "password": "<PASSWORD>"}' --header 'Content-Type: application/json' -c cookie.txt https://<ADDRESS>/api/auth/login | grep x-csrf-token`
curl -k -X POST --data '{"attrs": ["xput_download", "xput_upload", "latency", "time"], "start": 1683864000000, "end": 1683896400000}' -b cookie.txt --header 'Content-Type: application/json' --header "$token" "https://<ADDRESS>/proxy/network/api/s/default/stat/report/archive.speedtest" 

さて結果はと言うと、

{
    "meta": {
        "rc": "ok"
    },
    "data": [
        {
            "xput_download": 6044.109375,
            "xput_upload": 4539.74658203125,
            "latency": 11,
            "_id": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "oid": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "time": 1683871217000,
            "o": "speedtest"
        },
        {
            "xput_download": 5801.31103515625,
            "xput_upload": 4177.75341796875,
            "latency": 8,
            "_id": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "oid": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "time": 1683882017000,
            "o": "speedtest"
        },
        {
            "xput_download": 1274.9173583984375,
            "xput_upload": 2726.9267578125,
            "latency": 14,
            "_id": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "oid": "xxxxxxxxxxxxxxxxxxxxxxxx",
            "time": 1683892818000,
            "o": "speedtest"
        }
    ]
}

Webインタフェースでは表示されないレイテンシ含めて取得できました!

さらに、UDM-SEの中に入ってxput_downloadでjsファイルをgrepかけたところ、

{ ping: Math.round(a.latency || 0), xput_download: Math.ceil(a.xput_download), xput_upload: Math.ceil(a.xput_upload), server: a.server }

こんなことをやっている部分があったので、serverに関しても追加で情報収集できるようです(やったらできました)。

"server": {
                "cc": "JP",
                "country": "Japan",
                "city": "Tokyo",
                "provider": "Misaka Network, Inc.",
                "lon": 139.769,
                "provider_url": "https://www.misaka.io/",
                "lat": 35.6803
            },

(自己)満足。

スポンサーリンク

まとめ

スピードテストを定期実行させたいと思ったことをきっかけに、UniFi OSコンソールのAPIの叩き方を知り、実際にデータを取得することができました。

APIを叩いてデータを取得できると、Observability系の他システムと連携させる時などに役立ちそうです。非公認のため突然使えなくなるリスクはありますが、Webインタフェースをスクレイピングするよりは安定しているような気もします(UbiquitiのコミュニティページでもAPIに関する質問がたまに出てきているので、黙認といったところかも)。

ただ、自分でヒント0でAPIを探すのは難しい気がします。UniFi Dream Machineの中にSSHで入ると、主なコードはnode.jsで書かれていることがわかるのですが、どのようなエンドポイントが公開されているかはぱっと見では分かりません(内部的にはgRPCで呼んでるっぽいけどprotoファイルはなかった)。

差し支えない範囲でオフィシャルに情報公開されるのがベストだと思いますが、まずは先人の成果にのっかる形でAPIを叩くのが良いと思います。いくつかのコードを覗いてみましたが、上記PHPライブラリのプロジェクトが多くのAPIに対応しているようですので、他の言語で実装する時にも参考にするとよいのではないかと思います。

蛇足:スピードテストの結果

スピードテストの結果は一過性のものなので、長期にわたってブログに掲載するのもどうかとは思いますが、現時点での傾向を記録に残すということで、掲載します。

テストの実行はかなりラフで、その間ネットワークの利用を控えたりはしていません。あくまで雰囲気ということでご了承ください。

環境は

  • フレッツ光クロス(ドコモ光10ギガ) + ドコモnet(IPoE + MAP-E)
  • NTT東日本エリア
  • XG-100NEとUDM-SEはCAT6Aケーブルで接続
  • 2023/5/9から1週間、3時間おきに計測

となります。

ドコモ光は昼間が強い、という噂をまことしやかに聞きましたが、結果その通りのようですね。またしばらくして気が向いたら計測してみます。

コメント

タイトルとURLをコピーしました