Secrets

The Problem

Throughout your code, you'll need to reference sensitive data such as usernames, passwords, API keys, etc. This is problematic as you may want to share your code with others, backup to Github, etc. While Home Assistant provides an easy way to redact secrets from your configuration files, Node-RED does not.

Previous Solution

One solution is to use a special node called node-red-contrib-credentials. While this approach initially seemed promising, it came with too many drawbacks:

  • Must add a node everywhere you want to access a secret (this quickly makes a mess of your flows)
  • No centralized secrets management - the secrets added in one node are not available to another
  • Copy/paste of nodes causes their secrets to be erased

Current Solution

My current solution is easy to setup, easy to use, and leverages built-in Node-RED features.

How it works

Context is a way you can share data between nodes and flows without using the msg object. My solution leverages our own custom, persistent context storage we'll call "secrets". By using the global keyword whenever we read/write secrets, we are able to access our secrets across all nodes and flows.

Ultimately, all secrets are stored, in plain text, in /config/node-red/secrets/global/global.json.

warning

Be sure to exclude the /config/node-red/secrets path in your .gitignore file (since the secrets are stored in plain text).

Setup

  1. Add the following section to your settings.js:
contextStorage: {
default : { module: "memory" },
secrets: {
module: "localfilesystem",
config: {
dir: "/config/node-red",
base: "secrets"
}
}
},
  1. Restart Node-RED

Adding / Editing Secrets

You can manually create and or edit the secrets file (/config/node-red/secrets/global/global.json):

{
"first_secret": "123",
"second_secret": "abc"
}
note

While immediately available in memory, secrets modifications through Node-RED (i.e. the methods below) will take a few seoncds to write the changes to the secrets file.

You can also create and or edit each secret by running the below in a function node (replacing your_key and your_value appropriately):

global.set("your_key", "your_value", "secrets");

And you can even create and or edit secrets with change nodes:

Secret Edit
[{"id":"bf2ad73a.23bdb8","type":"change","z":"977bc9ad.687238","name":"Save Secret","rules":[{"t":"set","p":"#:(secrets)::some_secret","pt":"global","to":"some_value","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":260,"wires":[[]]},{"id":"5c5c19b4.549de8","type":"inject","z":"977bc9ad.687238","name":"Press","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":260,"wires":[["bf2ad73a.23bdb8"]]}]

Accessing Secrets

You can access secrets through any node that can access context (be sure to select the "secrets" context storage).

ex. the inject node:

Secrets Access

ex. the change node:

Secrets Access

ex. the switch node:

Secrets Access

You can also access through the function node:

var secret = global.get("your_key", "secrets");