ちょっと話題の記事

SORACOM BeamとSORACOM APIでSIM単位の認証を実装する #soracom

2015.10.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。
9/30に発表されたIoTプラットフォームのSORACOM、みなさん触っていますか?今回はSORACOMのプロキシサービスであるSORACOM BeamSORACOMのWeb APIを組み合わせた、SIM単位の認証をご紹介します。

アーキテクチャ概要

SORACOM Beamには、バックエンドへのトラフィック転送時にSIMの固有IDであるIMSI(イムジー)をトラフィックに付与する機能があります。一方、SORACOMのWeb APIにはSIMごとのIMSIを参照するAPIがあります。これらを組み合わせることで、SIMを搭載するデバイスでIMSIを意識することなく、バックエンドでIMSIによる認証を行うことができます。認証に関する実装がデバイスに無いということは実装が楽になるうえに、IMSIの詐称を防ぎ、デバイスの盗難にも効果があると考えられます。バックエンドの認証もSORACOM APIに問い合わせることで、バックエンド側でのIMSI管理が不要になります。

今回はSORACOMの開発者向けドキュメントをベースに、Amazon API Gateway + AWS Lambdaという以下の構成で組んでみました。

beam-auth01

  • 検証したリージョン : オレゴン(us-west-2)リージョン
  • soracomモジュールのバージョン : 0.0.3

1. AWS Lambdaの実装

まずは、バックエンドのロジックになるLambda関数を実装します。今回はNode.jsで記述してみます。SORACOM APIのコールには、昨日公開されたばかりのsoracomモジュールを利用してみました。

以下のindex.jsを作成しました。SORACOM APIのGET /subscribers(Subscriber≒SIMの一覧)を叩いてAPI Gatewayから渡されるIMSI(event.imsi)とマッチングする、最低限のコードです。認証の用途によっては、SORACOMの特定グループにIMSIが含まれるかをGET /groups/{group_id}/subscribersで取ってくるのも良いでしょう。SORACOM APIについては、こちらのエントリーを参照ください。

index.js

var email    = 'YOUR_SORACOM_EMAIL'
var password = 'YOUR_SORACOM_PASSWORD'
var Soracom  = require('soracom');

exports.handler = function(event, context) {
  var soracom = new Soracom({ email: email, password: password });
  soracom.post('/auth', function(err, res, auth) {
    if (!err) {
      soracom.defaults(auth);
      soracom.get('/subscribers', function(err, res, body) {
        var result = body.some(function(subscriber) {
          return subscriber.imsi == event.imsi;
        })
        if(result) {
          context.succeed({ Message: 'Authorization succeed : ' + event.imsi });
        }else {
          context.fail('Not authorized your device');
        };
      });
    }
  });
};

soracomモジュールを同じディレクトリにインストールし、ZIPで固めてバンドルを作成します。

$ npm install soracom
soracom@0.0.3 node_modules/soracom
└── request@2.64.0 (aws-sign2@0.5.0, forever-agent@0.6.1, stringstream@0.0.4, oauth-sign@0.8.0, caseless@0.11.0, tunnel-agent@0.4.1, isstream@0.1.2, json-stringify-safe@5.0.1, extend@3.0.0, node-uuid@1.4.3, qs@5.1.0, form-data@1.0.0-rc3, tough-cookie@2.1.0, mime-types@2.1.7, combined-stream@1.0.5, bl@1.0.0, http-signature@0.11.0, hawk@3.1.0, har-validator@1.8.0)
$ ls
index.js      node_modules/
$ zip -r ../soracom-lambda.zip *
  : 
$

Lambda管理画面からLambda関数を新規作成し、作成したバンドルをアップロードします。画面では[Step 1: Select blueprint]は[Skip]で飛ばし、[Code entry type]で「Upload a .ZIP file」を選択しアップロードします。他の設定は任意で問題ありません。

soracom-lambda01

続いて、作成したLambda関数のプロパティから[API endpoints] - [Add API endpoint]をクリックします。

soracom-lambda02

[API endpoint type]に「API Gateway」、[API name]にAPI Gatewayで作成する適当なAPI名、[Deploment stage]はAPI Gatewayで作成する適当なステージ名、[Security]は[Open with access key]を選択して[Submit]をクリックします。

soracom-lambda03

Lambda実行のトリガーとなるAPI Gatewayの定義が作成されました。

soracom-lambda04

2. API Gatewayの構成

