0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

署名検証付きカスタムオーソライザー(ユーザー名/パスワード)を使用して AWS Iot Core に接続する方法

Posted at

概要

IoT Core はカスタムオーソライザーを使用して認証し、IoT Core に接続することができます。
カスタムオーソライザーは署名の検証を行うことができ、また実装方法によっては
ユーザー名とパスワードを使用して認証することができます。

本記事は、この実装に関する備忘録です。

前提条件

  • Mac 環境であること
  • AWS のリソース作成は CDK を利用
  • Iot Core への接続認証は MQTTX のソフトを利用

手順

CDK のプロジェクトを作成

適当なディレクトリを作成します。

mkdir cdk-iot-custom-auth && cd cdk-iot-custom-auth

CDK プロジェクトを作成します。

cdk init app --language typescript

オーソライザー用の Lambda の作成

Lambda のコード格納先のディレクトリを作成します。

mkdir -p lambda/custom-auth && touch lambda/custom-auth/index.ts

AWS のドキュメントをもとに認証様の関数を作成する。参照

この Lambda は IoT Core のポリシーを返却する必要があります。
password=test の場合、publish/subscribe などの操作を許可する IoT ポリシーを返却します。

lib/cdk-iot-custom-auth-stack.js
exports.handler = function (event, context, callback) {
  var uname = event.protocolData.mqtt.username;
  var pwd = event.protocolData.mqtt.password;
  var buff = Buffer.from(pwd, "base64");
  var passwd = buff.toString("ascii");
  switch (passwd) {
    case "test":
      callback(null, generateAuthResponse(passwd, "Allow"));
      break;
    default:
      callback(null, generateAuthResponse(passwd, "Deny"));
  }
};

interface AuthResponse {
  isAuthenticated: boolean;
  principalId: string;
  disconnectAfterInSeconds: number;
  refreshAfterInSeconds: number;
  policyDocuments: [
    {
      Version: string;
      Statement: Statement[];
    }
  ];
}

interface Statement {
  Action: string;
  Effect: "Allow" | "Deny";
  Resource: string;
}

// Helper function to generate the authorization response.
const generateAuthResponse = function (token, effect) {
  const authResponse: AuthResponse = {
    isAuthenticated: effect === "Allow" ? true : false,
    principalId: "TEST123",
    policyDocuments: [
      {
        Version: "2012-10-17",
        Statement: [],
      },
    ],
    disconnectAfterInSeconds: 3600,
    refreshAfterInSeconds: 300,
  };

  const publishStatement: Statement = {
    Action: "iot:Connect",
    Effect: effect,
    Resource: "*",
  };
  const connectStatement: Statement = {
    Action: "iot:Publish",
    Effect: effect,
    Resource: "*",
  };
  const subscribeStatement: Statement = {
    Action: "iot:Subscribe",
    Effect: effect,
    Resource: "*",
  };
  const receiveStatement: Statement = {
    Action: "iot:Receive",
    Effect: effect,
    Resource: "*",
  };
  authResponse.policyDocuments[0].Statement[0] = connectStatement;
  authResponse.policyDocuments[0].Statement[1] = publishStatement;
  authResponse.policyDocuments[0].Statement[2] = subscribeStatement;
  authResponse.policyDocuments[0].Statement[3] = receiveStatement;

  return authResponse;
};

password=test の場合、publish/subscribe などを許可する、iot ポリシーを返却するようにします。
上記のコードで返却される、Iot ポリシーは以下になります。

Iotポリシー
{
    "isAuthenticated": true,
    "principalId": "TEST123",
    "policyDocuments": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "iot:Publish",
                "Effect": "Allow",
                "Resource": "*"
            },
            {
                "Action": "iot:Connect",
                "Effect": "Allow",
                "Resource": "*"
            },
            {
                "Action": "iot:Subscribe",
                "Effect": "Allow",
                "Resource": "*"
            },
            {
                "Action": "iot:Receive",
                "Effect": "Allow",
                "Resource": "*"
            }
        ]
    },
    "disconnectAfterInSeconds": 3600,
    "refreshAfterInSeconds": 300
}

CDK にて Lambda を定義します。

lib/cdk-iot-custom-auth-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";

export class CdkIotCustomAuthStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // オーソライザー用の関数を作成
    const iotAuthFunc = new nodejs.NodejsFunction(this, "IotAuthFunc", {
      functionName: "iot-custom-auth",
      runtime: lambda.Runtime.NODEJS_22_X,
      entry: "lambda/custom-auth/index.ts",
    });
  }
}

