OWASP Kubernetes Top 10이란 무엇입니까?

OWASP Kubernetes Top 10은 Kubernetes 환경에서 가장 중요한 보안 위험을 식별합니다. Kubernetes는 컨테이너 오케스트레이션의 사실상 표준이 되었지만 유연성과 복잡성으로 인해 수많은 보안 문제가 발생합니다. 이 가이드에서는 워크로드 잘못된 구성, RBAC 문제, 비밀 관리, 네트워크 분할 등을 다룹니다.

1️⃣ K01 - 안전하지 않은 작업 부하 구성

Critical

개요

안전하지 않은 워크로드 구성은 가장 일반적인 Kubernetes 보안 문제입니다. 과도한 권한, 쓰기 가능한 파일 시스템 또는 리소스 제한 없이 루트로 실행되는 컨테이너는 심각한 공격 표면을 만듭니다. 기본 구성은 안전하지 않은 경우가 많으므로 명시적으로 강화해야 합니다.

위험

루트 권한과 호스트 액세스 권한이 있는 손상된 컨테이너는 컨테이너 샌드박스를 탈출하고, 호스트 파일 시스템에 액세스하고, 다른 워크로드로 피벗할 수 있습니다. 잘못 구성된 포드에서 권한을 에스컬레이션하면 전체 클러스터가 손상될 수 있습니다.

취약한 코드 예

YAML (Kubernetes Pod) ❌ Bad
apiVersion: v1
kind: Pod
metadata:
  name: insecure-app
spec:
  containers:
  - name: app
    image: myapp:latest          # Mutable tag!
    securityContext:
      privileged: true            # Full host access!
      runAsUser: 0                # Running as root!
    volumeMounts:
    - mountPath: /host
      name: host-fs
  volumes:
  - name: host-fs
    hostPath:
      path: /                    # Entire host filesystem!

보안 코드 예

YAML (Kubernetes Pod) ✅ Good
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp@sha256:abc123...  # Immutable digest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      runAsUser: 1000
      capabilities:
        drop: ["ALL"]
    resources:
      limits:
        cpu: "500m"
        memory: "256Mi"
      requests:
        cpu: "100m"
        memory: "128Mi"

완화 체크리스트

2️⃣ K02 - 과도한 권한 부여 구성

Critical

개요

Kubernetes RBAC(역할 기반 액세스 제어)는 강력하지만 복잡합니다. 과도한 권한이 부여된 역할(특히 클러스터-관리자 바인딩, 와일드카드 권한, 과도한 서비스 계정 권한)은 클러스터 리소스, 비밀 및 워크로드에 대한 무단 액세스를 허용합니다.

위험

과도한 권한이 있는 서비스 계정에 대한 액세스 권한을 얻은 공격자는 비밀을 나열하고, 권한이 있는 Pod를 생성하고, 배포를 수정하고, 클러스터 관리자로 에스컬레이션할 수 있습니다. 와일드카드 RBAC 규칙은 Kubernetes에서 루트 액세스 권한을 부여하는 것과 동일합니다.

취약한 코드 예

YAML (Kubernetes RBAC) ❌ Bad
# ClusterRoleBinding granting cluster-admin to a service account
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: app-admin-binding
subjects:
- kind: ServiceAccount
  name: my-app            # App SA with cluster-admin!
  namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin      # Full cluster control!
  apiGroup: rbac.authorization.k8s.io

보안 코드 예

YAML (Kubernetes RBAC) ✅ Good
# Scoped Role with minimum required permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: my-app-ns
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "list"]     # Read-only, specific resources
  resourceNames: ["app-config"]  # Named resource only
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-role-binding
  namespace: my-app-ns
subjects:
- kind: ServiceAccount
  name: my-app
  namespace: my-app-ns
roleRef:
  kind: Role
  name: app-role
  apiGroup: rbac.authorization.k8s.io

완화 체크리스트

3️⃣ K03 - 비밀 관리 실패

