NodeRED in OpenShift

Tony Hickman
6 min readJul 23, 2020

--

In a recent engagement I’ve been working on hosting a NodeRED instance in an OpenShift cluster. I thought it might be useful to share my experiences here.

The requirement I had was to implement a secured instance of NodeRED running on HTTPS with non self-signed certificates. Working with a colleague (Mark Taylor) who is far more OpenShift literate than myself and we set about this using the provided NodeRED docker instructions (https://nodered.org/docs/getting-started/docker) as a starting point.

Getting an initial default image up and running was simple and Mark created the following script to allow us to do this

## Create a new namespace
oc new-project tony-nodered
## Create a Node-Red app from Docker Hub
oc new-app --name nodered nodered/node-red
## Default it to perform rolling update (new and old pods running at
## same time). We need a stop/restart due to Write Once access to
## /data
oc patch dc/nodered --patch '{"spec":{"strategy":{"type":"Recreate"}}}'
## oc new-app creates a mount to /data as ephemeral container
## storage, replace with persistent storage
oc set volume dc/nodered --add --name=nodered-volume-1 -t pvc --claim-size=512M --claim-class=ibmc-block-gold --overwrite
## Make nodered available on port 80
oc expose svc/nodered
## Confirm the url
oc get route nodered

This script creates a new project (tony-nodered) and adds a new application to that project called nodered and this is created from the node-red image in dockerhub. Once create the create application is updated to support the way NodeRED operates against its underlying storage and we make sure this storage is persisted across restarts (the main reason for this is to support the ability to change the flows).

This step proved how we could get the basics up and running so the next step was to enable password protection on NodeRED and set up access to be via HTTPS. This require the settings.js file of NodeRED to be changed as documented in the NodeRED docs. Firstly we commented in the “adminAuth” section.

// Securing Node-RED
// -----------------
// To password protect the Node-RED editor and admin API, the
// following
// property can be used. See http://nodered.org/docs/security.html // for details.
adminAuth: {
type: "credentials",
users: [{
username: "admin",
password: "$2b$08$js31PkmXfKUX4LYsYjiiwuhR4vg39mISKwqUij4UUTZDNNI9YFaWi",
permissions: "*"
}]
},

The password needs to be encrypted using a bcrypt algorithm and the instructions for this are covered in the NodeRED docs.

The next step is the set up HTTPS and this is where things get more interesting when combined with OpenShift. OpenShift enables the use of Lets Encrypt to generate certificates which can be used to secure routes. To do this we used the following commands.

oc annotate service nodered service.beta.openshift.io/serving-cert-secret-name=nodered-tlsoc set volume dc/nodered --name nodered-tls \
--add --type secret --secret-name nodered-tls \
--mount-path /var/run/secrets/nodered
oc create route reencrypt --service=nodered

These commands set up encryption on the external route and store the generated certificate and key in the mount path specified which in our case was:

/var/run/secrets/nodered

With the ability to create the necessary certificate and key we returned to the settings.js file and updated the HTTPS function to pick up these files (NB: the files are named tls when they are generated)

//https function:
https: function() {
// This function should return the options object, or a Promise
// that resolves to the options object
return {
key: require("fs").readFileSync('/var/run/secrets/nodered/tls.key'),
cert: require("fs").readFileSync('/var/run/secrets/nodered/tls.crt')
}
},

So now we had all the moving parts in place. The final step was to make sure the DockerFile pulled in settings.js

ARG ARCH=amd64
ARG NODE_VERSION=10
ARG OS=alpine
#### Stage BASE ########################################################################################################
FROM ${ARCH}/node:${NODE_VERSION}-${OS} AS base
# Copy scripts
COPY scripts/*.sh /tmp/
# Install tools, create Node-RED app and data dir, add user and set rights
RUN set -ex && \
apk add --no-cache \
bash \
tzdata \
iputils \
curl \
nano \
git \
openssl \
openssh-client && \
mkdir -p /usr/src/node-red /data && \
deluser --remove-home node && \
adduser -h /usr/src/node-red -D -H node-red -u 1000 && \
chown -R node-red:root /data && chmod -R g+rwX /data && \
chown -R node-red:root /usr/src/node-red && chmod -R g+rwX /usr/src/node-red
# chown -R node-red:node-red /data && \
# chown -R node-red:node-red /usr/src/node-red
# Set work directory
WORKDIR /usr/src/node-red
# package.json contains Node-RED NPM module and node dependencies
COPY package.json .
#### Stage BUILD #######################################################################################################
FROM base AS build
# Install Build tools
RUN apk add --no-cache --virtual buildtools build-base linux-headers udev python && \
npm install --unsafe-perm --no-update-notifier --no-fund --only=production && \
/tmp/remove_native_gpio.sh && \
cp -R node_modules prod_node_modules
#### Stage RELEASE #####################################################################################################
FROM base AS RELEASE
ARG BUILD_DATE
ARG BUILD_VERSION
ARG BUILD_REF
ARG NODE_RED_VERSION
ARG ARCH
ARG TAG_SUFFIX=default
LABEL org.label-schema.build-date=${BUILD_DATE} \
org.label-schema.docker.dockerfile=".docker/Dockerfile.alpine" \
org.label-schema.license="Apache-2.0" \
org.label-schema.name="Node-RED" \
org.label-schema.version=${BUILD_VERSION} \
org.label-schema.description="Low-code programming for event-driven applications." \
org.label-schema.url="https://nodered.org" \
org.label-schema.vcs-ref=${BUILD_REF} \
org.label-schema.vcs-type="Git" \
org.label-schema.vcs-url="https://github.com/node-red/node-red-docker" \
org.label-schema.arch=${ARCH} \
authors="Dave Conway-Jones, Nick O'Leary, James Thomas, Raymond Mouthaan"
COPY --from=build /usr/src/node-red/prod_node_modules ./node_modulesCOPY settings.js /usr/src/node-red/node_modules/node-red/settings.js# Chown, install devtools & Clean up
RUN chown -R node-red:root /usr/src/node-red && \
/tmp/install_devtools.sh && \
rm -r /tmp/*
USER node-red# Env variables
ENV NODE_RED_VERSION=$NODE_RED_VERSION \
NODE_PATH=/usr/src/node-red/node_modules:/data/node_modules \
FLOWS=flows.json
# ENV NODE_RED_ENABLE_SAFE_MODE=true # Uncomment to enable safe start mode (flows not running)
# ENV NODE_RED_ENABLE_PROJECTS=true # Uncomment to enable projects option
# User configuration directory volume
VOLUME ["/data"]
COPY flows.json /data/flows.json# Expose the listening port of node-red
EXPOSE 1880
# Add a healthcheck (default every 30 secs)
# HEALTHCHECK CMD curl http://localhost:1880/ || exit 1
ENTRYPOINT ["npm", "start", "--cache", "/data/.npm", "--", "--userDir", "/data"]

You can see that we copy our settings.js file to /usr/src/node-red/node_modules/node-red/settings.js

With everything in place it was simple a case of rebuilding the Docker Image, pushing it to Docker Hub and running the following script to deploy to OpenShift.

## Create a new namespace
oc new-project tony-nodered-test
## Create a Node-Red app from Docker Hub
oc new-app --name nodered --docker-image=tonyhickman/node-red:latest
## Default it to perform rolling update (new and old pods running at same time). We need a stop/restart due to Write Once access to /data
oc patch dc/nodered --patch '{"spec":{"strategy":{"type":"Recreate"}}}'
## oc new-app creates a mount to /data as ephemeral container storage, replace with persistent storage
oc set volume dc/nodered --add --name=nodered-volume-1 -t pvc --claim-size=512M --claim-class=ibmc-block-gold --overwrite
oc annotate service nodered service.beta.openshift.io/serving-cert-secret-name=nodered-tlsoc set volume dc/nodered --name nodered-tls \
--add --type secret --secret-name nodered-tls \
--mount-path /var/run/secrets/nodered
oc create route reencrypt --service=nodered## Confirm the url
oc get route nodered

A quick check and we were able to connect to our NodeRED instance and validate it was picking up the Lets Encrypt certificate.

We still need to do some testing around the management of the Flows across restarts but the basics are in place. We might also look at alternative starting points for building NodeRED on OpenShift, for example from source to code using S2I (source to image).

--

--

Tony Hickman

I‘ve worked for IBM all of my career and am an avid technologist who is keen to get his hands dirty. My role affords me this opportunity and I share what I can