soracom-cli のちょっと便利な Tips

この記事は 株式会社ソラコム Advent Calendar 2021 の 12/4 の記事です。

qiita.com

こんにちは。私はソラコムで soracom-cli を開発しています。soracom-cli は、IoT プラットフォーム SORACOM が提供している APIコマンドラインシェルスクリプトなどから簡単に呼び出せるコマンドです。

例えば、持っている SIM の一覧を取得するには

soracom sims list

というコマンドを実行するだけです。 結果は JSON で返ってくるので、jq コマンドなどでさらに処理をつなげることができて便利です。

インストール方法はとっても簡単で、README に書いてありますが、MacLinux であれば

brew tap soracom/soracom-cli
brew install soracom-cli

これだけでインストールできます。

ではここからは、すでに soracom-cli をお使いいただいている皆様のために、あまり知られていないけど知っているといざというときちょっと便利な Tips を 2 つご紹介したいと思います。

API Key や API Token が欲しい・・・」

何らかの理由で唐突に API Key や API Token が欲しくなることって、よくありますよね。

たとえば API リファレンスでちょっと API を試してみたくなったときとか(通常はそのページから認証を行って API Key や API Token を取得しますが、実はこのページに API Key / API Token / Operator ID を直接手で入力することも可能なのです!)、soracom-cli でたくさんのコマンドを実行したいときに実行時間を短縮させるために認証を一度だけ行って API Key と API Token を使いまわしたい時とか、あるいは API Token の JWT を無性に解読してみたくなったときとか、自分の API Key の UUID を占いに使いたくなったときとか、自分の Operator ID で語呂合わせを考えたくなったりしたような場合です。(まともな用途が一つしかありませんね・・・さてどれでしょう!?)

そんなときには soracom auth コマンドを実行していただくことでいつでも簡単に API Key や API Token が欲しいという欲求を満たしていただくことができます。

soracom auth --email <email> --password <password>

しかし、メールアドレスはまだしもパスワードを直接書くのはなんか嫌ですね。シェルから実行するとしても画面を後ろから覗き込んでる人がいないかとか心配になりますし、実行した後もヒストリに残ったりしてしまいます。ましてやスクリプトの中になんか絶対に書いてはいけませんね。

そもそも意識の高い皆様はパスワード認証ではなく認証キーによる認証を行われていることでしょうから、keyId-NIWXcjCh..... とか secret-pJHehi5i..... みたいな記号の羅列を記憶しておいて入力するなんてことは現実的ではありませんし、これもスクリプトに書いておいてはいけない秘密の情報です。

ではどうすればよいのか?

みなさん、soracom-cli を使い始める一番最初に soracom configure を実行されたかと思います。

そのときに、パスワードとか認証キーを入力しましたよね? それは安全な形で(自分自身しか読み書きできない権限で)コンピュータのストレージに保存されています。

これを使えばいいのです!

では soracom configure したときに入力したそれらパスワードや認証キー(いわゆるクレデンシャル)はどこに保存されているのでしょうか?

