How to send proxy and Certs over a axios or node-fetch call from nodejs

Viewed 893

I've been trying to make an API call using axios and node-fetch and the end point needs to be connected via a proxy and also needs certs. below is what I'm trying to do

import axios from "axios";
import { getPrefetchedSSLConfig } from "./get-ssl-certs";
import HttpsProxyAgent from "https-proxy-agent";
import https from "https";

const callEndPoint = () => {
  axios.defaults.baseURL = "https://api.endpoint.com";

  const sslInfo = getPrefetchedSSLConfig();
  let httpsAgent = new HttpsProxyAgent({
    host: proxy.hostname,
    port: proxy.port,
    rejectUnauthorized: false,
    ca: sslInfo.ca,
  });

  return axios({
    proxy: false,
    method: "GET",
    url: `/v1/api/endpoint`,
    responseType: "json",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      "Access-Control-Allow-Origin": "*",
    },
    httpsAgent,
  }).then((res) => {
    return {
      data: res.data,
      status: res.status,
    };
  });
};

export default callEndPoint;

I've also tried the options mentioned here and also tried node-fetch too.

I'm not sure if I'm missing something obvious or something I should know that I don't.

2 Answers

This is very hard to diagnose without knowing what issue you've hit (but let's try!).

The issue you link to suggests that in order for the https-proxy-agent to work with axios, you need to patch the provided agent.

Combining the suggest configuration with what you have provided us, you should try the following:

import { HttpsProxyAgent, HttpsProxyAgentOptions } from 'https-proxy-agent';
import { ClientRequest, RequestOptions } from 'agent-base';
import { Socket } from 'net';
import axios from 'axios';
import { getPrefetchedSSLConfig } from "./get-ssl-certs";

class PatchedHttpsProxyAgent extends HttpsProxyAgent {
  private ca: any;

  constructor(opts: HttpsProxyAgentOptions) {
    super(opts);
    this.ca = opts.ca;
    }

  async callback(req: ClientRequest, opts: RequestOptions): Promise<Socket> {
    return super.callback(req, Object.assign(opts, { ca: this.ca }));
  }
}

const agent = new PatchedHttpsProxyAgent({
  host: sslInfo.hostname,
  port: sslInfo.port,
  ca: sslInfo.ca
});

const axiosProxyConfig = {
  proxy: false,
  httpsAgent: agent,
  baseURL: 'https://api.endpoint.com'
}

const client = axios.create(axiosProxyConfig);

client.get('/v1/api/endpoint', {
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Access-Control-Allow-Origin': '*',
  }
}).then((res) => {
  console.log('Status', res.status);
  console.log('Received Data: ', res.data);
}).catch((error) => {
  console.log('Caught Error', error);
});

This will use a patched version of the HttpsProxyAgent and pass it to axios, along with your own configuration.

I would usually recommend treating root certificate authorities in your deployment design as environmental data, since that tends to be the most common option and will lead to the simplest code:

STEPS

Create a certificate bundle file, perhaps called myrootcerts.pem:

# First root CA
-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIJAJs2HTtg02ZxMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV
BAMTIFNlbGYgU2lnbmVkIENBIGZvciBteWNvbXBhbnkuY29tMB4XDTIxMDgyOTEz
-----END CERTIFICATE-----

# Other root CAs ...
...

Then start your process using the standard environment variable:

export NODE_EXTRA_CA_CERTS=/mypath/myrootcerts.pem && npm start

Then in axios you just need to use code like this:

const options = {
    url: targetUrl,
    method: 'GET',
    headers: {
        'accept': 'application/json',
    },
    httpsAgent: new ProxyAgent(proxyUrl),
};
const response = await axios.request(options);

Here is some code of mine that works when using self signed root CAs:

I tend to use the proxy agent library, though this is just a helper utility and not essential.

OTHER SYSTEMS

It is becoming common when using a Private PKI to deploy root CAs as a file and then set an environment variable in a similar way to above. Here are some examples:

  • In Java you deploy a trust store to which certificate bundles have been deployed
  • In third party components such as the NGINX reverse proxy you deploy the bundle file and set an NGINX variable called ssl_trusted_certificate.
Related