“node-red-contrib-azure-iot-hub” considered harmful?

Node-Red is a flow-based development tool for visual programming.

It is intensely popular as a programming environment for controlling events. It’s even built in into hardware for flow-base programming and control and has a large community of proud users.

A library is available also with many nodes for al kinds of use cases. If you search for ‘azure’, three pages of nodes and flows are available.

One of them is this node-red-contrib-azure-iot-hub which is one of the most popular nodes:

This project is open-source and available on GitHub. It comes with sufficient documentation.

I used this for a small project and checked out all features. It works as documented but still, I have some doubt using it in production.

The main issue is that it mixes both the Azure IoT Device SDK and the Azure IoT server-side SDK. This makes it a “Jack of all trades, master of none”.

Let me show what I mean with some examples.

There are four nodes available:


These are:

  1. Azure IoT Hub. Send messages to an Azure IoT hub
  2. Azure IoT Registry. Register new devices on the IoT Hub
  3. Azure IoT Hub receiver. Use this for receiving Cloud to Device messages
  4. Azure IoT Hub Device Twin. Receive Azure IoT Hub Device Twin(s)

Let’s check out these nodes in detail. Let’s look at the pros and cons of these nodes.

Azure IoT Hub node

We start with the module which is useful for sending messages to an IoT Hub.

For this, you need the credentials of your IoT Device:

We want to send a specific message to the cloud:

{
  "valueStr": "string",
  "valueInt": 42,
  "valueDateTime": "2020-05-08T14:13:45.4745936Z",
  "valueBool" : false
}

For this we use this flow of nodes. I inject the message using an Inject node. I convert the message using a Change node before I send it to the Azure IoT Hub node:

The Azure IoT Hub node only handles incoming messages in a certain format, that why I have to convert it using this expression:

{
  "deviceId":"iotDevice",
  "key":"2AM6....i8htA=",
  "protocol":"amqp",
  "data": { 
      "valueStr": payload.valueStr,
      "valueInt": $number(payload.valueInt),
      "valueDateTime": payload.valueDateTime,
      "valueBool" : $boolean(payload.valueBool)
    }
}

Notice, the device ID and a SAS key of the device are part of the message. The original message values are merged and converted if needed.

And we have to fill in the protocol used to communicate to the cloud. Here I use ‘amqp’ but ‘mqtt’ does work too.

This message is sent to the Azure IoT Hub node. This node has to be configured with the (full) hostname:

[name of the iothub].azure-devices.net

We also have to repeat the protocol:

We are now ready to deploy our flow.

Once the flow is deployed, hit the inject button to start sending one message:

As shown in the debug window, the message is sent. Using the Azure IoT Explorer, we see the arrival of the message in the cloud:

Pros and cons

I have not seen how I can send extra message properties which is very helpful for IoT Hub routing. And due to the lack of specific properties needed for decoding the body, I can’t use message body properties either for routing.

Still, this is exactly as expected and the right implementation of sending a message. The name of the node like ‘Azure IoT Hub device’ would be a better fit.

Azure IoT Registry node

The Azure IoT Registry node makes it possible to register new, extra IoT devices in the IoT Hub.

In line with the API, this node needs an IoT Hub Policy key with at least ‘Registry write’ (and therefore also ‘Registry read’) permissions:

Then, we need to trigger the creation of a device by feeding the node this JSON message with the device id ‘someDevice’:

{
  "deviceId": "someDevice"
}

I use the Inject node to inject the JSON:

Again, deploy this flow and hit the Inject node button:

The debug window shows some message with an undefined payload.

On the IoT Hub, we see the this extra device being registered:

Pros and cons

Ho, ho ho, now I can create devices in an IoT Hub!

But… this code is actually executed on an IoT Device.

Thou shalt not use IoT Policy key outside the Azure fortress!

It’s not wise to expose keys of IoT Hub policies in devices. This key is available in plain text. The chance this key will be misused when falling in the wrong hands (when the device is replaced, modified, sold or the code is copy and pasted) is just too big.

If you want to create a device, please just send a special message which is then handled in eg. some Azure Function. There, you can indeed protect your secrets.

How it is implemented right now, you are not able to send data to that new device. You have no access to the SAS keys of the new device!

I have no idea why you want to create devices on devices in the first place…. just use it in the Azure cloud and you are save.