API Gatewayの管理画面に切り替え、作成されたAPIのツリーから/SoracomLambda/GETを選択、右ペインの[Method Request]をクリックします。

soracom-lambda05

[HTTP Request Headers] - [Add header]をクリックします。

soracom-lambda06

[Name]列に「X-SORACOM-IMSI」と入力し、チェックをクリックして確定します。このあとのRequest Integrationと組み合わせてBeamが付与するX-SORACOM-IMSI(IMSIが入る)ヘッダをLambdaに渡します。

soracom-lambda07

ペインの左上にある[←Method Execution]で前の画面に戻り、[Integration Request]をクリックします。

soracom-lambda08

[Lambda Function]の項目にある鉛筆アイコンをクリックし、関数名は変更せずにチェックをクリックします。

soracom-lambda09

API GatewayからLambda関数を実行するための権限付与を確認するメッセージが表示されるので、[Ok]をクリックします。

soracom-lambda10

続いて、[Mapping Templates] - [Add mapping template]をクリックします。

soracom-lambda11

[Content-Type]列に「application/json」と入力、ペイン右上のドロップダウンから「Mapping template」を選択し、右下のテキストエリアに以下を貼り付けます。これでLambda関数からevent.imsiでIMSIが参照できるようになります。チェックマークをクリックして確定します。

#set($inputRoot = $input.path('$'))
{
  "imsi" : "$input.params('X-SORACOM-IMSI')"
}

soracom-lambda12

画面左上の[Deploy API]をクリックし、[Deployment Stage]から「1」を選択、[Deploy]をクリックしてデプロイします。

soracom-lambda13

デプロイしたステージにAPIキーを設定します。API Gatewayのメニューから[APIs] - [API Keys]をクリックします。

soracom-lambda14

任意のキー名でAPIキーを作成し、[API Stage Association]から作成したAPI、Stageを選択し、[Add]をクリックします。

soracom-lambda15

これで、AWS側の設定は完了です。

3. SORACOM Beamの構成

SORACOMユーザーコンソールでBeamの設定を追加します。Beamの設定はグループに対して行うため、メニューの[グループ]からグループを選択、[+] - [HTTPエントリポイント]をクリックします。

soracom-lambda16

任意の設定名を入力、[エントリポイント]は[パス]に「/」を入力します。

soracom-lambda17

転送先は[ホスト名]にAPI Gatewayのエンドポイントのホスト名XXXX.execute-api.<リージョン名>.amazonaws.com、[ポート番号]は空欄のまま、[パス]にデプロイしたステージのパスを入力します。

soracom-lambda18

ヘッダ操作は[IMSIヘッダ付与]を「ON」にし、[+]をクリックしてカスタムヘッダを構成します。

soracom-lambda19

API GatewayのAPIキーを[アクション]:「追加」、[ヘッダ名]:「X-API-KEY」、[値]にキーの文字列を入力します。

soracom-lambda20

これで設定完了です。

3. 動作確認

それでは、SORACOM Air SIMを搭載したマシンからcURLでリクエストを送ってみます。

vagrant@debian-wheezy:~$ curl http://beam.soracom.io:8888/
{"Message":"Authorization succeed : <SIMのIMSI>"}
vagrant@debian-wheezy:~$

クライアントではヘッダを一切操作していないにもかかわらず、認証に通りました!Beamがヘッダを付与している様子がわかります。今度は、API Gatewayのキーのみを付与して、Beamを経由せずAPI Gatewayに直接リクエストを送ってみます。

vagrant@debian-wheezy:~$ curl -H "X-API-KEY:XXXX" https://XXXXXXXXXX.execute-api.us-west-2.amazonaws.com/1/SoracomLambda
{"errorMessage":"Not authorized your device"}
vagrant@debian-wheezy:~$

IMSIが付与されていないため、認証が通っていないことがわかりますね!

まとめ

Beamのヘッダ付与機能を利用して、IMSIによるSIM単位の認証の実装をご紹介しました。今回はバックエンドがAPI Gateway+LambdaでしたがHTTPヘッダの読み取りとHTTPSのAPIコールできれば、任意のサーバーでも同様の実装が可能です。mod_mruby/ngx_mruby/h2o_mruby(h2oにマージ済み)あたりを使うと、楽に実装出来そうな気がします。

従量課金のSIMだけではない、SORACOMの魅力を感じ取っていただければ嬉しいです。