[AWS IoT Core] カスタム認証を使用してHTTPSでPublishしてみました

2021.08.21

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

1 はじめに

IoT事業部の平内(SIN)です。

AWS IoT Coreへのアクセスでは、カスタム認証によって、独自の認証認可を構築することができますが、今回は、HTTPSを使用して、カスタム認証でメッセージブローカーにメッセージをパブリッシュしてみました。

下記は、IoT Coreでサポートされているプロトコルの一覧ですが、このうちの9番目のものとなります。


Protocols, authentication, and port mappings

No. Protocol Operations supported Authentication Port ALPN protocol name
1 MQTT over WebSocket Publish, Subscribe Signature Version 4 443 N/A
2 MQTT over WebSocket Publish, Subscribe Custom authentication 443 N/A
3 MQTT Publish, Subscribe X.509 client certificate 443 x-amzn-mqtt-ca
4 MQTT Publish, Subscribe X.509 client certificate 8883 N/A
5 MQTT Publish, Subscribe Custom authentication 443 mqtt
6 HTTPS Publish only Signature Version 4 443 N/A
7 HTTPS Publish only X.509 client certificate 443 x-amzn-http-ca
8 HTTPS Publish only X.509 client certificate 8443 N/A
9 HTTPS Publish only Custom authentication 443 N/A


参考:How to Use Your Own Identity and Access Management Systems to Control Access to AWS IoT Resources

2 Lambda

HTTPSでアクセスした場合、下記のようなリクエストがLambdaに到着します。

{
    "protocolData": {
        "tls": {
            "serverName": "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
        },
        "http": {
            "headers": {
                "x-amz-customauthorizer-signature": "xxxxxxxxxxxx",
                "content-length": "22",
                "host": "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com",
                "user-agent": "my-agent",
                "x-amz-customauthorizer-name": "custom_auth_2021_08_21",
                "content-type": "application/x-www-form-urlencoded",
                "accept": "*/*",
                "my-token": "sample-token"
            },
            "queryString": ""
        }
    },
    "protocols": [
        "tls",
        "http"
    ],
    "token": "sample-token",
    "signatureVerified": True,
    "connectionMetadata": {
        "id": "5c72112f-3510-5f81-42e5-d43acc09a1ac"
    }
}

そして、Lambdaから返すのは、以下の内容になります。

  • IsAuthenticated: 認証の有効無効を返すブール値
  • PrincipalId: 識別子(1〜128文字)
  • PolicyDocuments: JSON形式のポリシー(各ポリシーは、2,048文字以内で、10個まで指定可能)
  • DisconnectAfterInSeconds: AWS IoT ゲートウェイへの接続期間(300〜86,400秒)
  • RefreshAfterInSeconds: ポリシーをキャッシュする期間(300〜86,400秒)

オーソライザーで署名を有効としている場合、署名の検証に成功した場合(signatureVerifiedは、Trueとなっています)だけ、Lambdaがコールされるので、ここでは、下記のようなコードで、トピック(my-topic)にPublishできる権限を返すことにします。

custom_auth_http_2021_08_21

import json

policy = {
  "Version": "2012-10-17",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": "iot:Publish",
        "Resource": "arn:aws:iot:ap-northeast-1:*:topic/my-topic"
    }
  ]
}

def lambda_handler(event, context):
    
    print("eventy: {}".format(json.dumps(event)))
    
    is_authenticated = False
    if "signatureVerified" in event:
        is_authenticated = event["signatureVerified"]

    principal_id = "CustomAuthId"
    disconnect = 84000
    refresh = 300
    
    return {
        "isAuthenticated": is_authenticated,
        "principalId": principal_id,
        "disconnectAfterInSeconds": disconnect,
        "refreshAfterInSeconds": refresh,
        "policyDocuments": [
            policy
        ]
    }

3 秘密鍵・公開鍵の作成

署名が有効なオーソライザを作成するためには、公開鍵の登録が必要です。

