使用无服务器框架为多个IoT设备设置AWS IoT

时间:2018-09-04 02:22:28

标签: amazon-cloudformation iot serverless-framework serverless aws-iot

我的目标是使用serverless framework在AWS上为多个IoT设备创建一个系统,以将JSON负载发送到AWS IoT,然后将其保存到DynamoDB。

除了创建EC2服务器之外,我对使用AWS还很陌生,这是我的第一个使用serverless framework的项目。

参考an example之后,我想到的修改版本发布在下面。

问题::该示例似乎仅用于一台设备连接到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个Thing,那么我们可以进行哪些修改以使可以使用1个以上Thing?

也许设置serverless.yaml之外的所有内容?这意味着只删除SensorPolicyPrincipalAttachmentCertSensorThingPrincipalAttachmentCert

此外,我们应该如何在Resource中将SensorThingPolicy属性设置为?它们当前设置为"*",这太过分了吗?还是有一种方法可以只限制事物。

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 }]

1 个答案:

答案 0 :(得分:1)

编辑05/09/2018::我发现了这篇博客文章,很好地描述了我的方法:Ensure Secure Communication with AWS IoT Core Using the Certificate Vending Machine Reference Application

-

您可以查看Just-in-Time Provisioning或基于Programmatic Provisioning构建自己的解决方案。

我已经多次处理这个主题,并且不得不意识到它在很大程度上取决于用例,这更有意义。安全也是值得关注的一个方面。您不希望整个Internet都可以访问负责JIT设备注册的公共API。

一个简单的基于Programmatic Provisioning的场景如下所示:您构建了一个东西(可能是传感器),它应该能够连接到AWS IoT并具有内部供应过程。

简单的设置过程:

  1. 建物
  2. 东西有序列号
  3. 事物通过内部服务器注册自身

服务器上运行的注册代码可能类似于以下内容(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来说很重要)。