Skip to content

prego-docker 취약점 수치 미개선 원인 분석 및 감소 기획

작성 배경: 새로 빌드한 Docker 이미지(iamfork/prego-repo:latest)의 Docker Scout 취약점 수치가 이전과 동일(CRITICAL 8, HIGH 51, MEDIUM 61, LOW 76, 기타 2)하게 나와, 개선이 전혀 반영되지 않은 원인과 대응 방향을 정리한다.

관련 문서: prego-docker-image-verify-and-vulnerability-plan.md §2·§3 (취약점 대응 원칙·실행 가이드).


1. 취약점 수치가 개선되지 않는 원인

1.1 구조적 원인 (레이어·입력 동일)

원인설명
동일한 빌드 입력Dockerfile, 베이스 이미지 태그(python:3.11-slim-bookworm), NODE_VERSION, apps.json의 앱 브랜치/태그, apt-get install 목록이 바뀌지 않으면 레이어 해시가 동일하거나 거의 동일해, 스캔 결과도 동일하게 나온다.
“새 이미지”의 의미prego-saas-app 태그만 바꾼 재빌드라면 애플리케이션 레이어만 바뀌고, 베이스·시스템·Node·대부분의 Python 의존성 레이어는 그대로다. Docker Scout/Trivy는 전체 레이어를 스캔하므로, 대부분을 차지하는 기존 레이어의 취약점 수는 그대로 유지된다.
베이스·앱 레이어 비중Docker Hub Image Layers 기준, python:3.11... 베이스 레이어와 iamfork/prego-repo(bench 앱들) 레이어가 대부분의 CVE를 차지한다. 앱 코드만 갱신해도 베이스·frappe/erpnext/hrms 의존성은 동일하면 수치가 그대로다.

즉, 취약점을 줄이기 위한 변경(베이스 digest, apt, Node, pip 의존성 등)을 하지 않은 재빌드라면 수치가 동일한 것이 자연스러운 결과다.

1.2 베이스 이미지

항목현재영향
태그만 사용FROM python:3.11-slim-bookworm (digest 미고정)같은 태그를 쓰면 캐시된 레이어 또는 동일한 베이스 버전을 쓰게 되어, 베이스 쪽 CVE 수가 고정된다.
베이스 갱신 부재Debian/python 공식 이미지가 보안 업데이트를 반영해도, 우리가 명시적으로 최신 digest로 올리지 않으면 빌드가 예전 베이스를 계속 사용한다.

베이스 레이어에서 나오는 Critical/High를 줄이려면 의도적인 베이스 이미지 갱신이 필요하다.

1.3 애플리케이션 레이어( bench / pip / npm )

항목현재영향
Frappe/ERPNext/HRMSapps.json의 브랜치(version-15 등) 고정upstream에서 보안 패치가 나와도 브랜치/태그를 올리지 않으면 동일한 의존성 세트가 유지되고, 해당 CVE 수는 그대로다.
prego-saas-app태그만 갱신(예: v1.0.0 → v1.0.1)의존성 목록(install_requires 등)이 같으면 pip 레이어는 동일하고, 앱 코드만 바뀌어도 CVE 수치는 변하지 않는다.
Node/npmNODE_VERSION=20.18.0, bench init 시 설치되는 프론트 의존성Node 패치 버전이나 npm 패키지 업데이트 없이 재빌드하면 동일한 npm CVE가 그대로 남는다.

즉, 의존성 버전을 올리거나 제거하지 않는 한 앱만 올려도 취약점 수는 줄지 않는다.

1.4 수치가 “동일”하게 나오는 요약

  • 입력이 같다
    Dockerfile·베이스·Node·apps.json·apt가 동일 → 레이어 구성이 동일 또는 거의 동일.
  • 취약점 감소를 위한 변경이 없다
    베이스 digest 갱신, apt/Node/pip 업데이트, 불필요 패키지 제거 등이 없음.
  • 스캔은 전체 이미지 기준
    “새로 만든 이미지”라도 전체 레이어를 스캔하므로, 변경 없는 레이어의 CVE는 그대로 집계됨.

