Skip to content

Security: jawsbaek/scp-gitops-sample

Security

docs/SECURITY.md

보안 및 규정 준수 가이드

SCP GitOps Infrastructure 보안 설계 및 운영 지침


보안 통제 구현 상태 대시보드

stateDiagram-v2
    direction LR

    state "자격 증명 관리" as CRED {
        [*] --> 환경변수주입: CI/CD
        환경변수주입 --> Vault동적발급: prod
        Vault동적발급 --> 자동만료: TTL 1h
    }

    state "정책 게이트" as POLICY {
        [*] --> Conftest: terraform plan
        Conftest --> OPA: 정책 평가
        OPA --> Kyverno: K8s 런타임
    }

    state "감사·로깅" as AUDIT {
        [*] --> CloudTrail: API 이벤트
        CloudTrail --> OpenSearch: 수집
        OpenSearch --> 알람: 이상 탐지
    }

    state "네트워크 격리" as NET {
        [*] --> Firewall: L3/L4
        Firewall --> SecurityGroup: 서비스별
        SecurityGroup --> NetworkPolicy: Pod 레벨
    }

    CRED --> POLICY : 검증
    POLICY --> AUDIT : 기록
    AUDIT --> NET : 보호
Loading

통제 구현 현황 요약

보안 영역 통제 항목 구현 상태 자동화 검증
자격 증명 환경변수 주입 (CI/CD) 완료 GitHub Actions secret scan
자격 증명 Vault 동적 자격 증명 (prod) 완료 vault token lookup
자격 증명 코드 하드코딩 탐지 완료 gitleaks / trufflehog
상태 파일 KMS 암호화 완료 aws s3api get-bucket-encryption
상태 파일 버전 관리 완료 aws s3api get-bucket-versioning
정책 OPA/Conftest (Terraform) 완료 make policy-test
정책 Kyverno (K8s 런타임) 완료 kubectl get clusterpolicy
네트워크 SCP Firewall 규칙 완료 make security-scan
네트워크 Network Policy 완료 kubectl get netpol -A
감사 CloudTrail 활성화 완료 SCP 콘솔 확인
감사 K8s 감사 로그 완료 kubectl get configmap -n kube-system
제로 트러스트 mTLS (Istio/Envoy) 진행중 istioctl verify-install
제로 트러스트 SPIFFE/SPIRE 워크로드 ID 계획

목차

  1. Sovereign Cloud 대응 전략
  2. 데이터 주권 정책
  3. 자격 증명 관리
  4. 상태 파일 보안
  5. Policy-as-Code 보안 정책
  6. 네트워크 보안
  7. 제로 트러스트 아키텍처
  8. 시크릿 로테이션 절차
  9. 보안 기준선 위반 탐지
  10. 감사 및 로깅
  11. 인시던트 대응

1. Sovereign Cloud 대응 전략

SCP Sovereign Cloud 개요

Samsung Cloud Platform은 국내 데이터 주권을 보장하는 Sovereign Cloud 아키텍처를 제공합니다. 본 GitOps 플랫폼은 이에 맞춰 설계되었습니다.

주권 보장 원칙

원칙 구현 방법
데이터 국내 상주 모든 리소스를 kr-west1 (서울) 또는 kr-east1 (부산) 리전에 배치
운영 제어권 외부 클라우드 서비스에 대한 의존성 최소화
감사 가능성 모든 작업 로그의 국내 보관 및 접근 제어
암호화 키 관리 SCP KMS를 통한 자체 키 관리 (BYOK 지원)

규정 준수 프레임워크

SCP Sovereign Cloud 규정 준수 체계:

국내 관련:
├── 개인정보 보호법 (PIPA)
├── 정보통신망법
├── 전자금융감독규정 (금융 워크로드)
└── 클라우드컴퓨팅법

국제 표준:
├── ISO 27001 (정보보안 관리)
├── ISO 27017 (클라우드 보안)
├── SOC 2 Type II
└── CSAP (클라우드 보안 인증제)

