メインコンテンツまでスキップ

IoT Coreの証明書でIAM Roles Anywhere的なことをする(アリかもしれない)

· 約7分
moritalous

前回、IAM Roles Anywhereの検証をしてとても良い機能だなと感じました。

今すぐローカル開発環境のアクセスキー/シークレットキーを削除してIAM Roles Anywhereを使いなさい!
2fae4b3b17230b8931e2

そういえば、証明書を使った認証にIoT Coreがあったな🤔と思い出し、同じことができるかやってみました!試したところちょっとした違いがあり、IoT Coreでやるのもアリかもしれません。

手順

IAMロールを作成

  1. IoT Coreの認証情報プロバイダーが引き受けるIAMロールを作成します。 信頼されたエンティティタイプをカスタム信頼ポリシーにし、信頼ポリシーに以下のJSONを入力します。

    {
    "Version": "2012-10-17",
    "Statement": {
    "Effect": "Allow",
    "Principal": {
    "Service": "credentials.iot.amazonaws.com"
    },
    "Action": "sts:AssumeRole"
    }
    }

    image.png

  1. 許可を追加で必要な権限を付与し次へをクリックします。

  2. ロールに名前をつけて保存します。(iot-roles-anywhere-role)としました。

IoT ロールエイリアスを作成

  1. IoT Coreの管理画面に移動し、管理メニュー -> セキュリティ -> ロールエイリアスを表示します。 ロールエイリアスを作成をクリックします。

    image.png

  2. ロールエイリアスに名前をつけ、先程作成したロールを選択して、作成をクリックします。

    image.png

作成したロールエイリアスのARNをメモしておきます。

IoT ポリシーを作成

  1. 管理メニュー -> セキュリティ -> ポリシーへ移動します。 ポリシーを作成をクリックします。

    image.png

  2. ポリシー名を入力します。 ポリシードキュメントは以下を入力して、作成をクリックします。

    • ポリシー効果: 許可
    • ポリシーアクション:iot:AssumeRoleWithCertificate
    • ポリシーリソース:作成したロールエイリアスのARN

    image.png

IoT モノのグループを作成

  1. 管理メニュー -> すべてのデバイス -> モノのグループへ移動します。 モノのグループを作成をクリックします。

    image.png

  2. モノの静的グループを作成を選択して、次へをクリックします。

  3. モノのグループ名を入力して、モノのグループを作成をクリックします。

  4. 作成したグループを選択して、ポリシータブを選択。ポリシーを管理をクリックします。

    image.png

  5. 先程作成したIoTポリシーを選択して、ポリシーの更新をクリックします。

    image.png