Critical

개요

Kubernetes 비밀은 기본적으로 암호화되지 않고 base64로만 인코딩됩니다. 일반 ConfigMap, 환경 변수 또는 암호화되지 않은 보안 비밀에 민감한 데이터(API 키, 데이터베이스 비밀번호, TLS 인증서)를 저장하면 API 액세스 권한이 있는 모든 사람에게 노출됩니다. 유휴 암호화가 활성화되지 않은 경우 비밀은 etcd에도 표시됩니다.

위험

노출된 비밀을 통해 공격자는 데이터베이스, 클라우드 계정 및 외부 서비스에 액세스할 수 있습니다. 암호화 없이 etcd에 저장된 비밀은 etcd 액세스 권한이 있는 사람이라면 누구나 읽을 수 있습니다. 환경 변수 비밀은 포드 사양 및 프로세스 목록에 표시됩니다.

취약한 코드 예

YAML (Kubernetes) ❌ Bad
# Secret in plain ConfigMap — visible to anyone
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  DB_PASSWORD: "SuperSecret123!"   # Plaintext password!
  API_KEY: "sk-live-abc123xyz"      # API key in ConfigMap!
---
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: myapp:latest
    envFrom:
    - configMapRef:
        name: app-config    # All values as env vars!

보안 코드 예

YAML (Kubernetes + External Secrets) ✅ Good
# Use External Secrets Operator with a vault backend
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: app-secrets
  data:
  - secretKey: db-password
    remoteRef:
      key: secret/myapp/db
      property: password
---
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: myapp@sha256:abc123...
    volumeMounts:
    - name: secrets
      mountPath: /etc/secrets
      readOnly: true          # Mount as read-only files
  volumes:
  - name: secrets
    secret:
      secretName: app-secrets

완화 체크리스트

4️⃣ K04 - 클러스터 수준 정책 시행 부족

High

개요

클러스터 수준 정책이 없으면 안전하지 않은 워크로드 배포를 방지하는 가드레일이 없습니다. PSS(Pod Security Standards), 승인 컨트롤러 및 OPA Gatekeeper 또는 Kyverno와 같은 정책 엔진은 모든 네임스페이스에 걸쳐 보안 기준을 적용하는 데 필수적입니다.

위험

정책 시행 없이 모든 개발자는 권한 있는 컨테이너를 배포하거나, 호스트 경로 볼륨을 사용하거나, 보안 제어를 비활성화할 수 있습니다. 잘못 구성된 단일 워크로드로 인해 전체 클러스터가 손상될 수 있습니다. 수동 검토를 통해 모든 위반 사항을 포착할 수는 없습니다.

취약한 코드 예

Bash ❌ Bad
#!/bin/bash
# No admission control — any pod spec is accepted
# No Pod Security Standards configured

# Anyone can deploy a privileged container
kubectl run hacker-pod --image=alpine \
  --overrides='{"spec":{"containers":[{
    "name":"hack",
    "image":"alpine",
    "securityContext":{"privileged":true},
    "command":["nsenter","--target","1","--mount","--uts","--ipc","--net","--pid","--","bash"]
  }]}}'

# No policy blocks this — now has host root access!

보안 코드 예

YAML (Kyverno ClusterPolicy) ✅ Good
# Enforce Pod Security Standards with Kyverno
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: deny-privileged
    match:
      any:
      - resources:
          kinds: ["Pod"]
    validate:
      message: "Privileged containers are not allowed."
      pattern:
        spec:
          containers:
          - securityContext:
              privileged: "false"
  - name: require-run-as-non-root
    match:
      any:
      - resources:
          kinds: ["Pod"]
    validate:
      message: "Containers must run as non-root."
      pattern:
        spec:
          securityContext:
            runAsNonRoot: true

완화 체크리스트

5️⃣ K05 - 네트워크 분할 제어 누락

High

개요

