Contents

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:

  1. Accept a plaintext password as an argument to the script
  2. use this plaintext password and hash it with htpasswd
  3. export the variable which has the result of htpasswd
  4. use the exported variable to replace the node-red.tpl.env file to node-red.env with the value
  5. 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!