sudo 권한을 가진 운영 계정 및 서비스 전용 Unix 계정(<APP_USER>)
보안 취약점 조치 · 런타임 업그레이드
Node.js 보안 취약점 패치 및 LTS 무중단 판올림
Node.js 보안 권고(CVE)에 대응하여 Ubuntu·Rocky Linux 환경에서 NodeSource 또는 nvm 기반 취약 버전을 최신 보안 LTS로 무중단(Zero-downtime) 업그레이드하고, PM2 프로세스 리로드와 npm 의존성 감사를 수행하는 표준 조치 절차입니다.BEFORE YOU START
시작 전에 준비하세요
현재 구동 중인 Node.js 서비스의 PM2 ecosystem 설정 및 .env 환경 파일의 검증된 백업 경로
Node.js 공식 보안 공지(CVE) 및 승인된 목표 보안 LTS 버전(v20.x 또는 v22.x LTS)
서비스 포트 및 헬스체크 엔드포인트(예: /healthz 또는 HTTP 200 응답 경로)
권장 대상 보안 감사 및 긴급 CVE 보안 권고에 따라 운영 중인 Node.js 런타임을 무중단으로 최신 보안 패치 LTS 버전으로 안전하게 판올림하려는 서버 관리자·DevOps 엔지니어
FOLLOW THE RECIPE
8단계 구성·점검 레시피
현재 Node.js 취약 버전 및 PM2 프로세스 진단
현재 서버에 설치된 Node.js 및 npm 버전이 최근 공지된 CVE 취약점 대상인지 확인하고, 운영 중인 PM2 프로세스 목록과 실행 바이너리 경로를 읽기 전용으로 진단합니다.
node -v
npm -vwhich node
cat /etc/os-releasesudo -iu <APP_USER> pm2 list
sudo -iu <APP_USER> pm2 describe <APP_NAME>- 현재 구동 중인 Node.js 버전과 공지된 취약 CVE 버전 대조 완료
- 서비스 실행 Unix 계정(<APP_USER>)과 PM2 홈 디렉터리 확인
- Node.js 설치 방식(NodeSource 패키지 vs nvm) 파악 완료
Node.js 버전이 취약점 영향 범위에 속하거나 nvm과 시스템 패키지 경로가 혼재되어 있다면 공식 패키지 저장소(NodeSource) 또는 nvm 관리 경로를 하나로 확정합니다.
운영 포트 점유 상태와 package.json 의존성 보안 감사를 수행합니다.
서비스 리스닝 포트 및 npm 의존성 취약점 점검
애플리케이션이 수신 중인 내부 포트, NGINX 프록시 연동 상태를 확인하고 package.json 내 서드파티 패키지의 취약점(npm audit)을 사전에 점검합니다.
sudo ss -tulpn | grep -E ':(3000|8080|<APP_PORT>)\b'sudo -iu <APP_USER> bash -lc 'cd /srv/myapp && npm audit --omit=dev || true'df -h / /var /srv- 서비스 포트 충돌이 없으며 NGINX upstream이 정상 프록시 중임을 확인
- npm audit에서 Critical/High 등급 취약점 존재 여부 파악
- 새 패키지 설치 및 빌드를 위한 충분한 디스크 여유 공간 확보
npm audit에서 취약점이 다수 발견되더라도 런타임 자체 CVE 패치와 애플리케이션 라이브러리 패치를 단계별로 분리하여 진행합니다.
공식 저장소에서 최신 보안 패치 Node.js LTS 패키지를 설치합니다.
NodeSource 공식 저장소 갱신 및 최신 보안 LTS 설치
배포판 공식 패키지 관리자(APT/DNF)를 통해 검증된 NodeSource 최신 보안 패치 LTS(v20.x 또는 v22.x) 바이너리를 설치하고 버전을 확인합니다.
sudo apt-get update
sudo apt-get install --only-upgrade -y nodejs || sudo apt-get install -y nodejssudo dnf makecache
sudo dnf upgrade -y nodejs || sudo dnf install -y nodejsnode -v
npm -v- 공식 보안 패치가 적용된 LTS 버전으로 node 바이너리가 갱신되었음을 확인
- npm 버전이 함께 최신 보안 버전으로 갱신되었음을 확인
바이너리 교체 후에도 기존 실행 중인 PM2 프로세스는 메모리상에서 구버전으로 계속 동작 중이므로 설정 백업 후 리로드가 필요합니다.
설정 파일 백업 및 네이티브 C++ 애드온 재빌드를 진행합니다.
설정 파일 백업 및 글로벌 모듈·네이티브 애드온 정합성 검증
런타임 변경 전 애플리케이션의 package.json, package-lock.json 및 .env 환경설정 파일을 백업하고, Node.js ABI 변경에 따른 네이티브 애드온(node-gyp)을 재빌드합니다.
sudo -u <APP_USER> cp -p /srv/myapp/package.json /srv/myapp/package.json.bak
sudo -u <APP_USER> cp -p /srv/myapp/.env /srv/myapp/.env.baksudo npm install -g pm2@latestsudo -iu <APP_USER> bash -lc 'cd /srv/myapp && npm rebuild'sudoedit /srv/myapp/ecosystem.config.cjsmodule.exports = {
apps: [
{
name: "myapp",
script: "server.js",
instances: "max",
exec_mode: "cluster",
wait_ready: true,
listen_timeout: 10000,
kill_timeout: 5000,
env: {
NODE_ENV: "production",
PORT: 3000,
},
},
],
};- package.json 및 .env 백업 파일(.bak) 생성 확인
- Node.js 버전 판올림에 맞춘 PM2 전역 패키지 동기화 완료
- npm rebuild 실행 시 C++ 애드온 컴파일 에러 없음 확인
ABI 호환성 문제가 생기면 npm rebuild 또는 클린 설치(npm ci)를 통해 의존 모듈을 다시 빌드합니다.
PM2 Zero-Downtime 무중단 리로드를 수행합니다.
PM2 무중단 리로드(Zero-Downtime) 및 프로세스 저장
클러스터 모드로 구동 중인 PM2 워커 프로세스들을 하나씩 순차적으로 재시작(reload)하여 무중단으로 새 Node.js 런타임을 적용하고 startup 설정을 저장합니다.
sudo -iu <APP_USER> pm2 reload all --update-envsudo -iu <APP_USER> pm2 updatesudo -iu <APP_USER> pm2 save- 다운타임 없이 각 워커 프로세스가 순차적으로 online 상태로 전환되었음을 확인
- pm2 update로 백그라운드 PM2 데몬도 최신 Node 바이너리를 참조하도록 갱신
- pm2 save로 부팅 시 자동 시작 구성 저장 완료
프로세스가 errored 상태로 빠지거나 재시작 루프가 발생하면 즉시 logs 명령으로 예외 원인을 추적합니다.
서비스 헬스체크 및 런타임 정상 동작을 검증합니다.
무중단 전환 검증 및 런타임 취약점 패치 확인
로컬 헬스체크 엔드포인트를 호출하여 HTTP 200 응답을 확인하고, PM2 프로세스가 실제로 신규 Node.js 런타임 버전에서 실행 중인지 프로세스 정보를 대조합니다.
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:<APP_PORT>/healthz || curl -s -I http://127.0.0.1:<APP_PORT>/sudo -iu <APP_USER> pm2 status
sudo -iu <APP_USER> pm2 describe <APP_NAME> | grep -E 'node version|status|uptime'sudo -iu <APP_USER> pm2 logs <APP_NAME> --lines 40 --nostream- HTTP 200 정상 응답 및 기대 지연 시간 이내 처리 확인
- pm2 describe 출력에서 신규 보안 LTS Node 버전 번호 확인
- 에러 로그에 UncaughtException 또는 경고 발생 없음 확인
정상 응답이 확인되면 런타임 수준의 CVE 패치가 완료된 상태입니다. 이후 보안 강화를 진행합니다.
npm audit 기반 프로덕션 의존성 취약점 자동 교정 및 권한 강화를 수행합니다.
프로덕션 의존성 취약점 교정 및 런타임 보안 강화
프로덕션 의존성 패키지 취약점을 자동 교정(npm audit fix)하고, 서비스 계정의 파일 권한과 Node.js 런타임 플래그(--disable-proto 등)를 검토합니다.
sudo -iu <APP_USER> bash -lc 'cd /srv/myapp && npm audit fix --omit=dev'sudo chown -R <APP_USER>:<APP_USER> /srv/myapp
sudo chmod 750 /srv/myappsudo ss -tulpn | grep -E ':(3000|8080|<APP_PORT>)\b'- npm audit 결과 Critical/High 취약점이 해소되었음을 확인
- Node.js 프로세스가 root가 아닌 <APP_USER> 권한으로만 실행됨을 확인
- 애플리케이션 포트가 외부가 아닌 127.0.0.1(루프백)에 바인딩되어 NGINX를 통해서만 접근됨을 확인
Breaking change가 있는 메이저 라이브러리 취약점은 별도 스테이징 환경에서 테스트 후 패키지 업그레이드를 계획합니다.
장애 발생 시 안전하게 이전 버전으로 복구하는 롤백 절차를 숙지합니다.
취약점 패치 실패 시 이전 Node.js 런타임 및 의존성 복원
신규 Node.js 런타임과의 비호환성이나 앱 충돌로 장애가 발생할 경우 백업된 package.json과 설정 파일(.bak)을 복구하고 이전 런타임 버전으로 롤백합니다.
sudo -u <APP_USER> cp -p /srv/myapp/package.json.bak /srv/myapp/package.json
sudo -u <APP_USER> cp -p /srv/myapp/.env.bak /srv/myapp/.envsudo apt-get install --allow-downgrades -y nodejs=<PREVIOUS_SAFE_VERSION>sudo dnf downgrade -y nodejs-<PREVIOUS_SAFE_VERSION>sudo -iu <APP_USER> bash -lc 'cd /srv/myapp && npm ci --omit=dev && pm2 reload all'- 백업된 package.json 및 .env 파일로 원복 완료
- 이전 안정화 Node.js 버전으로 패키지 다운그레이드 확인
- PM2 무중단 롤백 후 HTTP 헬스체크 정상 응답 확인
롤백 후 즉시 모니터링 로그를 확인하고, 호환성 문제의 근본 원인을 분석하여 런타임 재패치 계획을 수립합니다.
서비스 정상 복구 확인 후 인시던트 로그를 정리합니다.
SECURITY CHECK
운영 전 마지막 보안 점검
- Node.js 런타임이 root 권한이 아닌 전용 서비스 계정(<APP_USER>)으로 구동되는지 확인
- 프로덕션 환경에 불필요한 devDependencies가 제거되고 npm audit 조치가 완료되었는지 확인
- HTTP Request Smuggling 방지를 위해 NGINX와 Node.js 간 HTTP keep-alive 및 헤더 처리 정합성 검토
- reverse proxy(NGINX)를 거치지 않고 직접 외부에 Node.js 포트가 노출되지 않도록 로컬 루프백 바인딩 확인
COMMON ERRORS
자주 막히는 지점
pm2 reload 후에도 이전 Node.js 버전으로 표시됨
- 증상
- node -v는 최신 버전인데 pm2 describe에서는 구버전 node 경로가 표시됨
- 가능한 원인
- PM2 백그라운드 마스터 데몬이 구버전 Node 환경에서 실행된 상태로 캐시되어 있음
- 확인 순서
- sudo -iu <APP_USER> pm2 update 명령으로 PM2 데몬을 갱신하면 기존 프로세스를 유지하며 최신 바이너리로 동기화됩니다.
npm rebuild 시 node-gyp C++ 컴파일 실패
- 증상
- node-gyp rebuild 오류 및 C++ ABI 불일치로 네이티브 모듈 로드 실패
- 가능한 원인
- Node.js 메이저 버전 변경으로 V8 엔진 ABI가 변경되었거나 빌드 도구(gcc/g++, python3) 부재
- 확인 순서
- build-essential 패키지를 확인하고 sudo -iu <APP_USER> npm rebuild를 실행하거나 node_modules를 클린 재설치(npm ci)합니다.
PM2 리로드 중 NGINX에서 502 Bad Gateway 발생
- 증상
- 무중단 리로드 중 일부 요청이 502 Bad Gateway로 실패
- 가능한 원인
- 워커 프로세스가 listen 준비(ready)되기 전에 이전 프로세스가 kill되었거나 wait_ready 설정 미흡
- 확인 순서
- ecosystem.config.cjs에 wait_ready: true와 listen_timeout을 지정하고 앱 소스에서 process.send('ready')를 명시 호출합니다.
PRIMARY REFERENCES
공식 문서
- Node.js · Security Releases and Vulnerability Reports
- Node.js · 공식 릴리스와 LTS 상태
- PM2 · Cluster Mode and Zero-downtime Reload
설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.