Middleware for outgoing requests

Hello,
I have question if would it be possible to add option to define middleware for outgoing http requests (via http nodes)?
Use cases:

  • Node-RED hosted in local network where there are more services running eg. http server on localhost:5000. In some cases you do not want to access it.

  • Node-RED running in larger internal network, in that case Node-RED can act like a proxy to your internal resources. For example you could block urls *.intranet + ip ranges in middleware.

I don`t know if it will be possible also with TCP/UDP.

1 Like

You don't need middleware for this since you can already use upstream nodes.

Which nodes? http-request doesn't need middleware nor does http-in/-out for the reason given.

That is effectively an API server and you don't need middleware for that, just a flow that formats the request properly and handles the result. Common stuff for Node-RED.

That is "just" a proxy. You can either have a transparent proxy but using some tool that automatically diverts any external requests via the proxy or you can have a manually configured proxy where you tell Node-RED to use the proxy via the appropriate settings in settings.js.

I should say that Node-RED would make a particularly poor choice for either type of proxy. You should use a proper tool for that.

I think that you didn`t get my point. I am aware how I can protect this outside of Node-RED.

Lets say that I have Node-RED running (and exposed to internet) somewhere in my local network. Apart of Node-RED I can have lots of more services hosted. I can create an account of my nodered to on of my friends and he will be able to reach and use it. I don't want him to be able to access my other internal resources, http servers etc hosted on same network from http request nodes. I know exactly how to do that in some ways but the question is if wouldn`t it be possible to do only on Node-RED side.

How it might work:

Somebody wants to do HTTP GET to 192.168.0.420/home or mysite.local, he puts it into http request node, in http request before preforming request it goes trough middleware function where i can check if I want user to access this url, throw an error if not or something. After that preform request if url is ok.

Also this issue is present when I have embeeded Node-RED inside kubernetes with separte DB and i want to allow pod with Node-RED to comunnicate with DB (Node-RED authorization) but I do not want to allow user to reach db from http request node (DB is only example)

The risk with an approach like that is the false-sense of security it gives you.

A user could install another node from the catalog that allows them to do http requests, just without any 'middleware' as you describe in place.

Or they could use the Function node's external modules feature and gain access to the raw http module and code a request however they wanted.

You've also made the point it would have to apply to the TCP nodes.

I am aware of that, that is why I deleted exec node, disabled external modules feature and disabled ability to install new nodes. The only thing that is missing from Node-RED settings is what I described.

You cannot protect more secure services by putting security into the less secure service. You HAVE to put it into the more secure service. To do that, you do not need any middleware in the less secure instance of Node-RED, it would be pointless.

You have to put the security where your users can't get at it. This does not require any changes to Node-RED. Node-RED already supports security middleware for incoming calls which is what you need to protect. Better still, use a transparent reverse proxy external to Node-RED but still running inside your more secure network and that way only what you want through gets through and if you need user authentication and authorisation, you can do it there.

Pratical example, Node-RED running inside kubernetes cluster where there is also DB and other services, passwords to Node-RED are stored in database and there is need to communication beetwen pods with Node-RED and DB.

function getUsers(app, _id) {
  return function (username) {
    return new Promise(async function (resolve) {
      const users = (await tenants(app).findOne({_id})).auth.ui.users; // DB communication
      if (users.hasOwnProperty(username)) {
        resolve({username: username, permissions: username === 'node-red-rw' ? '*' : 'read'});
      } else {
        resolve(null);
      }
    });
  };
}

function getAuthenticate(app, _id) {
  return function (username, password) {
    return new Promise(async function (resolve) {
      const users = (await tenants(app).findOne({_id})).auth.ui.users; // DB communication
      if (users.hasOwnProperty(username) && bcrypt.compareSync(password, users[username])) {
        resolve({username: username, permissions: username === 'node-red-rw' ? '*' : 'read'});
      } else {
        resolve(null);
      }
    });
  };
}

