Pod และ Cluster Security
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”การทำให้ Pod แข็งแรงขึ้นคือการวางหลายชั้นควบคุมที่เป็นอิสระต่อกัน คือ securityContext ต่อ container, ระดับ Pod Security Admission ต่อ namespace, และการป้องกันระดับ cluster อย่าง etcd encryption ซึ่งไม่มีชั้นไหนทดแทนอีกชั้นได้
SecurityContext: ทำให้ container แข็งแรงจากข้างใน
หัวข้อที่มีชื่อว่า “SecurityContext: ทำให้ container แข็งแรงจากข้างใน”securityContext ตั้งได้ทั้งระดับ Pod (เป็นค่า default ให้ทุก container) และระดับ container (override เฉพาะ container นั้น) field ที่สำคัญที่สุดสำหรับ hardening มีดังนี้
apiVersion: v1kind: Podmetadata: name: checkoutspec: 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 0runAsUser/runAsGroupล็อก UID/GID ที่ container รันจริง ไม่ว่าDockerfileของ image จะระบุไว้ว่ายังไงreadOnlyRootFilesystemmount root filesystem ของ container แบบอ่านอย่างเดียว ทำให้คนที่ได้ code execution ไม่สามารถเขียน payload ลง disk ให้อยู่ถาวรได้allowPrivilegeEscalation: falseกันไม่ให้ process ได้สิทธิ์มากกว่า parent ของตัวเอง เช่นผ่าน setuid binaryprivileged: falseไม่ให้ container เข้าถึง device และ kernel capability ของ host แบบเต็มรูปแบบเด็ดขาด container ที่เป็น privileged นั้นเทียบเท่ากับ root บนโหนดโดยตรง ดังนั้นควรใช้เป็นทางเลือกสุดท้าย ไม่ใช่ defaultcapabilitiesLinux แบ่งสิทธิ์ของ root ออกเป็น capability ย่อย ๆ การ dropALLแล้วเพิ่มกลับเฉพาะที่จำเป็นจริง ๆ (เช่นNET_BIND_SERVICEสำหรับ bind port ต่ำกว่า 1024) ปลอดภัยกว่าการรันด้วย user ที่มี capability set แบบ default เต็มชุดมาก
kubectl get pod checkout -o jsonpath='{.spec.securityContext}'kubectl get pod checkout -o jsonpath='{.spec.containers[0].securityContext}'Pod Security Admission: บังคับ baseline ระดับ namespace
หัวข้อที่มีชื่อว่า “Pod Security Admission: บังคับ baseline ระดับ namespace”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 แยกเลย
kubectl label --overwrite ns checkout \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/audit=restrictedapiVersion: v1kind: Namespacemetadata: 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 ให้เข้มขึ้นจริง
นอกเหนือจาก Pod: การควบคุมระดับ cluster และ network
หัวข้อที่มีชื่อว่า “นอกเหนือจาก Pod: การควบคุมระดับ cluster และ network”ทั้ง 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"]