使用无服务器框架为多个 IoT 设备设置 AWS IoT
Setting up AWS IoT using Serverless Framework for Multiple IoT Devices
我的目标是使用 serverless framework 在 AWS 上创建一个系统,供多个 IoT 设备向 AWS IoT 发送 JSON 有效载荷,然后将其保存到 DynamoDB。
除了创建 EC2 服务器之外,我对使用 AWS 还很陌生,这是我使用 serverless framework 的第一个项目。
参考an example修改后的版本贴在下面
问题: 看来该示例仅用于连接到 AWS IoT 的 1 台设备,这是我从正在使用的硬编码 IoT Thing 证书得出的结论,例如
SensorPolicyPrincipalAttachmentCert:
Type: AWS::IoT::PolicyPrincipalAttachment
Properties:
PolicyName: { Ref: SensorThingPolicy }
Principal: ${{custom.iotCertificateArn}}
SensorThingPrincipalAttachmentCert:
Type: "AWS::IoT::ThingPrincipalAttachment"
Properties:
ThingName: { Ref: SensorThing }
Principal: ${self:custom.iotCertificateArn}
如果serverless.yml
只配置了1个Thing这个结论是正确的,那么我们可以做哪些修改才能使用1个以上的Thing?
也许设置 serverless.yaml
之外的所有东西?这意味着只删除 SensorPolicyPrincipalAttachmentCert
和 SensorThingPrincipalAttachmentCert
?
另外,SensorThingPolicy
中的Resource
属性应该怎么设置呢?目前设置为"*"
,是不是太宽泛了?或者有什么方法可以限制为事物。
serverless.yml
service: garden-iot
provider:
name: aws
runtime: nodejs6.10
region: us-east-1
# load custom variables from a file
custom: ${file(./vars-dev.yml)}
resources:
Resources:
LocationData:
Type: AWS::DynamoDB::Table
Properties:
TableName: location-data-${opt:stage}
AttributeDefinitions:
-
AttributeName: ClientId
AttributeType: S
-
AttributeName: Timestamp
AttributeType: S
KeySchema:
-
AttributeName: ClientId
KeyType: HASH
-
AttributeName: Timestamp
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
SensorThing:
Type: AWS::IoT::Thing
Properties:
AttributePayload:
Attributes:
SensorType: soil
SensorThingPolicy:
Type: AWS::IoT::Policy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: ["iot:Connect"]
Resource: ["${self:custom.sensorThingClientResource}"]
- Effect: "Allow"
Action: ["iot:Publish"]
Resource: ["${self:custom.sensorThingSoilTopicResource}"]
SensorPolicyPrincipalAttachmentCert:
Type: AWS::IoT::PolicyPrincipalAttachment
Properties:
PolicyName: { Ref: SensorThingPolicy }
Principal: ${{custom.iotCertificateArn}}
SensorThingPrincipalAttachmentCert:
Type: "AWS::IoT::ThingPrincipalAttachment"
Properties:
ThingName: { Ref: SensorThing }
Principal: ${self:custom.iotCertificateArn}
IoTRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Principal:
Service:
- iot.amazonaws.com
Action:
- sts:AssumeRole
IoTRolePolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: IoTRole_Policy
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Action:
- dynamodb:PutItem
Resource: "*"
-
Effect: Allow
Action:
- lambda:InvokeFunction
Resource: "*"
Roles: [{ Ref: IoTRole }]
编辑 05/09/2018: 我找到了这个博客 post,它很好地描述了我的方法:Ensure Secure Communication with AWS IoT Core Using the Certificate Vending Machine Reference Application
--
你可以看看Just-in-Time Provisioning or build your own solution based on Programmatic Provisioning。
我已经多次处理过这个主题,并且不得不意识到它在很大程度上取决于用例,这更有意义。安全性也是需要关注的一个方面。您不想让 public API 负责整个 Internet 都可以访问的 JIT 设备注册。
一个简单的基于程序化配置的场景可能如下所示:您构建一个东西(可能是一个传感器),它应该能够连接到 AWS IoT 并有一个内部配置过程。
简单的配置过程:
- 建造的东西
- 东西有序列号
- 事物通过内部服务器自行注册
服务器上的注册码运行可能是这样的(JS + AWS JS SDK):
// Modules
const AWS = require('aws-sdk')
// AWS
const iot = new AWS.Iot({ region: process.env.region })
// Config
const templateBodyJson = require('./register-thing-template-body.json')
// registerThing
const registerThing = async ({ serialNumber = null } = {}) => {
if (!serialNumber) throw new Error('`serialNumber` required!')
const {
certificateArn = null,
certificateId = null,
certificatePem = null,
keyPair: {
PrivateKey: privateKey = null,
PublicKey: publicKey = null
} = {}
} = await iot.createKeysAndCertificate({ setAsActive: true }).promise()
const registerThingParams = {
templateBody: JSON.stringify(templateBodyJson),
parameters: {
ThingName: serialNumber,
SerialNumber: serialNumber,
CertificateId: certificateId
}
}
const { resourceArns = null } = await iot.registerThing(registerThingParams).promise()
return {
certificateArn,
certificateId,
certificatePem,
privateKey,
publicKey,
resourceArns
}
}
const unregisterThing = async ({ serialNumber = null } = {}) => {
if (!serialNumber) throw new Error('`serialNumber` required!')
try {
const thingName = serialNumber
const { principals: thingPrincipals } = await iot.listThingPrincipals({ thingName }).promise()
const certificates = thingPrincipals.map((tp) => ({ certificateId: tp.split('/').pop(), certificateArn: tp }))
for (const { certificateId, certificateArn } of certificates) {
await iot.detachThingPrincipal({ thingName, principal: certificateArn }).promise()
await iot.updateCertificate({ certificateId, newStatus: 'INACTIVE' }).promise()
await iot.deleteCertificate({ certificateId, forceDelete: true }).promise()
}
await iot.deleteThing({ thingName }).promise()
return {
deleted: true,
thingPrincipals
}
} catch (err) {
// Already deleted!
if (err.code && err.code === 'ResourceNotFoundException') {
return {
deleted: true,
thingPrincipals: []
}
}
throw err
}
}
register-thing-template-body.json:
{
"Parameters": {
"ThingName": {
"Type": "String"
},
"SerialNumber": {
"Type": "String"
},
"CertificateId": {
"Type": "String"
}
},
"Resources": {
"thing": {
"Type": "AWS::IoT::Thing",
"Properties": {
"ThingName": {
"Ref": "ThingName"
},
"AttributePayload": {
"serialNumber": {
"Ref": "SerialNumber"
}
},
"ThingTypeName": "NewDevice",
"ThingGroups": ["NewDevices"]
}
},
"certificate": {
"Type": "AWS::IoT::Certificate",
"Properties": {
"CertificateId": {
"Ref": "CertificateId"
}
}
},
"policy": {
"Type": "AWS::IoT::Policy",
"Properties": {
"PolicyName": "DefaultNewDevicePolicy"
}
}
}
}
确保您已准备好所有 "NewDevice" 事物类型、组和策略。还要记住 ThingName = SerialNumber(对于 unregisterThing 很重要)。
我的目标是使用 serverless framework 在 AWS 上创建一个系统,供多个 IoT 设备向 AWS IoT 发送 JSON 有效载荷,然后将其保存到 DynamoDB。
除了创建 EC2 服务器之外,我对使用 AWS 还很陌生,这是我使用 serverless framework 的第一个项目。
参考an example修改后的版本贴在下面
问题: 看来该示例仅用于连接到 AWS IoT 的 1 台设备,这是我从正在使用的硬编码 IoT Thing 证书得出的结论,例如
SensorPolicyPrincipalAttachmentCert:
Type: AWS::IoT::PolicyPrincipalAttachment
Properties:
PolicyName: { Ref: SensorThingPolicy }
Principal: ${{custom.iotCertificateArn}}
SensorThingPrincipalAttachmentCert:
Type: "AWS::IoT::ThingPrincipalAttachment"
Properties:
ThingName: { Ref: SensorThing }
Principal: ${self:custom.iotCertificateArn}
如果serverless.yml
只配置了1个Thing这个结论是正确的,那么我们可以做哪些修改才能使用1个以上的Thing?
也许设置 serverless.yaml
之外的所有东西?这意味着只删除 SensorPolicyPrincipalAttachmentCert
和 SensorThingPrincipalAttachmentCert
?
另外,SensorThingPolicy
中的Resource
属性应该怎么设置呢?目前设置为"*"
,是不是太宽泛了?或者有什么方法可以限制为事物。
serverless.yml
service: garden-iot
provider:
name: aws
runtime: nodejs6.10
region: us-east-1
# load custom variables from a file
custom: ${file(./vars-dev.yml)}
resources:
Resources:
LocationData:
Type: AWS::DynamoDB::Table
Properties:
TableName: location-data-${opt:stage}
AttributeDefinitions:
-
AttributeName: ClientId
AttributeType: S
-
AttributeName: Timestamp
AttributeType: S
KeySchema:
-
AttributeName: ClientId
KeyType: HASH
-
AttributeName: Timestamp
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
SensorThing:
Type: AWS::IoT::Thing
Properties:
AttributePayload:
Attributes:
SensorType: soil
SensorThingPolicy:
Type: AWS::IoT::Policy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: ["iot:Connect"]
Resource: ["${self:custom.sensorThingClientResource}"]
- Effect: "Allow"
Action: ["iot:Publish"]
Resource: ["${self:custom.sensorThingSoilTopicResource}"]
SensorPolicyPrincipalAttachmentCert:
Type: AWS::IoT::PolicyPrincipalAttachment
Properties:
PolicyName: { Ref: SensorThingPolicy }
Principal: ${{custom.iotCertificateArn}}
SensorThingPrincipalAttachmentCert:
Type: "AWS::IoT::ThingPrincipalAttachment"
Properties:
ThingName: { Ref: SensorThing }
Principal: ${self:custom.iotCertificateArn}
IoTRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Principal:
Service:
- iot.amazonaws.com
Action:
- sts:AssumeRole
IoTRolePolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: IoTRole_Policy
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Action:
- dynamodb:PutItem
Resource: "*"
-
Effect: Allow
Action:
- lambda:InvokeFunction
Resource: "*"
Roles: [{ Ref: IoTRole }]
编辑 05/09/2018: 我找到了这个博客 post,它很好地描述了我的方法:Ensure Secure Communication with AWS IoT Core Using the Certificate Vending Machine Reference Application
--
你可以看看Just-in-Time Provisioning or build your own solution based on Programmatic Provisioning。
我已经多次处理过这个主题,并且不得不意识到它在很大程度上取决于用例,这更有意义。安全性也是需要关注的一个方面。您不想让 public API 负责整个 Internet 都可以访问的 JIT 设备注册。
一个简单的基于程序化配置的场景可能如下所示:您构建一个东西(可能是一个传感器),它应该能够连接到 AWS IoT 并有一个内部配置过程。
简单的配置过程:
- 建造的东西
- 东西有序列号
- 事物通过内部服务器自行注册
服务器上的注册码运行可能是这样的(JS + AWS JS SDK):
// Modules
const AWS = require('aws-sdk')
// AWS
const iot = new AWS.Iot({ region: process.env.region })
// Config
const templateBodyJson = require('./register-thing-template-body.json')
// registerThing
const registerThing = async ({ serialNumber = null } = {}) => {
if (!serialNumber) throw new Error('`serialNumber` required!')
const {
certificateArn = null,
certificateId = null,
certificatePem = null,
keyPair: {
PrivateKey: privateKey = null,
PublicKey: publicKey = null
} = {}
} = await iot.createKeysAndCertificate({ setAsActive: true }).promise()
const registerThingParams = {
templateBody: JSON.stringify(templateBodyJson),
parameters: {
ThingName: serialNumber,
SerialNumber: serialNumber,
CertificateId: certificateId
}
}
const { resourceArns = null } = await iot.registerThing(registerThingParams).promise()
return {
certificateArn,
certificateId,
certificatePem,
privateKey,
publicKey,
resourceArns
}
}
const unregisterThing = async ({ serialNumber = null } = {}) => {
if (!serialNumber) throw new Error('`serialNumber` required!')
try {
const thingName = serialNumber
const { principals: thingPrincipals } = await iot.listThingPrincipals({ thingName }).promise()
const certificates = thingPrincipals.map((tp) => ({ certificateId: tp.split('/').pop(), certificateArn: tp }))
for (const { certificateId, certificateArn } of certificates) {
await iot.detachThingPrincipal({ thingName, principal: certificateArn }).promise()
await iot.updateCertificate({ certificateId, newStatus: 'INACTIVE' }).promise()
await iot.deleteCertificate({ certificateId, forceDelete: true }).promise()
}
await iot.deleteThing({ thingName }).promise()
return {
deleted: true,
thingPrincipals
}
} catch (err) {
// Already deleted!
if (err.code && err.code === 'ResourceNotFoundException') {
return {
deleted: true,
thingPrincipals: []
}
}
throw err
}
}
register-thing-template-body.json:
{
"Parameters": {
"ThingName": {
"Type": "String"
},
"SerialNumber": {
"Type": "String"
},
"CertificateId": {
"Type": "String"
}
},
"Resources": {
"thing": {
"Type": "AWS::IoT::Thing",
"Properties": {
"ThingName": {
"Ref": "ThingName"
},
"AttributePayload": {
"serialNumber": {
"Ref": "SerialNumber"
}
},
"ThingTypeName": "NewDevice",
"ThingGroups": ["NewDevices"]
}
},
"certificate": {
"Type": "AWS::IoT::Certificate",
"Properties": {
"CertificateId": {
"Ref": "CertificateId"
}
}
},
"policy": {
"Type": "AWS::IoT::Policy",
"Properties": {
"PolicyName": "DefaultNewDevicePolicy"
}
}
}
}
确保您已准备好所有 "NewDevice" 事物类型、组和策略。还要记住 ThingName = SerialNumber(对于 unregisterThing 很重要)。