2. 데이터 주권 정책 (Geopatriation)

리전 제한 OPA 정책

# policies/opa/terraform/data_sovereignty.rego
package terraform.data_sovereignty

import future.keywords.if
import future.keywords.in

# 허용된 SCP 리전 (국내)
allowed_regions := {"kr-west1", "kr-east1"}

# 리전 외 리소스 배포 금지
deny[msg] if {
  resource := input.planned_values.root_module.resources[_]

  # 리전 속성을 가진 SCP 리소스
  startswith(resource.type, "scp_")

  # 허용 리전 체크
  not resource.values.region in allowed_regions

  msg := sprintf(
    "리소스 '%s'가 허용되지 않은 리전 '%s'에 배포됩니다. 허용 리전: %v",
    [resource.address, resource.values.region, allowed_regions]
  )
}

자동화 검증 방법

# Conftest로 terraform plan 검증
cd environments/prod
tofu plan -out=tfplan.binary
tofu show -json tfplan.binary > tfplan.json
conftest test tfplan.json --policy ../../policies/conftest/terraform/

# OPA 단위 테스트 실행
opa test policies/opa/ -v

# 통합 검증 (Makefile)
/usr/bin/make policy-test

Kyverno 노드 선택기 정책

# policies/kyverno/enforce-region-nodeaffinity.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: enforce-kr-region
  annotations:
    policies.kyverno.io/description: |
      모든 워크로드를 국내 노드에만 배포합니다.
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-kr-node-affinity
      match:
        any:
          - resources:
              kinds: [Deployment, StatefulSet, DaemonSet]
      mutate:
        patchStrategicMerge:
          spec:
            template:
              spec:
                affinity:
                  nodeAffinity:
                    requiredDuringSchedulingIgnoredDuringExecution:
                      nodeSelectorTerms:
                        - matchExpressions:
                            - key: topology.kubernetes.io/region
                              operator: In
                              values: ["kr-west1", "kr-east1"]

3. 자격 증명 관리

자격 증명 우선순위 체계

우선순위 (높음 → 낮음):
1. 환경 변수 (CI/CD 파이프라인 권장)
   SCP_ACCESS_KEY, SCP_SECRET_KEY, SCP_PROJECT_ID

2. ~/.scpconf 파일 (로컬 개발 환경)
   [default] 프로파일

3. IAM 인스턴스 프로파일 (SCP VM에서 실행 시)
   자동 감지

4. SCP Vault 동적 자격 증명 (권장 - 운영 환경)
   자동 만료되는 단기 토큰

~/.scpconf 보안 설정

# ~/.scpconf - 파일 권한: chmod 600 ~/.scpconf
[default]
auth_method = iam
access_key = <액세스 키>
secret_key = <시크릿 키>
project_id = <프로젝트 ID>
region = kr-west1

[prod]
auth_method = vault
vault_addr = https://vault.internal.scp.company.com
vault_path = scp/creds/terraform-prod
# Vault 토큰은 환경변수로 주입: VAULT_TOKEN

GitHub Actions 시크릿 설정

# GitHub 저장소 시크릿 (Settings → Secrets)
# 환경별로 분리하여 설정:

# Repository secrets (모든 환경 공통):
# SCP_PROJECT_ID

# Environment secrets (환경별):
# dev:
#   SCP_ACCESS_KEY_DEV
#   SCP_SECRET_KEY_DEV
#
# staging:
#   SCP_ACCESS_KEY_STAGING
#   SCP_SECRET_KEY_STAGING
#
# prod:
#   SCP_ACCESS_KEY_PROD
#   SCP_SECRET_KEY_PROD
#   ARGOCD_AUTH_TOKEN_PROD

SCP Vault 동적 자격 증명 (운영 환경 권장)

# SCP Vault를 통한 동적 자격 증명 설정
# environments/prod/provider.tf
provider "vault" {
  address = var.vault_addr
}

