Записки

Nodejs и самоподписанный сертификат в Windows

Проблема

Вам надо сделать запрос из Nodejs приложения к API с самоподписанным сертификатом. Вы такой: —«Изи-пизи! Сейчас установлю свои серты простым батником и всё будет работать!»

certutil -addstore Root YOUR-COMPANY-root.crt

certutil -addstore CA YOUR-COMPANY-intermediate.crt

Но вот не задача: Nodejs не использует системное хранилище сертификатов, поэтому у приложений запущенных локально возникнут проблемы с запросами к API подписанными вашим сертификатом: UNABLE_TO_GET_ISSUER_CERT_LOCALLY.

Вот как на скрине:

Решения

Вариант №1: Код

Передать свой сертификат в Nodejs можно с помощью кода ниже. Если не погружаться в дебри модели OSI и внутренности Node.js (а я не погружался), то он определяет статический метод createSecureContext который используется некоторыми внутренними функциями для создания защищённого соединения. Просто передаёте путь до сертификата в переменную окружения CERTIFICATE и пользуетесь на здоровье.

import fs from 'fs'
import tls from 'tls'

const origCreateSecureContext = tls.createSecureContext

if (process.env.NODE_ENV == 'development') {
  tls.createSecureContext = options => {
    const certPath = process.env.CERTIFICATE
    if (!certPath) {
      throw new Error(`Certificate path is not defined!`)
    }

    const resolvedPath = path.resolve(__dirname, certPath)

    if (certPath && fs.existsSync(resolvedPath)) {
      const secureContext = origCreateSecureContext(options)
      const pem = fs
        .readFileSync(resolvedPath, {
          encoding: 'ascii'
        })
        .replace(/\r\n/g, '\n')

      const certs = pem.match(
			      	/-----BEGIN CERTIFICATE-----\n[\s\S]+?\n-----END CERTIFICATE-----/g
      			)

      if (!certs) {
        throw new Error(`Could not parse certificate «${certPath}»`)
      }

      certs.forEach(cert => {
        secureContext.context.addCACert(cert.trim())
      })

      return secureContext
    } 
    throw new Error(`Could not find certificate path «${certPath}»`)
  }
}

// const app = express() //дальше идёт ваш обычный код

Это работает, но это костыль, который надо вставлять в каждый ваш проект.

Вариант №2: отключение строгой проверки

Подумаешь путь до корневого сертификата не будет так строго проверяться? Что плохого может случиться? Просто ставим переменную окружения set NODE_TLS_REJECT_UNAUTHORIZED=0 и всё! Да, Node.js будет спамить нам сообщениями об этой переменной в консоль, но кого это волнует? MitM? Не, не слышал.

Чушь. Поехали дальше…

Вариант №3: для тех у кого webpack dev server

Ставим в конфиге secure: false и все наши запросы гоняются по открытому каналу. Смотри выше и choose your destiny, как говорится.

Пример из документации Webpack:

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': {
        target: 'https://other-server.example.com',
        secure: false,
      },
    },
  },
};

Вариант №4: переменная окружения

Берём наш корневой сертификаты и кладём его в какую-нибудь папку которую вы случайно не удалите: C:\Users\%ИМЯРЕК%\AppData\Shared\certs (такой папки в системе нет, если что, я сам создал) и запоминаем любым удобным для Вас способом путь до него.

Открываем настройки системы:

В появившемся окне жмём “Создать”

Вводим имя переменной NODE_EXTRA_CA_CERTS значение C:\Users\%ИМЯРЕК%\AppData\Shared\certs\наш-корневой-сертификат.crt (не забываем про расширение файла!), далее OK OK

Переменная будет доступна В НОВОМ инстансе CMD:

Поздравляю, проблема решена! Вы великолепны!