Kubernetes Practice
개요
앞서 학습한 Kubernetes 개념, 역할, 실행 환경을 실제 코드와 함께 살펴봅니다.
실습에서 사용된 코드 베이스는 c2z GitHub Repository에서 확인하실 수 있습니다.
Context
실습 목적 및 환경
실습을 위해 작성한 코드와 목적을 정리하면 다음과 같습니다:
k8s사용 목적: 컨테이너 기반의 침투 테스트 환경 구축- 오픈 소스 코드로
IaC를 사용하여 어느 환경에서도 쉽게 실행될 수 있는 환경을 구축합니다. k3d를 사용하여 로컬 환경에서k8s를 실행합니다.Helm을 사용하여k8s리소스를 관리합니다.Helm Chart를 사용하여k8s리소스를 관리합니다.
- 오픈 소스 코드로
k8s실행 환경:minikubeOS:ARM Mac OS
실행 환경
Kubernetes Cluster를 실행하기 위해서 k3d를 사용합니다.
k3d는 리눅스, 윈도우, 맥OS에서 실행할 수 있습니다.
OS 별로 k3s, k3d, kubeadm을 사용할 수 있습니다.
- 리눅스:
k3s,kubeadm - 윈도우:
k3d - 맥OS:
minikube,k3d
Helm Chart 구조
실제 프로젝트에서 사용한 Helm Chart 구조는 다음과 같습니다:
charts/c2z/
├── Chart.yaml # 차트 메타데이터
├── values.yaml # 기본 설정 값
└── templates/ # Kubernetes 리소스 템플릿
├── namespaces.yaml
├── network-policies.yaml
├── resource-quotas.yaml
├── attacker-zone/
│ ├── kali-deployment.yaml
│ ├── kali-service.yaml
│ └── kali-pvc.yaml
├── monitoring/
│ ├── prometheus-deployment.yaml
│ ├── grafana-deployment.yaml
│ └── loki-deployment.yaml
└── scenarios/ # 웹 취약점 침투 시나리오
└── web-vuln/
├── dvwa-deployment.yaml
├── dvwa-service.yaml
├── juiceshop-deployment.yaml
└── juiceshop-service.yaml
Chart.yaml 메타 데이터
apiVersion: v2
name: c2z
description: A Helm chart for the c2z penetration testing lab environment
type: application
version: 0.1.0
appVersion: "1.0.0"
values.yaml 예시
values.yaml은 Chart의 설정을 중앙에서 관리할 수 있게 해줍니다.
템플릿 파일에서 {{ .Values.xxx }}로 참조할 수 있습니다.
global:
imageRegistry: docker.io
imagePullPolicy: IfNotPresent
# 공격자 환경
attacker:
enabled: true
kali:
image: kalilinux/kali-rolling:latest # kalilunux 이미지를 사용합니다.
resources: # 공격자 환경의 리소스를 할당
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
# 시나리오 설정
scenarios:
webVuln:
enabled: true
dvwa:
image: vulnerables/web-dvwa:latest
replicas: 1 # 웹 취약점 시나리오의 복제본의 갯수
Helm 배포
Helm 배포란 Chart를 사용하여 Kubernetes 리소스를 배포하는 과정을 의미합니다.
배포가 완료되면 Kubernetes 리소스가 생성되어, 쿠버네티스 클러스터에서 실행됩니다.
# Chart 설치
helm upgrade --install c2z ./charts/c2z \
--namespace c2z-system \
--create-namespace \
--wait \
--timeout 10m
# 릴리스 확인
helm list -n c2z-system
# 설정 값 확인
helm get values c2z -n c2z-system
# 동적으로 값 변경하여 업그레이드
helm upgrade c2z ./charts/c2z \
--namespace c2z-system \
--reuse-values \
--set scenarios.webVuln.enabled=true
주요 명령어 설명:
upgrade --install: 없으면 설치, 있으면 업그레이드--reuse-values: 기존 values 유지하면서 새 값만 덮어쓰기--wait: 모든 리소스가 Ready 상태가 될 때까지 대기
네임스페이스(Namespace) 격리
Namespace는 쿠버네티스 클러스터 내에서 논리적으로 환경을 분리하기 위한 개념입니다. Linux의 네임스페이스와 유사하지만,
k8s에선 쿠버네티스 리소스를 논리적으로 분리하는 역할을 합니다.
참조: 리눅스 네임스페이스
Kubernetes 클러스터 내에서 논리적으로 환경을 분리하기 위해 네임스페이스를 사용합니다. 이는 리소스 격리, 접근 제어, 리소스 쿼터 적용 등에 유용합니다.
네임스페이스 생성
# templates/namespaces.yaml
apiVersion: v1
kind: Namespace
metadata:
name: c2z-system
labels:
app.kubernetes.io/part-of: c2z
tier: infrastructure
---
apiVersion: v1
kind: Namespace
metadata:
name: scenario-web-vuln
labels:
app.kubernetes.io/part-of: c2z
tier: scenario
scenario: web-vuln
네임스페이스별 역할
프로젝트에서는 다음과 같이 네임스페이스를 분리했습니다:
| 네임스페이스 | 용도 | 포함 리소스 |
|---|---|---|
c2z-system | 인프라 및 공격자 환경 | Kali Pod, Prometheus, Grafana, Loki |
scenario-web-vuln | Web 취약점 시나리오 | DVWA, Juice Shop |
scenario-container-escape | Container Escape 시나리오 | Privileged Pod 등 |
scenario-network-attack | Network Attack 시나리오 | Legacy Services |
네임스페이스 조회
# 모든 네임스페이스 조회
kubectl get namespaces
# 특정 네임스페이스의 리소스 조회
kubectl get all -n c2z-system
kubectl get pods -n scenario-web-vuln
네트워크 정책(Network Policy)
기본적으로 Kubernetes는 모든 Pod 간 통신을 허용합니다.
보안 강화를 위해 NetworkPolicy를 사용하여 명시적으로 허용된 트래픽만 통과시킬 수 있습니다.
이번 프로젝트에선 NetworkPolicy를 사용하여 인프라 네임스페이스에 대한 인바운드 트래픽을 제한했습니다.
추후 공격 시나리오 설계가 완성되고, 스크립트를 추가하여 침투 테스트 시뮬레이션을 할 때 수정할 것 같습니다.
기본 Deny 정책
# templates/network-policies.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: scenario-web-vuln
spec:
podSelector: {} # 모든 Pod에 적용.
policyTypes:
- Ingress
- Egress
위 정책은 scenario-web-vuln 네임스페이스의 모든 Pod에 대해 기본적으로 모든 트래픽을 차단합니다.
선택적 허용 정책
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-attacker
namespace: scenario-web-vuln
spec:
podSelector:
matchLabels:
tier: vulnerable-app
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
tier: infrastructure
- podSelector:
matchLabels:
role: attacker
정책 설명:
scenario-web-vuln네임스페이스의tier: vulnerable-app레이블을 가진 Pod만 대상tier: infrastructure네임스페이스에서 오는 트래픽 허용role: attacker레이블을 가진 Pod에서 오는 트래픽 허용
DNS 및 외부 통신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: scenario-web-vuln
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
DNS 조회를 위해 kube-system 네임스페이스의 53번 포트(DNS)로의 Egress를 허용합니다.
리소스 쿼터(Resource Quota)
네임스페이스별로 CPU, 메모리, Pod 개수 등을 제한하여 리소스 과다 사용을 방지할 수 있습니다.
ResourceQuota 정의
# templates/resource-quotas.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: scenario-quota
namespace: scenario-web-vuln
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
pods: "10"
services: "5"
persistentvolumeclaims: "3"
설정 항목:
requests.cpu: 요청 가능한 총 CPU 코어 수requests.memory: 요청 가능한 총 메모리limits.cpu/memory: 제한 가능한 최대 리소스pods: 네임스페이스 내 최대 Pod 개수services: 최대 Service 개수persistentvolumeclaims: 최대 PVC 개수
리소스 쿼터 확인
# ResourceQuota 조회
kubectl get resourcequota -n scenario-web-vuln
# 상세 정보 확인 (현재 사용량 포함)
kubectl describe resourcequota scenario-quota -n scenario-web-vuln
출력 예시:
Name: scenario-quota
Namespace: scenario-web-vuln
Resource Used Hard
-------- ---- ----
limits.cpu 2 8
limits.memory 4Gi 16Gi
pods 3 10
requests.cpu 1 4
requests.memory 2Gi 8Gi
services 2 5
Pod 리소스 요청 및 제한
각 Pod(컨테이너)는 requests(최소 보장 리소스)와 limits(최대 사용 가능 리소스)를 설정할 수 있습니다.
Deployment에서 리소스 설정
# templates/attacker-zone/kali-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kali
namespace: c2z-system
spec:
replicas: 1
selector:
matchLabels:
app: kali
role: attacker
template:
metadata:
labels:
app: kali
role: attacker
spec:
containers:
- name: kali
image: kalilinux/kali-rolling:latest
command: ["/bin/sleep", "infinity"]
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
volumeMounts:
- name: kali-storage
mountPath: /root
volumes:
- name: kali-storage
persistentVolumeClaim:
claimName: kali-pvc
리소스 설정 의미:
requests.cpu: "1000m": 1 CPU 코어 보장 (1000m = 1 core)requests.memory: "2Gi": 2GB 메모리 보장limits.cpu: "2000m": 최대 2 CPU 코어 사용 가능limits.memory: "4Gi": 최대 4GB 메모리 사용 가능
리소스 단위
- CPU:
1= 1 vCPU/Core,1000m= 1 vCPU,500m= 0.5 vCPU - 메모리:
Gi(Gibibyte),Mi(Mebibyte),Ki(Kibibyte)
영구 스토리지(Persistent Volume)
Pod는 삭제되면 데이터도 함께 사라집니다.
PersistentVolumeClaim(PVC)를 사용하면 Pod의 생명주기와 무관하게 데이터를 유지할 수 있다고 합니다.
PVC 정의
# templates/attacker-zone/kali-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: kali-pvc
namespace: c2z-system
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: local-path # K3s 기본 StorageClass
주요 필드:
accessModes:ReadWriteOnce: 단일 노드에서 읽기/쓰기ReadOnlyMany: 여러 노드에서 읽기 전용ReadWriteMany: 여러 노드에서 읽기/쓰기
storageClassName: 스토리지 프로비저너 지정
Pod에서 PVC 마운트
volumes:
- name: kali-storage
persistentVolumeClaim:
claimName: kali-pvc
volumeMounts:
- name: kali-storage
mountPath: /root
이렇게 하면 Kali Pod를 삭제하고 재생성해도 /root 디렉토리의 데이터는 보존됩니다.
서비스(Service) 노출
Pod는 IP가 동적으로 변경되므로 Service를 통해 안정적인 엔드포인트를 제공합니다.
Service 타입
| 타입 | 용도 | 접근 범위 |
|---|---|---|
ClusterIP | 클러스터 내부 통신 | 내부 전용 (기본값) |
NodePort | 노드 포트를 통한 외부 노출 | 외부 접근 가능 |
LoadBalancer | 클라우드 LB 프로비저닝 | 외부 접근 (클라우드) |
ExternalName | 외부 DNS 매핑 | 외부 서비스 참조 |
ClusterIP Service 예시
# templates/scenarios/web-vuln/dvwa-service.yaml
apiVersion: v1
kind: Service
metadata:
name: dvwa
namespace: scenario-web-vuln
spec:
type: ClusterIP
selector:
app: dvwa
ports:
- protocol: TCP
port: 80
targetPort: 80
클러스터 내부에서 dvwa.scenario-web-vuln.svc.cluster.local:80으로 접근할 수 있습니다.
Port-Forward를 통한 로컬 접근
# 로컬 8080 포트를 Pod의 80 포트로 포워딩
kubectl port-forward -n scenario-web-vuln svc/dvwa 8080:80
# 브라우저에서 http://localhost:8080 접속
모니터링 스택
실시간 메트릭과 로그 수집을 위해 Prometheus, Grafana, Loki를 배포했습니다.
Prometheus (메트릭 수집)
# templates/monitoring/prometheus-deployment.yaml (간소화)
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: c2z-system
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
spec:
containers:
- name: prometheus
image: prom/prometheus:latest
args:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=7d'
ports:
- containerPort: 9090
Grafana (시각화)
# templates/monitoring/grafana-deployment.yaml (간소화)
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: c2z-system
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
spec:
containers:
- name: grafana
image: grafana/grafana:latest
env:
- name: GF_SECURITY_ADMIN_PASSWORD
value: "admin"
ports:
- containerPort: 3000
모니터링 접근
# Prometheus 접근
kubectl port-forward -n c2z-system svc/prometheus 9090:9090
# Grafana 접근
kubectl port-forward -n c2z-system svc/grafana 3000:3000
배포 플로우
전체 시스템을 배포하는 과정을 정리하면 다음과 같습니다:
1. 자동 설치 스크립트
#!/bin/bash
# install.sh (핵심 로직만 발췌)
# Python 가상환경 설정
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# OS별 K8s 설치
OS_TYPE=$(uname -s)
if [[ "$OS_TYPE" == "Darwin" ]]; then
# macOS -> k3d
k3d cluster create c2z --port "80:80@server:0" ...
else
# Linux -> K3s
curl -sfL https://get.k3s.io | sh -s - ...
fi
# Helm 설치
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# c2z Chart 배포
helm upgrade --install c2z ./charts/c2z \
--namespace c2z-system \
--create-namespace \
--wait \
--timeout 10m
2. CLI를 통한 시나리오 관리
# c2z-cli.py (핵심 로직)
import click
import subprocess
@click.command()
@click.argument('scenario_id')
def deploy(scenario_id):
"""시나리오 배포 (활성화)"""
key = to_config_key(scenario_id) # 'web-vuln' -> 'webVuln'
cmd = [
"helm", "upgrade", "c2z", "./charts/c2z",
"--namespace", "c2z-system",
"--reuse-values",
"--set", f"scenarios.{key}.enabled=true",
"--wait"
]
subprocess.run(cmd, check=True)
3. 배포 확인
# 전체 Pod 상태 확인
kubectl get pods -A
# 특정 네임스페이스 리소스 확인
kubectl get all -n c2z-system
kubectl get all -n scenario-web-vuln
# 로그 확인
kubectl logs -n scenario-web-vuln deployment/dvwa --tail=50 -f
Hindsight
사이버 보안 과정의 네트워크 보안 수업에서 들은 내용이 다수 있어서, 복습과 새로 공부할 내용이 많습니다.
정리하자면, 이번 실습을 통해 다음과 같은 Kubernetes 핵심 개념을 학습했습니다:
-
클러스터 구축
- K3s (Linux 네이티브)
- k3d (Docker 기반)
- OS별 차이점과 선택 기준
-
Helm을 통한 패키지 관리
- Chart 구조와 템플릿화
- values.yaml을 통한 중앙 설정 관리
- 동적 values 변경을 통한 업그레이드
-
네임스페이스 격리
- 논리적 환경 분리
- 라벨을 통한 그룹핑
- 네임스페이스별 리소스 관리
-
네트워크 정책
- 기본 Deny 정책
- 선택적 Ingress/Egress 허용
- 레이블 기반 트래픽 제어
-
리소스 관리
- ResourceQuota를 통한 네임스페이스별 제한
- Pod별 requests/limits 설정
- 리소스 효율성과 안정성 균형
-
영구 스토리지
- PVC를 통한 데이터 지속성
- StorageClass와 프로비저닝
- Volume 마운트 방식
-
서비스 노출
- ClusterIP, NodePort, LoadBalancer 차이
- Port-forward를 통한 로컬 접근
- 서비스 디스커버리 (DNS)
-
모니터링
Prometheus메트릭 수집Grafana대시보드Loki로그 집계
다음 글에서는 Pod를 구성하는 Helm의 yaml 포맷 및 문법을 학습해보도록 하겠습니다.