data "vault_generic_secret" "scp_creds" {
  path = "scp/creds/terraform-prod"
}

provider "scp" {
  access_key = data.vault_generic_secret.scp_creds.data["access_key"]
  secret_key = data.vault_generic_secret.scp_creds.data["secret_key"]
  project_id = var.scp_project_id
  region     = var.scp_region
}

자동화 검증 방법

# 코드에 하드코딩된 시크릿 탐지
gitleaks detect --source . --verbose

# trufflehog로 Git 히스토리 전체 스캔
trufflehog git file://. --only-verified

# GitHub Actions에서 자동 실행 (PR마다)
# .github/workflows/security-scan.yml 참조
/usr/bin/make security-scan

4. 상태 파일 보안

암호화 설정

# 상태 파일 버킷 암호화 설정 (Terraform)
resource "scp_object_storage_bucket" "tfstate" {
  name   = "scp-gitops-tfstate-${var.environment}"
  region = "kr-west1"

  # 서버 측 암호화 (SCP KMS 사용)
  server_side_encryption {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "aws:kms"  # SCP KMS 호환
        kms_master_key_id = scp_kms_key.tfstate.key_id
      }
    }
  }

  # 버전 관리 (실수로 삭제된 상태 복구)
  versioning {
    enabled = true
  }

  # 공개 접근 차단
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true

  tags = {
    Environment = var.environment
    DataClass   = "CONFIDENTIAL"
    ManagedBy   = "terraform"
  }
}

resource "scp_kms_key" "tfstate" {
  description             = "Terraform 상태 파일 암호화 키 - ${var.environment}"
  deletion_window_in_days = 30
  enable_key_rotation     = true

  tags = {
    Environment = var.environment
    Purpose     = "TerraformStateEncryption"
  }
}

상태 파일 접근 제어

# 상태 버킷 접근 정책
resource "scp_object_storage_bucket_policy" "tfstate" {
  bucket = scp_object_storage_bucket.tfstate.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "DenyUnencryptedObjectUploads"
        Effect = "Deny"
        Principal = "*"
        Action    = "s3:PutObject"
        Resource  = "${scp_object_storage_bucket.tfstate.arn}/*"
        Condition = {
          StringNotEquals = {
            "s3:x-amz-server-side-encryption" = "aws:kms"
          }
        }
      },
      {
        Sid    = "AllowTerraformAccess"
        Effect = "Allow"
        Principal = {
          AWS = [
            "arn:scp:iam::${var.account_id}:role/terraform-${var.environment}"
          ]
        }
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:DeleteObject",
          "s3:ListBucket"
        ]
        Resource = [
          scp_object_storage_bucket.tfstate.arn,
          "${scp_object_storage_bucket.tfstate.arn}/*"
        ]
      }
    ]
  })
}

자동화 검증 방법

# 암호화 설정 확인
aws s3api get-bucket-encryption \
  --bucket scp-gitops-tfstate-prod \
  --endpoint-url https://objectstorage.kr-west1.scp.samsungsds.com

# 버전 관리 확인
aws s3api get-bucket-versioning \
  --bucket scp-gitops-tfstate-prod \
  --endpoint-url https://objectstorage.kr-west1.scp.samsungsds.com

# 공개 접근 차단 확인
aws s3api get-public-access-block \
  --bucket scp-gitops-tfstate-prod \
  --endpoint-url https://objectstorage.kr-west1.scp.samsungsds.com

5. Policy-as-Code 보안 정책

보안 정책 목록

Terraform 플랜 정책 (OPA/Conftest)

정책 설명 위반 시
required_tags 필수 태그 강제 PR 차단
allowed_regions 국내 리전만 허용 PR 차단
encryption_required 저장 데이터 암호화 필수 PR 차단
no_public_access 공개 접근 금지 PR 차단
instance_type_allowlist 허용된 인스턴스 타입만 PR 경고
cost_threshold 비용 임계값 초과 PR 경고 + 승인 필요

Kubernetes 런타임 정책 (Kyverno)