下記では、opensslを使用して、秘密鍵(id_rsa)と公開鍵(id_rsa.pub)を作成しています。

% openssl genrsa -out id_rsa 4096
% openssl rsa -in id_rsa -pubout -out id_rsa.pub

% cat id_rsa.pub
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxHb3PH7Mmfufj8pKhX78
・・・略・・・
e6iQM2IL41oq1bTk8McSG2ECAwEAAQ==
-----END PUBLIC KEY-----

4 オーソライザー

IoT Coreのコンソールから安全性 - オーソライザーと辿り、新規にオーソライザーを作成します。 設定した内容は、以下の通りです

  • オーソライザーの名前: custom_auth_2021_08_21としました
  • オーソライザー関数: custom_auth_http_2021_08_21 上記で作成したもの
  • トークン署名を有効化: チェック(デフォルト値)
  • トークンヘッダ名: my-token としました
  • キー名: device-001としました
  • 値: 上記で作成した公開鍵
  • オーソライザーアプティブ: チェック

「オーソライザーの名前」「トークンヘッダ名」は、アクセス時に必要なので、名前を控えておきます。「オーソライザーアプティブ」は、デフォルトで、チェックされていませんが、オーソライザー自体を有効とするためにチェッックを忘れないように注意が必要です。

IoT Coreのコンソールからオーソライザーを作成すると、設定したLambdaに自動的にリソースベースのポリシーが追加されます。

5 動作確認

動作確認のために、トークンに署名を行います。トークンは、何でも構いませんが、ここでは、sample-tokenとしました。

% echo -n "sample-token" | openssl dgst -sha256 -sign id_rsa | openssl base64 -A

curlでアクセスする場合、以下のようになります。

curl -X POST -k -i -N -H "Host: <endpoint>" \
-H "X-Amz-CustomAuthorizer-Name: <authorizer_name>" \
-H "X-Amz-CustomAuthorizer-Signature: <token_signature>" \
-H "<token_key_name>: sample-token" \
-H "User-Agent: my-agent" \
--data 'Hello from custom auth' https://<endpoint>/topics/my-topic
  • endpooint IoT Coreのエンドポイントです
% aws iot describe-endpoint --endpoint-type iot:Data-ATS
  • authorizer_name オーソライザーの名前です。(custom_auth_2021_08_21)
  • token_signature 署名したシグネチャ openssl dgstで出力したもの
  • token_key_name オーソライザーの設定で指定した「トークンヘッダ名」(my-token)

アクセスに成功すると、StatusCode 200が返されます。

% curl -X POST -k -i -N -H "Host: xxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com" -H "X-Amz-CustomAuthorizer-Name: custom_auth_2021_08_21" -H "X-Amz-CustomAuthorizer-Signature: xxxx" -H "my-token: sample-token" -H "User-Agent: my-agent" --data 'Hello from custom auth' https://xxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com/topics/my-topic
HTTP/1.1 200 OK
content-type: application/json
content-length: 65
date: Sat, 21 Aug 2021 00:44:31 GMT
x-amzn-RequestId: 5f5376f1-243c-d8a6-00e9-26aa376ab2cd
connection: keep-alive

テストクライアントで、到着しているメッセージが確認できます。

6 最後に

今回は、HTTPSを使用して、カスタム認証でメッセージブローカーにメッセージをパブリッシュしてみました。

この仕組みを利用すると、HTTPSからのメッセージ送信は、「認証情報」や「X.509クライアント証明書」が必要なくなります。 送信ヘッダを自由にプログラムできる場合は、一つの手段として有力かもしれません。

7 参考リンク


AWS IoT Enhanced Custom Authorizer Demo
Connecting to AWS IoT Core by using custom authentication
[AWS IoT Core] カスタム認証を使用してHTTPSでPublishしてみました
[AWS IoT Core] カスタム認証(ユーザー名・パスワード)を使用してMQTTでPublish/Subscribeしてみました
[AWS IoT Core] カスタム認証を使用してMQTT over WebSocketでPublish/Subscribeしてみました