Custom node not able to make request to api with a self-signed certificate

Hello! I am new to node-red and am currently working on creating a custom node that makes several https requests to an API in order to get a JSON response in return. I am having trouble making these requests because the remote certificate is invalid. The certificate is self-signed and not added as a trusted certificate. When I import the certificate from the API into my browser as a trusted certificate, the node works correctly, otherwise I get the following error: net:: ERR_CERT_AUTHORITY_INVALID.
How can I customize my node to allow for requests to be made to this API with an invalid certificate, without having to import the certificate manually? I have tried setting the environment variable NODE_TLS_REJECT_UNAUTHORIZED to zero in settings.js but have had no luck with that. I am using xmlhttprequest to make the https calls in my html file, because I need to update a select list dynamically based on the JSON data I retrieve. When using the http request node, instead of my custom node, to make my calls to this API I had to generate a ssh key on my local machine and upload this private key in the tls/ssl configuration option. Is there a way to incorporate this functionality into my custom node, without using the request module as was done in the http-request node provided by node-red? Thank you!

Hi Jenny,
I assume it is normal that this won't work, because you tell the server side of Node-RED to not reject unauthorized certificates. But you do the http request on your client machine, so that is not related to each other ...

On the server side (i.e. the js file of your node), you have all kind of ways to accomplish this (as you can see in this Stackoverflow discussion). They also mention a solution to load the self-signed certificate into a ca certstore, which seem far more safe to me:

ca: [fs.readFileSync([certificate path], {encoding: 'utf-8'})]
...

When you would be interested in this kind of stuff, you can always add an http endpoint to your node (js file) and call that endpoint from your html file. So html code --> your endpoint --> the remote site (instead of directly: html code --> the remote site).

I'm not sure if you can do something similar on the client side. I think that the browsers don't allow that, which would be a good thing ...

Bart

2 Likes

Bart,

Thank you so much for your response! Would there be a way to add an http endpoint so that it would be possible to change the NODE_TLS_REJECT_UNAUTHORIZED to zero. So if I changed my flow from html code-->my end point that sets the env variable in js file-->the remote server. I know that changing this env variable is warned against, but in my case I know that this API is trustworthy. Also, how are http endpoints created? Would I use something like RED.httpAdmin.get("name of my js file which this end point is being inserted into")? Thank you again for your help!

Hi Jenny,

  • A client-side ajax call (to your endpoint) can look like this (in your html file):

    $.ajax({
       type: "GET",
       url: "your_unique_id/some_name", 
       success: function(response, status, xhr){ 
          console.log(response);
       },
       error: function(xhr, textStatus, errorThrown) {
          console.log(xhr.responseText);
       }
    });
    
  • A server-side endpoint could look like this (in your JS file):

     const https = require('https');
    
     // Make some server resources from this node available for the FLOW EDITOR
     RED.httpAdmin.get('/your_unique_id/*', function(req, res){ 
         if (req.params[0] === "some_name") {
             // Call https://postman-echo.com/get?foo1=bar1&foo2=bar2
             var options = {
                 host : 'postman-echo.com',
                 port : 443,
                 path : '/get?foo1=bar1&foo2=bar2',
                 method : 'GET'
             }
             
             https.request(options, function(newRes) {
                 var data = "";
                 newRes.on('data', function (chunk) {
                     data += chunk;
                 })
                 .on('end', function(){
                     // All chunks have arrived
                     res.json(JSON.parse(data)); 
                 });
    
             }).on('error', function(e) {
                 res.status(500).json({error: e});
             }).end(); // Execute the request
         }
     });
    

    This endpoint sends a request to postman, which returns your request in json format:

  • Let's now use https://badssl.com/ which contains a lot of test URLs, which we can use to test the security stuff on our part. In our case we will use the URL with the self-signed certificate:

    image

  • We adapt our endpoint to call the above link with the self signed certificate:

     RED.httpAdmin.get('/your_unique_id/*', function(req, res){ 
         if (req.params[0] === "some_name") {
             // Call https://self-signed.badssl.com
             var options = {
                 host : 'self-signed.badssl.com',
                 port : 443,
                 method : 'GET',
                 // See https://levelup.gitconnected.com/how-to-resolve-certificate-errors-in-nodejs-app-involving-ssl-calls-781ce48daded
                 rejectUnauthorized: false
             }
             
             https.request(options, function(newRes) {
                 var data = "";
                 newRes.on('data', function (chunk) {
                     data += chunk;
                 })
                 .on('end', function(){
                     // All chunks have arrived
                     res.send(data); 
                 });
    
             }).on('error', function(e) {
                 res.status(500).send(e);
             }).end(); // Execute the request
         }
     });
    
  • When we use the rejectUnauthorized: true, we will get an error due to the self-signed certificate:

    image

  • When we use the rejectUnauthorized: false, the self-signed certificate problem will be ignored and we will get the website content:

    image

1 Like

Hello Jenny (@jennyeg),
Did you manage to get it working via my mechanism, or have you solved it another way?
Just to make sure that we all learn from it ...
Bart

Hi @BartButenaers! Thanks for your help with this issue. I ended up not needing to add the endpoint as my issue was only occurring in development, and our certificate is not self-signed in production, so it would not be an issue in the future for our users. For development, I ended up just importing the self-signed certificate into the browser I was working off of. If I try your mechanism in the future, I will let you know. It seems like it would have worked.

1 Like

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