기본적으로 Kubernetes 클러스터의 모든 Pod는 아무런 제한 없이 서로 통신할 수 있습니다. 이 플랫 네트워크 아키텍처는 손상된 포드가 다른 포드, 서비스 또는 Kubernetes API 서버에 도달할 수 있음을 의미합니다. 네트워크 정책은 마이크로 세분화를 구현하는 데 필수적입니다.

위험

네트워크 분할이 없으면 측면 이동이 쉽지 않습니다. 손상된 프런트엔드 포드는 백엔드 데이터베이스, 내부 서비스 및 메타데이터 API(169.254.169.254)에 직접 액세스할 수 있습니다. 이는 네트워크 계층에서 최소 권한 원칙을 위반합니다.

취약한 코드 예

YAML (Kubernetes) ❌ Bad
# No NetworkPolicy — all pods can communicate freely
apiVersion: v1
kind: Namespace
metadata:
  name: production
  # No default deny policy
  # No network policies at all
  # Any pod can reach:
  #   - Other pods in any namespace
  #   - Kubernetes API server
  #   - Cloud metadata API (169.254.169.254)
  #   - External internet

보안 코드 예

YAML (Kubernetes NetworkPolicy) ✅ Good
# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes: ["Ingress", "Egress"]
---
# Allow only frontend to backend communication
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes: ["Ingress"]
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - port: 8080
      protocol: TCP

완화 체크리스트

6️⃣ K06 - 과도하게 노출된 Kubernetes 구성요소

High

개요

Kubernetes 제어 평면 구성 요소(API 서버, etcd, kubelet, 대시보드) 및 애플리케이션 서비스는 실수로 인터넷이나 신뢰할 수 없는 네트워크에 노출될 수 있습니다. 노출된 대시보드, 인증되지 않은 kubelet, 공개적으로 액세스 가능한 API 서버는 일반적인 공격 벡터입니다.

위험

노출된 Kubernetes 구성요소는 클러스터 관리에 대한 직접적인 액세스를 제공합니다. 인증되지 않은 kubelet API는 컨테이너 실행을 허용합니다. 기본 자격 증명이 포함된 노출된 대시보드는 클러스터 관리자 액세스 권한을 부여합니다. 공개 etcd 액세스는 비밀을 포함한 모든 클러스터 데이터를 노출합니다.

취약한 코드 예

YAML (Kubernetes Service) ❌ Bad
# Kubernetes Dashboard exposed via LoadBalancer
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  type: LoadBalancer        # Public internet access!
  ports:
  - port: 443
    targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
# Dashboard bound to cluster-admin service account
# No authentication required — full cluster access!

보안 코드 예

YAML (Kubernetes) ✅ Good
# Dashboard accessible only via kubectl proxy
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  type: ClusterIP           # Internal only
  ports:
  - port: 443
    targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
---
# Access via: kubectl proxy, then browse to:
# http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
# Or use kubectl port-forward for secure access

완화 체크리스트

7️⃣ K07 - 잘못 구성되고 취약한 클러스터 구성 요소

High

개요

Kubernetes 클러스터는 API 서버, etcd, kubelet, kube-proxy, CoreDNS 및 타사 추가 기능(수신 컨트롤러, 서비스 메시, 모니터링) 등 다양한 구성 요소로 구성됩니다. 잘못 구성되었거나 패치가 적용되지 않은 구성 요소로 인해 취약점이 발생합니다. 기본 구성은 강화되지 않은 경우가 많습니다.

위험

취약한 클러스터 구성 요소는 원격 코드 실행, 권한 상승 또는 서비스 거부에 악용될 수 있습니다. kubelet, Ingress 컨트롤러 또는 CNI 플러그인의 CVE는 컨테이너 이스케이프 및 클러스터 인수로 이어질 수 있습니다. 오래된 구성 요소에는 알려진 취약점이 축적됩니다.

취약한 코드 예

