SSE 기반 MCP 서버 - Trivy를 활용한 보안 취약점 스캔 및 SBOM 생성
Kakao PlayMCP와 호환되는 MCP(Model Context Protocol) 서버로, 파일시스템 및 컨테이너 이미지에 대한 보안 취약점 스캔과 SBOM(Software Bill of Materials) 생성 기능을 제공합니다.
- 🔍 취약점 스캔: 파일시스템 및 컨테이너 이미지 보안 취약점 분석
- 📋 SBOM 생성: SPDX, CycloneDX 형식 지원
- 📊 결과 요약: 심각도별 분류, Top CVE/패키지, 권장 조치
- 🌊 SSE 스트리밍: 실시간 진행 상태 전송
- 🔒 보안: Path traversal 방지, 동시 실행 제한
# 1. 프로젝트 클론
git clone <repository-url>
cd trivy_mcp
# 2. 스캔할 프로젝트 준비
mkdir -p scan-input
cp -r /path/to/your/project ./scan-input/
# 3. 실행
docker-compose up -d --build
# 4. 로그 확인
docker-compose logs -f
# 5. 테스트
curl http://localhost:8000/mcp/tools/list | jq .# 1. Python 환경 준비
python -m venv venv
source venv/bin/activate
# 2. 의존성 설치
pip install -r requirements.txt
# 3. Trivy 설치 (macOS)
brew install trivy
# 4. 환경 변수 설정
cp .env.example .env
# .env 파일을 적절히 수정
# 5. 디렉토리 생성
mkdir -p /tmp/trivy-mcp/{results,logs,cache,input}
# 6. 환경변수 설정 후 실행
export SCAN_ROOT=/tmp/trivy-mcp/input
export RESULTS_DIR=/tmp/trivy-mcp/results
export TRIVY_CACHE_DIR=/tmp/trivy-mcp/cache
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000상세 가이드는 CONNECTION_GUIDE.md를 참고하세요.
- 로컬 연결:
http://localhost:8000/mcp/tools/list - 외부 연결(터널링):
ssh -R 80:localhost:8000 serveo.net실행 후 생성된 URL +/mcp/tools/list
또는 Docker 내부 네트워크 사용 시:
http://trivy-mcp:8000/mcp/tools/list
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /mcp/tools/list |
사용 가능한 도구 목록 및 스키마 |
| POST | /mcp/tools/call |
도구 실행 (run_id 반환) |
| GET | /mcp/runs/{id}/events |
SSE 진행 이벤트 스트림 |
| GET | /mcp/runs/{id}/result |
최종 결과 조회 |
| GET | /mcp/runs/{id}/download/{artifact} |
결과 파일 다운로드 |
서버 및 Trivy 상태 확인
curl -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{"name": "healthcheck", "arguments": {}}'파일시스템 취약점 스캔
curl -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{
"name": "scan_filesystem",
"arguments": {
"path": "/scan/input/my-project",
"severity": "HIGH,CRITICAL"
}
}'컨테이너 이미지 취약점 스캔
# 이미지 레퍼런스로 스캔
curl -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{
"name": "scan_image",
"arguments": {
"image_ref": "nginx:latest"
}
}'
# tar 파일로 스캔
curl -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{
"name": "scan_image",
"arguments": {
"image_tar_path": "/scan/input/image.tar"
}
}'파일시스템 SBOM 생성
curl -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{
"name": "generate_sbom_filesystem",
"arguments": {
"path": "/scan/input/my-project",
"sbom_format": "spdx-json"
}
}'이미지 SBOM 생성
curl -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{
"name": "generate_sbom_image",
"arguments": {
"image_ref": "python:3.11-slim",
"sbom_format": "cyclonedx"
}
}'스캔 결과 요약
curl -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{
"name": "summarize_scan_result",
"arguments": {
"raw_result_path": "/data/results/{run_id}/trivy-result.json",
"top_n": 10
}
}'스캔 실행 후 SSE로 진행 상태를 실시간 모니터링:
# 1. 스캔 시작
RESPONSE=$(curl -s -X POST http://localhost:8000/mcp/tools/call \
-H "Content-Type: application/json" \
-d '{"name": "scan_filesystem", "arguments": {"path": "/scan/input/project"}}')
RUN_ID=$(echo $RESPONSE | jq -r '.run_id')
# 2. SSE 이벤트 구독
curl -N http://localhost:8000/mcp/runs/$RUN_ID/events| 이벤트 | 설명 |
|---|---|
start |
스캔 시작 |
progress |
진행 단계 (downloading-db, scanning, done 등) |
log |
Trivy stdout/stderr |
result |
최종 결과 |
error |
오류 발생 |
POST /mcp/tools/call + GET /mcp/runs/{id}/events 분리 방식을 채택했습니다.
- 비차단 응답: POST 요청이 즉시
run_id를 반환하여 클라이언트가 블로킹되지 않음 - 재연결 지원: SSE 연결이 끊어져도 동일한
run_id로 재연결 가능 - 다중 구독: 여러 클라이언트가 동일 작업의 진행 상태를 동시에 구독 가능
- PlayMCP 호환성: 요청/응답과 이벤트 스트림이 분리되어 표준 MCP 프로토콜과 호환
# DB 다운로드 전용 컨테이너 실행
docker run -it --rm \
-v trivy-db-cache:/root/.cache/trivy \
aquasec/trivy:latest \
image --download-db-only
# 캐시 디렉토리 추출
docker run --rm \
-v trivy-db-cache:/cache \
-v $(pwd):/output \
alpine tar czf /output/trivy-db.tar.gz -C /cache .scp trivy-db.tar.gz internal-server:/path/to/# docker-compose.yml이 있는 디렉토리에서
mkdir -p trivy-cache
tar xzf trivy-db.tar.gz -C trivy-cache/
# docker-compose.yml 수정: 볼륨을 로컬 디렉토리로 변경
# - trivy-cache:/data/trivy-cache
# → - ./trivy-cache:/data/trivy-cache
# OFFLINE_MODE 활성화
# environment:
# - OFFLINE_MODE=true
docker-compose up -dLLM이 다음 도구들을 순차적으로 호출합니다:
-
scan_filesystem: 취약점 스캔
{"name": "scan_filesystem", "arguments": {"path": "/scan/input/project"}} -
generate_sbom_filesystem: SBOM 생성
{"name": "generate_sbom_filesystem", "arguments": {"path": "/scan/input/project"}} -
(선택) summarize_scan_result: 결과 요약
{"name": "summarize_scan_result", "arguments": {"raw_result_path": "/data/results/{scan_id}/trivy-result.json"}}
| 변수 | 기본값 | 설명 |
|---|---|---|
HOST |
0.0.0.0 | 서버 바인드 주소 |
PORT |
8000 | 서버 포트 |
SCAN_ROOT |
/scan/input | 스캔 허용 루트 디렉토리 |
TRIVY_CACHE_DIR |
/data/trivy-cache | Trivy DB 캐시 |
RESULTS_DIR |
/data/results | 결과 저장 디렉토리 |
MAX_CONCURRENT_SCANS |
2 | 동시 스캔 최대 수 |
DEFAULT_TIMEOUT_SEC |
600 | 스캔 타임아웃 |
OFFLINE_MODE |
false | DB 자동 다운로드 비활성화 |
- Path Traversal 방지: 모든 경로는
SCAN_ROOT하위로 제한 - 동시 실행 제한:
MAX_CONCURRENT_SCANS로 리소스 보호 - 요청 크기 제한:
MAX_REQUEST_SIZE_MB로 대용량 요청 차단 - 명령 주입 방지: 모든 subprocess 호출은
shell=False - 비루트 실행: Docker 컨테이너는 전용
trivy사용자로 실행
MIT License