GitLab CI를 사용한 DevSecOps 파이프라인 구축은(는) 무엇을 하나요?
개요
GitLab은 보안 테스트를 CI/CD 파이프라인에 직접 내장하는 통합 DevSecOps 플랫폼을 제공합니다. GitLab의 내장 보안 스캐너(SAST, DAST, 컨테이너 스캐닝, 의존성 스캐닝, 비밀 탐지, 라이선스 준수)를 활용함으로써 팀은 보안을 왼쪽으로 이동(Shift Left)하여 배포 후가 아닌 개발 중에 취약점을 포착할 수 있습니다. GitLab Duo AI는 SAST 취약점에 대한 오탐지 감지를 지원하여 보안 팀이 실제 문제에 집중할 수 있도록 돕습니다.
사용 시기
- 환경에 DevSecOps 파이프라인 구축 GitLab CI 기능을 배포하거나 구성할 때
- 규정 준수 요구 사항에 부합하는 보안 제어를 수립할 때
- 이 도메인에 대한 보안 아키텍처를 구축하거나 개선할 때
- 이 구현이 필요한 보안 평가를 수행할 때
전제 조건
- GitLab Ultimate 라이선스 (전체 보안 스캐너 제품군 필요)
- 구성된 GitLab Runner (공유 또는 자체 호스팅)
.gitlab-ci.yml파이프라인 구성에 대한 숙련도- 컨테이너 빌드를 위한 Docker-in-Docker (DinD) 또는 Kaniko
- DAST 스캐닝을 위해 스테이징 환경에 배포된 애플리케이션
핵심 보안 스캐닝 단계
정적 애플리케이션 보안 테스트 (SAST)
SAST는 컴파일 전에 소스 코드의 취약점을 분석합니다. GitLab은 Semgrep, SpotBugs, Gosec, Bandit, NodeJsScan과 같은 분석기를 사용하여 14개 이상의 언어를 지원합니다. 가장 간단한 포함 방법은 GitLab의 관리형 템플릿을 사용하는 것입니다.
동적 애플리케이션 보안 테스트 (DAST)
DAST는 HTTP 엔드포인트에 공격 페이로드를 시뮬레이션하여 실행 중인 애플리케이션을 테스트합니다. XSS, SQLi, CSRF 및 정적 분석으로는 찾을 수 없는 기타 런타임 취약점을 탐지합니다. DAST에는 배포되어 접근 가능한 대상 URL이 필요합니다.
컨테이너 스캐닝
Trivy를 사용하여 Docker 이미지에서 OS 패키지 및 애플리케이션 의존성의 알려진 CVE를 스캔합니다. Docker 빌드 단계 후에 실행되어 이미지가 레지스트리에 도달하기 전에 게이트합니다.
의존성 스캐닝
의존성 매니페스트(package.json, requirements.txt, pom.xml, Gemfile.lock)를 검사하여 알려진 취약한 버전을 찾습니다. 소스 코드 수준에서 작동하며 컨테이너 스캐닝을 보완합니다.
비밀 탐지
패턴 매칭 및 엔트로피 분석을 사용하여 실수로 커밋된 자격 증명, API 키, 토큰 및 개인 키를 커밋에서 스캔합니다. 모든 커밋에서 실행되어 비밀이 저장소에 도달하는 것을 방지합니다.
구현
전체 파이프라인 구성
# .gitlab-ci.yml
stages:
- build
- test
- security
- deploy-staging
- dast
- deploy-production
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
SECURE_LOG_LEVEL: "info"
# GitLab 관리형 보안 템플릿 포함
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml
- template: DAST.gitlab-ci.yml
- template: Security/License-Scanning.gitlab-ci.yml
build:
stage: build
image: docker:24.0
services:
- docker:24.0-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
rules:
- if: $CI_COMMIT_BRANCH
unit-tests:
stage: test
image: $DOCKER_IMAGE
script:
- npm ci
- npm run test:coverage
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
junit: junit-report.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
# SAST를 security 단계에서 실행하도록 재정의
sast:
stage: security
variables:
SAST_EXCLUDED_PATHS: "spec,test,tests,tmp,node_modules"
SEARCH_MAX_DEPTH: 10
# 컨테이너 스캐닝 재정의
container_scanning:
stage: security
variables:
CS_IMAGE: $DOCKER_IMAGE
CS_SEVERITY_THRESHOLD: "HIGH"
# 의존성 스캐닝 재정의
dependency_scanning:
stage: security
# 비밀 탐지 재정의
secret_detection:
stage: security
# 라이선스 준수 스캐닝
license_scanning:
stage: security
deploy-staging:
stage: deploy-staging
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/app app=$DOCKER_IMAGE -n staging
- kubectl rollout status deployment/app -n staging --timeout=300s
environment:
name: staging
url: https://staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# DAST는 배포된 스테이징 환경에 대해 실행
dast:
stage: dast
variables:
DAST_WEBSITE: https://staging.example.com
DAST_FULL_SCAN_ENABLED: "true"
DAST_BROWSER_SCAN: "true"
needs:
- deploy-staging
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-production:
stage: deploy-production
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/app app=$DOCKER_IMAGE -n production
- kubectl rollout status deployment/app -n production --timeout=300s
environment:
name: production
url: https://app.example.com
when: manual
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
보안 승인 정책
필수 보안 스캔을 강제하는 스캔 실행 정책을 구성합니다:
- Security & Compliance > Policies로 이동
- 모든 브랜치에 SAST 및 비밀 탐지를 요구하는 "Scan Execution Policy" 생성
- 중요 취약점이 감지될 때 보안 팀 승인을 요구하는 "Merge Request Approval Policy" 생성
사용자 정의 SAST 규칙 구성
분석기 동작을 사용자 정의하려면 .gitlab/sast-ruleset.toml을 생성합니다:
[semgrep]
[[semgrep.ruleset]]
dirs = ["src"]
[[semgrep.passthrough]]
type = "url"
target = "/sgrep-rules/custom-rules.yml"
value = "https://semgrep.dev/p/owasp-top-ten"
[[semgrep.passthrough]]
type = "url"
target = "/sgrep-rules/java-rules.yml"
value = "https://semgrep.dev/p/java"
보안 대시보드 및 취약점 관리
취약점 보고서
GitLab은 모든 스캐너 결과를 단일 취약점 보고서로 통합하며, Security & Compliance > Vulnerability Report에서 액세스할 수 있습니다. 각 취약점에는 다음이 포함됩니다:
- 심각도 등급 (Critical, High, Medium, Low, Info)
- 스캐너 출처 (SAST, DAST, Container, Dependency, Secret)
- 소스 코드 또는 이미지 레이어 내 위치
- 수정 지침 및 제안된 수정 사항
- 상태 추적 (Detected, Confirmed, Dismissed, Resolved)
병합 요청 보안 위젯
모든 병합 요청에는 보안 스캐닝 위젯이 표시되어 다음을 보여줍니다:
- MR에 의해 도입된 새로운 취약점
- MR에 의해 해결된 취약점
- 대상 브랜치 기준선과의 비교
파이프라인 최적화
- 병렬 실행: 보안 스캐너가 security 단계에서 동시에 실행됩니다.
- 캐싱: 의존성 다운로드를 위해 CI 캐시를 사용하여 스캐닝 속도를 높입니다.
- 증분 스캐닝:
SAST_INCREMENTAL: "true"를 사용하여 SAST가 변경된 파일만 스캔할 수 있습니다. - 실패 조건: 중요한 스캐너에
allow_failure: false를 설정하여 품질 게이트를 강제합니다.
모니터링 및 메트릭
| 메트릭 | 설명 | 목표 |
|---|---|---|
| 파이프라인 보안 커버리지 | 모든 스캐너가 활성화된 프로젝트의 비율 | > 95% |
| 중요 취약점 MTTR | 중요 발견 사항의 탐지부터 해결까지의 평균 시간 | < 48시간 |
| 오탐지율 | 오탐지로 기각된 발견 사항의 비율 | < 15% |
| 비밀 탐지 차단율 | 푸시 규칙에 의해 차단된 비밀 커밋의 비율 | > 99% |