YAML (Kubernetes) ❌ Bad
# kubelet with insecure configuration
# /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: true              # Anyone can access kubelet API!
  webhook:
    enabled: false             # No authorization!
authorization:
  mode: AlwaysAllow            # All requests permitted!
readOnlyPort: 10255            # Unauthenticated read port open!

보안 코드 예

YAML (Kubernetes) ✅ Good
# Hardened kubelet configuration
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: false             # Deny anonymous access
  webhook:
    enabled: true              # Use API server for auth
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook                # API server authorizes requests
readOnlyPort: 0                # Disable read-only port
protectKernelDefaults: true
eventRecordQPS: 5

완화 체크리스트

8️⃣ K08 - 클러스터에서 클라우드로의 측면 이동

Critical

개요

클라우드 환경(AWS, GCP, Azure)에서 실행되는 Kubernetes 클러스터는 노드에 연결된 클라우드 메타데이터 API 및 IAM 역할에 액세스할 수 있습니다. 포드를 손상시킨 공격자는 이를 사용하여 클러스터에서 클라우드 계정으로 권한을 승격하고 S3 버킷, 데이터베이스 및 기타 클라우드 서비스에 액세스할 수 있습니다.

위험

노드 수준 IAM 역할은 해당 노드의 모든 포드에 상속됩니다. 손상된 포드는 메타데이터 API(169.254.169.254)를 쿼리하여 클라우드 자격 증명을 얻은 다음 노드 역할이 허용하는 모든 클라우드 리소스에 액세스할 수 있습니다. 이를 통해 Kubernetes에서 더 넓은 클라우드 환경으로의 측면 이동이 가능해집니다.

취약한 코드 예

Terraform (AWS EKS) ❌ Bad
# Node IAM role with excessive permissions
resource "aws_iam_role_policy_attachment" "node_admin" {
  role       = aws_iam_role.node_role.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
  # All pods on this node inherit admin access!
}

# No IMDS restrictions — any pod can get node credentials
# curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

보안 코드 예

Terraform (AWS EKS with IRSA) ✅ Good
# Use IAM Roles for Service Accounts (IRSA)
resource "aws_iam_role" "app_role" {
  name = "my-app-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Federated = aws_iam_openid_connect_provider.eks.arn
      }
      Action = "sts:AssumeRoleWithWebIdentity"
      Condition = {
        StringEquals = {
          "${aws_iam_openid_connect_provider.eks.url}:sub" =
            "system:serviceaccount:my-ns:my-app"
        }
      }
    }]
  })
}

# Pod-level scoped IAM via service account annotation
# Only this specific pod gets these specific permissions

완화 체크리스트

9️⃣ K09 - 손상된 인증 메커니즘

Critical

개요

Kubernetes는 인증서, 토큰, OIDC, 웹훅 인증 등 다양한 인증 메커니즘을 지원합니다. 손상된 인증에는 정적 토큰 사용, kubeconfig 파일 공유, 인증서 순환 안 함, ID 공급자와의 통합 실패 등이 포함됩니다. 서비스 계정 토큰 자동 탑재로 인해 불필요한 공격 표면이 생성됩니다.

위험

도난당하거나 유출된 kubeconfig 파일과 서비스 계정 토큰은 직접적인 클러스터 액세스를 제공합니다. 만료되지 않는 정적 토큰은 지속적인 액세스를 제공합니다. 중앙 집중식 ID 관리가 없으면 퇴사한 팀원의 액세스 권한을 취소하는 것이 어렵고 오류가 발생하기 쉽습니다.

취약한 코드 예

YAML (Kubernetes) ❌ Bad
# Static token file for API server authentication
# /etc/kubernetes/token-auth-file.csv
# token,user,uid,"groups"
# abc123,admin,1,"system:masters"  # Static token, never expires!

# Pod with auto-mounted service account token
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  # automountServiceAccountToken defaults to true!
  # Token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token
  containers:
  - name: app
    image: myapp:latest
    # App doesn't need K8s API access but has a token anyway