オーソライザー用の公開鍵と秘密鍵の作成

カスタムオーソライザーでは、認証に公開鍵と秘密鍵が必要なので、それぞれ作成していきます。

  1. 鍵を格納するためのディレクトリを作成します

    mkdir secret
    
  2. 秘密鍵を作成します。

    openssl genrsa -out secret/private-key.pem 4096
    
  3. 秘密鍵に問題がないか検証します。

    openssl rsa -check -in secret/private-key.pem -noout
    
  4. 公開鍵を作成します。

    openssl rsa -in secret/private-key.pem -pubout -out secret/public-key.pem
    
  5. 公開鍵に問題がないか確認します。

    openssl pkey -inform PEM -pubin -in secret/public-key.pem -noout
    

    特にエラーが表示されなければ問題ないです。

カスタムオーソライザーの作成

CDK にて、カスタムオーソライザーを定義します。
この際、署名の検証を有効にします。

lib/cdk-iot-custom-auth-stack.js
import * as iot from "aws-cdk-lib/aws-iot";

//<一部省略>

// カスタムオーソライザーの作成
const iotCustomAuthorizer = new iot.CfnAuthorizer(this, "IotCustomAuthorizer", {
    authorizerName: "iot-custom-auth",
    status: "ACTIVE",
    authorizerFunctionArn: iotAuthFunc.functionArn,
    signingDisabled: false,
    tokenKeyName: "authorizer-token",
    tokenSigningPublicKeys: {
        Key1: process.env.PUBLIC_KEY || "",
    },
});

また、PUBLIC_KEY の環境変数を設定するよう、以下のコマンドも実行しておきます。

terminal
export PUBLIC_KEY="$(cat secret/public-key.pem)"

Iot Core に Lambda 関数の呼び出しを許可する。

カスタムオーソライザーに Lambda 関数を実行するための許可を付与します

lib/cdk-iot-custom-auth-stack.js
    // IotにLambda関数の実行許可を付与
    iotAuthFunc.addPermission("iotAuthFuncPermission", {
      principal: new iam.ServicePrincipal("iot.amazonaws.com"),
      sourceArn: iotCustomAuthorizer.attrArn,
      action: "lambda:InvokeFunction",
    });

AWS のリソースを生成

CDK の準備が完了しましたので、cdk deployを実行し、リソースを生成していきます。
この際、PUBLIC_KEYに環境変数を設定しているか、再確認してください。

MQTTX を使用して接続確認をする

MQTTX をインストールし、MQTTX のアプリを開きます。
インストールは以下の記事がわかりやすかいと思います。

接続に以下の情報が必要になるので、各情報を取得します。

  • AWS の Iot エンドポイント
  • トークンの署名

AWS の Iot エンドポイントの確認

CLI の以下のコマンドを実行し、エンドポイントの値を取得します。

aws iot describe-endpoint --endpoint-type iot:Data-ATS

トークンの署名

次のコマンドを実行してトークンに署名をしていきます。

echo -n TOKEN_VALUE | openssl dgst -sha256 -sign PEM encoded RSA private key | openssl base64

本環境で実際に実行したコマンドはこちらになります。
また、トークンの署名の値は変数に格納しておきます。

TOKEN_SIGNATURE=$(echo -n "Key1" | openssl dgst -sha256 -sign secret/private-key.pem | openssl base64 -A)

署名したトークンの値を使用して MQTT 接続するには、署名したトークンの値を URL エンコードする必要があるので、
以下のコマンドを実行し、エンコードしていきます。

echo -n $TOKEN_SIGNATURE | jq -sRr @uri

出力結果をメモしておきます。

MQTTX から Iot Core への接続

接続に必要な情報が集まったので、MQTTX から Iot Core に接続していきます。

  1. MQTTX にて、「新しい接続」を押下する

  2. 各項目に以下の値を設定し、「接続」を押下する。

    • 名前:<任意>
    • ホスト:wss://
    • Path:/mqtt
    • ユーザ名:<ユーザ名>?x-amz-customauthorizer-name=<オーソライザー名>&x-amz-customauthorizer-signature=<エンコード後の TOKEN_SIGNATURE>&<トークンのキー名>=<トークンの値>
      • 例:mqttuser?x-amz-customauthorizer-name=iot-custom-auth&x-amz-customauthorizer-signature=<エンコード後の TOKEN_SIGNATURE>&authorizer-token=Key1
    • パスワード:<パスワード>
      • 例:test
    • SSL/TLS:有効
    • SSL 証明書:無効

無事に接続できれば成功です!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?