Status for Waverley’s Ukraine Office: READ HERE

AWS IoT + Cognito: IoT Devices and User Management Solution

Introduction

Technology growth has led to the emergence of a new class of devices capable of interacting with each other and the outside world through different communication channels, and, in particular, the World Wide Web.. As a result, a new concept has appeared with its own set of rules and standards: the Internet of Things, IoT.

The Internet of Things (IoT) describes physical objects (or groups of such objects), that are embedded with sensors, processing ability, software, and other technologies, and that connect and exchange data with other devices and systems over the Internet or other communications networks [Wiki].

Very often, the desire to control any device via the Internet reaches the point of absurdity. Nevertheless, more and more companies which are involved in a process of producing consumer electronics – from small startups to influential “business sharks”, are willing to invest in solutions that would allow customers to control their products remotely over the Internet. This process becomes even more “widely available” and simplified for players from different weight categories. The reason is the existing infrastructure solutions from cloud providers – AWS (Amazon), GCP (Google), Azure (Microsoft), etc. The main advantage here is that there is no need to setup and maintain an infrastructure for managing IoT devices from scratch, it is enough to use a bunch of services provided by a cloud platform and work according to the “pay as you go” plan.

Despite the huge “arsenal” of available options/features, existing cloud services have to be properly configured – miscalculations in configuration settings and architecture can lead to undesirable results, especially when the question of “scalability” arises. One of the main problems that developers of IoT-based infrastructures have to solve is security and confidentiality. It is necessary to clearly understand and be aware of who and what level of access they have for the corresponding IoT resources: registries, topics etc.

One of the popular cloud providers used for developing IoT-based projects is Amazon (AWS), with its well-known AWS IoT Core service. Extensive debugging capabilities, as well as native integration with other components of the platform, makes it possible to create complete design solutions in a short period of time for faster time-to-market. However, one should always keep in mind the limitations imposed by the AWS IoT Core service, this knowledge will definitely help to reduce the risk of introducing errors during the project architecture design phase.

Let’s consider yet another approach for granting access of a group of users (user applications) to a number of IoT devices called things, taking into account the existing security and policy limitations of the AWS IoT Core service.

Problem Statement

In general, the following relationships between users (users application) and things (IoT devices) are available (Fig. 1):

  1. One User – One Thing (1:1).
  2. One User – Multiple Things (1:M).
  3. Multiple Users – One Thing (M:1).
  4. Multiple Users – Multiple Things (M:M).
Figure 1 – Diversity in Relationship Between Users and Things

One of the challenging tasks of IoT Core service setup is permissions configuration for different types of relationships using policy documents. And, if for a 1:1 this can be easily implemented, but for the other types of relations like 1:M, M:1 and M:M the process is not so straightforward, especially for the cases when users and devices can change their group affiliation during the entire life cycle.

Let’s considered the following initial input data (Fig. 2):

  1. Amazon Cognito (Cognito User Pool, Cognito Federated Identities) – registration, authentication, authorization and storing user identities.
  2. Amazon Core IoT – things managing (registration, deletion, granting access), supporting interaction between things and cloud infrastructure.
  3. Amazon DynamoDB – storing relationships between entities: users, user groups, things etc. Notes: Amazon DynamoDB is proposed as an example for quick prototyping, but any other databases can be used as well.
  4. A user group can only have access to a list of things that belong to it.
  5. A user can only be a member of a single group at a time.
  6. A thing can only belong to a single user group at a time.
Figure 2 – Domain Description – Interaction Between Entities.

AWS IoT+Cognito: Coarse Grained Architecture

Based on the requirements defined in the problem statement section, let’s consider a coarse grained architecture and an interaction between Amazon services (Fig. 3) which are used for providing user group access to things.

Figure 3 – AWS IoT+Cognito: Coarse Grained Architecture

Things use MQTT (Message Queuing Telemetry Transport) protocol to communicate with AWS IoT Message Broker. Users (or user apps) in a group are able to receive data and manage IoT devices in near real-time using MQTT over WebSocket. AWS Cognito in conjunction with the Identity and Access Management (IAM) service is used to authenticate and authorize users as well as granting permissions to different parts of the system. Extra resource permissions, e.g. MQTT message topic subscription, message publishing etc. are defined by AWS IoT Core policy documents.

AWS IoT Core Policy

