
보안 솔루션으로 Zscaler를 사용하면, Node.JS 환경에서 HTTPS 요청 시 unable to get local issuer certificate 오류가 발생하는 경우가 있습니다. 보안은 항상 중요하지만, 이 문제가 작업 시 한번씩 튀어나와 은근한 스트레스가 되고 있었습니다. (그 외에도 http 1.1 로 강제 된다던가...)
There was an issue establishing a connection while requesting
(node:73839) UnhandledPromiseRejectionWarning: Error: unable to get local issuer certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1058:34)
at TLSSocket.emit (events.js:198:13)
at TLSSocket.EventEmitter.emit (domain.js:448:20)
at TLSSocket._finishInit (_tls_wrap.js:636:8)
문제의 원인
Node.js는 시스템의 CA(인증 기관) 를 사용하지 않고 내부적으로 CA 목록을 node_root_certs.h에 하드코딩하여 사용합니다. 하지만 Zscaler의 인증서는 기본적으로 여기에 포함되어 있지 않기 때문에, HTTPS 요청을 보낼 때 신뢰할 수 없는 인증서로 간주되어 오류가 발생합니다.
근런데 Zscaler의 인증서는 기업마다 다르게 발급되기 때문에, Node.js 공식 CA 목록에 포함되기 어렵습니다. 하지만, 그렇다고 해서 계속 이대로 지낼 수 는 없었습니다.
우린 답을 찾을 것이다. 늘 그랬듯이
Graceful 한 해결방법을 찾아간 과정
일반적으로 다음과 같은 방법으로 문제를 우회할 수 있었습니다.
NODE_TLS_REJECT_UNAUTHORIZED 비활성화 (비추천)
export NODE_TLS_REJECT_UNAUTHORIZED=0
이 방법은 가장 간편한 방법이지만 보안적으로 취약하며, MITM(중간자 공격) 위험이 있습니다. 또한, 일부 경우에 적용되지 않는 경우가 있습니다. 대표적으로 next/font 를 사용하는 경우 build 과정에서 서체 파일을 다운로드 하게 되는데 이 때 위 옵션을 사용 한 경우에도 아래와 같은 오류가 발생합니다.
[Error [FetchError]: request to https://~, reason: unable to get local issuer certificate] {
type: 'system',
errno: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
constructor: [Function: FetchError]
}
무엇보다 절대 운영 환경에서 사용하면 안된다고 설명 했음에도 해당 옵션을 운영 환경에서 사용하려는 작업자를 적발하였습니다. 다행히 코드 리뷰 과정에서 발견하였지만, 이러한 처리가 코드에 포함되지 않을 수 있는 다른 방법을 고민하게 되었습니다.
ps: zero trust 의 항상 신뢰할 수 없는 것으로 간주 에 대해 다시 한번 생각해 보게 되었습니다. (팀원도 믿으면 안된다)
NODE_EXTRA_CA_CERTS
export NODE_EXTRA_CA_CERTS=/path/to/zscaler-cert.pem
어느정도 요구사항을 만족 하였지만, 다른 불편함에 봉착 하였습니다.
- 1개의 파일만 지정할 수 있다. 여러 인증서를 사용 하려면 인증서를 합치는 작업이 필요하다.
- 인증서를 실수로 삭제하는 경우가 있다.
- 개발 환경 셋팅한지 오래된 사람의 경우 인증서를 추가 했다는 사실을 잊어서 신규 입사자 온보딩때 헤매는 경우가 있다.
- 일부 상황에서 여전히 발생한다.
특히, Next.js 에서 개발하는 경우에 오류 상황이 자주 재현 되었습니다.

