我的目标是使用多个物联网设备的无服务器框架在 AWS 上创建一个系统,以将 JSON 有效负载发送到 AWS IoT,而这些有效负载又将保存到 DynamoDB。
除了创建 EC2 服务器之外,我对使用 AWS 非常陌生,这是我使用无服务器框架的第一个项目。
参考示例后,我想出的修改版本发布在下面。
问题:该示例似乎仅针对 1 台设备连接到 AWS IoT,我从正在使用的硬编码 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 个事物配置,那么我们可以进行哪些修改才能使用多个事物?
也许设置serverless.yaml
之外的所有东西?这意味着只删除SensorPolicyPrincipalAttachmentCert
和SensorThingPrincipalAttachmentCert
?
另外,我们应该如何将Resource
属性设置为 inSensorThingPolicy
?他们目前设置为"*"
,这是不是太笨了?或者有没有办法限制为只有事物。
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: 我找到了这篇博文,它很好地描述了我的方法: 使用证书自动售货机参考应用程序确保与 AWS IoT Core 的安全通信
--
您可以查看实时预配或基于编程预配构建自己的解决方案。
我已经多次处理过这个主题,并且必须意识到它在很大程度上取决于用例,这更有意义。此外,安全性也是一个值得关注的方面。你不希望有一个公共 API 负责 JIT 设备注册,可供整个 Internet 访问。
一个简单的基于编程预置的方案可能如下所示:您构建了一个东西(可能是一个传感器(,它应该能够连接到 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"
}
}
}
}
确保您已准备好所有"新设备"事物类型、组和策略。还要记住 ThingName = 序列号(对于 unregisterThing 很重要(。