따라서 취약점 수치를 줄이려면, “재빌드”가 아니라 “취약점을 줄이기 위한 명시적인 변경”을 해야 한다.


2. 개선 방향 (기획)

2.1 목표

  • 단기: Critical 8 → 0 (이미 정책상 목표, CI는 Trivy로 Critical 0 검사 중).
  • 중기: High 51 감소 (가능한 범위에서 베이스·시스템·Node·선택적 pip 갱신).
  • 장기: Medium/Low는 정책(수용 기준)을 정한 뒤, 주기 스캔·리포트로 관리.

2.2 전제

  • Frappe/ERPNext/HRMS 호환성을 깨지 않는 범위에서만 버전·이미지 변경.
  • 재현성: Dockerfile·digest·버전 명시로 빌드 재현 가능 유지.
  • 우선순위: 베이스 → 시스템(apt) → Node → Python(custom app). Upstream(Frappe/ERPNext/HRMS) 의존성은 직접 올리지 않고, upstream 보안 릴리스 반영 시 태그/브랜치만 갱신.

3. 실행 단계별 기획

3.1 현황 정리 (CVE·레이어 매핑)

단계내용담당/도구
3.1.1Docker Hub → Image Layers → Vulnerabilities에서 Critical 8건 CVE ID·패키지·“Present in” 레이어 기록.수동
3.1.2동일하게 High 51건 중 상위 N건(예: 20건) 정리.수동/스프레드시트
3.1.3“Fixable” 체크 후 수치 비교. Fixable만 잡아도 얼마나 줄 수 있는지 파악.Docker Hub / Trivy
3.1.4CVE별 레이어 분류: 베이스(python) / apt / npm / pip.§3.2~3.4에서 어떤 레이어를 손댈지 결정하는 입력으로 사용.

산출물: docs/planning/prego-docker-cve-layer-mapping.md (또는 이슈) — CVE 목록 + 레이어 + 패키지 + Fixable 여부.

3.2 베이스 이미지 갱신

단계내용파일/위치
3.2.1Docker Hub에서 python:3.11-slim-bookworm 최신 digest 확인.hub.docker.com
3.2.2Dockerfile 1행을 FROM python:3.11-slim-bookworm@sha256:...digest 고정.prego-docker/Dockerfile
3.2.3빌드 후 Trivy/Docker Scout로 Critical/High 개수 재측정.CI 또는 수동
3.2.4주기(분기 등)마다 digest 재확인·갱신 절차를 runbook에 명시.Prego/docs/runbook

기대 효과: 베이스 레이어에서 나오는 Debian/파이썬 관련 CVE 감소. 수치 개선 여부는 3.1 매핑 결과와 3.2.3 측정으로 검증.

3.3 시스템 패키지(apt) 정리

단계내용파일/위치
3.3.1Dockerfile 내 base·builderapt-get install 목록 검토. 불필요 패키지 제거.prego-docker/Dockerfile
3.3.23.1 매핑에서 apt 레이어 CVE가 있는 패키지만 골라, 버전 명시 또는 대체 패키지 검토(호환성 유지).prego-docker/Dockerfile
3.3.3apt-get update 직후 빌드하여 보안 업데이트 반영. (베이스가 최신이면 일부는 이미 포함.)기존 유지

기대 효과: apt 레이어 CVE 감소. 변경 범위가 작으므로 3.2와 함께 진행 가능.

3.4 Node 버전·npm

