この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ども、大瀧です。
さくらのIoT Platformのαテストに参加しています。ドキュメントにてOutgoing Webhookのメッセージ署名アルゴリズムが公開されたので、今回はメッセージ署名をAWS Lambdaで検証するコードサンプルをご紹介します。
AWSの構成
さくらのIoT PlatformをAWSに連携するのエントリーと同じく、さくらのIoT PlatformのOutgoing WebhookでAmazon API GatewayのエンドポイントにPOSTリクエストを送出します。今回はAPI GatewayからAWS Lambdaを呼び出し、Lambdaでのリクエスト処理の中でメッセージ署名を検証することにしました *1。
AWS Lambdaの構成
今回はコードをランタイムNode.js 4.3向けに記述しました。古いNode.jsバージョンのランタイムでは動かないコードなので、注意してください。Outgoing Webhookのメッセージ署名はHMAC-SHA1で署名されているので、共有鍵(secret)を決めておき、後述のIoT Platformの設定で同じ値をセットします。署名自体はCryptoライブラリの一般的な検証フローでカバーできるので、特に難しい記述はありません。メッセージボディーをevent.body
、メッセージ署名をevent.signature
で参照しているので、API GatewayでLambdaに渡す値をこのあとの手順で調整します。
var crypto = require('crypto');
var secret = 'testkey'; # さくらのIoT PlatformのOutgoing WebhookのSecretに合わせる
exports.handler = function(event, context, callback) {
var signature = crypto.createHmac('sha1', secret).update(event.body).digest('hex');
if (event.signature == signature) {
console.log('The request was authorized.');
# 認証成功時の処理
} else {
# 認証失敗時の処理
callback(Error('The request was not authorized.'));
}
callback(null, event);
}
あとは、認証成功時の処理としてデータを登録したり、ログを残したりと任意の処理をアレンジしてください。Amazon ESにデータを投げてKibanaで可視化する例を次のブログで書きたいと思っています。
API Gatewayの構成
API Gatewayでは、IoT PlatformのOutgoing Webhookに合わせてリクエストヘッダとLambdaに渡すデータのマッピングを設定します。ポイントは2点あって、メッセージ署名が含まれるX-Sakura-Signature
ヘッダを拾うこととリクエストボディをパースせずにLambdaに渡すことです。では、実際の設定例を見ていきます。
まずは、API GatewayのAPIを作成し、適当なリソース(今回は/
)を作成、Outgoing Webhookの仕様に合わせてPOST
メソッドを定義します。[メソッドリクエスト]の[HTTPリクエストヘッダー]にX-Sakura-Signature
を設定しましょう。
続いて[統合リクエスト]でリクエストを処理するLambda関数を選択します。
[本文マッピングテンプレート]にContent-Type application/json
を追加し、以下のコードを記述します。
{
"body" : "$util.escapeJavaScript($input.body)",
"signature" : "$input.params().header.get('X-Sakura-Signature')"
}
ここで指定する要素がLambdaのevent
オブジェクトに対応します。body
要素はそのまま$input.body
を渡そうとするとJSONとしてパースされてしまうため、エスケーブ処理をかけています。signature
要素はなんとなく読めると思いますが、リクエストヘッダを取得して渡す感じです。
あと必須ではありませんが、認証に失敗したときのLambdaのエラーメッセージに合わせてAPI Gatewayから4XXレスポンスを返すようにするとAPIぽくなって良いと思います。
設定後は、ステージのデプロイを忘れずに実行しましょう。
さくらのIoT Platformの構成
以前の記事と同じく、デバイスはArduino UNOに接続しサンプルのスケッチを実行します。コントロールパネルでOutgoing Webhookサービスを追加し、API Gatewayのエンドポイントを指定します。
これでOKです。
動作確認
デバイスから1〜2秒間隔でカウントアップするデータが送信されるので、Lambdaのログを有効化してCloudWatch Logsで確認してみます。
認証が成功しているThe request was authorized.
というメッセージが出力されている様子がわかりますね!
手元のマシンからダミーのリクエストを投げてみると。。。
$ curl -s -v -X POST https://XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/ | jq '.'
* Trying 54.192.234.60...
* Connected to XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com (54.192.234.60) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.execute-api.ap-northeast-1.amazonaws.com
* Server certificate: Symantec Class 3 Secure Server CA - G4
* Server certificate: VeriSign Class 3 Public Primary Certification Authority - G5
> POST /prod/ HTTP/1.1
> Host: XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Content-Type: application/json
< Content-Length: 147
< Connection: keep-alive
< Date: Tue, 14 Jun 2016 07:54:43 GMT
< x-amzn-RequestId: 418d4000-3205-11e6-bc9a-2de3c94b9ce4
< X-Cache: Error from cloudfront
< Via: 1.1 df145b7fed9c16526e432395da27a563.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: -4kalFNmTpFpr8Se-yBiBnkOmSuKfhWcC4WiJPyjXD91vObbLVqycg==
<
{ [147 bytes data]
* Connection #0 to host XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com left intact
{
"errorMessage": "The request was not authorized.",
"errorType": "Error",
"stackTrace": [
"Error (native)",
"exports.handler (/var/task/index.js:23:18)"
]
}
エラーメッセージを含む403エラーが返ってきました。想定通りの動作ですね。
まとめ
さくらのIoT PlatformのOutgoing Webhookサービスのメッセージ署名を検証する処理を、AWS Lambdaで実装してみました。関係のないデバイスやホストからのデータ送出を遮断し、信頼性の高いデータ収集、分析を行う下地ができました。そろそろデータの可視化や具体的なセンサーデバイスのデータを取ってみたくなってきましたw