정책 설명 모드
require-resource-limits CPU/메모리 제한 필수 Enforce
disallow-privileged 특권 컨테이너 금지 Enforce
require-non-root 루트 실행 금지 Enforce
allowed-registries 승인된 레지스트리만 Enforce
require-readonly-rootfs 읽기 전용 파일시스템 Enforce
require-network-policy 네트워크 정책 필수 Enforce
disallow-host-namespaces 호스트 네임스페이스 금지 Enforce
add-default-securitycontext 기본 보안 컨텍스트 추가 Mutate

자동화 검증 방법

# Kyverno 정책 상태 확인
kubectl get clusterpolicy -o wide

# 정책 위반 리포트 조회
kubectl get policyreport -A
kubectl get clusterpolicyreport

# 특정 정책 위반 상세 확인
kubectl describe policyreport -n production

# Conftest 정책 테스트 실행
/usr/bin/make policy-test

# OPA 단위 테스트 전체 실행
opa test policies/opa/ -v --count=5

6. 네트워크 보안

SCP 네트워크 보안 아키텍처

인터넷
  │
  ▼
[SCP Firewall]  ← 외부 방화벽 (L3/L4 필터링)
  │
  ▼
[SCP Load Balancer]  ← SSL 종료, DDoS 보호
  │
  ▼
[Public Subnet]  ← Ingress Controller, Bastion
  │
  ▼
[SCP Security Group]  ← 서비스별 접근 제어
  │
  ▼
[Private Subnet]  ← SKE 노드, 데이터베이스
  │
  ▼
[SCP VPC Endpoint]  ← Object Storage, KMS 내부 접근

보안 그룹 기본 규칙

보안 그룹 규칙 전체 목록은 네트워크 설계를 참조하세요.


7. 제로 트러스트 아키텍처

제로 트러스트 원칙

SCP GitOps 인프라는 "절대 신뢰하지 말고, 항상 검증하라(Never Trust, Always Verify)" 원칙에 따라 설계됩니다.

제로 트러스트 4계층:

계층 1: 신원 검증 (Identity)
├── SCP IAM 역할 기반 접근 제어
├── OIDC 연동 (GitHub Actions → SCP)
└── Vault SPIFFE/SPIRE 워크로드 ID (계획)

계층 2: 장치/노드 신뢰 (Device)
├── SKE 노드 무결성 검증 (Secure Boot)
├── 노드 어피니티: 허용 리전 노드만
└── 이미지 서명 검증 (Cosign)

계층 3: 네트워크 마이크로세그멘테이션 (Network)
├── Kubernetes NetworkPolicy (Pod 레벨)
├── Istio mTLS (서비스 간 암호화)
└── SCP Security Group (인프라 레벨)

계층 4: 애플리케이션/데이터 (Application)
├── Kyverno 런타임 정책 강제
├── OPA 인가 (API 레벨)
└── SCP KMS 데이터 암호화

Kubernetes NetworkPolicy 기본 구성

# 기본 거부 정책 (네임스페이스별 적용)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
# 필요한 통신만 명시적 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-to-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgresql
      ports:
        - protocol: TCP
          port: 5432

Istio mTLS 설정

# 네임스페이스 전체 mTLS 강제
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: STRICT  # 모든 서비스 간 통신 mTLS 필수
---
# 인가 정책: 서비스 A → 서비스 B만 허용
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  selector:
    matchLabels:
      app: backend
  rules:
    - from:
        - source:
            principals: ["cluster.local/ns/production/sa/frontend"]
      to:
        - operation:
            methods: ["GET", "POST"]
            paths: ["/api/*"]

컨테이너 이미지 서명 검증

# Kyverno 이미지 서명 검증 정책
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-image-signature
      match:
        any:
          - resources:
              kinds: [Pod]
      verifyImages:
        - imageReferences:
            - "registry.example.com/*"
          attestors:
            - entries:
                - keyless:
                    issuer: "https://token.actions.githubusercontent.com"
                    subject: "https://github.com/your-org/scp-gitops-gemini/*"