答えは $HOME/.soracom/ ディレクトリです。(MacLinux の場合。Windows の場合は %HOME%\.soracom\ もしくは %USERPROFILE%\.soracom\

soracom configure--profile 引数を指定せずに実行した場合はそのディレクトリに default.json という名前で保存されています。

試しに cat $HOME/.soracom/default.json を実行してみてください。

パスワードもしくは認証キー情報が見えましたか?

ではこれを使うにはどうしたら良いでしょう? JSON ファイルなので jq コマンドで取り出す?

email="$( cat $HOME/.soracom/default.json | jq -r .email )"
password="$( cat $HOME/.soracom/default.json | jq -r .password )"
soracom auth --email "$email" --password "$password"

いえいえ、実はもっと簡単な方法があるのです。以下のようにして、soracom auth コマンドに直接そのファイルを渡してしまえばよいのです。

soracom auth --body @"$HOME/.soracom/default.json"

SORACOM の API は HTTP によるいわゆる REST API ですので、--body 引数は、API を呼び出すときのリクエストの Body の内容を指定するための引数です。

--body 引数の最初の文字を @ にすることで、それ以後の文字列はファイル名の指定とみなされます。その指定されたファイルの中身を読み込んでそのまま body として渡すという動作になります。

そして $HOME/.soracom/default.json に保存されている JSON ファイルの形式は、実は Auth API のリクエストボディと互換性のあるフォーマットになっているのです!なのでこのファイルを直接引数に渡すことが可能となっているのです。

もし複数の profile を使いこなされている場合は、引数の JSON ファイルを使い分けることで、それぞれの profile のための API Key や API Token を簡単に入手できます。

cred1="$( soracom auth --body @"$HOME/.soracom/profile1.json" )"
cred2="$( soracom auth --body @"$HOME/.soracom/profile2.json" )"

soracom-cli が実際に送受信しているデータを盗み見たい

何らかの理由で唐突に soracom-cli がやり取りしている HTTP リクエスト・レスポンスの中身を見たくなることって、よくありますよね。

レスポンスは大抵の場合は soracom-cli が表示する JSON そのものですから、主に見たいと思うのはリクエストのヘッダーやボディ、それにレスポンスのヘッダーなどかと思います。

たとえば「自分が指定したパラメータは期待したとおりに送信されているのだろうか?」とか「この API を自分のプログラムから呼び出してみたいけど、パラメータの指定の仕方がよくわからないなぁ。soracom-cli はどうやって送っているのだろう?」といったような場合です。

そんなときはとても簡単です。

SORACOM_VERBOSE=1 soracom sims list

このように SORACOM_VERBOSE 環境変数に何らかの値を設定してあげればよいのです。 (上の例では 1 を指定していますが、今のところこの値に特に意味はありません。この環境変数が空でなければ verbose な出力が得られます。)

これを付けた状態で実行してみると、以下のような出力が得られると思います。(センシティブな情報は (snip) で置き換えてあります)

$ SORACOM_VERBOSE=1 soracom sims list
POST /v1/auth? HTTP/1.1
Host: api.soracom.io
Content-Type: application/json
User-Agent: soracom-cli/0.10.4
X-Soracom-Lang: en

{"email":"(snip)","password":"(snip)"}
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/json
Date: Wed, 01 Dec 2021 06:46:59 GMT
X-Soracom-Cli-Version: v0.10.4


==========
GET /v1/sims? HTTP/1.1
Host: api.soracom.io
User-Agent: soracom-cli/0.10.4
X-Soracom-Api-Key: (snip)
X-Soracom-Lang: en
X-Soracom-Token: (snip)


HTTP/1.1 200 OK
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/json
Date: Wed, 01 Dec 2021 06:46:59 GMT
Vary: Accept-Encoding
X-Soracom-Cli-Version: v0.10.4


==========
[
        {
                "activeProfileId": "(snip)",
                "capabilities": {
                        "data": true,
                        "sms": false
                },
                "createdTime": 1445512349601,
                "expiryAction": null,
                "expiryTime": null,
                "groupId": null,
                ...

これを見ると、以下のようなことがわかります。

  1. まず最初に /v1/auth API が呼び出されていて認証処理が行われています。認証結果は表示されていませんが、実際にはレスポンスとして返って来ています。
  2. 次に /v1/sims API が呼び出されていてその結果が表示されています。/v1/auth API の結果で得られた API Key や API Token がリクエストのヘッダに指定されています。

次に、以下のような引数を渡すようなコマンドを実行してみましょう。どのように API が呼ばれるかがわかります。

$ SORACOM_VERBOSE=1 soracom sims update-speed-class --sim-id 89xxxxxxxxxxxxxxxxx --speed-class s1.4xfast
POST /v1/auth? HTTP/1.1
Host: api.soracom.io
Content-Type: application/json
User-Agent: soracom-cli/0.10.4
X-Soracom-Lang: en

{"email":"(snip)","password":"(snip)"}
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/json
Date: Wed, 01 Dec 2021 07:10:42 GMT
X-Soracom-Cli-Version: v0.10.4


==========
POST /v1/sims/89xxxxxxxxxxxxxxxxx/update_speed_class? HTTP/1.1
Host: api.soracom.io
Content-Type: application/json
User-Agent: soracom-cli/0.10.4
X-Soracom-Api-Key: (snip)
X-Soracom-Lang: en
X-Soracom-Token: (snip)

{"speedClass":"s1.4xfast"}
HTTP/1.1 200 OK
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/json
Date: Wed, 01 Dec 2021 07:10:42 GMT
Vary: Accept-Encoding
X-Soracom-Cli-Version: v0.10.4


==========
{
        "activeProfileId": "(snip)",
        "capabilities": {
                "data": true,
                "sms": false
        },
        "createdTime": 1590234187495,
        "expiryAction": null,
        "expiryTime": null,

        ...
        "speedClass": "s1.4xfast",
        ...

--sim-id で指定した SIM ID は POST 時の path の一部として、--speed-class で指定した速度クラスは body の JSON の中に入ってリクエストとして送られていることがわかりますね。

なお、SORACOM_VERBOSE を指定することで表示されるようになった部分は標準エラー出力 (stderr) に出力されており、API のレスポンスは従来どおり標準出力 (stdout) に出力されていますので、標準エラー出力の方は人間が目で確認しつつ標準出力の方はパイプなどで他のプログラムに渡して処理を行うといったようなこともできるようになっています。(既存のスクリプトデバッグのときなどに役立つかもしれません)

また、レスポンスのヘッダに X-Soracom-Cli-Version が含まれていることにお気づきでしょうか? このヘッダには現在リリースされている最新版の soracom-cli のバージョンが含まれています。 SORACOM API 側は、リクエストの User-Agent を見て soracom-cli からのリクエストのようであると判断した場合このヘッダを返します。 soracom-cli 側はこのヘッダを受け取ったら、自分自身のバージョンと比較して、新しいバージョンがリリースされている場合に以下のようにお知らせしてくれます。

$ soracom subscribers list
The latest version v0.10.4 is released which is newer than current version v0.10.3. Please update to the latest version.
[
        {
                "apn": "soracom.io",
                "createdAt": 1463402658584,
                "createdTime": 1463402658584,
                "expiredAt": null,
                "expiryAction": "doNothing",
                "expiryTime": null,
                ...

日本語環境ですと、以下のように日本語のメッセージになります。

現在お使いのバージョン v0.10.3 より新しい v0.10.4 がリリースされています。アップデートしてください。

このメッセージも標準エラー出力に出力されますので、標準出力を利用している既存のスクリプトを壊してしまうことがないようになっているはずです。

さいごに

いかがでしたでしょうか。今後のみなさまの soracom-cli ライフが捗りそうな情報は一つでもありましたか?

soracom-cliオープンソースとして開発されていますので、フィードバック、リクエスト、いつでも(クリスマスの時期に限らず) お寄せください。