The mechanism of data plane access within AWS IoT Core service is built on top of IoT Core policies, which are no more than just JSON documents. They follow the same conventions as AWS IAM policies and define explicitly what is allowed and what is not for a particular entity (certificate for a thing, Cognito Identity for a user) to which policy document is attached (Fig. 4).

Figure 4 – AWS IoT Core Policy Document and Thing Certificate/Cognito Identity Relationship.

Thus, to define access and permissions within AWS IoT Core service the following steps have to be performed:

  1. The policy document has to be created.
  2. The policy document has to be attached to a particular entity – either thing certificate or Cognito identity.  

Let’s consider several examples.

Thing Policy

The following policy explicitly forbids the clients with the identifiers client1 и client2 to connect to AWS IoT Core, but allows them to connect things with the names (IDs) that were registered before in the AWS IoT Registry.:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
        "arn:aws:iot:us-east-1:123456789012:client/client1",
        "arn:aws:iot:us-east-1:123456789012:client/client2"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
"arn:aws:iot:us-east-1:123456789012:client/${iot:Connection.Thing.ThingName}"
      ]
    }
  ]
}

For registered in the AWS IoT Registry devices, the following policy grants permissions to subscribe to my/topic and to publish on a thing name-specific MQTT topic, except the one that is ending with bar prefix.

{
  "Effect": "Allow",
  "Action": [
    "iot:Publish"
  ],
  "Resource": [
    "arn:aws:iot:us-east-1:123456789012:topic/${iot:Thing.ThingName}/*"
  ]
},
{
  "Effect": "Deny",
  "Action": [
    "iot:Publish"
  ],
  "Resource": [
    "arn:aws:iot:us-east-1:123456789012:topic/${iot:Thing.ThingName}/bar"
  ]
},
{
  "Effect": "Allow",
  "Action": [
    "iot:Subscribe"
  ],
  "Resource": [
    "arn:aws:iot:us-east-1:123456789012:topicfilter/my/topic"
  ]
}

Cognito Identity Policy

To grant Cognito Identity permission to connect to AWS IoT Core (MQTT over WebSocket) a user group policy should contain the following section:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect",
      ],
      "Resource":[
   "arn:aws:iot:us-east-1:123456789012:client/${cognito-identity.amazonaws.com:sub}"
      ]
    }
  ]
}

The rest of the policy document may have similar sections that are applied for IoT devices. For example, the following fragment of a policy document allows clients to subscribe to a thing foobar “shadows” (default, named) as well as update it:

{
  "Effect": "Allow",
  "Action": [
    "iot:Publish",
    "iot:Receive"
  ],
  "Resource": [
    "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/foobar/shadow/*"
  ]
}

A policy document is an important component that determines access rights and permissions across corresponding resources of IoT Core services with respect to things and user applications. And, if for IoT devices, this document can have a regular and fixed structure available for sharing across multiple things using wildcard characters; then for user groups it may have significant differences with constant updates required every time when adding/removing users/devices to/from a user group.

Before creating policy documents for our use-case, let’s consider existing limitations and restrictions when building access policy within AWS IoT Core service.

AWS IoT Core: Security and Identity Limits

Like any other cloud service provider, AWS IoT Core has a quota and limits [AWS IoT quotas] that must be taken into account when designing a scalable system architecture. Let us consider and analyze only those restrictions that are closely related to the current topic: “Security and Identity” (Table 1, Fig. 5).

Figure 5 – AWS IoT Core Security and Identity Limits

Maximum number of named policy versions per policy

To update a policy, one should create a new policy version. If the policy has five versions, you must delete an existing version before creating a new one. In a dynamically changing environment, Policy Documents will definitely have a lot of changes/change requests which should be properly handled to minimize race conditions during multiple document updates.

Maximum policy document size

This restriction is a very important one. If you have a lot of resources to be described in a Policy Document in terms of characters: topic names, client IDs (users/things), action types (allow/disallow sections) – this can be a potential bottleneck for future project scaling. Because, when you add a new user to a group or update a policy record you have to be sure that document size is no more than 2048 characters long. If the limit is exceeded this will require extra efforts on handling Policy Document description, i.e. the content of a policy must be splitted and shared across multiple documents. Even more, the number of documents that can be attached is also a restricted value.

Maximum number of policies that can be attached to a certificate or Amazon Cognito identity

Yet another restriction to be taken into account when defining access rights for user identities or IoT device certificates is the number of policies that can be attached. So, if to follow a strategy when a separate policy document will be created per thing, then the maximum number of IoT devices that user (Congito identity) can manage is equal to 10.

Use Case: Manage Things Across User Groups

Based on the existing constraints in the security and identity of AWS IoT Core service and the requirements defined in the problem statement section, let’s consider an approach of granting permissions to user groups to access respective IoT devices. This approach should consider scaling and keep as minimal the actions required when changing users and things, and their group affiliation during the life cycle.  

One of the first steps regarding policy access management process optimization is decreasing the number of API requests used for performing document updates. A good practice is to keep a policy document with a well-defined regular structure which can be easily, but not very often, changed. To make this process even more efficient, one can use templating and wildcard symbols supported in AWS IoT Core policy document syntax. In this case the only step left is to attach an appropriate policy document with a thing certificate and/or Cognito identity for users (applications) (Fig. 6).

Figure 6 – Access Policy Management for Things based on Multiple Policy Documents Attachment

Attaching a policy document works well in most cases for IoT devices, but can be a bottleneck when there is a need to share a particular thing across multiple users. The main problem is AWS IoT Core policy restrictions – a maximum 10 documents (through users) can be attached to a thing certificate. An obvious method is to define a single group access policy document – the User Group Policy Document that belongs to a certain group and defines AWS IoT resources access policy for all users within a group. The classic approach in this case, involves editing the User Group Policy Document every time when adding/removing an IoT device to/from a group. And again, every time when editing a document, one should not forget about the maximum 2048 characters.

An alternative approach is to use templates along with wildcard characters. The main idea is that each IoT device (a thing) obtains some unique group prefix, which is present in its name, and the User Group Policy Document uses a template engine to generate access (Fig. 7).

Figure 7 – Access Policy Management for User Groups

Let’s consider the following User group Policy Document for a Group #1:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:connect"
      ],
      "Resource": [
        "arn:aws:iot:us-east-1:123456789012:client/${cognito-identity.amazonaws.com:sub}"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Subscribe"
      ],
      "Resource": [
        "arn:aws:iot:us-east-1:123456789012:topicfilter/$aws/things/YReY8z9f*/shadow/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:us-east-1:123456789012:topic/$aws/things/YReY8z9f*/shadow/*"
      ]
    }
  ]
}

According to the document presented above, users from the Group #1 with the tag YReY8z9f can subscribe and publish any messages to shadows-related topics of things which have the declared prefix as a part of their name. As an extra bonus, there is no need to update policy documents every time when adding/removing things to/from a group. And, of course, one should pay attention to the process of generating a group prefix along with the device ID when registering a new thing in the AWS IoT Registry. First of all, the name of a thing must be unique across the registry and also it’s necessary to exclude collisions when generating group prefixes. The best way to go about this, is to follow the best practices when obtaining unique values for string constants and to find a balance between “willingness” to use common techniques (e.g. UUID) and “acceptability” in terms of readability of names of IoT devices. One can add verbose thing names to a group prefix, for example.: YReY8z9f-kitchen-light-sensor, YReY8z9f-central-lock.

Thus, the proposed approach has a number of advantages:

  1. User (application) access to the IoT devices from a particular group is accomplished by “attaching” a Cognito identity to a corresponding Use Group Policy Document. Within existing restrictions users can belong to no more than 10 different groups.
  2. There is no need to update the User Group Policy Document each time when adding/removing users to/from a group.
  3. User Group Policy Document has a fixed, well-defined, commonly used structure (content) with only a difference in the prefix and can be generated during a group creation process.

Conclusion

This post discusses yet another way on how to build an IoT access management system for different “participants” (user/user applications, IoT devices etc.) around AWS IoT Core infrastructure and services provided by Amazon. The core concept is to operate with well-structured, pre-defined policy documents created in particular for IoT devices as well as for user groups. These documents are not supposed to be updated very often, rather, the process should be built around attaching/detaching them to a particular entity – an IoT certificate for a thing or Cognito identity for a user/user application.

It should be mentioned that the proposed approach is not a “golden bullet” and doesn’t claim to be an absolute truth. Each project is mostly unique and, potentially, will require a slightly different implementation. One can exclusively use the capabilities of the AWS IoT Core Service itself (thing groups, group attributes, and thing attributes) to store the relationships between entities, IoT device group affiliations, or any other kind of data.

Anyway, before IoT project architecture design, there is a need to dive a little bit deeper into the business area to identify constraints and requirements. And finally, one should follow AWS IoT security best practices to prevent user data “leakage”.