자동화 검증 방법

# Istio 설치 및 mTLS 상태 확인
istioctl verify-install
istioctl proxy-status

# mTLS 연결 확인
istioctl authn tls-check <pod-name>.<namespace>

# NetworkPolicy 적용 여부 확인
kubectl get netpol -A
kubectl describe netpol default-deny-all -n production

# 이미지 서명 검증 정책 확인
kubectl get clusterpolicy verify-image-signature -o yaml

8. 시크릿 로테이션 절차

시크릿 유형별 로테이션 주기

시크릿 유형 로테이션 주기 자동화 담당
SCP Access Key (dev) 90일 수동 개발팀
SCP Access Key (staging) 60일 수동 인프라팀
SCP Access Key (prod) 30일 Vault 자동 SRE팀
Vault 동적 자격 증명 (prod) 1시간 TTL 자동 Vault
ArgoCD Auth Token 90일 수동 SRE팀
DB 관리자 비밀번호 180일 수동 DBA팀
TLS 인증서 (내부) 1년 (cert-manager 자동) 자동 인프라팀
TLS 인증서 (외부) 90일 (Let's Encrypt) 자동 인프라팀
GPG 서명 키 1년 수동 개인

환경별 SCP Access Key 로테이션

dev 환경 로테이션

# 1. 새 액세스 키 발급 (SCP 콘솔 또는 CLI)
# scp iam create-access-key --user-name terraform-dev

# 2. GitHub Environment Secret 업데이트
gh secret set SCP_ACCESS_KEY_DEV \
  --env dev \
  --body "<새_액세스_키>"

gh secret set SCP_SECRET_KEY_DEV \
  --env dev \
  --body "<새_시크릿_키>"

# 3. 새 키로 파이프라인 실행 검증
gh workflow run terraform-validate.yml \
  --ref main \
  --field environment=dev

# 4. 구 액세스 키 비활성화 (24시간 후 삭제)
# scp iam deactivate-access-key --access-key-id <구_KEY_ID>

staging 환경 로테이션

# dev와 동일한 절차, 환경명만 변경
gh secret set SCP_ACCESS_KEY_STAGING --env staging --body "<새_액세스_키>"
gh secret set SCP_SECRET_KEY_STAGING --env staging --body "<새_시크릿_키>"

# staging 배포 파이프라인 검증
gh workflow run terraform-validate.yml --ref main --field environment=staging

prod 환경 로테이션 (Vault 사용)

# prod는 Vault 동적 자격 증명 사용 → 수동 로테이션 불필요
# Vault 시크릿 엔진 설정 확인
vault read scp/config/root
vault read scp/roles/terraform-prod

# TTL 및 최대 TTL 확인
vault read scp/roles/terraform-prod | grep -E "ttl|max_ttl"

# Vault 토큰 자체 로테이션 (90일 주기)
vault token renew
# 또는 GitHub Secret 업데이트
gh secret set VAULT_TOKEN --env prod --body "<새_VAULT_TOKEN>"

ArgoCD Auth Token 로테이션

# 1. 새 ArgoCD API 토큰 생성
argocd account generate-token \
  --account ci-deployer \
  --expires-in 90d

# 2. GitHub Secret 업데이트
gh secret set ARGOCD_AUTH_TOKEN_PROD \
  --env prod \
  --body "<새_토큰>"

# 3. 기존 토큰 무효화
argocd account list  # 토큰 ID 확인
argocd account delete-token --account ci-deployer --id <토큰_ID>

DB 관리자 비밀번호 로테이션

# 1. 새 비밀번호 생성 (최소 24자, 특수문자 포함)
NEW_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | head -c 24)

# 2. SCP DBaaS 비밀번호 변경 (콘솔 또는 API)
# scp dbaas change-password --instance-id <DB_ID> --new-password "$NEW_PASSWORD"

