Contents

Parallel MQTT sessions between a single Device and AWS IoT Core

Today I’ll write about the parallel MQTT sessions between a single Device and AWS IoT Core.

I will also share an unexpected finding! To be honest, I am not sure if that is a bug or a feature of the AWS IoT Core.

Introduction

The AWS IoT Core message broker does not allow two clients with identical client ID to stay connected at the same time. After the second client connects, the MQTT Broker (part of the AWS IoT Core) closes the existing connection.

Further reading: Check the iot:Connect IoT Policy Action description in the AWS documentation.

The above constraint typically does not impact our design as in the same MQTT session client can Subscribe and Publish MQTT messages.

/posts/parallel_mqtt_sessions/img/connection_01.png

There are a few edge cases, where that constraint applies.

For example, when we want to use multiple applications on our IoT device. Applications will constantly disconnect each other if they use Device’s credentials and their own MQTT connection.

/posts/parallel_mqtt_sessions/img/connection_02.png

IoT Lab setup

IoT Policy

In our IoT Policy, we will use the following Thing policy variable:

iot:Connection.Thing.ThingName - It resolves to the name of the IoT Thing in the AWS IoT Core registry for which the IoT Policy is being evaluated. AWS IoT Core uses the X.509 Certificate the device presents when it authenticates to determine which IoT Thing to use to verify the connection.

https://docs.aws.amazon.com/iot/latest/developerguide/thing-policy-variables.html

IoT Policy document:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:client/${iot:Connection.Thing.ThingName}"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topic/dt/temperature"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topic/cmd/temperature"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topicfilter/cmd/temperature"
    }
  ]
}

To test our setup, start the first MQTT session and subscribe to the cmd/temperature topic:

1
mosquitto_sub -h a1pmmrdn6yc5il-ats.iot.eu-west-1.amazonaws.com -i temp-sensor-01 --key certs/aws-cert.key --cert certs/aws-cert.pem --cafile certs/AmazonRootCA1.pem -t 'cmd/temperature' -d -q 1

Then open the second terminal, create an additional MQTT session, and publish to the dt/temperature topic:

NOTE: a single client can subscribe and publish using the same MQTT session, but in some cases, we need to establish multiple MQTT sessions from the same device using the same credentials.

1
mosquitto_pub -h a1pmmrdn6yc5il-ats.iot.eu-west-1.amazonaws.com -i temp-sensor-01 --key certs/aws-cert.key --cert certs/aws-cert.pem --cafile certs/AmazonRootCA1.pem -t 'dt/temperature' -m '20' -d -q 1

Carefully observe the state of the first MQTT connection:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Client temp-sensor-01 sending CONNECT
Client temp-sensor-01 received CONNACK (0)
Client temp-sensor-01 sending SUBSCRIBE (Mid: 1, Topic: cmd/temperature, QoS: 1, Options: 0x00)
Client temp-sensor-01 received SUBACK
Subscribed (mid: 1): 1


Client temp-sensor-01 sending CONNECT   <--- the MQTT session was disconnected and client had to reconnect
Client temp-sensor-01 received CONNACK (0)
Client temp-sensor-01 sending SUBSCRIBE (Mid: 2, Topic: cmd/temperature, QoS: 1, Options: 0x00)
Client temp-sensor-01 received SUBACK
Subscribed (mid: 2): 1

Reason:

The AWS IoT Core message broker doesn’t allow two clients with the same client ID to stay connected at the same time. After the second client connects, the broker closes the existing connection.

To overcome this limitation, we can try to use a different client ID for the second MQTT session:

1
mosquitto_pub -h a1pmmrdn6yc5il-ats.iot.eu-west-1.amazonaws.com -i temp-sensor-01_01 --key certs/aws-cert.key --cert certs/aws-cert.pem --cafile certs/AmazonRootCA1.pem -t 'dt/temperature' -m '20' -d -q 1

Let’s try to connect using the temp-sensor-01_01 as the client ID.

1
2
Client temp-sensor-01_01 sending CONNECT
Error: The connection was lost.

Our IoT Policy does not allow for the iot:Connect action using temp-sensor-01_01 as the client ID.

Initial solution

That is the first solution I wanted to propose and I thought it would work.

Please check my comment in the IoT Policy below (you might need to scroll right):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": [
        "arn:aws:iot:eu-west-1:693854281758:client/${iot:Connection.Thing.ThingName}",
        "arn:aws:iot:eu-west-1:693854281758:client/${iot:Connection.Thing.ThingName}_*"  # this Resource should allow connections using various Client IDs with the same prefix (but it does not work!)
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topic/dt/temperature"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topic/cmd/temperature"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topicfilter/cmd/temperature"
    }
  ]
}

Let’s create a new version of this IoT Policy and verify if that will solve our issue:

