X.509クライアント証明書で接続しているデバイスでAWS Secrets Managerを使ってみました
1 はじめに
CX事業本部の平内(SIN)です。
IoT Coreに接続されるデバイスは、証明書を使用して接続されているものが多いと思います。(※1)
今回は、そのようなデバイスで、「シークレットな情報」を扱う場面をイメージし、AWS Secrets Managerを使用する要領を確認してみました。
なお、AWS Secrets Managerでは、主要な用途として、データベースの認証情報(※2)がありますが、すいません、これには触れておらず、その他のシークレットとして、JSONテキストの保存のみを使っています。
(※1) X.509クライアント証明書による認証では、証明書数の上限もなく、また、事前登録無しで利用できる仕組み(JITR、JITP、Fleet Provisioning)も用意されていることから、 他の認証方法(Amazon Cognito、AWS IAM、カスタム認証)に比べて、採用されている場面は多いと思います。
(※2) Amazon RDS(MySQL、PostgreSQL)、及び、Amazon Auroraでは、組み込み統合機能でLambda関数によるローテーション統合が可能です。
2 AWS Secrets Manager
secrets_manager_sample_2021_05_01という名前で、シークレットを作成しました。
暗号化キーは、デフォルトのもので、ローテーションも無効になっています。
3 IAM
作成したシークレットに対して、secretsmanager:GetSecretValueのみを許可した ロール(secrets_manager_sample_role_2021_05_01)を作成しました。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "secretsmanager:GetSecretValue" ], "Effect": "Allow", "Resource": "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxxxx:secret:secrets_manager_sample_2021_05_01-1QT9Ya" } ] }
なお、このロールは、IoT Coreから使用されるため、信頼関係を以下の通りとします。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "credentials.iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
4 Role Alias
上記のロールのエリアス(secrets_manager_sample_alias_2021_05_01)を作成しています。
5 ポリシー
上記ロールエリアスを使用することができるポリシー(secrets_manager_sample_policy_2021_05_01)を作成します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iot:AssumeRoleWithCertificate", "Resource": [ "arn:aws:iot:ap-northeast-1:*:rolealias/secrets_manager_sample_alias_2021_05_01" ] } ] }
後は、このポリシーを使用する証明書にアタッチするだけです。
6 コード
デバイス側のコードです。 STSで一時認証情報を取得して、boto3でsecretsmanagerを使用しています。
from mqtt import Mqtt from datetime import datetime import time import json import requests import boto3 root_ca = "./certs/RootCA.pem" key = "./certs/a328148d74-private.pem.key" cert = "./certs/a328148d74-certificate.pem.crt" endpoint = "https://xxxxxxxxxxx.credentials.iot.ap-northeast-1.amazonaws.com" region = 'ap-northeast-1' role_alias = 'secrets_manager_sample_alias_2021_05_01' secret_name = 'secrets_manager_sample_2021_05_01' result = requests.get( '{}/role-aliases/{}/credentials'.format(endpoint, role_alias), cert=(cert, key) ) if(result.status_code != 200): exit() body = json.loads(result.text) access_key = body["credentials"]["accessKeyId"] secret_key = body["credentials"]["secretAccessKey"] token = body["credentials"]["sessionToken"] session = boto3.Session(aws_access_key_id=access_key, aws_secret_access_key=secret_key, aws_session_token=token, region_name = region) client = session.client( service_name='secretsmanager', region_name=region ) response = client.get_secret_value( SecretId = secret_name ) print(response)
get_secret_value()で、下記のようなレスポンスが返ります。SecretStringに、格納情報が取得できていることを確認できます。
<Response [200]> {\ 'ARN': 'arn:aws:secretsmanager:ap-northeast-1:xxxxxxxx:secret:secrets_manager_sample_2021_05_01-1QT9Ya',\ 'Name': 'secrets_manager_sample_2021_05_01',\ 'VersionId': '469b2d39-25a8-4e47-85de-2493df66dce9',\ 'SecretString': '{"user_id":"123456","password":"ABCDEFG"}',\ 'VersionStages': ['AWSCURRENT'],\ 'CreatedDate': datetime.datetime(2021, 5, 1, 10, 27, 33, 902000, tzinfo=tzlocal()),\ 'ResponseMetadata':{\ 'RequestId': 'f3f5f1ab-124f-411a-906b-0d1e799eaefd',\ 'HTTPStatusCode': 200,\ 'HTTPHeaders': \ {\ 'date': 'Sat,01 May 2021 02:09:42 GMT',\ 'content-type': 'application/x-amz-json-1.1',\ 'content-length': '331',\ 'connection': 'keep-alive',\ 'x-amzn-requestid': 'f3f5f1ab-124f-411a-906b-0d1e799eaefd'\ },\ 'RetryAttempts': 0\ }\ }
7 最後に
今回は、証明書で接続するデバイスで、AWS Secrets Managerから「シークレットな情報」を取得する要領を確認してみました。
情報の配置場所をAWS Secrets Managerにすることで、使用の都度、データを取得する運用となるので、ハードコーディングしてしまって、更新が難しくなるというような問題も同時に解決できるかも知れません。