# 3. GitHub Secret 업데이트
gh secret set TF_VAR_DB_ADMIN_PASSWORD \
  --env prod \
  --body "$NEW_PASSWORD"

# 4. Vault에도 저장 (감사 추적 목적)
vault kv put scp/db/prod-postgres \
  password="$NEW_PASSWORD" \
  rotated_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

# 5. 애플리케이션 시크릿(K8s Secret) 업데이트
kubectl create secret generic db-credentials \
  --from-literal=password="$NEW_PASSWORD" \
  --namespace production \
  --dry-run=client -o yaml | kubectl apply -f -

TLS 인증서 자동 로테이션 (cert-manager)

# cert-manager Certificate 리소스 예시
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: internal-tls
  namespace: production
spec:
  secretName: internal-tls-secret
  duration: 8760h    # 1년
  renewBefore: 720h  # 만료 30일 전 자동 갱신
  issuerRef:
    name: scp-internal-ca
    kind: ClusterIssuer
  dnsNames:
    - "*.internal.scp.company.com"

9. 보안 기준선 위반 탐지

기준선 정의 및 탐지 방법

보안 기준선(Security Baseline)은 인프라의 정상 상태를 정의합니다. 이로부터의 이탈(드리프트)을 자동으로 탐지하고 경보합니다.

Terraform 드리프트 탐지

# 드리프트 탐지 수동 실행
cd environments/prod
tofu plan \
  -detailed-exitcode \
  -var-file=prod.tfvars \
  2>&1 | tee /tmp/drift-report.txt

# 종료 코드 해석
# 0: 변경 없음 (기준선 일치)
# 1: 오류 발생
# 2: 드리프트 탐지 (변경 필요)
echo "종료 코드: $?"

자동화된 드리프트 탐지는 .github/workflows/drift-detect.yml에서 평일 KST 09:00에 실행됩니다.

Kyverno 정책 위반 탐지

# 정책 위반 리포트 조회 (전체 클러스터)
kubectl get policyreport -A -o json | \
  jq '.items[] | select(.summary.fail > 0) | {namespace: .metadata.namespace, fail: .summary.fail}'

# 위반 상세 내역
kubectl get policyreport -n production -o json | \
  jq '.results[] | select(.result == "fail") | {policy: .policy, resource: .resources[0].name, message: .message}'

# 클러스터 레벨 정책 위반
kubectl get clusterpolicyreport -o json | \
  jq '.items[] | select(.summary.fail > 0)'

Falco 런타임 이상 탐지

# cluster-services/falco/rules-custom.yaml
# 커스텀 Falco 룰 — 비정상 행위 탐지
- rule: Unexpected Shell in Container
  desc: 컨테이너 내 예기치 않은 쉘 실행 탐지
  condition: >
    spawned_process and
    container and
    shell_procs and
    not proc.pname in (allowed_parent_processes)
  output: >
    컨테이너 쉘 실행 탐지 (user=%user.name pod=%k8s.pod.name
    ns=%k8s.ns.name image=%container.image.repository:%container.image.tag
    cmd=%proc.cmdline)
  priority: WARNING
  tags: [container, shell, mitre_execution]

- rule: Write to Sensitive Directory
  desc: 민감 디렉토리 쓰기 시도 탐지
  condition: >
    open_write and
    container and
    (fd.name startswith /etc or fd.name startswith /usr/bin)
  output: >
    민감 디렉토리 쓰기 탐지 (file=%fd.name pod=%k8s.pod.name
    ns=%k8s.ns.name user=%user.name cmd=%proc.cmdline)
  priority: ERROR
  tags: [container, filesystem, mitre_persistence]

보안 기준선 자동 검증 스크립트

#!/bin/bash
# scripts/security-baseline-check.sh
# 보안 기준선 위반 항목 전체 점검

echo "=== 보안 기준선 점검 시작 ==="

VIOLATIONS=0

