httpNode top level middleware to replace basic-auth

Currently, if we set httpNodeAuth in settings.js, a middleware function using basic-auth is applied at the top-level of the httpNode express app. It protects all routes beneath it including those of the http in node, dashboard, and any routes added by other nodes to httpNode. And depending on how you define httpNodeRoot and httpStaticRoot, it may also protect static routes.

httpNodeAuth: { user:"user", pass:"$2b$14$B.jppbe..."},

If the httpNodeAuth property can also except a middleware function or array of middleware functions, then we can inject our own custom authentication system at the same location where the basic-auth middleware would have been added at the top-level:

httpNodeAuth: [
  httpBlocker, // blocks ip(s) that were dynamically banned
  httpRouter, // express router handles login/logout routes
  httpSession, // attaches session to all routes not matched in httpRouter
  httpVerify, // verifies session has authorization to access resource
],

I have implemented this on my system for the last couple months and it is working well with the following points:

  • backwards compatible / non breaking change
  • does not affect httpAdmin authentication
  • browsers offers to save and populate the user/pass in the login form
  • advanced features such as tracking and terminating socket. io connections
  • only allowing user to be logged in from 1 device at a time by managing sessions
  • dynamically blocking ip addresses for multiple failed login attempts
  • function node receives event for failed login attempts and then can send alert msg to admin

And I must point out that is not equivalent to using httpNodeMiddleware, since that only adds middleware functions to the individual http in node routes and not at the top level. The name of that property implies middleware is added to the httpNode, but unfortunately it does not. It probably should be called httpInMiddleware, but maybe it's too late to change that without creating deprecation warnings and all that noise.

Also, being able to create a custom auth system is not a substitute for using other software such as iptables and reverse proxies, but it should work in conjunction with them. For example, if an ip gets blocked due to multiple failed login attempts, the function node listening to the events can send a msg to a file node and write the ip to the blocked list while also sending an alert email to the system admin. Perhaps some cron process can occasionally read from that file and update iptables with the current list of naughty ip addresses, etc.

It would only be a few lines of code to change and I would be more than happy to submit the pr.

4 Likes

Agree that it would seem sensible to have the ability to use a function instead of a simplistic user id/password for the httpNodeAuth. Would bring it more into line with adminAuth.

However both the node-red docs and the settings.js file would need updates to reflect any changes. Would be good to have the node-red security docs updated to include something like your last but 1 para. And also to add the warning that authentication should not be done without first configuring https.

2 Likes

This does indeed seems like a sensible enhancement (to me)... If you raise a PR please target it at the dev branch - and with 4.0 imminent then the sooner the better (imho) :slight_smile:

5 Likes
4 Likes

@knolleary, thank you for merging the pull request.

If anybody is interested in creating their own authentication system to replace the basic-auth that protects the "http in node" and dashboard and other nodes that provide http urls, let me know so we can open a new thread. I will share my work in progress so that we can collaborate and work out the details with the goal of hardening the auth.

7 Likes

Great work, Kevin -- I can definitely use this feature. One question: can this work with the httpAdmin routes, like /admin and other built-in apis? Or is that not configurable in settings.js?

1 Like

No, this only applies to the HTTP Node routes.

I suspected so... but was also interested in whether there was any technical reason why it could NOT be applied to httpAdmin routes? I mean other than it's not coded that way yet. Could the editor, flow, comms, etc. still work correctly with additional middleware in place?

This is because there are 2 ExpressJS instances used by Node-RED, one for admin use and one for user-facing use. Each, of course, therefore needs its own middleware. Technically (from an ExpressJS/Node.js perspective at least) there is nothing stopping a similar approach to be used for the admin web server. But it would be (and indeed should be) separate from the user-facing version.

Yep, that's what I was looking for -- so the same technique could potentially be configured in a separate section of the settings.js and applied to the admin express instance? If the community found it useful, of course, and the core team did not see any potential issues with it...

1 Like

While you could add extra middleware to protect the admin routes, such as some middleware that detects multiple failed login attempts and moves that ip to a blocked list, I would definitely not try to replace the built-in admin authentication system. And for testing purposes, I did try just now to use my auth system as middleware to protect the admin http routes and it was successful. BUT, it would not be able to secure the websocket comms and that would leave you less secure than the built-in auth.

The ip blocker middleware I reference is what lead me to working on my httpNode auth. I simply wanted to have my admin ip blocker middleware also work to protect the httpNode but realized that the built-in basic auth wasnt flexible enough to allow it. And then of course I went off the deep end...

2 Likes

Hey Kevin-

This topic is once again popping up in our current project -- so can I ask what you do with the results of your node auth chain? You mentioned updating iptables and sending emails, which would all be external to the node-red runtime.

I have to admit that I've never really understood the node-red user session, and how to use it inside my flow's nodes and/or functions. But ideally the information gathered while authenticating the user would be stored in the user's session, and available for controlling their flows.

Things like JWT cookies and SAML headers seem to contain information that was gathered during authentication -- but each http in node would have to be smart enough to dig it out of the req.headers, if I understand correctly. Instead, we would like the auth middleware to place what it finds into the user session -- would you have any examples of how to do that?

How extensive are your services Steve?

Really, for enterprise/commercial services, this is best done outside of Node-RED rather than trying to make Node-RED expert at everything.

With an IDAM tool in place working with your reverse proxy, any user information should either:

  • be passed into Node-RED in headers - probably with a sub-flow or maybe a link-call node that does the header-to-msg process for you and so would be easily inserted to any inbound endpoint node.
  • or, be queried from Node-RED via API call into the IDAM tool.

Initial setup is a bit more complex but the result is going to probably perform better, be more scalable and almost certainly be more secure and managable.

1 Like

For my purposes, it worked well. I will admit that progress had stopped because i was waiting to see if my merge request was going to be accepted. The good news it that it was accepted into node-red v4 beta. Unfortunately, I was too late to get it into v3.

Yes, that is how I would probably secure my system, but have not even tried it yet. Just brainstorming a possible workflow to help strengthen a system against the bad guys.

That's a good idea. I never thought about doing authorization per flow. That could probably done with a function node that is plugged in right after a http in node where it could read the request/response objects and do some things with it.

Before going too far, I will state the obvious that I am not a professional nor a security expert.

With that out of the way, I am curious what your ultimate goal is and how elaborate is your setup.

  • How many users?
  • Do the users have different levels of access?
  • What type of data or service do you provide to the users?
  • Just http api, or do you also use websockets or other transport types?