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 : 보호
| 보안 영역 | 통제 항목 | 구현 상태 | 자동화 검증 |
|---|---|---|---|
| 자격 증명 | 환경변수 주입 (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 | 계획 | — |
- Sovereign Cloud 대응 전략
- 데이터 주권 정책
- 자격 증명 관리
- 상태 파일 보안
- Policy-as-Code 보안 정책
- 네트워크 보안
- 제로 트러스트 아키텍처
- 시크릿 로테이션 절차
- 보안 기준선 위반 탐지
- 감사 및 로깅
- 인시던트 대응
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 (클라우드 보안 인증제)
# 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# 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"]우선순위 (높음 → 낮음):
1. 환경 변수 (CI/CD 파이프라인 권장)
SCP_ACCESS_KEY, SCP_SECRET_KEY, SCP_PROJECT_ID
2. ~/.scpconf 파일 (로컬 개발 환경)
[default] 프로파일
3. IAM 인스턴스 프로파일 (SCP VM에서 실행 시)
자동 감지
4. SCP Vault 동적 자격 증명 (권장 - 운영 환경)
자동 만료되는 단기 토큰
# ~/.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 저장소 시크릿 (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를 통한 동적 자격 증명 설정
# 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# 상태 파일 버킷 암호화 설정 (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| 정책 | 설명 | 위반 시 |
|---|---|---|
required_tags |
필수 태그 강제 | PR 차단 |
allowed_regions |
국내 리전만 허용 | PR 차단 |
encryption_required |
저장 데이터 암호화 필수 | PR 차단 |
no_public_access |
공개 접근 금지 | PR 차단 |
instance_type_allowlist |
허용된 인스턴스 타입만 | PR 경고 |
cost_threshold |
비용 임계값 초과 | PR 경고 + 승인 필요 |
| 정책 | 설명 | 모드 |
|---|---|---|
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인터넷
│
▼
[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 내부 접근
보안 그룹 규칙 전체 목록은 네트워크 설계를 참조하세요.
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 데이터 암호화
# 기본 거부 정책 (네임스페이스별 적용)
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# 네임스페이스 전체 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| 시크릿 유형 | 로테이션 주기 | 자동화 | 담당 |
|---|---|---|---|
| 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년 | 수동 | 개인 |
# 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># 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 동적 자격 증명 사용 → 수동 로테이션 불필요
# 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>"# 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># 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 -# 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"보안 기준선(Security Baseline)은 인프라의 정상 상태를 정의합니다. 이로부터의 이탈(드리프트)을 자동으로 탐지하고 경보합니다.
# 드리프트 탐지 수동 실행
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에 실행됩니다.
# 정책 위반 리포트 조회 (전체 클러스터)
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)'# 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{
"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}}"
}
}]
}]
}로그 소스:
├── Terraform 작업 로그 → GitHub Actions 아티팩트
├── Argo CD 감사 로그 → Elasticsearch
├── Kubernetes API 감사 로그 → SCP Log Service
├── 애플리케이션 로그 → Fluent Bit → OpenSearch
└── SCP CloudTrail → SCP Object Storage (규정 준수 보관)
# 감사 로그 정책 예시
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"]아래 조건 중 하나라도 해당되면 즉시 인시던트를 선언하고 대응 절차를 시작합니다.
| 트리거 조건 | 인시던트 레벨 | 자동 탐지 방법 |
|---|---|---|
| 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=prodflowchart 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- 재발 방지 대책 수립"]
| 문서 | 내용 |
|---|---|
| NETWORK.md | 네트워크 보안 설계·보안 그룹 규칙 상세 |
| security-requirements/ | CSAP·ISO27001·PIPA·SOC2 규정 준수, 위협 모델 |
| operator-guide.md | 일상 운영 보안 점검 절차 |
마지막 업데이트: 2026-03-09