# 1. Kyverno 정책 위반 확인
KYVERNO_FAIL=$(kubectl get policyreport -A -o json 2>/dev/null | \
  jq '[.items[].summary.fail] | add // 0')
echo "[Kyverno] 정책 위반: ${KYVERNO_FAIL}"
[ "$KYVERNO_FAIL" -gt 0 ] && VIOLATIONS=$((VIOLATIONS + KYVERNO_FAIL))

# 2. 특권 컨테이너 실행 중인지 확인
PRIVILEGED=$(kubectl get pods -A -o json | \
  jq '[.items[] | select(.spec.containers[].securityContext.privileged == true)] | length')
echo "[K8s] 특권 컨테이너: ${PRIVILEGED}"
[ "$PRIVILEGED" -gt 0 ] && VIOLATIONS=$((VIOLATIONS + PRIVILEGED))

# 3. NetworkPolicy 없는 네임스페이스 확인
NO_NETPOL_NS=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}' | \
  tr ' ' '\n' | while read ns; do
    count=$(kubectl get netpol -n "$ns" 2>/dev/null | wc -l)
    [ "$count" -le 1 ] && echo "$ns"
  done | grep -v "^kube-" | grep -v "^default")
echo "[K8s] NetworkPolicy 없는 네임스페이스: ${NO_NETPOL_NS:-없음}"

# 4. registry.example.com 외 레지스트리 이미지 실행 여부
EXTERNAL_IMAGES=$(kubectl get pods -A -o json | \
  jq -r '.items[].spec.containers[].image' | \
  grep -v "registry.example.com" | grep -v "^$" | sort -u)
if [ -n "$EXTERNAL_IMAGES" ]; then
  echo "[K8s] 비인가 레지스트리 이미지 감지:"
  echo "$EXTERNAL_IMAGES"
  VIOLATIONS=$((VIOLATIONS + 1))
fi

echo "=== 점검 완료: 총 ${VIOLATIONS}건 위반 ==="
exit $VIOLATIONS

OpenSearch 알람 설정 (이상 탐지)

{
  "name": "보안-이상-탐지-알람",
  "type": "monitor",
  "schedule": { "period": { "interval": 5, "unit": "MINUTES" } },
  "inputs": [{
    "search": {
      "indices": ["falco-events-*", "k8s-audit-*"],
      "query": {
        "bool": {
          "should": [
            { "match": { "rule": "Unexpected Shell in Container" } },
            { "match": { "rule": "Write to Sensitive Directory" } },
            { "match": { "verb": "delete" }, "match": { "objectRef.resource": "secrets" } }
          ],
          "minimum_should_match": 1,
          "filter": [{ "range": { "@timestamp": { "gte": "now-5m" } } }]
        }
      }
    }
  }],
  "triggers": [{
    "name": "보안-이상-탐지",
    "severity": "1",
    "condition": { "script": { "source": "ctx.results[0].hits.total.value > 0" } },
    "actions": [{
      "name": "Slack-알람",
      "destination_id": "slack-security-channel",
      "message_template": {
        "source": "보안 이상 탐지: {{ctx.results[0].hits.total.value}}건\n최신 이벤트: {{ctx.results[0].hits.hits[0]._source}}"
      }
    }]
  }]
}

10. 감사 및 로깅

로깅 아키텍처

로그 소스:
├── Terraform 작업 로그 → GitHub Actions 아티팩트
├── Argo CD 감사 로그 → Elasticsearch
├── Kubernetes API 감사 로그 → SCP Log Service
├── 애플리케이션 로그 → Fluent Bit → OpenSearch
└── SCP CloudTrail → SCP Object Storage (규정 준수 보관)

Kubernetes 감사 정책

# 감사 로그 정책 예시
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # Secrets 접근 로깅
  - level: Metadata
    resources:
      - group: ""
        resources: ["secrets"]

  # 관리자 작업 전체 로깅
  - level: RequestResponse
    users: ["admin", "system:masters"]

  # 일반 읽기 작업 축소 로깅
  - level: None
    verbs: ["get", "list", "watch"]
    resources:
      - group: ""
        resources: ["pods", "services"]