function authentication(app) {
  if (app.get('noAuth') === true) {
    return undefined;
  }
  const _id = app.get('tenant');
  return {
    sessionExpiryTime: 86400,
    type: 'credentials',
    users: getUsers(app, _id),
    authenticate: getAuthenticate(app, _id),
    
  };
}

So in that case I have embeeded Node-RED in express app which needs to communicate with DB.
If I for example set HTTP_PROXY and HTTPS_PROXY on pod and disable communication with other pods in cluster connection with DB will not work, and also with other services backend, etc.
For that particular case I have to protect it on application level. My case is very specific, http request node cannot access services in my cluster but node.js process must access it. And yes I have ugly workaround for that but I only asked If it is possible to iplement this functionality in a way that it is possible to set this via settings in Node-RED. If you think that this feature can be useful I can contribute with development.

Sorry, jus back from hols. I'm not sure I totally understand.

If you need to protect the DB access, you need to keep the user-facing Node-RED out of the way. It is really hard to protect an Instance of Node-RED that users have access to. That is because Node-RED is designed to be easy to use and flexible and it is very good at it. So once 1 user has access, it is very hard to prevent a valid user from undoing (accidentally or deliberately) security. It is MUCH better in that case to have TWO instances of Node-RED. One acting as an API service and that contains the securty but users have no access to the Editor and one acting as the user-facing service. Now, you can create a user access API so that even if a valid user undoes your user-facing security, they still won't get unfettered access to your secure NR instance or the DB (or better yet, use a dedicated service via PassportJS and let that 3rd-party dedicated service handle its own security).

Node-RED does not need to be amended in that case as far as I can see (though maybe I'm missing something).

According to your answer,

It is MUCH better in that case to have TWO instances of Node-RED

Ye, but imagine you have for example 200 Node-RED tenants running, thats will make cost of hosting twice as higher.

I am not sure If I understood you correctly but If i do -> It all requires development that can be also done on nodered level.

Also,

It is really hard to protect an Instance of Node-RED that users have access to.

Yes, I perfectly know It, I agree, but it is possible becouse I protected everything. The only thing is about this HTTP requests and TCP/UDP (which causes me pain). As i wrote I have ugly workaround for http, and I disabled tcp/udp until I will find solution for that.

Anyway thanks for your responses. I think we can close this topic, I see that my feature request is let`s say very "personal".

Not necessarily. Your API server could just be a single instance across tenants. A lot depends on your architecture.

Did you stop node installs, block filing system access (file and watch nodes) and block access to the exec node? Is Node-RED running under its own OS user ID with files and folders locked down? Is the server locked down and isolated from any other servers/services to prevent access? Have you ensured that environment variables are not being used to pass credentials? Have you protected any credentials your NR server needs to access db's and other services? ...

And not terribly secure I'm afraid.

Did you stop node installs

Yes I did, user cannot install new nodes by himself, it can be done by admin after security check.

block filing system access (file and watch nodes)

Each tenant has separate filesystem so no need for blocking access, just some tweaks

block access to the exec node

Yup

Is Node-RED running under its own OS user ID with files and folders locked down

There is no issue with that

Is the server locked down and isolated from any other servers/services to prevent access

The issue is here, in current architecture everything is in one openshift cluster. Apart of nodereds there are dbs, backends, frontends, some of endpoints eg from backend are not exposed to internet but only internally inside cluster and they are reachable by nodered http request node (issue) if i dont protect it. Also cluster is hosted inside internal network and there is the same issue with http request if I dont protect it.

There will be not issue if connection for other internal services will be not needed but in a way it is done (embeeded in express app) it is necessery to have this connection.

Have you ensured that environment variables are not being used to pass credentials

Yes, i simply disabled ability to use them by overwritting nodered code (as this functionality is also dont provided in settings)

Have you protected any credentials your NR server needs to access db's and other services

Yup

And not terribly secure I'm afraid.

Yup, but If someone will also need this feature he will probably be aware of all risks, presence of this functionality doesn`t lower whole nodered secuirity. There also might be use case if someone wants to add some headers to every outgoing http request.

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.