ข้ามไปยังเนื้อหา

Pod และ Cluster Security

การทำให้ Pod แข็งแรงขึ้นคือการวางหลายชั้นควบคุมที่เป็นอิสระต่อกัน คือ securityContext ต่อ container, ระดับ Pod Security Admission ต่อ namespace, และการป้องกันระดับ cluster อย่าง etcd encryption ซึ่งไม่มีชั้นไหนทดแทนอีกชั้นได้

securityContext ตั้งได้ทั้งระดับ Pod (เป็นค่า default ให้ทุก container) และระดับ container (override เฉพาะ container นั้น) field ที่สำคัญที่สุดสำหรับ hardening มีดังนี้

apiVersion: v1
kind: Pod
metadata:
name: checkout
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
containers:
- name: checkout
image: checkout:2.3.0
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
privileged: false
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
  • runAsNonRoot ปฏิเสธไม่ให้ container start ถ้า image ของตัวเองจะรันเป็น UID 0
  • runAsUser / runAsGroup ล็อก UID/GID ที่ container รันจริง ไม่ว่า Dockerfile ของ image จะระบุไว้ว่ายังไง
  • readOnlyRootFilesystem mount root filesystem ของ container แบบอ่านอย่างเดียว ทำให้คนที่ได้ code execution ไม่สามารถเขียน payload ลง disk ให้อยู่ถาวรได้
  • allowPrivilegeEscalation: false กันไม่ให้ process ได้สิทธิ์มากกว่า parent ของตัวเอง เช่นผ่าน setuid binary
  • privileged: false ไม่ให้ container เข้าถึง device และ kernel capability ของ host แบบเต็มรูปแบบเด็ดขาด container ที่เป็น privileged นั้นเทียบเท่ากับ root บนโหนดโดยตรง ดังนั้นควรใช้เป็นทางเลือกสุดท้าย ไม่ใช่ default
  • capabilities Linux แบ่งสิทธิ์ของ root ออกเป็น capability ย่อย ๆ การ drop ALL แล้วเพิ่มกลับเฉพาะที่จำเป็นจริง ๆ (เช่น NET_BIND_SERVICE สำหรับ bind port ต่ำกว่า 1024) ปลอดภัยกว่าการรันด้วย user ที่มี capability set แบบ default เต็มชุดมาก
Terminal window
kubectl get pod checkout -o jsonpath='{.spec.securityContext}'
kubectl get pod checkout -o jsonpath='{.spec.containers[0].securityContext}'

Pod Security Admission ถูกฝังอยู่ใน API server และ GA มาตั้งแต่ Kubernetes 1.25 โดย มาแทนที่ PodSecurityPolicy รุ่นเก่า ซึ่งถูกถอดออกไปแล้วใน 1.25 นั่นคือ PodSecurityPolicy หายไปเลย ไม่ใช่แค่ deprecated ดังนั้น cluster ที่รัน 1.25 ขึ้นไปจึงต้องใช้ Pod Security Admission (หรือ policy engine ของ third-party) แทน

Pod Security Admission ทำงานผ่าน namespace label ล้วน ๆ ไม่ต้องสร้างหรือ bind policy object แยกเลย

Terminal window
kubectl label --overwrite ns checkout \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
apiVersion: v1
kind: Namespace
metadata:
name: checkout
labels:
pod-security.kubernetes.io/enforce: restricted

มี Pod Security Standards ระดับมาตรฐานสามระดับ เรียงจากหลวมสุดไปเข้มสุด

  • privileged ไม่มีข้อจำกัดใด ๆ เลย เท่ากับปิด Pod Security Admission สำหรับ namespace นั้น
  • baseline บล็อกช่องทาง privilege-escalation ที่รู้จักกันชัดเจนที่สุด (host namespace, privileged container, volume type อันตรายบางแบบ) โดยยังเข้ากันได้กับ workload ทั่วไปส่วนใหญ่
  • restricted เข้มงวดมาก ต้องมี runAsNonRoot, drop capabilities, และห้าม privilege escalation ตามแนวทาง best practice ของการทำ Pod hardening ในปัจจุบันอย่างใกล้ชิด

แต่ละระดับมีสามโหมดที่เป็นอิสระต่อกัน คือ enforce ที่บล็อก Pod ที่ไม่ผ่านเงื่อนไขจริง ๆ ไม่ให้ถูก admit ส่วน audit กับ warn เป็นแบบไม่บล็อก คือแค่ log การละเมิดหรือโชว์ warning ฝั่ง client โดยไม่หยุด Pod รูปแบบการ rollout ที่นิยมกันคือ enforce ระดับที่ cluster ผ่านอยู่แล้ว พร้อม warn/audit ที่ระดับเข้มกว่า เพื่อดูว่าอะไรจะพังก่อนที่จะขยับ enforce ให้เข้มขึ้นจริง

ทั้ง SecurityContext และ Pod Security Admission ทำงานที่ระดับ Pod แต่ security posture ที่แท้จริงต้องมีการควบคุมระดับ cluster ด้วย

Secret ถูก encode ด้วย base64 เท่านั้นโดย default ที่เป็นแค่ encoding ไม่ใช่ encryption ใครก็ตามที่มีสิทธิ์อ่าน Secret ผ่าน API (หรือเข้าถึงไฟล์ข้อมูลของ etcd โดยตรง) สามารถ decode ออกมาได้ง่ายมาก การควบคุมระดับ cluster ที่ปกป้อง Secret data at rest ได้จริงคือ etcd encryption at rest ซึ่ง API server จะ apply ให้อัตโนมัติ ทำให้ข้อมูลของ Secret ถูก encrypt ก่อนที่จะถูกเขียนลง storage ของ etcd

NetworkPolicy (พูดถึงในโมดูล Networking) ก็เป็นส่วนสำคัญของ security posture ด้วยเช่นกัน RBAC กับ Pod Security Admission ควบคุมว่าตัวตนของ Pod ทำอะไรกับ API ได้บ้างและ Pod ถูก configure ยังไง แต่ทั้งสองอย่างไม่ได้จำกัดว่า Pod ไหนคุยกับ Pod ไหนได้ผ่าน network นั่นเป็นหน้าที่ของ NetworkPolicy และ security story ที่สมบูรณ์ต้องมีทั้งสามชั้นทำงานร่วมกัน

flowchart LR
  submit["Pod submitted to namespace"] --> check{"Complies with\nnamespace enforce level?"}
  ns["Namespace label:\npod-security.kubernetes.io/enforce"] --> check
  check -- "yes" --> admitted["Pod admitted"]
  check -- "no" --> rejected["Pod rejected"]
Pod ถูก admit หรือ reject โดย Pod Security Admission ตามระดับ enforce ของ namespace นั้น
อะไรมาแทนที่ PodSecurityPolicy และตั้งแต่เมื่อไหร่
Pod Security Standards สามระดับเรียงจากหลวมสุดไปเข้มสุดคืออะไร
SecurityContext ระดับ container ควบคุมอะไรบ้างจริง ๆ
ทำไม Secret ที่ encode ด้วย base64 แล้วยังต้องมี etcd encryption at rest เพื่อความลับที่แท้จริง