단계내용파일/위치
3.4.1NODE_VERSION을 20 LTS 최신 패치(예: 20.18.x 이상)로 상향.prego-docker/Dockerfile ARG NODE_VERSION
3.4.23.1 매핑에서 npm CVE가 많으면, Frappe/ERPNext upstream 이슈·보안 릴리스 확인. 우리가 직접 npm 패키지 버전을 올리는 것은 호환성 리스크가 있으므로 선택적.-
3.4.3prego-saas-app에 package.json이 있으면 해당 레포에서 npm audit / npm audit fix 후 반영.prego-saas-app

기대 효과: Node 런타임·npm 관련 CVE 일부 감소.

3.5 Python(custom app)·Upstream

단계내용파일/위치
3.5.1prego_saas_appinstall_requires·requirements.txt 점검. stripe 등 외부 라이브러리만 안전 버전으로 상향.prego-saas-app
3.5.2Frappe/ERPNext/HRMS는 버전을 직접 올리지 않음. upstream 보안 릴리스·권장 브랜치 반영 시 apps.json 브랜치/태그만 갱신 후 이미지 재빌드.apps.json, prego-docker 빌드

기대 효과: pip 레이어 중 우리가 제어 가능한 부분만 감소. 나머지는 upstream 반영 시점에 따라 감소.

3.6 정책·CI·모니터링

단계내용참고
3.6.1정책 유지: Critical 0 (CI 실패), High는 보고만 또는 N 이하로 threshold.prego-docker-image-verify-and-vulnerability-plan.md §5
3.6.2Trivy 스캔은 vuln만 사용(시크릿 스캔 제외). 빌드 직후·주기 실행 유지.trivy-scan.yml
3.6.3개선 전후 수치를 문서 또는 이슈에 기록해, 베이스/apt/Node 변경이 실제로 CVE 수를 줄였는지 확인.docs/planning 또는 GitHub Issue

4. 요약

구분내용
원인새 이미지라도 Dockerfile·베이스·의존성 버전이 동일하면 레이어가 동일해 취약점 수치도 동일. “재빌드”만으로는 수치가 줄지 않음.
개선(1) 현황 정리: Critical/High CVE·레이어·Fixable 매핑. (2) 베이스 digest 갱신. (3) apt 최소화·버전 정리. (4) Node 패치 버전 상향. (5) custom app pip 최소 업데이트. (6) 정책·CI 유지·수치 추적.
다음 단계§3.1 현황 정리(CVE·레이어·Fixable) 수행 후, §3.2 베이스 digest 갱신부터 순서대로 적용하고, 빌드·스캔으로 수치 변화를 측정한다.

이 문서는 원인 분석과 개선 방향 기획만 다룬다. 실제 CVE 목록·매핑·Dockerfile 패치는 §3.1 산출물과 팀 결정에 따라 별도 작업으로 진행한다.


5. Frappe/ERPNext/HRMS(수정 불가) 기여도 검증

5.1 이미지 구조 정리 (Frappe “이미지”가 아님)

prego-docker는 Frappe 공식 Docker 이미지를 FROM으로 쓰지 않는다.

구분실제 구조비고
베이스FROM python:3.11-slim-bookwormPython 공식 이미지 1개만 사용.
앱 설치bench init --apps_path=apps.jsonGitHub에서 Frappe·ERPNext·HRMS·prego-saas-app 소스 클론 후, 각 앱의 pip 의존성(requirements·setup.py)과 npm/yarn 의존성(package.json)을 설치.
결과하나의 이미지 레이어 안에 우리가 설치한 apt + frappe-bench + Frappe/ERPNext/HRMS가 끌어온 pip/npm 패키지 전부 포함.Docker Hub에서 “Frappe 이미지를 가져온 것”이 아니라, 같은 이미지 안에 Frappe 계열 앱과 그 의존성이 함께 들어간 것이다.

즉, “Frappe 이미지”는 별도 베이스가 아니라, 우리 이미지의 애플리케이션 레이어를 구성하는 Frappe/ERPNext/HRMS 소스 + 이들이 가져오는 패키지를 말한다.

5.2 수정 가능 vs 수정 불가