이유는 Next.js 는 system 환경 변수 에 NODE_EXTRA_CA_CERTS 가 지정되어 있더라도 런타임에 이를 사용하지 않아 undefined 가 되며 --experimental-https 옵션을 사용할 경우 NODE_EXTRA_CA_CERTS 가 ~/Library/Application Support/mkcert/rootCA.pem 로 변경됩니다. 이 때문에 유독 Next.js 에서 인증서 문제가 지속적으로 발생 하였습니다.
NODE_OPTIONS=--use-openssl-ca
NODE_OPTIONS=--use-openssl-ca
위 방법의 경우에는 openssl 에 포함되어 있으나 Node.js CA 목록에 없는 경우에는 유효 하지만 이번과 같이 Zscaler 처럼 사설 인증서의 경우 해당되지 않습니다.
Node.js에 Zscaler 인증서 추가하여 직접 빌드하기
문득, 이런 생각을 했습니다. Node.js 에 Zscaler 인증서가 없는게 문제라면 추가하면 되는게 아닐까?
그래서 Node.js 코드를 수정해 직접 빌드 해보기로 했습니다.
1. Node.js 소스 코드 다운로드
# Node.js 최신 LTS 버전 다운로드
git clone [email protected]:nodejs/node.git
cd node
2. Zscaler 인증서 추가
Node.js는 src/node_root_certs.h 파일에 기본 CA 목록을 포함하고 있습니다. 여기에 회사의 Zscaler 인증서를 추가합니다. 마지막 줄에는 개행문자가 없고 끝에 쉼표가 있는것에 주의 합니다.
"-----BEGIN CERTIFICATE-----\n"
"(여기에 회사 Zscaler 인증서 내용을 추가)\n"
"-----END CERTIFICATE-----",
3. Node.js 빌드
BUILDING.md 를 참고하여 빌드를 진행합니다.
저는 nvm 을 통해 직접 빌드한 버전을 포함하여 여러 버전을 사용할 계획이기 때문에 추후 쉽게 구분하기 위해서 node_version.h 의 NODE_TAG 의 값을 변경하여 postfix 를 -zscaler 로 수정 하였습니다.
./configure
make -j$(nproc)
여기서 make -j$(nproc) 중 nproc 은 현재 시스템에서 사용 가능한 논리 프로세서 또는 CPU 코어의 개수를 출력하는 명령어 입니다.
mac 에서는 nproc 을 포함하여 일부 리눅스 명령어가 기본 제공되지 않기 떄문에 직접 숫자를 지정 하거나 GUN coreutils 를 설치해 gnproc 으로 대체할 수 잇습니다.
brew install coreutils
./configure
make -j$(gnproc)
# 또는
./configure
make -j4
이제 빌드된 Node.js 를 사용하면, Zscaler 인증서 문제 없이 HTTPS 요청을 정상적으로 처리할 수 있습니다.
# system 에 설치된 Node.js 를 교체 설치 하는 경우
sudo make install
그런데 저는 nvm 으로 관리 및 사내 공유를 위해 다음과 같이 진행 하였습니다.
make install DESTDIR=$(pwd)/node-dist
tar -czvf nodejs-v24.0.0-zscaler.tar.gz -C node-dist usr/local
이제 nodejs-v24.0.0-zscaler.tar.gz 파일을 공유하여 여러 사람이 동일 빌드를 사용할 준비가 되었습니다.
설치하는 쪽 에서는 다음과 같이 진행 합니다.
mkdir -p ~/.nvm/versions/node/v24.0.0-zscaler
tar -xzf ./nodejs-v24.0.0-zscaler.tar.gz -C ~/.nvm/versions/node/v24.0.0-zscaler --strip-components=2
nvm ls
이 때 중요한 점은 공식 배포판과 동일하게 ~/.nvm/versions/node/v24.0.0-zscaler 하위에 bin, lib, include, share 가 존재해야 합니다.

이제 nvm 을 통해 커스텀 빌드 버전과 정식 버전을 옮겨다닐 수 있습니다.
그리고 몇 일 전 feat: added support for reading certificates from macOS system store 이 PR 이 merge 되었습니다. 향후에는 이러한 과정 없이 인증서 문제를 해결 할 수 있기를 기대합니다.
Read more

npm 레지스트리 키 로테이션으로 인한 corepack 서명 오류 해결하기
npm 레지스트리 키 로테이션으로 인한 corepack pnpm 서명 오류 해결 방법을 소개합니다. Node.js 패키지 관리 도구의 보안 업데이트로 인한 문제 해결 가이드입니다.

Bastion Host, 내부 네트워크를 지키는 보안 요새
외부와 내부 네트워크 간의 보안 게이트웨이 역할을 하는 Bastion Host에 대해 알아보세요. 네트워크 보안을 강화하고 안전한 접근을 보장하는 핵심 구성요소입니다.

🍺 비어웨어(Beerware) 라이선스 개발자의 유쾌한 철학
오픈소스 라이선스의 특별한 예시인 비어웨어(Beerware)에 대해 알아보세요. "코드가 마음에 들면 맥주 한 잔 사주세요!"라는 유쾌한 철학을 가진 라이선스입니다.