보안 코드 예

YAML (Kubernetes) ✅ Good
# Disable auto-mounted token for pods that don't need API access
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app
  namespace: my-app-ns
automountServiceAccountToken: false  # No auto-mount
---
# For pods that need API access, use bound tokens with expiry
apiVersion: v1
kind: Pod
metadata:
  name: api-consumer
spec:
  serviceAccountName: api-consumer-sa
  automountServiceAccountToken: false
  containers:
  - name: app
    image: myapp@sha256:abc123...
    volumeMounts:
    - name: token
      mountPath: /var/run/secrets/tokens
      readOnly: true
  volumes:
  - name: token
    projected:
      sources:
      - serviceAccountToken:
          expirationSeconds: 3600  # 1-hour expiry
          audience: api-server

완화 체크리스트

🔟 K10 - 부적절한 로깅 및 모니터링

Medium

개요

Kubernetes는 API 서버 요청에 대한 감사 이벤트를 생성하지만 감사 로깅이 비활성화되거나 잘못 구성되는 경우가 많습니다. 적절한 로깅 및 모니터링이 없으면 무단 비밀 액세스, RBAC 변경, 컨테이너 이스케이프와 같은 악의적인 활동이 감지되지 않습니다. 컨테이너 및 노드 수준의 런타임 보안 모니터링이 필수적입니다.

위험

감사 로깅이 없으면 공격자는 감지되지 않은 채 작업합니다. 백도어 서비스 계정을 생성하고, 비밀을 추출하고, 기록 없이 워크로드를 수정할 수 있습니다. 무슨 일이, 언제, 누구에 의해 발생했는지 확인할 수 있는 감사 추적이 없으면 사고 대응이 심각하게 저하됩니다.

취약한 코드 예

YAML (Kubernetes) ❌ Bad
# API server with no audit logging configured
# kube-apiserver flags:
#   --audit-log-path=""           # No audit log!
#   --audit-policy-file=""        # No audit policy!

# No runtime security monitoring
# No alerting on suspicious activities
# Container logs not collected centrally
# Default log retention — logs lost on pod restart

보안 코드 예

YAML (Kubernetes Audit Policy) ✅ Good
# Comprehensive audit policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Log all secret access at Metadata level
- level: Metadata
  resources:
  - group: ""
    resources: ["secrets"]
# Log RBAC changes at RequestResponse level
- level: RequestResponse
  resources:
  - group: rbac.authorization.k8s.io
    resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"]
# Log pod exec/attach (potential attacks)
- level: Request
  resources:
  - group: ""
    resources: ["pods/exec", "pods/attach", "pods/portforward"]
# Default: log at Metadata level
- level: Metadata

완화 체크리스트

📊 요약표

ID 취약점 심각성 주요 완화
K01안전하지 않은 워크로드 구성Critical루트가 아닌 읽기 전용 FS, 삭제 기능, 리소스 제한
K02과도한 권한 부여Critical네임스페이스 범위 RBAC, 와일드카드 없음, 감사 바인딩
K03비밀 관리 실패Critical외부 비밀, 저장 암호화, 파일 마운트
K04정책 집행 부족High포드 보안 표준, Kyverno/OPA Gatekeeper
K05네트워크 분할 누락High기본 거부 NetworkPolicy, 마이크로 세분화
K06과도하게 노출된 구성요소HighClusterIP 서비스, kubectl 프록시, 방화벽 규칙
K07잘못 구성된 클러스터 구성 요소HighCIS 벤치마크, 익명 인증 비활성화, 패치 관리
K08클러스터에서 클라우드로의 측면 이동CriticalIRSA/워크로드 아이덴티티, 블록 메타데이터 API, IMDSv2
K09손상된 인증 메커니즘CriticalOIDC 통합, 토큰 바인딩, 자동 마운트 비활성화
K10부적절한 로깅 및 모니터링Medium감사 로깅, Falco/Tetragon, SIEM 통합