Skip to content

Split View: 컨테이너 보안 & 공급망 보안 완전 가이드 2025: 이미지 스캐닝, Sigstore, SBOM, 런타임 보안

✨ Learn with Quiz
|

컨테이너 보안 & 공급망 보안 완전 가이드 2025: 이미지 스캐닝, Sigstore, SBOM, 런타임 보안

목차

1. 컨테이너 위협 모델: 공격 표면 이해

컨테이너 환경은 기존 VM 기반 인프라와 다른 고유한 위협 표면을 가진다. 이미지 취약점, 설정 오류, 런타임 공격, 공급망 위협까지 다층적 보안 전략이 필요하다.

1.1 컨테이너 공격 표면

컨테이너 보안 레이어:
┌─────────────────────────────────────────────────┐
│ Layer 6: 공급망 (Supply Chain)                    │
│  - 베이스 이미지 오염, 의존성 변조, CI/CD 파이프라인 │
├─────────────────────────────────────────────────┤
│ Layer 5: 오케스트레이션 (Kubernetes)               │
│  - RBAC 미설정, API 서버 노출, etcd 미암호화      │
├─────────────────────────────────────────────────┤
│ Layer 4: 네트워크                                 │
│  - 네트워크 정책 미설정, 서비스 메시 미적용         │
├─────────────────────────────────────────────────┤
│ Layer 3: 런타임                                   │
│  - 컨테이너 탈출, 권한 상승, 암호화폐 채굴         │
├─────────────────────────────────────────────────┤
│ Layer 2: 이미지                                   │
│  - CVE 취약점, 하드코딩된 시크릿, 과도한 패키지    │
├─────────────────────────────────────────────────┤
│ Layer 1: 호스트 OS / 컨테이너 런타임               │
│  - 커널 취약점, Docker 소켓 노출, runc 취약점      │
└─────────────────────────────────────────────────┘

1.2 주요 공격 벡터

1. 이미지 취약점 악용:
   공격자 → 공개 이미지의 알려진 CVE 악용 → 컨테이너 침투

2. 공급망 공격:
   공격자 → 베이스 이미지/라이브러리에 악성코드 주입 → 다운스트림 오염

3. 런타임 공격:
   공격자 → 취약한 앱 악용 → 컨테이너 탈출 → 호스트 접근

4. 설정 오류 악용:
   공격자 → 과도한 권한/미설정 네트워크 정책 → 횡적 이동

2. 이미지 보안: 안전한 컨테이너 이미지 구축

2.1 최소 베이스 이미지 선택

# BAD: 풀 이미지 사용 (200MB+, 수백 개 CVE 가능)
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

# GOOD: Distroless 이미지 (최소한의 바이너리만 포함)
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

FROM gcr.io/distroless/python3-debian12
COPY --from=builder /app /app
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
WORKDIR /app
CMD ["app.py"]

2.2 Chainguard 이미지

# Chainguard Images: 매일 리빌드, 제로 CVE 목표
FROM cgr.dev/chainguard/python:latest-dev AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

FROM cgr.dev/chainguard/python:latest
COPY --from=builder /app /app
COPY --from=builder /home/nonroot/.local/lib/python3.12/site-packages /home/nonroot/.local/lib/python3.12/site-packages
WORKDIR /app
ENTRYPOINT ["python", "app.py"]

2.3 멀티스테이지 빌드와 보안

# Go 앱 멀티스테이지 빌드
FROM golang:1.22-alpine AS builder

# 보안: 빌드 시에만 필요한 도구 설치
RUN apk add --no-cache git ca-certificates

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# 보안: 정적 바이너리 빌드 (CGO 비활성화)
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-w -s" -o /app/server ./cmd/server

# 최종 이미지: scratch (완전히 비어있는 이미지)
FROM scratch

# 보안: 비 root 사용자
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /app/server /server

USER 65534:65534
ENTRYPOINT ["/server"]

2.4 Dockerfile 보안 베스트 프랙티스

# 보안 강화된 Dockerfile 예시
FROM node:20-slim AS builder

# 보안: 비 root 사용자 생성
RUN groupadd -r appuser && useradd -r -g appuser -d /app appuser

WORKDIR /app

# 보안: package.json만 먼저 복사 (레이어 캐시 활용)
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production

COPY . .
RUN yarn build

# 프로덕션 이미지
FROM node:20-slim

# 보안: 비 root 사용자
RUN groupadd -r appuser && useradd -r -g appuser -d /app appuser

WORKDIR /app

# 보안: 필요한 파일만 복사
COPY --from=builder --chown=appuser:appuser /app/dist ./dist
COPY --from=builder --chown=appuser:appuser /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appuser /app/package.json ./

# 보안: 비 root로 실행
USER appuser

# 보안: 읽기 전용 파일시스템 호환
ENV NODE_ENV=production

EXPOSE 3000
CMD ["node", "dist/index.js"]

3. 이미지 스캐닝: 취약점 탐지

3.1 스캐닝 도구 비교

도구라이센스특징CI/CD 통합SBOM 생성
TrivyApache 2.0종합 스캐너(이미지/IaC/시크릿)우수지원
GrypeApache 2.0빠른 취약점 스캔우수syft 연동
Docker Scout프리미엄Docker Desktop 통합Docker Hub지원
Snyk Container프리미엄수정 권고 우수우수지원

3.2 Trivy 사용법

# 이미지 스캔
trivy image nginx:1.25

# 심각도 필터링
trivy image --severity HIGH,CRITICAL nginx:1.25

# SARIF 형식 출력 (GitHub Security 연동)
trivy image --format sarif --output trivy-results.sarif nginx:1.25

# 시크릿 스캔
trivy image --scanners secret myapp:latest