Tests:

The first test, publishing using the “client ID”:

1
mosquitto_pub -h a1pmmrdn6yc5il-ats.iot.eu-west-1.amazonaws.com -i temp-sensor-01 --key certs/aws-cert.key --cert certs/aws-cert.pem --cafile certs/AmazonRootCA1.pem -t 'dt/temperature' -m '20' -d -q 1

works:

1
2
3
4
5
Client temp-sensor-01 sending CONNECT
Client temp-sensor-01 received CONNACK (0)
Client temp-sensor-01 sending PUBLISH (d0, q1, r0, m1, 'dt/temperature', ... (2 bytes))
Client temp-sensor-01 received PUBACK (Mid: 1, RC:0)
Client temp-sensor-01 sending DISCONNECT

The second test, publishing using the “client ID_01”:

1
mosquitto_pub -h a1pmmrdn6yc5il-ats.iot.eu-west-1.amazonaws.com -i temp-sensor-01_01 --key certs/aws-cert.key --cert certs/aws-cert.pem --cafile certs/AmazonRootCA1.pem -t 'dt/temperature' -m '20' -d -q 1

does not work:

1
2
Client temp-sensor-01_01 sending CONNECT
Error: The connection was lost.

It looks like the client ID of the MQTT connection must be the same as the IoT Thing name.

My reasoning

  1. A Device sends a connection request to the AWS IoT Core including its X.509 Certificate.
  2. The IoT Core verifies the IoT Policy attached to the presented X.509 Certificate.
  3. The ${iot:Connection.Thing.ThingName} variable value is calculated using the IoT Thing name attached to the respective X.509 Certificate.
  4. IoT Core takes action based on the IoT Policy with calculated variables.

IMPORTANT: please mind that this is the way “it should work” in my opinion, but the above test proves otherwise!

/posts/parallel_mqtt_sessions/img/issue_01.png

Working solution

The below solution will work, but I do not like it. I had to hardcode the IoT Thing name in the IoT Policy.

Once again, I do not know why AWS IoT Core behaves this way.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": [
        "arn:aws:iot:eu-west-1:693854281758:client/temp-sensor-01",  # IoT Thing name is HARDCODED in the IoT Policy (not a best practice!)
        "arn:aws:iot:eu-west-1:693854281758:client/temp-sensor-01_*" # IoT Thing name is HARDCODED in the IoT Policy (not a best practice!)
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topic/dt/temperature"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topic/cmd/temperature"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:eu-west-1:693854281758:topicfilter/cmd/temperature"
    }
  ]
}

Create a new version of the IoT Policy and test.

The first test, publishing using the “client ID”:

1
mosquitto_pub -h a1pmmrdn6yc5il-ats.iot.eu-west-1.amazonaws.com -i temp-sensor-01 --key certs/aws-cert.key --cert certs/aws-cert.pem --cafile certs/AmazonRootCA1.pem -t 'dt/temperature' -m '20' -d -q 1

works:

1
2
3
4
5
Client temp-sensor-01 sending CONNECT
Client temp-sensor-01 received CONNACK (0)
Client temp-sensor-01 sending PUBLISH (d0, q1, r0, m1, 'dt/temperature', ... (2 bytes))
Client temp-sensor-01 received PUBACK (Mid: 1, RC:0)
Client temp-sensor-01 sending DISCONNECT

The second test, publishing using the “client ID_01”:

1
mosquitto_pub -h a1pmmrdn6yc5il-ats.iot.eu-west-1.amazonaws.com -i temp-sensor-01_01 --key certs/aws-cert.key --cert certs/aws-cert.pem --cafile certs/AmazonRootCA1.pem -t 'dt/temperature' -m '20' -d -q 1

works as well:

1
2
3
4
5
Client temp-sensor-01_01 sending CONNECT
Client temp-sensor-01_01 received CONNACK (0)
Client temp-sensor-01_01 sending PUBLISH (d0, q1, r0, m1, 'dt/temperature', ... (2 bytes))
Client temp-sensor-01_01 received PUBACK (Mid: 1, RC:0)
Client temp-sensor-01_01 sending DISCONNECT

We were able to establish a new MQTT session between our Device and the AWS IoT Core using a client ID different than the IoT Thing name.

Conclusion

We managed to establish a parallel MQTT session between our Device and AWS IoT Core, but I am not satisfied with the implementation. I need to do some more research on this topic, but it looks like a bug in the AWS IoT Core.

Lab materials

If you would like to receive the complete code samples used in this article, you can purchase them using this link.

The free IoT Lab is the easiest way to interact with the sample code. You can find details on this page.

All the best!
LMtx

Support quality content❤️ Donate💰

Sign up for news: (by subscribing you accept the privacy policy)