Permissions for IoT Things and Cognito User/Identity Pools

1

Hello,

I am having some issues architecting a good security scheme for managing IoT Thing access for Cognito users. My use case is the following:

  • We have a number of users (corresponding to users in a User Pool, with an associated Identity Pool). Each user belongs to a particular "Company". Currently this is done via an attribute (custom:Company).
  • We have a number of IoT Things. Each of these Things belongs to a static Thing Group, whose name matches the attribute above.

I'd like for a given User/Identity to be able to receive the MQTT data stream from Things that belong to a static group that matches their custom:Company attribute.

Example:

  • I have 6 Things: A, B, C, D, E, F.
  • A, B & C belong to static group "FirstCompany"
  • D, E & F belong to static group "SecondCompany"
  • I have two cognito users/identities: Alice and Bob.
  • Alice has the custom attribute custom:Company = FirstCompany
  • Bob has the custom attribute custom:Company = SecondCompany

I'd like for Alice to be able to subscribe to the MQTT topics for devices A, B and C, but NOT D, E and F. This means permissions for iot:Connect, iot:Receive, iot:Publish and iot:Subscribe.

The pseudo-policy I'd like to assign to all users is something like this:

effect = allow,
action = ["iot:Receive", ...]
condition: target thing group == ${aws:PrincipalTag/custom:Company}

Unfortunately I haven't found something as straight-forward as this. As I see it, my options are:

  1. Draft custom policies for each customer, in which each Thing (and associated topics) are explicitly allowed. This seemingly wouldn't scale well if a customer has thousands of Things.
  2. Create a custom IoT authorizer that compares the principal's attribute with the Thing's static group. This seems like it'd run into rate limiting issues, especially if I have to check which groups a Thing belongs to for every MQTT message.
  3. Come up with a naming scheme for devices that includes a customer name in some way (e.g., instead of A, B, C I'd have FirstCustomer-A, FirstCustomer-B, FirstCustomer-C). This doesn't feel like a great approach.

However, it seems like this situation would be pretty common! Is there a particular way this should be done? Any guidance would be appreciated!


Edit: Following up on the suggestion from Pronoy_C, I've set up the following IoT Core Policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "*"
    },
    {
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:sub": "${iot:Connection.Thing.Attributes[Owner]}"
        }
      },
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": "arn:aws:iot:us-east-1:xxxxxxxxxxxx:thing/*"
    }
  ]
}

and I've attached this policy both to Thing A and to Alice's Identity. However, while Alice is able to connect to the MQTT host, I cannot publish to $aws/things/A/shadow/get. The AWS logs indicate AUTHORIZATION_FAILURE. I do indeed have the Owner attribute set to Alice's Identity ID. I tried testing with the CLI tool, but have run into issues there (see this thread).

3 Answers
1

This will not work as you intend to as You need to be aware about differences between IoT Policy and IAM Policy, they both have different options and variables they allow for specific actions. Your 'cognito-identity.amazonaws.com:sub' is an IAM level variable, not IoT Policy variable hence it will not work in IoT Policy.

In your case you would need to map a custom user attribute to either a thing or a topic or anything else within the IAM policy and then allow access based on that. For example - have a Cognito user with custom attribute "department" with value "accounting", then add a custom attribute mapping in Cognito Identity settings so department : custom:department.

Once you have done that go to IAM and open a role that you Cognito is using for Authenticated users. In trust relationship tab of that role add "sts:TagSession" as permitted action on top of the one that is there already.

Then go to Policy that is attached to this role and in there you can now use ${aws:PrincipalTag/department} as a variable and match it against your actions for iot. So for example:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iot:Subscribe",
            "Resource": "arn:aws:iot:*:*:topic/*${aws:PrincipalTag/department}*"
        }
    ]
}

Will now allow subscription to any topic that contains "accounting" in its path. See here for more in depth explanation: https://docs.aws.amazon.com/cognito/latest/developerguide/attributes-for-access-control.html Hope this helps.

AWS
answered 2 years ago
  • This is correct. I confused IoT policy with IAM policies. Those are two different things. Thanks for setting the record straight.

0

You'll need to map custom attributes from Amazon Cognito to AWS IoT Core Thing or Thing Type attributes. So Cognito user pools are going to go to specific Identity Pools which will have IAM permissions to IoT Core topics.

IoT Core policies can be crafted using custom attributes as policy variables.

You may not want to use the group as the entity to compare to grant permissions. Instead when onboarding a new device use a pre-provisioning lambda to assign an attribute to the Thing itself.

Please let me know if I have misunderstood something.

AWS
answered 2 years ago
  • I think this is along the way I was thinking. The issue for me is that I'd like for a customer (e.g., Alice), to be able to subscribe & publish to $aws/things/A/shadow, $aws/things/B/shadow, $aws/things/C/shadow but NOT to $aws/things/D/shadow (or any of the other device shadows).

    It appears that in order to use the IoT Core Policy variables for Things (e.g., iot:Connection.Thing.Attributes), I must attach the IoT Core Policy to the Thing's Certificate, to the Cognito Identity and attach the Identity to the Thing. And even then Alice must connect using "A" as the client ID, meaning only one Thing's shadow can be accessed per session.

0

I'm not sure I follow exactly. I'm not certain where the IoT Core policies fit in, exactly? I understand that there must be an IoT Core policy attached to each Identity in order for that particular user to access IoT functionality. Are you suggesting that this IoT Core policy contain permissions for specific MQTT channels? I'm not sure what that IoT Core policy would look like? What I tried that didn't seem to work was something similar to this (pseudocode):

effect = Allow
actions = ["iot:Receive", "iot:Publish"]
resources = *
condition = ${iot:Connection.Thing.Attributes[custom:Company]} == ${aws:PrincipalTag/custom:Company}

Are you suggesting that perhaps I specifically call out resources (e.g., the specific MQTT channels for each Thing)?

On a related note, I am somewhat unclear on the differences between the actions in the IoT Core policies (e.g. iot:Receive) and the IAM policies (also seemingly named iot:Receive). Must these be reciprocal?

Thank you very much!

answered 2 years ago
  • Would it be possible for you to reach out to me directly via pronoc@amazon.com? I may have misunderstood your use-case and would like to help out further with this.

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions