Protect Node Red from anywhere

Node-RED Security Variants Discussed and Tested

Context

The goal is to protect a Node-RED installation exposed through a router, while keeping the setup simple and understandable.

The following four variants were discussed and tested in stages.

Common assumptions:

  • Node-RED runs on port 1880.

  • Node-RED admin authentication is enabled.

  • Nginx is used as a reverse proxy.

  • Nginx access logs are enabled.

  • Failed Node-RED editor logins appear as:

    POST /auth/token ... 403

  • Fail2ban monitors the Nginx access log.

  • The Fail2ban filter used is:

[Definition]
failregex = ^ .* "POST /auth/token HTTP/." 403 .
ignoreregex =

================================================
VARIANT 1
HTTP + Nginx + Fail2ban Full IP Ban

Architecture:

Internet / LAN
|
v
Nginx :80
|
v
Node-RED :1880

Fail2ban watches:

POST /auth/token ... 403

Jail configuration:

[nodered]
enabled = true
filter = nodered
backend = polling

action = route

logpath = /var/log/nginx/access.log

maxretry = 5
findtime = 600
bantime = 300

Meaning:

  • 5 failed login attempts
  • within 10 minutes
  • full source IP ban
  • ban duration: 5 minutes

The important line is:

action = route

This blocks the entire source IP address, not only Node-RED.

As a result, the banned IP loses access to:

  • Nginx
  • Node-RED
  • SSH
  • other services on the same host

Advantages:

  • Very simple.
  • Easy to understand.
  • Strong response to repeated failed logins.
  • No nftables multiport configuration is required.

Disadvantage:

  • The administrator can accidentally ban their own IP and lose SSH access.

This variant was tested successfully.

================================================
VARIANT 2
HTTP + Nginx + Fail2ban Ban Only Port 80

Architecture:

Internet / LAN
|
v
Nginx :80
|
v
Node-RED :1880

Fail2ban still watches:

POST /auth/token ... 403

However, instead of blocking the entire source IP, the ban is applied only to TCP port 80.

nftables is required.

Example action:

action = nftables[type=multiport, port="80", protocol="tcp"]

Result:

  • HTTP access through Nginx port 80 is blocked.
  • SSH port 22 remains accessible.
  • Direct Node-RED port 1880 remains accessible if Node-RED is still listening on all interfaces.

Advantages:

  • Safer for remote administration.
  • Accidental login failures do not lock the administrator out of SSH.

Disadvantages:

  • More complex than Variant 1.
  • Direct port 1880 must still be closed separately.

This variant was also tested successfully.

================================================
VARIANT 3
HTTPS + Nginx + Fail2ban Ban Only Port 443

Architecture:

Internet
|
v
Nginx HTTPS :443
|
v
Node-RED :1880

A self-signed certificate is used.

Example Nginx configuration:

server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

ssl_certificate /etc/nginx/ssl/nodered.crt;
ssl_certificate_key /etc/nginx/ssl/nodered.key;

access_log /var/log/nginx/access.log;

location / {
    proxy_pass http://127.0.0.1:1880;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

}

Fail2ban action:

action = nftables[type=multiport, port="443", protocol="tcp"]

Example jail:

[nodered]
enabled = true
filter = nodered
backend = polling

port = 443
protocol = tcp

action = nftables[type=multiport, port="443", protocol="tcp"]

logpath = /var/log/nginx/access.log

maxretry = 5
findtime = 600
bantime = 600

Meaning:

  • 5 failed login attempts
  • within 10 minutes
  • only HTTPS port 443 is blocked
  • ban duration: 10 minutes

Result:

  • HTTPS access is blocked for the attacker.
  • SSH remains accessible.
  • Node-RED direct port 1880 remains accessible if not separately restricted.

Advantages:

  • Encrypted connection.
  • Fail2ban protects only the public web entry point.
  • SSH remains available.

Disadvantages:

  • More configuration.
  • Self-signed certificates generate browser warnings.
  • Port 1880 still needs separate protection.

This variant was tested successfully.

================================================
VARIANT 4
HTTPS + Nginx + Fail2ban + Node-RED Bound to Localhost

This is the hardened version of Variant 3.

Architecture:

Internet
|
v
Nginx HTTPS :443
|
v
127.0.0.1:1880
|
v
Node-RED

Node-RED is changed from:

0.0.0.0:1880

to:

127.0.0.1:1880

In Node-RED settings.js:

uiHost: "127.0.0.1",

Example file:

/mnt/dietpi_userdata/node-red/settings.js

After restart:

sudo systemctl restart node-red

Verification:

sudo ss -ltnp | grep -E '1880|443'

Expected result:

127.0.0.1:1880
0.0.0.0:443
[::]:443

Effect:

  • Node-RED port 1880 is no longer reachable directly from LAN or WAN.

  • Only local processes on the same machine can access Node-RED on port 1880.

  • Nginx can still proxy requests to:

    http://127.0.0.1:1880

  • All external access must go through Nginx.

Advantages:

  • Direct access to Node-RED port 1880 is completely removed.
  • Nginx becomes the single public web entry point.
  • Fail2ban protection cannot be bypassed by connecting directly to port 1880.
  • This is the strongest architecture of the four variants.

Disadvantage:

  • Slightly more configuration than Variant 1.

================================================
Summary

Variant 1:
HTTP :80
Nginx
Node-RED :1880
Fail2ban full IP ban
action = route

Variant 2:
HTTP :80
Nginx
Node-RED :1880
Fail2ban ban only port 80
nftables multiport

Variant 3:
HTTPS :443
Nginx
Node-RED :1880
Fail2ban ban only port 443
nftables multiport

Variant 4:
HTTPS :443
Nginx
Node-RED only on 127.0.0.1:1880
Fail2ban on the public Nginx entry point

Recommended progression:

  1. Start with Variant 1 because it is simple and easy to verify.
  2. Add localhost binding for Node-RED port 1880.
  3. Add HTTPS if encrypted remote access is required.
  4. Use port-specific nftables bans if preserving SSH access during a web ban is important.

================================================
Additional Observation

The Node-RED editor login can be detected reliably in Nginx logs by:

POST /auth/token ... 403

However, other Node-RED interfaces such as /ui may use different authentication mechanisms.

For example, failed access to /ui may appear as:

GET /ui ... 401

Therefore, a separate Fail2ban rule may be required if the dashboard/user interface must also be protected against repeated authentication attempts.

Wouldn’t it be easier to just use Cloudflare Access (zero trust network access)? It’s free for up to 50 users.