Use `htpasswd` to secure your node-RED Container
Securing Node-RED
node-RED containers can be made secure when using Admin Authorization features
using its settings.js
file and mounting it to the container. Sounds quite simple,
however unlike other containers that let your configure their environments via environment
variables whereby the stored credentials are in plaintext, node-RED passwords typically are
stored in encrypted format i.e., bcrypt.
Automation: Encrypted Passwords
There is a possibility of obtaining the hashed (encrypted) password using an ephemeral container using:
docker run -it --rm --entrypoint /bin/bash nodered/node-red:2.2.2-12-minima
One in the container use:
bash-5.0$ node-red-admin hash-pw
which will prompt you to enter the password and the resultant hash password will be available for you to use.
This is however, a bit more of manual work and in case where you wish to perform more easy, automated setups we might need to find a different way.
Automation Script via htpasswd
Apache provide a great CLI tool called htpasswd
that can encrypt our plaintext passwords
into the required bcrypt
hashed format which the node-RED UI and Admin Auth backend will decrypt.
For most of the distributions the package name is apache2-utils
so for Ubuntu/Debian you can install
htpasswd
using:
sudo apt-get install apache2-utils
Jumping right into it!
You can go through the man
pages of htpasswd
to find how it might suit for your needs. I am cutting
to the chase as to how I used it.
In order to generate my hashed password that will be compatible with node-RED’s decryption backend, I had
to find the total Computation Cost (rounds) needed, which I found out the value was 8
based on some
code diving using node-red-admin repository.
htpasswd -nb -B -C 8 admin testpassword
will produce the following result:
admin:$2y$08$JTOJDi/whZxz8qwsdwCkfedhVOhh00QiKHICdHLImGS8XM2v.fng2
Creating an Environment Variable Template file for node-RED Docker Container
we will make the automation a bit more easier for us, whereby we can use envsubst
commandline to fill
and environment variable called NODERED_AUTH_ADMIN
in a file called node-red.env
. But in order leverage
the power of envsubst
we will create a template file called node-red.tpl.env
which has the following
content:
cat node-red.tpl.env
NODERED_AUTH_ADMIN='${NODERED_AUTH_ADMIN}'
We create simple bash script which will do the following:
- Accept a plaintext password as an argument to the script
- use this plaintext password and hash it with
htpasswd
- export the variable which has the result of
htpasswd
- use the exported variable to replace the
node-red.tpl.env
file tonode-red.env
with the value - unset variable
The script is as follows:
cat nodeREDPwgen.sh
#!/bin/env bash
function nodeREDPW {
# 1st argument to this script is plaintext password
local NODERED_AUTH_ADMIN=$(htpasswd -nb -B -C 8 admin "$1")
export NODERED_AUTH_ADMIN
touch node-red.env
envsubst '${NODERED_AUTH_ADMIN}' < node-red.tpl.env > node-red.env
unset NODERED_AUTH_ADMIN
}
nodeREDPW $1
Example Usage:
chmod +x nodeREDPwgen.sh
./nodeREDPWgen.sh testpasswd
will produce a node-red.env
file after successful execution with the following content:
NODERED_AUTH_ADMIN='admin:$2y$08$/8NVmF2gJ3JDrmv0tX2ud.P7S9gqvB0ds2cRByhnZcRdZOWhTnPN.'
Creating a settings.js
file for Node-RED Container
copy the settings.js
file from the node-red repository and adapt the adminAuth
object
as follows:
adminAuth: {
type: "credentials",
users: [{
username: process.env.NODERED_AUTH_ADMIN.slice(0, process.env.NODERED_AUTH_ADMIN.indexOf(':')),
password: process.env.NODERED_AUTH_ADMIN.slice(process.env.NODERED_AUTH_ADMIN.indexOf(':')+1)
}]
},
}
Creating a docker-compose.yml
file
services:
node-red:
image: nodered/node-red:2.2.2-12-minimal
container_name: secure_nodered
env_file:
- ./node-red.env
ports:
- "1880:1880"
volumes:
- ./settings.js:/data/settings.js
bring the container up:
docker compose up -d
The UI is available at http://localhost:1880
Problem
If you try to login in by typing testpassword
in the Admin UI, you will get Login Failed! But Why?
Investigations
It turns out that the password generated by htpasswd
produces a variant of bcrypt $2y
where as,
if we were to generate the password using node-red-admin hash-pw
and added testpassword
it would
generate a completely different looking password with the variant $2b
.
Example: $2b$08$sfPlvPNDVVz/duAbAC2bbuNEVuvrwmU01x7plOAQIem6H187Xxq9G
Solution
It turns out that the $2y
variant of bcrypt has compatibiltity and we could simply try and replace
$2y
to $2b
in our settings.js
as follows:
module.exports = {
// .. removed for brewity
adminAuth: {
type: "credentials",
users: [{
username: process.env.NODERED_AUTH_ADMIN.slice(0, process.env.NODERED_AUTH_ADMIN.indexOf(':')),
password: process.env.NODERED_AUTH_ADMIN.slice(process.env.NODERED_AUTH_ADMIN.indexOf(':')+1).replace("$2y$", "$2b$"),
permissions: "*"
}]
},
// removed for brewity
}
notice the .replace("$2y", "$2b")
at the end of password
key.
Let’s spin the container up again using docker compose up -d
and try to login with testpassword
et Voila!!
We are able to login into Node-RED!
I found that Nick O’Leary (founder of Node-RED and the great guy that he is..) already did some research on this, I just had to bring this all together.
If you have some suggestions, critiques than please get in touch with me and I would be happy to help.
Resources
GitHub Gist of the files for the impatient ones!