# IaC 스캔 (Dockerfile, Kubernetes YAML)
trivy config .

# SBOM 생성
trivy image --format cyclonedx --output sbom.json nginx:1.25
# .trivyignore - 특정 CVE 무시
# 이유를 주석으로 기록
# CVE-2023-xxxx: 우리 환경에서는 영향 없음 (해당 기능 미사용)
CVE-2023-xxxx

3.3 Grype와 syft

# syft로 SBOM 생성
syft packages nginx:1.25 -o cyclonedx-json > sbom.json

# Grype로 SBOM 기반 스캔
grype sbom:./sbom.json

# Grype 직접 이미지 스캔
grype nginx:1.25

# 심각도 임계값 설정 (CI/CD에서 사용)
grype nginx:1.25 --fail-on critical

3.4 CI/CD 통합 스캐닝

# .github/workflows/image-scan.yml
name: Container Image Scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t myapp:ci .

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:ci'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

      - name: Run Grype scan
        uses: anchore/scan-action@v4
        with:
          image: 'myapp:ci'
          fail-build: true
          severity-cutoff: 'high'

4. 이미지 서명: Sigstore와 cosign

4.1 Sigstore 생태계

Sigstore 구성 요소:
┌──────────────────────────────────────────────────┐
│  cosign                                           │
│  - 컨테이너 이미지/아티팩트 서명 및 검증            │
│  - 키리스(Keyless) 서명 지원 (OIDC 기반)           │
├──────────────────────────────────────────────────┤
│  Rekor                                            │
│  - 불변 투명성 로그 (Transparency Log)              │
│  - 모든 서명 기록을 공개적으로 저장                  │
├──────────────────────────────────────────────────┤
│  Fulcio                                           │
│  - 단기 인증서 발급 (Certificate Authority)         │
│  - OIDC 토큰을 X.509 인증서로 변환                 │
└──────────────────────────────────────────────────┘

4.2 cosign으로 이미지 서명

# 키 쌍 생성
cosign generate-key-pair

# 이미지 서명
cosign sign --key cosign.key myregistry.com/myapp:v1.0

# 이미지 서명 검증
cosign verify --key cosign.pub myregistry.com/myapp:v1.0

# 키리스 서명 (CI/CD에서 권장)
# OIDC 토큰으로 자동 인증 (GitHub Actions, GitLab CI)
cosign sign myregistry.com/myapp:v1.0

# 키리스 검증
cosign verify \
  --certificate-identity "https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  myregistry.com/myapp:v1.0

4.3 GitHub Actions에서 키리스 서명

# .github/workflows/sign-image.yml
name: Build, Sign, and Push

on:
  push:
    tags: ['v*']

permissions:
  contents: read
  packages: write
  id-token: write  # 키리스 서명에 필요

jobs:
  build-sign-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: sigstore/cosign-installer@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: "${{ github.actor }}"
          password: "${{ secrets.GITHUB_TOKEN }}"

      - name: Build and push
        id: build
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: "ghcr.io/${{ github.repository }}:${{ github.ref_name }}"

      - name: Sign the image (keyless)
        run: |
          cosign sign --yes \
            "ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}"

4.4 Kubernetes Admission Controller로 서명 검증

