はじめに
前回の記事ではJITPによる証明書の作成・登録方法を試してみました。
上の表の分類で、JITPと同じく、Amazon以外のCAで発行した証明書を用いて、デバイスからの初回アクセス時に証明書の発行と事前登録を行うJust In Time Registration(JITR)について今回は試してみました。JITRとJITPの主な違いは、JITPは事前に作成したテンプレートをもとにしてプロビジョニングを行うのに対して、JITRはLambda関数を用意してプロビジョニングを行う、という違いになります。そのため、JITRの手順の前半はJITPと共通になります。
Just In Time Registration
それでは試してみたいと思います。
CA証明書と検証証明書の作成 (ここはJITPの手順と同じです)
まず、CA証明書と検証証明書をを作成する必要があります。CA証明書はOpenSSLのコマンドで作成することができます。
CA証明書を作成
$ openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
# 適宜入力
-----
Country Name (2 letter code) []:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) []:Tokyo
Organization Name (eg, company) []:MyOrg
Organizational Unit Name (eg, section) []:MyUnit
Common Name (eg, fully qualified host name) []:RootCA
Email Address []:hogehoge
AWS IoT registration code を取得
$ aws iot get-registration-code
{
"registrationCode": "017102e221eb356519.....(中略)"
}
検証証明書のキーペアを作成
$ openssl genrsa -out verificationCert.key 2048
検証証明書のCSRを作成 (Common Nameに registration codeを入力)
$ openssl req -new -key verificationCert.key -out verificationCert.csr
Country Name (2 letter code) []:JP
State or Province Name (full name) []:JP
Locality Name (eg, city) []:Tokyo
Organization Name (eg, company) []:MyOrg
Organizational Unit Name (eg, section) []:MyOU
Common Name (eg, fully qualified host name) []:017102e221eb356519 ......(中略)
Email Address []:
CSRから検証証明書を作成
$ openssl x509 -req -in verificationCert.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out verificationCert.crt -days 500 -sha256
AWS IoT CoreにCA証明書を登録
aws iot register-ca-certificate --ca-certificate rootCA.pem --verification-cert verificationCert.crt
登録すると、CA証明書のCertificateIDが表示されるので、登録したCA証明書をACTIVEに設定します。
aws iot update-ca-certificate --certificate-id <CertificateID> --new-status ACTIVE
デバイス証明書の自動登録を有効化します。
aws iot update-ca-certificate --certificate-id <CertificateID> --new-auto-registration-status ENABLE
IoT Ruleの設定とLambda関数の設定
JITRでは以下の図のように、IoT RuleとLambda関数を使用して証明書をACTIVEに設定し、Policyの作成、モノの作成と証明書のアタッチ等を行います。
IoTCoreではデバイス証明書の自動登録が有効化されると、新しいデバイスが初回接続された場合に、AWSの予約Topic $aws/events/certificates/registered/<caCertificateID>
に対してMQTTメッセージがPublishされます。
このメッセージのフォーマットは
{
"certificateId": "certificateID",
"caCertificateId": "caCertificateId",
"timestamp": timestamp,
"certificateStatus": "PENDING_ACTIVATION",
"awsAccountId": "awsAccountId",
"certificateRegistrationTimestamp": "certificateRegistrationTimestamp"
}
のようなフォーマットとなっています。このメッセージをルールエンジン経由でLambda関数に渡し、Lambda関数の中で証明書の有効化やPolicyの作成を行います。
まずはLambda関数を作成します。Lambdaのマネジメントコンソールから関数の作成をクリックし、Lambda関数を作成します。ここではPythonにて作成しました。
import json
import boto3
import sys
IOT = boto3.client('iot')
POLICY_NAME = 'My_IoT_Policy'
REGION = 'ap-northeast-1'
THINGNAME = 'MyTestThing0002'
def attach_Policy(awsID, CertID):
CertificateARN = 'arn:aws:iot:'+ REGION + ':' + awsID + ':cert/' + CertID
try:
IOT.attach_principal_policy(
policyName = POLICY_NAME,
principal = CertificateARN
)
except Exception as e:
print(e.message)
raise
def activate_Cert(ID):
try:
IOT.update_certificate(
certificateId = ID,
newStatus = 'ACTIVE'
)
except Exception as e:
print(e.message)
raise
def create_Thing():
try:
IOT.create_thing(
thingName = THINGNAME
)
except Exception as e:
print(e)
raise
def attach_Thing( awsID, CertID):
CertificateARN = 'arn:aws:iot:'+ REGION + ':' + awsID + ':cert/' + CertID
try:
IOT.attach_thing_principal(
thingName=THINGNAME,
principal=CertificateARN
)
except Exception as e:
print(e.message)
raise
def lambda_handler(event, context):
# just in time event structure
#{
# "certificateId": "<certificateID>",
# "caCertificateId": "<caCertificateId>",
# "timestamp": "<timestamp>",
# "certificateStatus": "PENDING_ACTIVATION",
# "awsAccountId": "<awsAccountId>",
# "certificateRegistrationTimestamp": "<certificateRegistrationTimestamp>"
#}
awsID = event['awsAccountId']
certID = event['certificateId']
caCert = event['caCertificateId']
print("awsID:"+ awsID)
print("certID:"+ certID)
print("caCert:"+ caCert)
try:
print('attach_poclicy')
attach_Policy(awsID, certID)
print ('activate_Cert')
activate_Cert(certID)
print ('create_Thing')
create_Thing()
print ('attach_Thing')
attach_Thing(awsID, certID)
except Exception as e:
print('Error on Lambda')
raise
return
すでに作成済みのデバイス用のPolicyがあったので、Policyを証明書にアタッチし、証明書をアクティベートし、モノを作成し、証明書をモノにアタッチしています。モノの名前やPolicyの名前は簡単のため決め打ちで行いました。本当であれば、デバイスから送られてくる情報からモノの名前がつけられるとよいのですが(JITPの場合にはCommon Nameにつけられましたよね。追記:証明書情報をパースしてCNを取り出すことが可能。describe_certificate)MQTTで上がってくる情報にそのような情報が含まれていなかったので、そのようなことをやりたい場合には別途デバイスから取得する必要がありそうです。Lambda関数の実行のPermissionを管理するロールをIAMにて作成します。以下のようなPolicyを作成してロールにアタッチしました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "iot:*",
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
次に、MQTTメッセージをLambdaに渡すIoT Ruleを作成します。IoT CoreのACT>ルールから作成を選択し、以下のルールクエリでルールを作成します。
SELECT * FROM '$aws/events/certificates/registered/<caCertificateID>'
アクションはLambda関数を呼び出し、を選択し先程作成したLambda関数を指定します。
これにて準備は完了です。
デバイスからの接続
まずは、先程作成したCAを使ってデバイス証明書を作成します。
まず、キーを作成。
openssl genrsa -out deviceCert.key 2048
CSRを作成
openssl req -new -key deviceCert.key -out deviceCert.csr
プロンプトで以下のように適宜入力
-----
Country Name (2 letter code) []:JP
State or Province Name (full name) []:State
Locality Name (eg, city) []:Tokyo
Organization Name (eg, company) []:Org
Organizational Unit Name (eg, section) []:OU
Common Name (eg, fully qualified host name) []:CN
Email Address []:
CSRからCAを使って証明書を作成
openssl x509 -req -in deviceCert.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out deviceCert.crt -days 365 -sha256
証明書とCAを連結します。
cat deviceCert.crt rootCA.pem > deviceCertAndCACert.crt
次に作成された証明書(deviceCertAndCACert.crt)、デバイスキー(deviceCert.key)を使ってアクセスしてみます。Amazonのサーバー認証用のCA証明書が必要となるのでこちらから事前にダウンロードしておきます。クライアントとしてmosquittoを使って接続しました。
初回の接続。
$ ./mosquitto_pub --cafile jitr_cert/root.cert --cert jitr_cert/deviceCertAndCACert.crt --key jitr_cert/deviceCert.key -h "<host name>-ats.iot.ap-northeast-1.amazonaws.com" -p 8883 -q 1 -d -t topic/test -m "Hello World" -i id01 --tls-version tlsv1.2
接続エラーとなります。
Client id01 sending CONNECT
Error: The connection was lost.
2回目の接続
./mosquitto_pub --cafile jitr_cert/root.cert --cert jitr_cert/deviceCertAndCACert.crt --key jitr_cert/deviceCert.key -h "<host name>-ats.iot.ap-northeast-1.amazonaws.com" -p 8883 -q 1 -d -t topic/test -m "Hello World" -i id01 --tls-version tlsv1.2
1回目の失敗時にデバイス証明書が登録されているため正しく接続されます。
Client id01 sending CONNECT
Client id01 received CONNACK (0)
Client id01 sending PUBLISH (d0, q1, r0, m1, 'topic/test', ... (11 bytes))
Client id01 received PUBACK (Mid: 1, RC:0)
Client id01 sending DISCONNECT
IoTのコンソールを確認すると、Lambdaで指定した名前でモノが作成され、証明書がアタッチさせていることが確認できます。
CloudWatchにてLambda関数のLogを確認すると、正しく実行されたことが確認できます。
まとめ
Just In Time Registration(JITR)による証明書の作成・登録について試してみました。