Upgraded to 3.1 - http request nodes are now giving ERR_TLS_CERT_ALTNAME_INVALID error. Rolled back to 3.0, error is gone

Hi all - I recently upgraded to Node-Red 3.1 (docker) and I'm having a weird issue with the "http request" nodes. I rolled back to 3.0 to confirm that it was the upgrade that caused the problem.

It seems like in 3.1 they've added the ability to set your own headers in the http request node. When I upgrade, I get a ERR_TLS_CERT_ALTNAME_INVALID error, stating that my hostname (localhost) doesn't match the hostname alts in my certificate.

My node-red is behind SWAG / Nginx, so I think I need to set a header. Something like X-Forwarded-For or something to indicate that its through a proxy. I tried setting "Enable secure (SSL/TLS) connection" and then making sure that "Verify server certificate" was unchecked. This resulted in the repose returning my "Welcome to SWAG" page from nginx. :confused: Also tried setting msg.rejectUnauthorized to false, same result.

Once I switched back to 3.0, the TLS connection was back to normal without errors, and was returning expected results. So I think whats happening is that the hostname for 3.1 is being defaulted to localhost, and when it tries to verify the requesting server using TLS, it doesn't match. So it fails.

Anyone know of the environment variable to set the hostname? I've tried setting VIRTUAL_HOST, and also just dnsname and hostname at the service level of my docker-compose, nothing seemed to work. That, or does anyone know what header I should be setting for these http request nodes? Thanks!

Where is that error coming from? NGINX or Node-RED?

In NGINX, there are a bunch of headers you might set. Here are the set that I use.

# Common reverse proxy settings
# Don't forget to also add proxy_pass url;

  proxy_set_header Forwarded "by=$host;for=$proxy_add_x_forwarded_for;host=$host;proto=$scheme";
  proxy_set_header Via       "$scheme/1.1 $host:$server_port";

  proxy_set_header Host              $host;
  proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Host  $host;
  proxy_set_header X-Forwarded-Port  $server_port;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Original-URI    $request_uri;
  proxy_set_header X-Real-IP         $remote_addr;

  # Proxy timeouts
  proxy_connect_timeout       60s;
  proxy_send_timeout          60s;
  proxy_read_timeout          60s;

  # If proxied responses happening too slowly, try turning off the buffering
  #proxy_buffering off;

Don't forget that you have to add them to EVERY separate route that you are proxying. So best to put them in their own file and then you can include them where you need to. Possibly slight overkill but it seems to work. :grin:

In addition, in your Node-RED settings.js, you should include:

    httpServerOptions: {
        // http://expressjs.com/en/api.html#trust.proxy.options.table
        'trust proxy': true,  // true/false; or subnet(s) to trust; or custom function returning true/false. default=false

Though the following comment in the default settings.js file shows another possible way:

    /** If you need to set an http proxy please set an environment variable
     * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.
     * For example - http_proxy=http://myproxy.com:8080
     * (Setting it here will have no effect)
     * You may also specify no_proxy (or NO_PROXY) to supply a comma separated
     * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk

Finally, you should consider this, also in settings.js to stop Node-RED listening except on localhost (assumes your proxy is on the same device, obviously don't do it if not).

    /** By default, the Node-RED UI accepts connections on all IPv4 interfaces.
     * To listen on all IPv6 addresses, set uiHost to "::",
     * The following property can be used to listen on a specific interface. For
     * example, the following would only allow connections from the local machine.
     * This can be useful security when putting NR behind a reverse proxy on the same device.
    uiHost: process.env.HOST || '',
1 Like

The error is displaying on the node label, and also in the debug log window. It's saying that localhost doesn't match ...[all subdomain alts in my letsencrypt cert]...

My nginx config works for v3.0 of node red for outbound http requests, just not for 3.1 outbound. So I'm thinking that the hostname has been set to localhost for some reason in 3.1 which is causing the TLS handshake to fail because it can't verify the server thats doing the calling?

I'm slightly confused (not hard!), I thought you meant requests inbound to Node-RED, not outbound from it?

Outbound requests (e.g. destined for outside your local Node-RED/reverse-proxy environment) wouldn't hit the REVERSE proxy. They might hit the same NGINX service as an outbound proxy but that would be a completely different set of config. in NGINX.

The error is "correct" in the sense that a publicly signed certificate can NEVER be valid for localhost, nor for an IP address, it can only ever be valid for a publicly registered DNS name.

Quite alright, I'm also trying to figure out whats going on, so it helps to talk it through.

The way I understand it is that in order to form a TLS connection, it needs to confirm the origination of the request. Meaning that the server host name that it originates from matches its certificate. This is to prevent man in the middle attacks of bad actors impersonating someones server.

I have an "http request" node where I'm making a GET out to another website.

  • When I do this under 3.0, it works perfectly fine. The response is the data I'm expecting.
  • When I do this under 3.1, it complains that my (node-red's) host name doesn't match the certificate that its using (from let's encrypt). This is a fully qualified domain name and the certificate chain is valid.

So I'm trying to figure out what changed between 3.0 and 3.1. Near as I can tell, they allowed us to explicitly set our own headers for the request in 3.1. So what headers were being set under 3.0, and how do I fix that under 3.1?

Hope that made sense!

OK, so backing up here.

Are you making a call from a http-request to something outside your proxy with client TLS configured?

Is the request to outside the network your proxy and Node-RED sit on? And can you confirm that your proxy is not intercepting the request?

Are you getting any errors in the Node-RED log?

The error implies that the recipient of the request is INSIDE your network since a request going, lets say to the Internet, would have your external IP address on it (as set by your router routing it to your ISP) and not localhost. The implication is that the request is going from the Node-RED server and staying on it which is why the recipient is seeing localhost BUT, somehow it is hitting a local endpoint that is configured for TLS on an DNS defined IP name.

An outbound request from the Node-RED server has no reason to trigger an error if you are not providing an outbound client TLS handshake (which is a relatively rare configuration) as shown in my image above. Instead it is the RESPONSE that is triggering the error in what is returned to the node and the remote endpoint that is saying it is delivering from localhost instead of the IP name defined in the Let's Encrypt certificate. The request node checks the validity of the server's certificate by default and so triggers the error.

If this is true, I'd say that you had probably been inadvertently "tromboning" your traffic out to the Internet and back when it shouldn't have been. If that is true, the error is doing you a favour by identifying this.

If it isn't true, something else odd is happening. In that case, take the request that the node is producing and put it in a curl request on the command line of the Node-RED server and see what happens. Also turn up the logging on your NGINX server so that you can check whether it is inadvertently intercepting the outbound call.

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