출처수정 가능 여부설명
python:3.11-slim-bookworm✅ 우리가 선택 가능digest 고정·갱신으로 베이스 CVE만 감소 가능.
Dockerfile 내 apt-get install✅ 우리가 수정 가능패키지 제거·버전 명시로 해당 CVE만 감소 가능.
NODE_VERSION, 전역 yarn✅ 우리가 수정 가능Node 패치 버전 상향 등으로 일부 CVE 감소 가능.
Frappe (pip·npm 의존성)수정 불가Frappe 레포의 requirements·package.json을 우리가 바꾸지 않음. upstream 보안 릴리스·브랜치/태그 반영 시에만 변경.
ERPNext (pip·npm 의존성)수정 불가동일. ERPNext 레포 의존성은 upstream만 수정.
HRMS (pip·npm 의존성)수정 불가동일. HRMS 레포 의존성은 upstream만 수정.
prego_saas_app (pip·npm)✅ 우리가 수정 가능install_requires·requirements·package.json 우리 레포에서 버전 상향 가능.

정리: 보고되는 8/51/61/76/2 중 상당수는 Frappe/ERPNext/HRMS가 끌어온 pip·npm 패키지에서 나오는 것이고, 이 부분은 우리가 직접 수정할 수 없다. 그래서 “Frappe 이미지(의존성)에서 나오는 오류/리스크”가 맞는지 확인이 필요하다.

5.3 검증 방법 (코드 생성 없이 확인 절차만)

아래는 가설 검증용 절차만 정리한다. 코드/스크립트는 만들지 않는다.

단계할 일목적
1Docker Hub(또는 Trivy) → Image Layers → Vulnerabilities에서 Critical 8건 각각 열기.CVE ID, Affected package(s)(패키지명), Present in(레이어) 확인.
2각 CVE의 패키지명으로 출처 추정:
python3.x, lib*, curl, nginx 등 → 베이스 또는 우리 apt우리가 줄일 수 있는 후보.
pillow, requests, cryptography, jinja2PyPI 패키지 → Frappe/ERPNext/HRMS의 requirements에 있으면 upstream(pip)수정 불가.
loader-utils, @babel/*, webpacknpm 패키지 → Frappe/ERPNext/HRMS의 package.json에 있으면 upstream(npm)수정 불가.
3High 51건 중 표본(예: 10~15건)만 같은 방식으로 분류.Critical과 동일한 패턴인지 확인.
4분류 결과를 표로 정리: CVE ID패키지

기대 결과: Critical·High 중 **대부분이 “pip upstream” 또는 “npm upstream”**으로 나오면, “Frappe/ERPNext/HRMS에서 가져온 의존성 때문에 나오는 오류/리스크가 맞다”는 것이 확인된다. 그 경우 우리가 할 수 있는 것은 (1) 베이스·apt·Node·prego_saas_app만 정리하고, (2) 나머지는 upstream 보안 릴리스 반영 시점에 apps.json 브랜치/태그 갱신 후 재빌드하는 것이다.

5.4 요약

  • Frappe “이미지”를 FROM으로 쓰는 것이 아니다. python 베이스 + bench init으로 Frappe/ERPNext/HRMS 소스와 의존성이 우리 이미지 한 레이어에 포함된다.
  • Frappe/ERPNext/HRMS의 pip·npm 의존성은 우리가 수정할 수 없다. 이들에서 나오는 CVE는 upstream 보안 릴리스와 브랜치/태그 갱신으로만 줄일 수 있다.
  • 리포트에 나오는 오류/리스크가 이 upstream에서 나온 것인지 확인하려면 §5.3처럼 CVE별로 패키지·출처(베이스/apt/npm 우리/npm upstream/pip 우리/pip upstream) 를 매핑하면 된다. 그 결과로 “Frappe·ERPNext·HRMS 기여 비중”을 문서화할 수 있다.
Help