11. 인시던트 대응

인시던트 트리거 조건

아래 조건 중 하나라도 해당되면 즉시 인시던트를 선언하고 대응 절차를 시작합니다.

트리거 조건 인시던트 레벨 자동 탐지 방법
SCP Access Key가 공개 저장소에 노출 P1 gitleaks / GitHub Secret Scanning
비인가 사용자가 prod 리소스 생성/삭제 P1 SCP CloudTrail 알람
특권 컨테이너 실행 탐지 P1 Falco → OpenSearch 알람
Terraform 상태 파일 무단 수정 P1 S3 버킷 이벤트 알람
허용 리전 외 리소스 생성 시도 P2 OPA 정책 위반 로그
Kyverno Enforce 정책 위반 Pod 생성 시도 P2 Kyverno 감사 로그
비인가 이미지 레지스트리 사용 P2 Kyverno policyreport
prod 환경 드리프트 탐지 (계획 외 변경) P2 drift-detect 워크플로우
TLS 인증서 만료 임박 (7일 이내) P3 cert-manager 이벤트
보안 정책 경고 수준 위반 누적 (10건/일) P3 OpenSearch 집계 알람
필수 태그 누락 리소스 탐지 P4 OPA cost-control 정책

보안 인시던트 분류

레벨 예시 대응 시간 대응 절차
P1 - 치명 자격 증명 유출, 데이터 침해 즉시 즉시 차단 + 에스컬레이션
P2 - 높음 무단 리소스 생성, 정책 우회 1시간 내 조사 + 격리
P3 - 중간 비준수 리소스 배포 24시간 내 수정 PR 생성
P4 - 낮음 정책 경고, 태그 누락 1주일 내 다음 스프린트 처리

자격 증명 침해 시 즉시 조치

# 1. 침해된 키 즉시 비활성화 (SCP 콘솔 또는 API)
# scp iam deactivate-access-key --access-key-id <KEY_ID>

# 2. 해당 키로 생성된 모든 리소스 감사
# scp cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=<KEY_ID>

# 3. Terraform 상태 백업 확인
/usr/bin/make state-list ENV=prod

# 4. 새로운 키 발급 및 GitHub Secrets 업데이트
# (GitHub Actions 환경 시크릿 업데이트)

# 5. 영향 받은 모든 환경 검증
/usr/bin/make plan ENV=dev
/usr/bin/make plan ENV=staging
/usr/bin/make plan ENV=prod

P1 대응 플로우

flowchart TD
    TRIGGER["트리거 조건 감지\n(자동 알람 또는 수동 보고)"]
    TRIGGER --> DECLARE["인시던트 선언\n(Slack #incident 채널 알림)"]
    DECLARE --> ISOLATE["즉시 격리\n- 침해된 자격 증명 비활성화\n- 영향 받은 Pod/노드 격리"]
    ISOLATE --> ASSESS["영향 범위 평가\n- CloudTrail 로그 분석\n- 영향 받은 리소스 목록화"]
    ASSESS --> CONTAIN["봉쇄 조치\n- 추가 피해 차단\n- 증거 보전"]
    CONTAIN --> REMEDIATE["복구 작업\n- 새 자격 증명 발급\n- 인프라 재프로비저닝"]
    REMEDIATE --> VERIFY["복구 검증\n- make validate\n- security-baseline-check.sh"]
    VERIFY --> POSTMORTEM["사후 분석\n- RCA 작성\n- 재발 방지 대책 수립"]
Loading

관련 문서

문서 내용
NETWORK.md 네트워크 보안 설계·보안 그룹 규칙 상세
security-requirements/ CSAP·ISO27001·PIPA·SOC2 규정 준수, 위협 모델
operator-guide.md 일상 운영 보안 점검 절차

상위 문서로 돌아가기 → README.md

마지막 업데이트: 2026-03-09

There aren’t any published security advisories