근묵자흑
[쿠버네티스] 서비스와 시크릿의 보안 취약점 및 대응 전략 본문
쿠버네티스(Kubernetes)는 현대 클라우드 네이티브 환경에서 컨테이너 오케스트레이션의 표준으로 자리잡았지만, 복잡한 아키텍처와 다양한 구성 요소로 인해 보안 취약점에 노출될 가능성이 있습니다. 특히 서비스(Service)와 시크릿(Secret) 리소스는 클러스터 운영에 필수적이지만, 잘못 구성되면 심각한 보안 위협을 초래할 수 있습니다.
1. 서비스(Service) 관련 취약점
1.1 서비스 계정 토큰 탈취
공격 방법:
- 공격자가 Pod 내부에 접근하면 자동으로 마운트되는
/var/run/secrets/kubernetes.io/serviceaccount/token
파일에서 서비스 계정 토큰을 추출할 수 있습니다. - 이 토큰은 기본적으로 만료 시간이 없어, 탈취 시 장기간 악용될 수 있습니다.
- 추출된 토큰으로 Kubernetes API 서버에 인증하여 권한 상승 공격을 수행할 수 있습니다.
실제 공격 예시:
# 공격자가 침투한 Pod 내부에서 서비스 계정 토큰 추출
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# API 서버에 인증하여 클러스터 리소스에 접근
curl -k -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default/api/v1/namespaces/default/pods
# 토큰의 권한 확인
curl -k -H "Authorization: Bearer $TOKEN" \
https://kubernetes.default/api/v1/namespaces/default/secrets
대응 방법:
- 서비스 계정 토큰 자동 마운트 비활성화:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
automountServiceAccountToken: false
containers:
- name: app
image: app:1.0
- Kubernetes 1.21+ 버전에서는 BoundServiceAccountTokenVolume 기능을 활성화하여 토큰에 만료 시간 설정:
apiVersion: v1
kind: ServiceAccount
metadata:
name: short-lived-token-account
annotations:
kubernetes.io/enforce-mountable-secrets: "true"
- 서비스 계정의 RBAC 권한 최소화:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
# secrets, configmaps 등 민감한 리소스에 대한 접근 제한
1.2 MITM(Man-in-the-Middle) 공격 (CVE-2020-8554)
취약점:
- 공격자가
ClusterIP
서비스를 생성하고spec.externalIP
를 조작하여 트래픽을 가로챌 수 있습니다. - 이 취약점은 낮은 권한으로도 클러스터 내 통신을 감청할 수 있게 합니다.
- 주요 위험은 Pod 간 통신이 공격자의 제어 하에 있는 노드로 리다이렉션되어 민감한 데이터가 유출될 수 있다는 점입니다.
취약한 서비스 예시:
apiVersion: v1
kind: Service
metadata:
name: vulnerable-service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: my-app
# 공격자가 자신의 제어 하에 있는 IP로 설정
externalIPs:
- 192.168.1.100
대응 방안:
externalIP
필드 사용을 제한하는 Admission Webhook 구현:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: restrict-external-ips
webhooks:
- name: restrict-external-ips.example.com
rules:
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["services"]
operations: ["CREATE", "UPDATE"]
clientConfig:
service:
namespace: webhook-system
name: validation-webhook
path: "/validate-external-ip"
admissionReviewVersions: ["v1"]
sideEffects: None
timeoutSeconds: 5
- OPA(Open Policy Agent) Gatekeeper 정책 설정:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sNoExternalIPs
metadata:
name: no-external-ips
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Service"]
- 네트워크 정책을 사용하여 Pod 간 통신 제한:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-pod-communications
spec:
podSelector:
matchLabels:
app: secure-app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
egress:
- to:
- podSelector:
matchLabels:
role: database
1.3 노출된 API 서버 공격
공격 방법:
- 인증 없이 Kubernetes API 서버가 외부에 노출된 경우, 공격자가 직접 API를 조작할 수 있습니다.
- 클라우드 환경에서 IAM 권한이 과도하게 설정된 경우, 클라우드 자격 증명을 통해 API 서버에 접근할 수 있습니다.
- 노출된 API 서버를 통해 공격자는 서비스를 생성, 수정, 삭제하고 클러스터를 완전히 장악할 수 있습니다.
취약점 확인 방법:
# 외부에서 API 서버 접근 가능 여부 확인
nmap -p 443,6443,8443 <cluster-ip>
# 인증 없이 API 접근 시도
curl -k https://<cluster-ip>:6443/api/v1/namespaces
대응 방안:
- API 서버에 대한 인증 강화:
# kube-apiserver 설정
--anonymous-auth=false
--enable-bootstrap-token-auth=false
--insecure-port=0 # 비보안 포트 비활성화
--secure-port=6443
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- API 서버 앞에 프록시/방화벽 설정:
# API 서버 접근 제한을 위한 방화벽 규칙
iptables -A INPUT -p tcp -s 10.0.0.0/24 --dport 6443 -j ACCEPT
iptables -A INPUT -p tcp --dport 6443 -j DROP
- 클라우드 IAM 권한 최소화 및 정기적인 감사:
// AWS IAM 정책 예시 - 최소 권한
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"eks:DescribeCluster",
"eks:ListClusters"
],
"Resource": "*"
}
]
}
1.4 NodePort 및 LoadBalancer 취약점
취약점:
- NodePort와 LoadBalancer 타입의 서비스는 클러스터 외부에 직접 노출되므로 보안 위험이 있습니다.
- 특히 NodePort 서비스는 모든 노드의 IP 주소를 통해 접근 가능하여 공격 표면이 넓어집니다.
- 인증 및 권한 부여가 제대로 구현되지 않은 경우, 무단 접근 위험이 있습니다.
취약한 서비스 구성:
apiVersion: v1
kind: Service
metadata:
name: vulnerable-nodeport
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30007 # 모든 노드의 30007 포트로 접근 가능
selector:
app: web
대응 방안:
- 인그레스 컨트롤러를 사용하여 NodePort 대신 중앙화된 접근 제어:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
spec:
tls:
- hosts:
- secure-app.example.com
secretName: tls-secret
rules:
- host: secure-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
- LoadBalancer 서비스에 소스 IP 제한 설정:
apiVersion: v1
kind: Service
metadata:
name: restricted-lb
annotations:
# AWS Load Balancer의 경우
service.beta.kubernetes.io/aws-load-balancer-source-ranges: "203.0.113.0/24,198.51.100.0/24"
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8443
selector:
app: secure-app
- 서비스 메시(예: Istio)를 사용하여 mTLS(상호 TLS) 구현:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
1.5 DNS 스푸핑 공격
취약점:
- 쿠버네티스는 클러스터 내 서비스 검색을 위해 DNS를 사용합니다.
- 공격자가 DNS 서비스(CoreDNS)에 접근하거나 조작할 경우, 서비스 간 통신을 가로채 스푸핑 공격을 수행할 수 있습니다.
- DNS 응답 위조를 통해 애플리케이션을 악성 서비스로 리다이렉션할 수 있습니다.
공격 시나리오:
- 공격자가 취약한 Pod에 접근하여 권한 상승
- CoreDNS ConfigMap 또는 DNS Pod에 대한 접근 권한 획득
- DNS 응답을 조작하여
database-service
에 대한 요청을 공격자가 제어하는 서비스로 리다이렉션
대응 방안:
- CoreDNS RBAC 권한 강화:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: kube-system
name: coredns-config-restricted
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["coredns"]
verbs: ["get"]
- DNS 보안 정책 적용:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-access
namespace: kube-system
spec:
podSelector:
matchLabels:
k8s-app: kube-dns
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
egress:
- {} # 필요한 경출 규칙 추가
- DNS 캐싱 및 검증 구현:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
cache 30
loop
reload
loadbalance
forward . /etc/resolv.conf
pprof
prometheus :9153
}
- mTLS를 통한 서비스 간 통신 암호화:
# Istio 서비스 메시 구성
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "default"
namespace: "istio-system"
spec:
mtls:
mode: STRICT
2. 시크릿(Secret) 관련 취약점
2.1 기본 암호화 미적용
문제점:
- 쿠버네티스 시크릿은 기본적으로 Base64 인코딩만 적용되며, etcd에 평문으로 저장됩니다.
- etcd 데이터베이스에 접근할 수 있는 공격자는 모든 시크릿을 쉽게 복호화할 수 있습니다.
- 기본 구성의 쿠버네티스에서는 시크릿이 노드의 tmpfs에 평문으로 저장됩니다.
취약한 시크릿 예시:
apiVersion: v1
kind: Secret
metadata:
name: unencrypted-secret
type: Opaque
data:
username: YWRtaW4= # "admin" (Base64 인코딩)
password: UEAkJHcwcmQ= # "P@$$w0rd" (Base64 인코딩)
복호화 공격 예시:
# 시크릿 데이터 추출
kubectl get secret unencrypted-secret -o jsonpath='{.data.password}' | base64 -d
# 결과: P@$$w0rd
# etcd에서 직접 데이터 추출 (etcd 접근 권한이 있는 경우)
ETCDCTL_API=3 etcdctl --cacert /etc/kubernetes/pki/etcd/ca.crt \
--cert /etc/kubernetes/pki/etcd/server.crt \
--key /etc/kubernetes/pki/etcd/server.key \
get /registry/secrets/default/unencrypted-secret
대응 방안:
- etcd 암호화 설정:
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <32바이트 base64 인코딩 키>
- identity: {}
- API 서버 설정:
# kube-apiserver 설정에 암호화 구성 파일 추가
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml
- 기존 시크릿 재암호화:
# 모든 시크릿을 재암호화
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
- 정기적인 암호화 키 순환:
# 새 키로 암호화 구성 업데이트
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key2 # 새 키
secret: <새 32바이트 base64 인코딩 키>
- name: key1 # 이전 키 (해독용)
secret: <이전 32바이트 base64 인코딩 키>
- identity: {}
2.2 RBAC 미스컨피그
공격 방법:
- 시크릿에 대한 과도한 읽기 권한이 부여된 경우, 공격자는 권한 있는 서비스 계정을 통해 모든 시크릿에 접근할 수 있습니다.
kubectl get secrets -A -o yaml
명령어로 모든 네임스페이스의 시크릿 데이터를 추출할 수 있습니다.- 특히 클러스터 관리자 권한이나 시크릿 조회 권한을 가진 서비스 계정이 노출될 경우 위험합니다.
취약한 RBAC 구성:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: global-secret-reader
subjects:
- kind: ServiceAccount
name: app-sa
namespace: default
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
대응 방안:
- 최소 권한 원칙 적용:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role # ClusterRole 대신 네임스페이스 범위 Role 사용
metadata:
namespace: app-namespace
name: limited-secret-access
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
resourceNames: ["app-specific-secret"] # 특정 시크릿으로 제한
- 네임스페이스별 시크릿 분리:
# 각 애플리케이션/팀별 네임스페이스 생성
apiVersion: v1
kind: Namespace
metadata:
name: team-a
---
# 네임스페이스 내 시크릿 생성
apiVersion: v1
kind: Secret
metadata:
name: team-a-db-credentials
namespace: team-a
type: Opaque
data:
username: dGVhbS1hLXVzZXI=
password: c2VjdXJlLXBhc3N3b3Jk
- RBAC 감사 및 모니터링:
# RBAC 권한 감사 스크립트
#!/bin/bash
echo "서비스 계정별 시크릿 접근 권한 조회:"
kubectl get serviceaccounts --all-namespaces -o json | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"' | while read sa; do
ns=$(echo $sa | cut -d/ -f1)
name=$(echo $sa | cut -d/ -f2)
echo "SA: $sa"
kubectl auth can-i --as=system:serviceaccount:$ns:$name get secrets --all-namespaces
kubectl auth can-i --as=system:serviceaccount:$ns:$name list secrets --all-namespaces
echo ""
done
2.3 장기간 유효한 토큰
위험성:
- 쿠버네티스 1.21 이전 버전에서는 서비스 계정 토큰이 만료 없이 영구적으로 유효했습니다.
- 토큰이 유출될 경우, 공격자는 제한 없이 오랜 시간 동안 API 접근이 가능합니다.
- 토큰 순환 메커니즘이 없으면 침해된 토큰을 무효화하기 어렵습니다.
문제 예시:
# 전통적인 서비스 계정 방식
apiVersion: v1
kind: ServiceAccount
metadata:
name: long-lived-token-sa
---
apiVersion: v1
kind: Secret
metadata:
name: long-lived-token-sa-token-abcde
annotations:
kubernetes.io/service-account.name: long-lived-token-sa
type: kubernetes.io/service-account-token
대응 방안:
- 토큰 만료 시간 설정 (Kubernetes 1.21+):
apiVersion: v1
kind: ServiceAccount
metadata:
name: short-lived-token-sa
---
# TokenRequest API를 사용하여 제한된 수명의 토큰 발급
apiVersion: v1
kind: Secret
metadata:
name: short-lived-token
annotations:
kubernetes.io/service-account.name: short-lived-token-sa
# 12시간 후 만료
kubernetes.io/service-account.expiration: "43200"
type: kubernetes.io/service-account-token
- 프로젝션된 서비스 계정 토큰 사용:
apiVersion: v1
kind: Pod
metadata:
name: short-lived-token-pod
spec:
containers:
- name: app
image: app:1.0
volumeMounts:
- name: token-volume
mountPath: /var/run/secrets/tokens
volumes:
- name: token-volume
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600 # 1시간 만료
audience: api-audience
- 정기적인 토큰 순환 구현:
#!/bin/bash
# 정기적인 서비스 계정 토큰 순환 스크립트
# 서비스 계정 토큰 시크릿 삭제 (자동으로 재생성됨)
kubectl delete secret $(kubectl get secrets | grep service-account-token | awk '{print $1}')
# TokenRequest API를 통한 토큰 순환
NEW_TOKEN=$(kubectl create token service-account-name --duration=24h)
# 새 토큰을 애플리케이션에 전달하는 로직 구현
2.4 시크릿 버전 관리 문제
문제점:
- 쿠버네티스는 기본적으로 시크릿 버전 관리 기능을 제공하지 않습니다.
- 시크릿 업데이트 시 이전 버전으로 롤백하기 어렵습니다.
- 시크릿 변경이 애플리케이션에 즉시 영향을 주어 서비스 중단을 초래할 수 있습니다.
시크릿 업데이트 예시:
# 시크릿 업데이트
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=newpassword123 \
-o yaml --dry-run=client | kubectl apply -f -
# 이 업데이트는 이전 버전을 덮어씁니다
대응 방안:
- 버전이 지정된 시크릿 이름 사용:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials-v2 # 버전 정보를 이름에 포함
type: Opaque
data:
username: YWRtaW4=
password: bmV3cGFzc3dvcmQxMjM=
- 시크릿에 레이블과 주석 추가:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
labels:
version: "2"
annotations:
description: "데이터베이스 인증 정보 v2"
updated-at: "2023-04-15T10:00:00Z"
type: Opaque
data:
username: YWRtaW4=
password: bmV3cGFzc3dvcmQxMjM=
- GitOps 접근 방식으로 시크릿 관리:
# Sealed Secrets를 사용한 버전 관리 예시
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: default
annotations:
sealedsecrets.bitnami.com/version: "1.2.3"
spec:
encryptedData:
username: AgBy8hCL8...
password: AgAskjl2H...
- 외부 시크릿 관리 도구 사용 (HashiCorp Vault):
# Vault와 통합하는 예시
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: db-credentials
spec:
vaultAuthRef: my-auth
mount: secret
path: db-credentials
version: 2 # 시크릿 버전 지정
destination:
name: db-credentials
create: true
2.5 CI/CD 파이프라인에서의 시크릿 노출
취약점:
- CI/CD 파이프라인에서 시크릿이 평문으로 저장되거나 로그에 노출될 수 있습니다.
- Git 저장소에 시크릿이 실수로 커밋될 위험이 있습니다.
- 배포 과정에서 시크릿 배포 중 보안 문제가 발생할 수 있습니다.
위험한 CI/CD 구성 예시:
# 위험한 Jenkins 파이프라인 예시
pipeline {
agent any
environment {
DB_PASSWORD = 'super-secret-password' # 하드코딩된 암호
}
stages {
stage('Deploy') {
steps {
sh 'kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=$DB_PASSWORD'
// 로그에 암호가 노출될 수 있음
}
}
}
}
대응 방안:
- CI/CD 시스템의 보안 시크릿 저장소 사용:
# GitLab CI/CD 보안 변수 사용 예시
deploy:
stage: deploy
script:
- kubectl create secret generic db-credentials
--from-literal=username=$DB_USERNAME
--from-literal=password=$DB_PASSWORD
variables:
# 보호된 변수로 설정
DB_USERNAME: $CI_DB_USERNAME
DB_PASSWORD: $CI_DB_PASSWORD
- Sealed Secrets 또는 SOPS 사용:
# SOPS를 사용한 암호화된 시크릿 생성
cat <<EOF > secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: $(echo -n "admin" | base64)
password: $(echo -n "secure-password" | base64)
EOF
# 시크릿 암호화
sops --encrypt --pgp <GPG_KEY_ID> secret.yaml > secret.enc.yaml
# CI/CD에서 복호화 및 적용
sops --decrypt secret.enc.yaml | kubectl apply -f -
- 외부 시크릿 관리 서비스와 통합:
# AWS Secrets Manager 통합 예시
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: aws-secrets
spec:
provider: aws
parameters:
objects: |
- objectName: "db-credentials"
objectType: "secretsmanager"
objectVersionLabel: "AWSCURRENT"
secretObjects:
- secretName: db-credentials
type: Opaque
data:
- objectName: db-credentials
key: username
- objectName: db-credentials
key: password
3. 방어 전략
3.1 서비스 보안 강화
1. API 서버 접근 제한:
# kube-apiserver 설정
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
containers:
- name: kube-apiserver
command:
- kube-apiserver
- --anonymous-auth=false # 익명 인증 비활성화
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
- --audit-log-path=/var/log/kubernetes/audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-policy-file=/etc/kubernetes/audit-policy.yaml
- --authorization-mode=Node,RBAC
- --tls-min-version=VersionTLS12
2. NetworkPolicy 적용:
# 기본 거부 정책
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
# 세부 허용 정책
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-specific-traffic
namespace: default
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 80
egress:
- to:
- podSelector:
matchLabels:
app: db
ports:
- protocol: TCP
port: 3306
3. 서비스 메시 구현:
# Istio 서비스 메시 설정
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
# 서비스 간 통신 정책
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: service-authz
namespace: default
spec:
selector:
matchLabels:
app: backend
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/frontend-sa"]
to:
- operation:
methods: ["GET"]
paths: ["/api/v1/*"]
4. 클러스터 수준 보안 정책:
# Pod Security Standards (PSS) 적용
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1
kind: PodSecurityConfiguration
defaults:
enforce: "restricted"
enforce-version: "latest"
audit: "restricted"
audit-version: "latest"
warn: "restricted"
warn-version: "latest"
exemptions:
usernames: []
runtimeClasses: []
namespaces: ["kube-system"]
3.2 시크릿 관리 개선
1. etcd 암호화 활성화:
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <32바이트 base64 인코딩 키>
- identity: {}
2. 외부 시크릿 관리 도구 사용:
AWS Secrets Manager 통합:
# AWS Secrets Manager CSI Driver 설정
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: aws-secrets
spec:
provider: aws
secretObjects:
- secretName: db-credentials
type: Opaque
data:
- objectName: db-username
key: username
- objectName: db-password
key: password
parameters:
objects: |
- objectName: "prod/db"
objectType: "secretsmanager"
3. Sealed Secrets 사용:
# 암호화된 시크릿 정의
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: mysecret
namespace: default
spec:
encryptedData:
password: AgBUqHkgMYP...
username: AgCym7j...
4. 시크릿 순환 자동화:
# Kubernetes CronJob을 사용한 시크릿 순환
apiVersion: batch/v1
kind: CronJob
metadata:
name: secret-rotation
spec:
schedule: "0 0 * * 0" # 매주 일요일 00:00
jobTemplate:
spec:
template:
spec:
serviceAccountName: secret-rotator
containers:
- name: rotator
image: secret-rotator:v1
command: ["./rotate-secrets.sh"]
restartPolicy: OnFailure
3.3 모니터링 및 감사 활성화
1. API 서버 감사 로그 설정:
# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# 시크릿 관련 작업 기록
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# 권한 변경 작업 기록
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"]
verbs: ["create", "update", "patch", "delete"]
# 서비스 변경 작업 기록
- level: Request
resources:
- group: ""
resources: ["services"]
verbs: ["create", "update", "patch", "delete"]
# 그 외 작업은 메타데이터만 기록
- level: Metadata
2. Falco 보안 모니터링 구성:
# Falco 룰 설정
apiVersion: v1
kind: ConfigMap
metadata:
name: falco-rules
namespace: security
data:
falco_rules.yaml: |-
- rule: Secret Access
desc: Detect accessing Kubernetes secret objects
condition: >
k8s.resource.type="secrets" and
ka.verb in (create, get, list, update, delete)
output: >
K8s secret accessed (user=%ka.user.name ns=%ka.target.namespace
secret=%ka.target.name verb=%ka.verb)
priority: WARNING
- rule: Pod With Sensitive Mount
desc: Detect a pod mounting a secret or configmap as volume
condition: >
k8s.pod.create and
(k8s.pod.volumes.secret.name != "" or
k8s.pod.volumes.configmap.name != "")
output: >
Pod created with sensitive mount (user=%ka.user.name pod=%ka.resp.name
ns=%ka.target.namespace vols=%jevt.value[/spec/volumes])
priority: INFO
3. 침입 탐지 및 대응 시스템 구축:
# Falco 알림 설정
apiVersion: v1
kind: ConfigMap
metadata:
name: falco-config
namespace: security
data:
falco.yaml: |-
program_output:
enabled: true
keep_alive: false
program: "curl -s -H 'Content-Type: application/json' -d @- http://alert-service:8080/falco"
3.4 네트워크 정책 구현
1. 기본 거부 정책 설정:
# 모든 네임스페이스에 기본 거부 정책 적용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: default # 각 네임스페이스에 적용
spec:
podSelector: {} # 모든 Pod 선택
policyTypes:
- Ingress
- Egress
2. 서비스별 통신 허용 정책:
# 프론트엔드 -> 백엔드 -> 데이터베이스 통신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: default
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
3. 네임스페이스 간 통신 정책:
# 서로 다른 네임스페이스 간 특정 통신만 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cross-namespace-policy
namespace: app
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
purpose: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090
4. DNS와 외부 통신 허용 정책:
# DNS 및 특정 외부 통신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-and-external
namespace: default
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Egress
egress:
# DNS 허용
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# 특정 외부 API 접근 허용
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
4. 실전 보안 시나리오
4.1 제로 트러스트 아키텍처 구현
1. 제로 트러스트 원칙:
- 내부 네트워크의 모든 통신도 기본적으로 신뢰하지 않습니다.
- 최소 권한 원칙을 모든 구성 요소에 적용합니다.
- 명시적으로 허용된 통신만 허용합니다.
2. mTLS 구현:
# Istio 서비스 메시를 통한 mTLS 적용
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
3. 애플리케이션 수준 인증 및 권한 부여:
# Istio RequestAuthentication 및 AuthorizationPolicy
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-auth
namespace: default
spec:
selector:
matchLabels:
app: backend
jwtRules:
- issuer: "issuer.example.com"
jwksUri: "https://issuer.example.com/.well-known/jwks.json"
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: backend-policy
namespace: default
spec:
selector:
matchLabels:
app: backend
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/frontend-sa"]
to:
- operation:
methods: ["GET"]
paths: ["/api/public/*"]
- from:
- source:
principals: ["cluster.local/ns/default/sa/admin-sa"]
to:
- operation:
methods: ["*"]
paths: ["/api/*"]
4. 네트워크 세그먼테이션:
# 네임스페이스 분리 및 네트워크 정책
apiVersion: v1
kind: Namespace
metadata:
name: frontend
---
apiVersion: v1
kind: Namespace
metadata:
name: backend
---
apiVersion: v1
kind: Namespace
metadata:
name: database
---
# 네임스페이스 간 네트워크 정책
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-to-backend
namespace: backend
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: frontend
참고 자료
'k8s' 카테고리의 다른 글
Nginx Ingress Controller (0) | 2025.04.27 |
---|---|
쿠버네티스 인그레스(Ingress) (0) | 2025.04.27 |
[쿠버네티스] 서비스, 네임스페이스, 컨피그맵, 시크릿 (0) | 2025.04.13 |
Pod, ReplicaSet, Deployment (0) | 2025.04.06 |
Kubernetes Pod 배포 시 발생하는 주요 오류와 해결 방법 (0) | 2025.04.06 |