IoT モノを作成

  1. 管理メニュー -> すべてのデバイス -> モノへ移動します。 モノを作成をクリックします。

    image.png

  2. 1 つのモノを作成を選択し、次へをクリックします。

  3. モノの名前を入力して、先ほど作成したモノのグループを選択。次へをクリックします。

    image.png

  4. 新しい証明書を自動生成 (推奨)を選択して、次へをクリックします。

  5. ポリシーはグループに割り当てているので未選択のまま、モノを作成`をクリックします。

  6. デバイス証明書、パブリックキーファイル、プライベートキーファイル、RSA 2048 ビットキー: Amazon ルート CA 1をダウンロードしてから、続行をクリックします。

    image.png

認証情報プロバイダーエンドポイントを取得する

CloudShellなど
aws iot describe-endpoint --endpoint-type iot:CredentialProvider
出力
{
"endpointAddress": "abcdefghijklm.credentials.iot.ap-northeast-1.amazonaws.com"
}

一時認証情報を取得する

一時認証情報は認証情報プロバイダーエンドポイントにリクエストを送信して取得します。

Shell
export ENDPOINT=abcdefghijklm.credentials.iot.ap-northeast-1.amazonaws.com
export ROLE_ALIAS_NAME=alias-for-roles-anywhere
export CERT_PATH=certificate.pem.crt
export PRIVATE_KEY_PATH=private.pem.key
export THING_NAME=client1
export CA_CERT_PATH=AmazonRootCA1.pem

curl \
--cert ${CERT_PATH} \
--key ${PRIVATE_KEY_PATH} \
-H "x-amzn-iot-thingname: ${THING_NAME}" \
--cacert ${CA_CERT_PATH} \
https://${ENDPOINT}/role-aliases/${ROLE_ALIAS_NAME}/credentials
出力
{
"credentials": {
"accessKeyId": "...",
"secretAccessKey": "...",
"sessionToken": "...",
"expiration": "2023-10-21T09:45:03Z"
}
}

credential_process の仕組みに合わせる

もうちょいです。

認証情報プロバイダーエンドポイントの出力をcredential_processのフォーマットに変換します。

credential_processのフォーマット
{
"Version": 1,
"AccessKeyId": "an AWS access key",
"SecretAccessKey": "your AWS secret access key",
"SessionToken": "the AWS session token for temporary credentials",
"Expiration": "ISO8601 timestamp when the credentials expire"
}
  • Versionを追加
  • 各キーの先頭を大文字にする

先程の出力をパイプでjqに繋いで変換します。

Shell
export ENDPOINT=abcdefghijklm.credentials.iot.ap-northeast-1.amazonaws.com
export ROLE_ALIAS_NAME=alias-for-roles-anywhere
export CERT_PATH=certificate.pem.crt
export PRIVATE_KEY_PATH=private.pem.key
export THING_NAME=client1
export CA_CERT_PATH=AmazonRootCA1.pem

curl \
--cert ${CERT_PATH} \
--key ${PRIVATE_KEY_PATH} \
-H "x-amzn-iot-thingname: ${THING_NAME}" \
--cacert ${CA_CERT_PATH} \
https://${ENDPOINT}/role-aliases/${ROLE_ALIAS_NAME}/credentials \
| jq '.credentials | {"Version":1, "AccessKeyId":.accessKeyId, "SecretAccessKey":.secretAccessKey, "SessionToken":.sessionToken, "Expiration":.expiration}'

出力
{
"Version": 1,
"AccessKeyId": "...",
"SecretAccessKey": "...",
"SessionToken": "...",
"Expiration": "2023-10-21T09:52:49Z"
}

いい感じです。 うまく行ったのでこいつをシェルスクリプトにして保存します。

credential-helper.sh
#!/bin/sh
ENDPOINT=ce4kc59ulqnzy.credentials.iot.ap-northeast-1.amazonaws.com
ROLE_ALIAS_NAME=alias-for-roles-anywhere
CERT_PATH=certificate.pem.crt
PRIVATE_KEY_PATH=private.pem.key
THING_NAME=client1
CA_CERT_PATH=AmazonRootCA1.pem

curl \
--cert ${CERT_PATH} \
--key ${PRIVATE_KEY_PATH} \
-H "x-amzn-iot-thingname: ${THING_NAME}" \
--cacert ${CA_CERT_PATH} \
https://${ENDPOINT}/role-aliases/${ROLE_ALIAS_NAME}/credentials \
| jq '.credentials | {"Version":1, "AccessKeyId":.accessKeyId, "SecretAccessKey":.secretAccessKey, "SessionToken":.sessionToken, "Expiration":.expiration}'

最後に.aws/configに登録します。

.aws/config
[profile iotrolesanywhere]
credential_process = sh <<credential-helper.shのフルパス指定>>

AWSコマンドを実行します。

Shell
aws sts get-caller-identity --profile iotrolesanywhere
出力
{
"UserId": "...",
"Account": "...",
"Arn": "arn:aws:sts::...:assumed-role/iot-roles-anywhere-role/..."
}

やったぜ

IAM Roles Anywhereとの違い

良いと思った点

  • IoT Coreだと証明証が無料でマネージド! IAM Roles AnywhereではAWS Private CA(有料)もしくは自前管理のプライベートCAが必要
  • 証明書の失効も簡単!

残念な点

  • なんでIoTやねん感
    • しっかりルールを明文化して管理しないと後々困りそう

参考

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-sourcing-external.html

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/authorizing-direct-aws.html

おまけ

AWS発行のIoT Coreの証明書に使われているRootCA証明書をIAM Roles Anywhereで使おうとするとエラーになりました😝

image.png