Azure IoT Hub Receiver node

This node is advertized as “This is a simple node for receiving device-to-cloud messages via default Azure Events Hub endpoint”.

This can be used in two ways:

  1. Connect it to the default Azure Event Hub endpoint of an IoT Hub and listen to all incoming messages using a SAS Key
  2. Connect to any Event Hub in Azure using a SAS Key

First, find the Event-Hub compatible endpoint of your IoT Hub:

Fill this in to your IoT Hub Reciever node:

Once a message is received from the IoT Hub, it will be exposed as a byte array.

Therefor, we convert it using a Function node:

Convert it with this function:

msg.payload = Buffer.from(msg.payload);
msg.payload = msg.payload.toString();
return msg;

And give the node a proper name: This will look like this:

Once deployed, send a message (using the Inject node connected to the IoT Hub node) to the IoTHub.

Right after that, see the arrival of the message in our IoT Hub Receiver node:

Notice the little yellow indicator, a message is received. And it is human-readable using the function.

This module can also listen to all messages sent over a regular Event Hub. Make sure you pick a SAS key with the rights to listen:

Pros and cons

This is a weird node.

First, as with policy key, think twice before exposing an Event Hub SAS key outside the Azure cloud. Just use this node there, not a device.

Next, this node exposes all messages from an Event Hub. In an IoT Hub with hundreds or even thousands of chatty devices, this will generate a lot of traffic. If this is intended to read data for a single device, the largest part will be ignored.

One use-case could be to listen to commands sent from the cloud to a device using an Event Hub. But then again, this is inefficient and you ‘leak’ message to other (unknown) listeners have the same SHARED key.

Azure IoT Hub Device Twin node

This should be a very promising node.

Every device in an IoT Hub has a private Device Twin:

Scherm afbeelding van dubbele eigenschappen van het apparaat

This makes it possible to send Desired Properties to a device to be picked up. Once the device is connected to the internet, it gets its private desired properties and processes it. After that, it can even report back how it reacts to the desired values using the reported properties.

The Device Twin also has this nice Tag feature. Here you can add some context to a device eg. the make or model of the device. Later on, this context can be queried, only on the backend, to make subsets of devices and activate jobs on them.

Unfortunately, this node makes use of the IoT Hub registry API.

So we need to pass the Azure IoT Hub Device Twin, again, an IoT Hub SAS policy key. This time, it needs the Registry read rights:

This node has to be triggered. We can pass it a name of a device as a string:

Once published and triggered, we see the arrival of the Device Twin. And with that, I mean everything:

Not only the desired properties are exposed, nut also the tags are seen on the device.

And next to that, I do not pass a string with a device id but something else like a timestamp, I receive the device twins of ALL registered devices:

Pros and cons

Again, do not use this on a device. There are several reasons for this:

  • You have to share a policy
  • Too much data for one device is exposed
  • All data of all devices is exposed
  • This generates a lot of traffic if you read all devices
  • This can potentially slow down your device for a long time and degrade service

And above all, why is this not using the regular API of the device twin using the key which is used by the Azure IoT Hub node?

The name of this node is doubtful. The name ‘Azure IoT Hub Registry Device Twin’ node is a better fit. Just use this in the Azure Cloud.

Conclusion

Looking at the overall pros ans cons, the best usage of this collection of nodes is in Node-Red in the cloud.

We also see these nodes being distributed a part of Node-Red on devices, though.

I can tell you: ‘here be dragons‘ or perhaps, more popular in IT world, I would classify these nodes ‘considered harmful‘.

This means you have to know what you are doing and use it with care! Think twice!

The implementation is mainly a mix of one node useful on the device (for sending messages). The other three nodes are not supposed to be used on a device due to the exposure of key which should be kept in the cloud. This is a mixture of both device-side and server-side SDKs.

It is tempting though to try to fiddle with it because it potentially is possible to react on desired properties or even wait for a message sent on an Event Hub. And that means exposing security keys on devices which should be kept in the cloud. This is cumbersome and insecure.

You have to get familiar with what Azure IoT is offering regarding server-side and device-side APIs to see these potential security issues.

For those interested in more background information about proper usage of D2C and C2D communication, please check out the workshop: Remotely monitor and control devices with Azure IoT Hub.