# Kyverno 정책: 서명된 이미지만 허용
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  background: false
  rules:
    - name: verify-cosign-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/myorg/*/.github/workflows/*@refs/heads/main"
                    issuer: "https://token.actions.githubusercontent.com"
                    rekor:
                      url: "https://rekor.sigstore.dev"

5. SBOM (Software Bill of Materials)

5.1 SBOM 포맷 비교

CycloneDX:
- OWASP 프로젝트
- 보안에 초점 (취약점, 라이센스)
- JSON, XML 포맷
- VEX (Vulnerability Exploitability eXchange) 지원

SPDX:
- Linux Foundation 프로젝트
- 라이센스 준수에 초점
- JSON, RDF, Tag-Value 포맷
- ISO/IEC 5962:2021 국제 표준

5.2 syft로 SBOM 생성

# CycloneDX 포맷으로 SBOM 생성
syft packages myapp:latest -o cyclonedx-json > sbom-cyclonedx.json

# SPDX 포맷으로 SBOM 생성
syft packages myapp:latest -o spdx-json > sbom-spdx.json

# 디렉토리 스캔
syft dir:./my-project -o cyclonedx-json > project-sbom.json

# SBOM을 이미지에 첨부 (cosign + SBOM)
cosign attach sbom --sbom sbom-cyclonedx.json myregistry.com/myapp:v1.0

# SBOM attestation 생성
cosign attest --predicate sbom-cyclonedx.json \
  --type cyclonedx \
  myregistry.com/myapp:v1.0

5.3 SBOM 기반 취약점 관리

# SBOM에서 취약점 스캔
grype sbom:./sbom-cyclonedx.json

# 특정 패키지 조회
syft packages myapp:latest | grep log4j

# 라이센스 분석
syft packages myapp:latest -o json | \
  jq '.artifacts[].licenses[].value' | sort | uniq -c | sort -rn

6. Pod Security Standards

6.1 보안 수준 비교

Privileged (특권):
- 제한 없음
- 알려진 권한 상승 허용
- 시스템 데몬, CNI 플러그인용

Baseline (기본):
- 알려진 권한 상승 방지
- hostNetwork, hostPID, hostIPC 차단
- 특권 컨테이너 차단
- 대부분의 워크로드에 적합

Restricted (제한):
- 최소 권한 원칙 적용
- 비 root 강제
- 모든 capability 삭제
- seccomp 프로필 필수
- 보안이 중요한 워크로드용

6.2 Pod Security Admission 설정

# Namespace에 Pod Security Standards 적용
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # Restricted 수준 강제
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    # Restricted 수준 경고
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
    # Restricted 수준 감사
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest

6.3 보안 강화된 Pod 명세

# Restricted 수준을 만족하는 Pod 명세
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      # 보안: 서비스 어카운트 토큰 자동 마운트 비활성화
      automountServiceAccountToken: false

      securityContext:
        # 보안: 비 root 그룹으로 실행
        runAsGroup: 1000
        fsGroup: 1000
        # 보안: Seccomp 프로필 적용
        seccompProfile:
          type: RuntimeDefault

      containers:
        - name: app
          image: ghcr.io/myorg/secure-app:v1.0
          ports:
            - containerPort: 8080
              protocol: TCP

          securityContext:
            # 보안: 비 root 사용자로 실행
            runAsNonRoot: true
            runAsUser: 1000
            # 보안: 읽기 전용 루트 파일시스템
            readOnlyRootFilesystem: true
            # 보안: 권한 상승 차단
            allowPrivilegeEscalation: false
            # 보안: 모든 capability 삭제
            capabilities:
              drop:
                - ALL

          # 임시 쓰기 디렉토리
          volumeMounts:
            - name: tmp
              mountPath: /tmp
            - name: cache
              mountPath: /app/cache

          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi

      volumes:
        - name: tmp
          emptyDir:
            sizeLimit: 100Mi
        - name: cache
          emptyDir:
            sizeLimit: 200Mi

7. 네트워크 정책 (Network Policy)

7.1 기본 거부 정책

# 모든 인그레스/이그레스 차단 (기본 거부)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

7.2 세밀한 네트워크 정책

# API 서비스: 특정 소스에서만 인그레스 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-service-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-service
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # 인그레스 컨트롤러에서만 허용
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
          podSelector:
            matchLabels:
              app: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080
  egress:
    # 데이터베이스 접근 허용
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - protocol: TCP
          port: 5432
    # DNS 허용
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

7.3 Cilium 네트워크 정책

# Cilium L7 네트워크 정책 (HTTP 경로 기반)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-l7-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api-service
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/api/v1/products"
              - method: "POST"
                path: "/api/v1/orders"

8. 런타임 보안: Falco와 Tetragon

8.1 Falco 규칙

# Falco 커스텀 규칙
- rule: Detect Reverse Shell
  desc: 컨테이너에서 리버스 셸 연결 시도 탐지
  condition: >
    evt.type=connect and
    evt.dir=< and
    container and
    fd.typechar=4 and
    fd.rip != "127.0.0.1" and
    proc.name in (bash, sh, zsh, dash, csh)
  output: >
    리버스 셸 탐지 (user=%user.name command=%proc.cmdline
    connection=%fd.name container=%container.name
    image=%container.image.repository)
  priority: CRITICAL
  tags: [network, shell, mitre_execution]

- rule: Detect Cryptomining
  desc: 컨테이너에서 암호화폐 채굴 프로세스 탐지
  condition: >
    spawned_process and
    container and
    (proc.name in (xmrig, minerd, minergate, cpuminer) or
     proc.cmdline contains "stratum+tcp" or
     proc.cmdline contains "cryptonight")
  output: >
    암호화폐 채굴 탐지 (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: CRITICAL
  tags: [crypto, mining, mitre_resource_hijacking]

- rule: Sensitive File Access
  desc: 민감한 파일 접근 탐지
  condition: >
    open_read and
    container and
    (fd.name startswith /etc/shadow or
     fd.name startswith /etc/passwd or
     fd.name startswith /proc/self/environ or
     fd.name startswith /run/secrets)
  output: >
    민감 파일 접근 (user=%user.name file=%fd.name
    command=%proc.cmdline container=%container.name)
  priority: WARNING
  tags: [filesystem, mitre_credential_access]

8.2 Falco Helm 배포

# Falco 설치
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

helm install falco falcosecurity/falco \
  --namespace falco --create-namespace \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/xxx" \
  --set falcosidekick.config.slack.minimumpriority=warning \
  --set driver.kind=ebpf

8.3 Tetragon eBPF 정책

# Tetragon 정책: 프로세스 실행 모니터링
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: monitor-process-execution
spec:
  kprobes:
    - call: "security_bprm_check"
      syscall: false
      args:
        - index: 0
          type: "linux_binprm"
      selectors:
        - matchBinaries:
            - operator: "In"
              values:
                - "/bin/bash"
                - "/bin/sh"
                - "/usr/bin/curl"
                - "/usr/bin/wget"
          matchNamespaces:
            - namespace: Mnt
              operator: NotIn
              values:
                - "host_mnt_ns"

---
# Tetragon 정책: 네트워크 연결 모니터링
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: monitor-network-connections
spec:
  kprobes:
    - call: "tcp_connect"
      syscall: false
      args:
        - index: 0
          type: "sock"
      selectors:
        - matchArgs:
            - index: 0
              operator: "NotDAddr"
              values:
                - "127.0.0.1"
                - "10.0.0.0/8"

9. 시크릿 관리

9.1 External Secrets Operator

# SecretStore 설정 (AWS Secrets Manager)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa

---
# ExternalSecret 정의
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets
    kind: SecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
  data:
    - secretKey: username
      remoteRef:
        key: prod/database
        property: username
    - secretKey: password
      remoteRef:
        key: prod/database
        property: password

9.2 Vault CSI Provider

# Vault SecretProviderClass
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
  namespace: production
spec:
  provider: vault
  parameters:
    vaultAddress: "https://vault.example.com"
    roleName: "app-role"
    objects: |
      - objectName: "db-username"
        secretPath: "secret/data/prod/database"
        secretKey: "username"
      - objectName: "db-password"
        secretPath: "secret/data/prod/database"
        secretKey: "password"

---
# Pod에서 사용
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  serviceAccountName: app-sa
  containers:
    - name: app
      image: myapp:latest
      volumeMounts:
        - name: secrets
          mountPath: "/mnt/secrets"
          readOnly: true
  volumes:
    - name: secrets
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: vault-db-creds

10. SLSA 프레임워크

10.1 SLSA 레벨

SLSA Level 1 - Provenance:
- 빌드 프로세스가 문서화됨
- 자동화된 빌드

SLSA Level 2 - Build Service:
- 호스팅된 빌드 서비스 사용
- 서명된 provenance 생성
- 버전 관리 소스

SLSA Level 3 - Build Integrity:
- 격리된 빌드 환경
- 변조 방지 provenance
- 소스 무결성 보장

SLSA Level 4 - Dependencies:
- 모든 종속성에 대한 재귀적 보증
- 재현 가능한 빌드
- (현재 이론적 단계)

10.2 SLSA Provenance 생성

# GitHub Actions에서 SLSA Provenance 생성
name: SLSA Build

on:
  push:
    tags: ['v*']

permissions:
  contents: read
  packages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      digest: "${{ steps.build.outputs.digest }}"
    steps:
      - uses: actions/checkout@v4

      - name: Build and push
        id: build
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: "ghcr.io/${{ github.repository }}:${{ github.ref_name }}"

  provenance:
    needs: build
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
    with:
      image: "ghcr.io/${{ github.repository }}"
      digest: "${{ needs.build.outputs.digest }}"
    secrets:
      registry-username: "${{ github.actor }}"
      registry-password: "${{ secrets.GITHUB_TOKEN }}"

11. CI/CD 보안 파이프라인

11.1 전체 파이프라인 구성

보안 CI/CD 파이프라인:
┌─────┐   ┌──────┐   ┌──────┐   ┌──────┐   ┌──────┐   ┌──────┐
│Build│ → │ Scan │ → │ Sign │ → │Attest│ → │Verify│ → │Deploy│
└─────┘   └──────┘   └──────┘   └──────┘   └──────┘   └──────┘
  │          │          │          │          │          │
  │          │          │          │          │          │
 빌드     취약점     cosign     SBOM      Kyverno   서명 검증
 이미지    스캔      서명      attestation  정책     후 배포
          Trivy               SLSA         검증
          Grype              provenance

11.2 CIS 벤치마크와 Kubescape

# kube-bench: CIS Kubernetes Benchmark 실행
kube-bench run --targets master,node

# Kubescape: NSA, MITRE, CIS 프레임워크 스캔
kubescape scan framework cis-v1.23 --exclude-namespaces kube-system

# 특정 컨트롤 스캔
kubescape scan control C-0002 --namespace production

# Kubescape YAML 스캔
kubescape scan .

12. 퀴즈

다음 퀴즈로 이 글에서 다룬 컨테이너 보안에 대한 이해를 점검해 보자.

Q1. 이미지 보안

질문: Distroless 이미지와 Alpine 이미지의 차이점은 무엇인가?

정답: Distroless 이미지는 셸(bash/sh)과 패키지 매니저가 없는 최소 이미지다. Alpine은 musl libc 기반의 경량 Linux 배포판으로, 셸과 패키지 매니저(apk)를 포함한다.

Distroless는 공격 표면이 더 작지만 디버깅이 어렵다. Alpine은 디버깅은 가능하지만 musl libc 호환성 문제가 발생할 수 있다. 프로덕션에서는 Distroless나 Chainguard 이미지를 권장하고, 디버깅이 필요한 경우 debug 태그 변종을 사용한다.

Q2. Sigstore 키리스 서명

질문: cosign 키리스 서명이 기존 키 기반 서명보다 CI/CD에서 선호되는 이유는 무엇인가?

정답: 키리스 서명은 OIDC 토큰(예: GitHub Actions의 워크플로 ID 토큰)을 사용하므로, 장기 비밀키를 관리하고 보호할 필요가 없다.

Fulcio가 OIDC 토큰을 단기 인증서로 변환하고, Rekor 투명성 로그에 서명이 기록된다. 키 순환, 키 유출, 키 보관 문제가 없어 CI/CD 환경에서 운영 부담이 크게 줄어든다.

Q3. Pod Security Standards

질문: Pod Security Standards의 Restricted 수준에서 반드시 충족해야 하는 보안 요구사항 3가지를 나열하시오.

정답:

  1. 비 root 사용자로 실행 (runAsNonRoot: true)
  2. 모든 Linux capability 삭제 (capabilities.drop: ALL)
  3. 권한 상승 차단 (allowPrivilegeEscalation: false)

추가적으로, seccomp 프로필 적용(RuntimeDefault 또는 Localhost), 읽기 전용 루트 파일시스템(readOnlyRootFilesystem: true)도 Restricted 수준의 권장 사항이다.

Q4. 런타임 보안

질문: Falco와 Tetragon의 아키텍처 차이를 설명하시오.

정답:

  • Falco: 커널 모듈 또는 eBPF 드라이버로 시스템 콜을 캡처하고, 사용자 공간의 규칙 엔진에서 이벤트를 분석한다. 탐지(detection) 중심으로, 규칙에 매칭되면 알림을 생성한다.
  • Tetragon: 순수 eBPF 기반으로 커널 수준에서 직접 정책을 실행한다. 탐지뿐만 아니라 차단(enforcement)도 가능하며, 프로세스 실행, 네트워크 연결, 파일 접근을 커널에서 직접 제어할 수 있다.

Q5. SLSA 프레임워크

질문: SLSA Level 3에서 요구하는 핵심 보안 속성 2가지는 무엇인가?

정답:

  1. 격리된 빌드 환경: 빌드가 다른 빌드와 격리된 환경에서 실행되어, 빌드 간 오염을 방지한다.
  2. 변조 방지(tamper-resistant) provenance: 빌드 시스템이 생성한 provenance를 빌드 자체가 수정할 수 없다. provenance는 빌드 서비스에 의해 서명되며, 소스와 빌드 프로세스의 무결성을 보장한다.

13. 참고 자료

  1. Trivy - https://aquasecurity.github.io/trivy/
  2. Sigstore (cosign, Rekor, Fulcio) - https://www.sigstore.dev/
  3. SLSA Framework - https://slsa.dev/
  4. Falco - https://falco.org/docs/
  5. Tetragon - https://tetragon.io/docs/
  6. Grype - https://github.com/anchore/grype
  7. syft - https://github.com/anchore/syft
  8. CycloneDX - https://cyclonedx.org/
  9. SPDX - https://spdx.dev/
  10. Kyverno - https://kyverno.io/docs/
  11. External Secrets Operator - https://external-secrets.io/
  12. Kubescape - https://kubescape.io/
  13. Chainguard Images - https://www.chainguard.dev/chainguard-images
  14. CIS Benchmarks - https://www.cisecurity.org/benchmark/kubernetes

이 글에서는 컨테이너 보안의 전체 라이프사이클을 다루었다. 이미지 보안(최소 베이스 이미지, 스캐닝), 서명 및 검증(Sigstore/cosign), SBOM, Pod Security Standards, 네트워크 정책, 런타임 보안(Falco/Tetragon), 시크릿 관리, SLSA 프레임워크, CI/CD 보안 파이프라인까지 포괄적으로 살펴보았다. 다층 방어(Defense in Depth) 전략을 채택하고, 보안을 개발 초기부터 CI/CD 파이프라인에 통합하는 것이 핵심이다.

Container Security & Supply Chain Complete Guide 2025: Image Scanning, Sigstore, SBOM, Runtime Security

Table of Contents

1. Container Threat Model: Understanding the Attack Surface

Container environments have a unique threat surface distinct from traditional VM-based infrastructure. A multi-layered security strategy is needed covering image vulnerabilities, misconfigurations, runtime attacks, and supply chain threats.

1.1 Container Attack Surface

Container Security Layers:
┌─────────────────────────────────────────────────┐
│ Layer 6: Supply Chain                            │
│  - Base image poisoning, dependency tampering     │
├─────────────────────────────────────────────────┤
│ Layer 5: Orchestration (Kubernetes)              │
│  - Missing RBAC, exposed API server, unencrypted │
├─────────────────────────────────────────────────┤
│ Layer 4: Network                                 │
│  - Missing network policies, no service mesh     │
├─────────────────────────────────────────────────┤
│ Layer 3: Runtime                                 │
│  - Container escape, privilege escalation, mining │
├─────────────────────────────────────────────────┤
│ Layer 2: Image                                   │
│  - CVEs, hardcoded secrets, excessive packages   │
├─────────────────────────────────────────────────┤
│ Layer 1: Host OS / Container Runtime             │
│  - Kernel vulns, Docker socket exposure, runc    │
└─────────────────────────────────────────────────┘

1.2 Key Attack Vectors

1. Image Vulnerability Exploitation:
   Attacker -> Exploit known CVEs in public images -> Container compromise

2. Supply Chain Attack:
   Attacker -> Inject malware in base image/library -> Downstream poisoning

3. Runtime Attack:
   Attacker -> Exploit vulnerable app -> Container escape -> Host access

4. Misconfiguration Exploitation:
   Attacker -> Excessive permissions/missing policies -> Lateral movement

2. Image Security: Building Secure Container Images

2.1 Choosing Minimal Base Images

# BAD: Full image (200MB+, potentially hundreds of CVEs)
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

# GOOD: Distroless image (minimal binaries only)
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

FROM gcr.io/distroless/python3-debian12
COPY --from=builder /app /app
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
WORKDIR /app
CMD ["app.py"]

2.2 Chainguard Images

# Chainguard Images: rebuilt daily, zero-CVE goal
FROM cgr.dev/chainguard/python:latest-dev AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

FROM cgr.dev/chainguard/python:latest
COPY --from=builder /app /app
COPY --from=builder /home/nonroot/.local/lib/python3.12/site-packages /home/nonroot/.local/lib/python3.12/site-packages
WORKDIR /app
ENTRYPOINT ["python", "app.py"]

2.3 Multi-Stage Builds and Security

# Go app multi-stage build
FROM golang:1.22-alpine AS builder

# Security: install build-only tools
RUN apk add --no-cache git ca-certificates

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# Security: static binary (CGO disabled)
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-w -s" -o /app/server ./cmd/server

# Final image: scratch (completely empty)
FROM scratch

# Security: non-root user
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /app/server /server

USER 65534:65534
ENTRYPOINT ["/server"]

2.4 Dockerfile Security Best Practices

# Security-hardened Dockerfile
FROM node:20-slim AS builder

# Security: create non-root user
RUN groupadd -r appuser && useradd -r -g appuser -d /app appuser

WORKDIR /app

# Security: copy package.json first (layer caching)
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production

COPY . .
RUN yarn build

# Production image
FROM node:20-slim

# Security: non-root user
RUN groupadd -r appuser && useradd -r -g appuser -d /app appuser

WORKDIR /app

# Security: copy only necessary files
COPY --from=builder --chown=appuser:appuser /app/dist ./dist
COPY --from=builder --chown=appuser:appuser /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appuser /app/package.json ./

# Security: run as non-root
USER appuser

# Security: read-only filesystem compatible
ENV NODE_ENV=production

EXPOSE 3000
CMD ["node", "dist/index.js"]

3. Image Scanning: Vulnerability Detection

3.1 Scanning Tool Comparison

ToolLicenseFeaturesCI/CD IntegrationSBOM Generation
TrivyApache 2.0Comprehensive scanner (image/IaC/secrets)ExcellentSupported
GrypeApache 2.0Fast vulnerability scanExcellentsyft integration
Docker ScoutPremiumDocker Desktop integrationDocker HubSupported
Snyk ContainerPremiumExcellent fix recommendationsExcellentSupported

3.2 Using Trivy

# Image scan
trivy image nginx:1.25

# Filter by severity
trivy image --severity HIGH,CRITICAL nginx:1.25

# SARIF output (GitHub Security integration)
trivy image --format sarif --output trivy-results.sarif nginx:1.25

# Secret scan
trivy image --scanners secret myapp:latest

# IaC scan (Dockerfile, Kubernetes YAML)
trivy config .

# Generate SBOM
trivy image --format cyclonedx --output sbom.json nginx:1.25
# .trivyignore - ignore specific CVEs
# Document reason in comments
# CVE-2023-xxxx: Not applicable in our environment (feature not used)
CVE-2023-xxxx

3.3 Grype and syft

# Generate SBOM with syft
syft packages nginx:1.25 -o cyclonedx-json > sbom.json

# Scan SBOM with Grype
grype sbom:./sbom.json

# Direct image scan with Grype
grype nginx:1.25

# Set severity threshold (for CI/CD)
grype nginx:1.25 --fail-on critical

3.4 CI/CD Integrated Scanning

# .github/workflows/image-scan.yml
name: Container Image Scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t myapp:ci .

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:ci'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

      - name: Run Grype scan
        uses: anchore/scan-action@v4
        with:
          image: 'myapp:ci'
          fail-build: true
          severity-cutoff: 'high'

4. Image Signing: Sigstore and cosign

4.1 Sigstore Ecosystem

Sigstore Components:
┌──────────────────────────────────────────────────┐
│  cosign                                           │
│  - Sign and verify container images/artifacts     │
│  - Keyless signing support (OIDC-based)           │
├──────────────────────────────────────────────────┤
│  Rekor                                            │
│  - Immutable transparency log                     │
│  - All signatures stored publicly                 │
├──────────────────────────────────────────────────┤
│  Fulcio                                           │
│  - Short-lived certificate issuance (CA)          │
│  - Converts OIDC tokens to X.509 certificates     │
└──────────────────────────────────────────────────┘

4.2 Signing Images with cosign

# Generate key pair
cosign generate-key-pair

# Sign image
cosign sign --key cosign.key myregistry.com/myapp:v1.0

# Verify image signature
cosign verify --key cosign.pub myregistry.com/myapp:v1.0

# Keyless signing (recommended for CI/CD)
# Auto-authenticates with OIDC token (GitHub Actions, GitLab CI)
cosign sign myregistry.com/myapp:v1.0

# Keyless verification
cosign verify \
  --certificate-identity "https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  myregistry.com/myapp:v1.0

4.3 Keyless Signing in GitHub Actions

# .github/workflows/sign-image.yml
name: Build, Sign, and Push

on:
  push:
    tags: ['v*']

permissions:
  contents: read
  packages: write
  id-token: write  # Required for keyless signing

jobs:
  build-sign-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: sigstore/cosign-installer@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: "${{ github.actor }}"
          password: "${{ secrets.GITHUB_TOKEN }}"

      - name: Build and push
        id: build
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: "ghcr.io/${{ github.repository }}:${{ github.ref_name }}"

      - name: Sign the image (keyless)
        run: |
          cosign sign --yes \
            "ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}"

4.4 Kubernetes Admission Controller for Signature Verification

# Kyverno policy: allow only signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  background: false
  rules:
    - name: verify-cosign-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/myorg/*/.github/workflows/*@refs/heads/main"
                    issuer: "https://token.actions.githubusercontent.com"
                    rekor:
                      url: "https://rekor.sigstore.dev"

5. SBOM (Software Bill of Materials)

5.1 SBOM Format Comparison

CycloneDX:
- OWASP project
- Security-focused (vulnerabilities, licenses)
- JSON, XML formats
- VEX (Vulnerability Exploitability eXchange) support

SPDX:
- Linux Foundation project
- License compliance focused
- JSON, RDF, Tag-Value formats
- ISO/IEC 5962:2021 international standard

5.2 Generating SBOM with syft

# Generate SBOM in CycloneDX format
syft packages myapp:latest -o cyclonedx-json > sbom-cyclonedx.json

# Generate SBOM in SPDX format
syft packages myapp:latest -o spdx-json > sbom-spdx.json

# Scan directory
syft dir:./my-project -o cyclonedx-json > project-sbom.json

# Attach SBOM to image (cosign + SBOM)
cosign attach sbom --sbom sbom-cyclonedx.json myregistry.com/myapp:v1.0

# Create SBOM attestation
cosign attest --predicate sbom-cyclonedx.json \
  --type cyclonedx \
  myregistry.com/myapp:v1.0

5.3 SBOM-Based Vulnerability Management

# Scan vulnerabilities from SBOM
grype sbom:./sbom-cyclonedx.json

# Query specific packages
syft packages myapp:latest | grep log4j

# License analysis
syft packages myapp:latest -o json | \
  jq '.artifacts[].licenses[].value' | sort | uniq -c | sort -rn

6. Pod Security Standards

6.1 Security Level Comparison

Privileged:
- No restrictions
- Known privilege escalation allowed
- For system daemons, CNI plugins

Baseline:
- Prevents known privilege escalation
- Blocks hostNetwork, hostPID, hostIPC
- Blocks privileged containers
- Suitable for most workloads

Restricted:
- Least privilege principle applied
- Non-root enforced
- All capabilities dropped
- seccomp profile required
- For security-critical workloads

6.2 Pod Security Admission Configuration

# Apply Pod Security Standards to Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # Enforce Restricted level
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    # Warn on Restricted violations
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
    # Audit Restricted violations
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest

6.3 Security-Hardened Pod Spec

# Pod spec satisfying Restricted level
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      # Security: disable auto-mount of SA token
      automountServiceAccountToken: false

      securityContext:
        # Security: run as non-root group
        runAsGroup: 1000
        fsGroup: 1000
        # Security: apply Seccomp profile
        seccompProfile:
          type: RuntimeDefault

      containers:
        - name: app
          image: ghcr.io/myorg/secure-app:v1.0
          ports:
            - containerPort: 8080
              protocol: TCP

          securityContext:
            # Security: run as non-root
            runAsNonRoot: true
            runAsUser: 1000
            # Security: read-only root filesystem
            readOnlyRootFilesystem: true
            # Security: block privilege escalation
            allowPrivilegeEscalation: false
            # Security: drop all capabilities
            capabilities:
              drop:
                - ALL

          volumeMounts:
            - name: tmp
              mountPath: /tmp
            - name: cache
              mountPath: /app/cache

          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi

      volumes:
        - name: tmp
          emptyDir:
            sizeLimit: 100Mi
        - name: cache
          emptyDir:
            sizeLimit: 200Mi

7. Network Policies

7.1 Default Deny Policy

# Block all ingress/egress (default deny)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

7.2 Fine-Grained Network Policies

# API service: allow ingress only from specific sources
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-service-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-service
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # Allow only from ingress controller
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
          podSelector:
            matchLabels:
              app: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080
  egress:
    # Allow database access
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - protocol: TCP
          port: 5432
    # Allow DNS
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

7.3 Cilium Network Policy

# Cilium L7 Network Policy (HTTP path-based)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-l7-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api-service
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/api/v1/products"
              - method: "POST"
                path: "/api/v1/orders"

8. Runtime Security: Falco and Tetragon

8.1 Falco Rules

# Falco custom rules
- rule: Detect Reverse Shell
  desc: Detect reverse shell connection attempts in containers
  condition: >
    evt.type=connect and
    evt.dir=< and
    container and
    fd.typechar=4 and
    fd.rip != "127.0.0.1" and
    proc.name in (bash, sh, zsh, dash, csh)
  output: >
    Reverse shell detected (user=%user.name command=%proc.cmdline
    connection=%fd.name container=%container.name
    image=%container.image.repository)
  priority: CRITICAL
  tags: [network, shell, mitre_execution]

- rule: Detect Cryptomining
  desc: Detect cryptocurrency mining processes in containers
  condition: >
    spawned_process and
    container and
    (proc.name in (xmrig, minerd, minergate, cpuminer) or
     proc.cmdline contains "stratum+tcp" or
     proc.cmdline contains "cryptonight")
  output: >
    Cryptomining detected (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: CRITICAL
  tags: [crypto, mining, mitre_resource_hijacking]

- rule: Sensitive File Access
  desc: Detect access to sensitive files
  condition: >
    open_read and
    container and
    (fd.name startswith /etc/shadow or
     fd.name startswith /etc/passwd or
     fd.name startswith /proc/self/environ or
     fd.name startswith /run/secrets)
  output: >
    Sensitive file access (user=%user.name file=%fd.name
    command=%proc.cmdline container=%container.name)
  priority: WARNING
  tags: [filesystem, mitre_credential_access]

8.2 Falco Helm Deployment

# Install Falco
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

helm install falco falcosecurity/falco \
  --namespace falco --create-namespace \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/xxx" \
  --set falcosidekick.config.slack.minimumpriority=warning \
  --set driver.kind=ebpf

8.3 Tetragon eBPF Policies

# Tetragon policy: process execution monitoring
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: monitor-process-execution
spec:
  kprobes:
    - call: "security_bprm_check"
      syscall: false
      args:
        - index: 0
          type: "linux_binprm"
      selectors:
        - matchBinaries:
            - operator: "In"
              values:
                - "/bin/bash"
                - "/bin/sh"
                - "/usr/bin/curl"
                - "/usr/bin/wget"
          matchNamespaces:
            - namespace: Mnt
              operator: NotIn
              values:
                - "host_mnt_ns"

---
# Tetragon policy: network connection monitoring
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: monitor-network-connections
spec:
  kprobes:
    - call: "tcp_connect"
      syscall: false
      args:
        - index: 0
          type: "sock"
      selectors:
        - matchArgs:
            - index: 0
              operator: "NotDAddr"
              values:
                - "127.0.0.1"
                - "10.0.0.0/8"

9. Secrets Management

9.1 External Secrets Operator

# SecretStore configuration (AWS Secrets Manager)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa

---
# ExternalSecret definition
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets
    kind: SecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
  data:
    - secretKey: username
      remoteRef:
        key: prod/database
        property: username
    - secretKey: password
      remoteRef:
        key: prod/database
        property: password

9.2 Vault CSI Provider

# Vault SecretProviderClass
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
  namespace: production
spec:
  provider: vault
  parameters:
    vaultAddress: "https://vault.example.com"
    roleName: "app-role"
    objects: |
      - objectName: "db-username"
        secretPath: "secret/data/prod/database"
        secretKey: "username"
      - objectName: "db-password"
        secretPath: "secret/data/prod/database"
        secretKey: "password"

---
# Using in Pod
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  serviceAccountName: app-sa
  containers:
    - name: app
      image: myapp:latest
      volumeMounts:
        - name: secrets
          mountPath: "/mnt/secrets"
          readOnly: true
  volumes:
    - name: secrets
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: vault-db-creds

10. SLSA Framework

10.1 SLSA Levels

SLSA Level 1 - Provenance:
- Build process documented
- Automated build

SLSA Level 2 - Build Service:
- Hosted build service
- Signed provenance generated
- Version-controlled source

SLSA Level 3 - Build Integrity:
- Isolated build environment
- Tamper-resistant provenance
- Source integrity guaranteed

SLSA Level 4 - Dependencies:
- Recursive assurance for all dependencies
- Reproducible builds
- (Currently theoretical)

10.2 Generating SLSA Provenance

# SLSA Provenance generation in GitHub Actions
name: SLSA Build

on:
  push:
    tags: ['v*']

permissions:
  contents: read
  packages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      digest: "${{ steps.build.outputs.digest }}"
    steps:
      - uses: actions/checkout@v4

      - name: Build and push
        id: build
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: "ghcr.io/${{ github.repository }}:${{ github.ref_name }}"

  provenance:
    needs: build
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
    with:
      image: "ghcr.io/${{ github.repository }}"
      digest: "${{ needs.build.outputs.digest }}"
    secrets:
      registry-username: "${{ github.actor }}"
      registry-password: "${{ secrets.GITHUB_TOKEN }}"

11. CI/CD Security Pipeline

11.1 Complete Pipeline Architecture

Security CI/CD Pipeline:
┌─────┐   ┌──────┐   ┌──────┐   ┌──────┐   ┌──────┐   ┌──────┐
│Build│ -> │ Scan │ -> │ Sign │ -> │Attest│ -> │Verify│ -> │Deploy│
└─────┘   └──────┘   └──────┘   └──────┘   └──────┘   └──────┘
  |          |          |          |          |          |
 Build     Vuln       cosign     SBOM      Kyverno   Signature
 image     scan       signing   attestation  policy   verified
          Trivy               SLSA         check      deploy
          Grype              provenance

11.2 CIS Benchmarks and Kubescape

# kube-bench: run CIS Kubernetes Benchmark
kube-bench run --targets master,node

# Kubescape: NSA, MITRE, CIS framework scan
kubescape scan framework cis-v1.23 --exclude-namespaces kube-system

# Scan specific control
kubescape scan control C-0002 --namespace production

# Kubescape YAML scan
kubescape scan .

12. Quiz

Test your understanding of container security covered in this article.

Q1. Image Security

Question: What is the difference between Distroless images and Alpine images?

Answer: Distroless images are minimal images without a shell (bash/sh) or package manager. Alpine is a lightweight Linux distribution based on musl libc that includes a shell and package manager (apk).

Distroless has a smaller attack surface but is harder to debug. Alpine allows debugging but may have musl libc compatibility issues. For production, Distroless or Chainguard images are recommended, using debug-tagged variants when debugging is needed.

Q2. Sigstore Keyless Signing

Question: Why is cosign keyless signing preferred over key-based signing in CI/CD?

Answer: Keyless signing uses OIDC tokens (e.g., GitHub Actions workflow ID tokens), eliminating the need to manage and protect long-lived private keys.

Fulcio converts OIDC tokens to short-lived certificates, and signatures are recorded in the Rekor transparency log. This eliminates key rotation, key leakage, and key storage concerns, significantly reducing operational burden in CI/CD environments.

Q3. Pod Security Standards

Question: List three security requirements that must be met at the Restricted level of Pod Security Standards.

Answer:

  1. Run as non-root user (runAsNonRoot: true)
  2. Drop all Linux capabilities (capabilities.drop: ALL)
  3. Block privilege escalation (allowPrivilegeEscalation: false)

Additionally, applying a seccomp profile (RuntimeDefault or Localhost) and read-only root filesystem (readOnlyRootFilesystem: true) are also recommended at the Restricted level.

Q4. Runtime Security

Question: Explain the architectural differences between Falco and Tetragon.

Answer:

  • Falco: Captures system calls via a kernel module or eBPF driver, analyzing events in a userspace rules engine. Detection-focused, generating alerts when rules match.
  • Tetragon: Pure eBPF-based, executing policies directly at the kernel level. Supports both detection and enforcement, directly controlling process execution, network connections, and file access at the kernel level.

Q5. SLSA Framework

Question: What are the two core security properties required at SLSA Level 3?

Answer:

  1. Isolated build environment: Builds run in an environment isolated from other builds, preventing cross-contamination.
  2. Tamper-resistant provenance: The provenance generated by the build system cannot be modified by the build itself. Provenance is signed by the build service, ensuring the integrity of both source and build process.

13. References

  1. Trivy - https://aquasecurity.github.io/trivy/
  2. Sigstore (cosign, Rekor, Fulcio) - https://www.sigstore.dev/
  3. SLSA Framework - https://slsa.dev/
  4. Falco - https://falco.org/docs/
  5. Tetragon - https://tetragon.io/docs/
  6. Grype - https://github.com/anchore/grype
  7. syft - https://github.com/anchore/syft
  8. CycloneDX - https://cyclonedx.org/
  9. SPDX - https://spdx.dev/
  10. Kyverno - https://kyverno.io/docs/
  11. External Secrets Operator - https://external-secrets.io/
  12. Kubescape - https://kubescape.io/
  13. Chainguard Images - https://www.chainguard.dev/chainguard-images
  14. CIS Benchmarks - https://www.cisecurity.org/benchmark/kubernetes

This article covered the entire lifecycle of container security: image security (minimal base images, scanning), signing and verification (Sigstore/cosign), SBOM, Pod Security Standards, Network Policies, runtime security (Falco/Tetragon), secrets management, the SLSA framework, and CI/CD security pipelines. Adopting a Defense in Depth strategy and integrating security from development through CI/CD pipelines is key.