คุณอาจรัน Docker ใน production มาหลายเดือนโดยไม่พบปัญหาที่มองเห็นได้ container เริ่มทำงาน แอปตอบสนอง ทุกอย่างดูปกติ แต่แค่ port ที่เปิดทิ้งไว้หรือสิทธิ์ที่ตั้งค่าผิดพลาดเพียงจุดเดียว ก็เปิดช่องให้ผู้โจมตีเข้ามาได้โดยไม่ต้องพยายามมาก ข้อผิดพลาดด้านความปลอดภัยส่วนใหญ่ใน Docker ไม่ได้ดูเหมือนข้อผิดพลาด จนกว่าจะเกิดเหตุการณ์จริง
บทความนี้ครอบคลุมการตั้งค่าเฉพาะที่ทำให้สภาพแวดล้อม container มีความเสี่ยง อธิบายว่าแต่ละการตั้งค่าเปิดช่องให้ผู้โจมตีทำอะไรได้บ้าง และปิดท้ายด้วย checklist ที่คุณนำไปตรวจสอบ setup ของตัวเองได้ทันที
ทำไมความปลอดภัยของ Docker จึงยากกว่าที่คิด
Container ให้ความรู้สึกว่าแยกตัวออกจากกัน คุณสร้างมันขึ้นมา มันรัน process space ของตัวเอง และจากภายใน container หนึ่งก็ไม่รู้จัก container อื่นเลย แต่การแยกตัวนั้นเป็นเพียงบางส่วน เพราะ container ใช้ kernel ของ host ร่วมกัน นั่นหมายความว่า process ภายใน container สามารถเข้าถึง host system ได้ทั้งหมดภายใต้เงื่อนไขบางอย่าง
Docker ถูกตั้งค่าเริ่มต้นมาเพื่อความสะดวกของนักพัฒนา ไม่ใช่เพื่อความปลอดภัยในระดับ production เปิด root access ไว้ ทุก port สามารถ bind กับทุก interface ไม่มี runtime monitoring นักพัฒนาส่วนใหญ่ยอมรับการตั้งค่าเหล่านั้น deploy container ออกไป แล้วก็ผ่านไป วิธีนี้สมเหตุสมผลสำหรับการเริ่มต้น แต่ไม่ใช่ security posture ที่สมบูรณ์
ตามที่ รายงาน 2024 State of Kubernetes Security ของ Red Hatองค์กรถึง 67% ล่าช้าหรือชะลอการ deploy แอปพลิเคชันเนื่องจากความกังวลด้านความปลอดภัยของ container หรือ Kubernetes ส่วนใหญ่ความล่าช้านั้นไม่ได้มาจากการถูกโจมตี แต่มาจากทีมที่ค้นพบว่า container setup ของตัวเองต้องการการ hardening ที่ยังไม่ได้ทำไว้
เราพบบ่อยครั้งที่ container รันใน production ด้วยการตั้งค่าชุดเดิมกับที่ใช้บนเครื่องของนักพัฒนา นั่นคือจุดที่ความผิดพลาดด้านความปลอดภัยของ Docker สะสมอย่างเงียบๆ โดยไม่มีสัญญาณใดๆ จนกว่าจะมีการตรวจสอบหรือเกิดปัญหาขึ้น
ความผิดพลาดที่ก่อให้เกิดช่องโหว่เหล่านั้นมีลักษณะเฉพาะ คาดเดาได้ และส่วนใหญ่หลีกเลี่ยงได้ตั้งแต่ระดับการตั้งค่า
ความผิดพลาดในการตั้งค่า Docker ที่พบบ่อย
การละเมิดความปลอดภัยของ container ส่วนใหญ่ไม่ได้เริ่มจาก zero-day exploit แต่เริ่มจากการตั้งค่าที่ทำไว้ตั้งแต่วันแรก โดยไม่ได้คิดมากเรื่อง network exposure หรือขอบเขตสิทธิ์
การตั้งค่าเริ่มต้นของ Docker ถูกออกแบบมาให้ใช้งานได้ ความเสี่ยงด้านความปลอดภัยของ Docker container สะสมอยู่ในช่องว่างระหว่าง "ใช้งานได้" กับ "ปลอดภัย" โดยเฉพาะใน self-hosted setup ที่ deploy แล้วไม่เคยกลับมาตรวจสอบอีก
เราเห็น pattern นี้บ่อยครั้ง: container บน server ที่มี public IP พร้อม port binding, การตั้งค่า user และ network configuration เหมือนกันทุกอย่างกับตอนที่ deploy ครั้งแรก
การรัน Container ในฐานะ Root
เมื่อคุณสร้าง Docker container โดยไม่ระบุ user มันจะรันในฐานะ root นั่นหมายความว่า process ทุกตัวภายใน container รวมถึงแอปพลิเคชันของคุณ จะมีสิทธิ์ระดับ root ภายใน namespace ของ container

การเป็น root ภายใน container ไม่เท่ากับการเป็น root บน host แต่การแยกส่วนนั้นไม่สมบูรณ์ Privilege escalation exploit ที่มุ่งเป้า runtime เช่น runc CVE-2019-5736 ที่มีเอกสารอ้างอิงชัดเจน และ runtime flaw ในลักษณะเดียวกัน มักต้องการ root container process เพื่อให้สำเร็จ
Container ที่ไม่ใช้ root จะตัดเงื่อนไขที่ exploit เหล่านั้นต้องพึ่งพาออกไป ซึ่งช่วยลด attack surface สำหรับช่องโหว่ประเภทนั้นได้อย่างมีนัยสำคัญ แม้จะไม่ได้กำจัดความเสี่ยงของ container escape ออกไปทั้งหมด
การเพิ่ม USER directive ใน Dockerfile ของคุณแก้ปัญหานี้ได้ image ทางการบางตัวมาพร้อม unprivileged user ที่คุณเปิดใช้งานได้ด้วย USER directive แต่หลายตัวยังคง default เป็น root โดยไม่มี app user สำเร็จรูป ในกรณีนั้น คุณต้องสร้าง user ใน Dockerfile ก่อนแล้วค่อย switch ไปใช้ สำหรับ self-hosted setup ส่วนใหญ่ การเปลี่ยนแปลงเพียงอย่างเดียวนี้ช่วยตัดความเสี่ยงจากการ escalation ทั้งหมวดออกไปได้
การเปิด Port มากเกินไปสู่ Public Internet
เมื่อคุณ publish port ด้วย Docker มันจะเขียน iptables rules โดยตรง rules เหล่านั้นทำงานก่อน firewall rules ระดับ host นี่คือ พฤติกรรมที่ community รายงานและรู้จักกันดี และ มีเอกสารใน packet filtering guide ของ Dockerไม่ใช่การตั้งค่าผิด และนั่นหมายความว่า UFW และเครื่องมือในลักษณะเดียวกันจะไม่บล็อก port ที่ Docker เปิดไว้แล้ว

Docker เขียนตรงไปที่ iptables โดยข้าม UFW และค่าเริ่มต้นของ firewalld บน Linux host หลายตัว นั่นหมายความว่า port ที่ bind กับ 0.0.0.0 อาจเข้าถึงได้จากสาธารณะแม้ firewall ของคุณดูเหมือนตั้งค่าแล้ว Cloud security group และ DOCKER-USER chain rules ยังสามารถบล็อก traffic นั้นได้ ดังนั้น exposure จริงขึ้นอยู่กับ network setup เฉพาะของคุณ
ผูก service ไว้กับ 127.0.0.1 ทุกครั้งที่ทำได้ ส่ง traffic สาธารณะผ่าน reverse proxy และเปิดเผยเฉพาะสิ่งที่ต้องการการเข้าถึงจากภายนอกจริงๆ reverse proxy คือวิธีที่เชื่อถือได้ที่สุดในการควบคุมว่าอะไรบ้างที่มองเห็นได้จากภายนอก host
การละเลยการแยก Network ระหว่าง Container
container ทุกตัวบน network เดียวกันสามารถเข้าถึงกันได้อย่างอิสระโดยไม่มีข้อจำกัด default bridge ไม่มีการกรอง traffic ระหว่าง container ที่ใช้งานร่วมกัน และส่วนใหญ่ไม่เคยเปลี่ยนการตั้งค่านี้

หาก container ตัวใดตัวหนึ่งถูกโจมตี การสื่อสารที่เปิดกว้างนั้นจะกลายเป็นเส้นทางสำหรับการเคลื่อนตัวข้างใน container ฝั่ง frontend สามารถเข้าถึง database, API ภายใน หรืออะไรก็ตามบน default bridge network เดียวกัน แม้ว่าการเข้าถึงนั้นจะไม่เคยตั้งใจให้เกิดขึ้นก็ตาม
User-defined network ให้คุณควบคุมได้อย่างชัดเจนว่า container ไหนสื่อสารกันได้บ้าง แต่ custom network เดียวที่ใช้ร่วมกันทั้งหมดก็ยังเปิดให้ traffic ไหลเวียนระหว่าง container ได้อย่างอิสระ การแยกที่แท้จริงต้องวาง service ที่ไม่ควรสื่อสารกันไว้บน network แยกกัน การปิด default bridge คือจุดเริ่มต้น ไม่ใช่จุดสุดท้าย
การมองข้าม Docker Socket
Docker socket ที่ /var/run/docker.sock คือ interface สำหรับควบคุม Docker engine ทั้งหมด การ mount เข้าไปใน container จะให้ container นั้นเข้าถึง API daemon ที่รันอยู่บน host โดยตรง

ด้วยการเข้าถึงนั้น container สามารถเริ่ม container ใหม่ mount directory ของ host ตรวจสอบและแก้ไข container ที่กำลังรันอยู่ และควบคุม host machine ได้อย่างมีประสิทธิภาพ พื้นที่โจมตีเทียบเท่ากับ root บน host ดังนั้นเครื่องมือใดที่ต้องการเข้าถึง socket จึงควรได้รับการพิจารณาอย่างรอบคอบ
สำหรับกรณีใช้งานส่วนใหญ่ มีทางเลือกที่ปลอดภัยกว่า เช่น API แบบกำหนดขอบเขต หรือ เครื่องมือจัดการ Docker ที่ไม่จำเป็นต้องเข้าถึง socket Docker-in-Docker มีข้อแลกเปลี่ยนด้านความปลอดภัยและการดำเนินงานของตัวเอง และไม่ใช่ทางเลือกทดแทนที่ตรงไปตรงมา
ความผิดพลาดด้าน configuration สร้างช่องโหว่เริ่มต้น ส่วนการเลือก image และ dependency กำหนดว่าช่องโหว่นั้นจะขยายตัวอย่างไรตามเวลา
ความผิดพลาดด้าน Image และ Secret ที่ยังคงอยู่แม้ Container จะหยุดทำงาน
เมื่อคุณหยุด container ความผิดพลาดด้าน configuration ภายในก็หยุดไปด้วย แต่เมื่อคุณ rebuild จาก image ที่มีช่องโหว่หรือ credential ที่ฝังค่าไว้ตายตัว ปัญหาก็จะกลับมาพร้อมกับ container ความผิดพลาดในระดับ image ไม่ได้ถูก reset ระหว่างการ deploy
ความผิดพลาดเหล่านั้นติดไปกับ image ทุกสภาพแวดล้อมที่ดึง image ไปใช้ ทุก registry ที่จัดเก็บ และทุกคนในทีมที่รัน image นั้น ความถาวรนี้ทำให้การจัดการ image และ secret เป็นความเสี่ยงประเภทหนึ่งที่แยกออกมาต่างหาก และควรตรวจสอบแยกจาก configuration
รูปแบบนี้เกิดขึ้นบ่อย นั่นคือ image ที่เลือกอย่างระมัดระวังตั้งแต่เริ่มโปรเจกต์และไม่เคย rebuild อีกเลย ค่อยๆ ห่างจาก baseline ด้านความปลอดภัยที่เคยเป็นอยู่
การใช้ Image ที่ไม่น่าเชื่อถือหรือล้าสมัย
ทุกคนสามารถอัปโหลดไปยัง public registry ได้ มี image อันตรายที่ถูกแจกจ่ายผ่าน Docker Hub โดยฝัง crypto-miner และ backdoor ไว้ใน layer history ที่ยังคงอยู่แม้ container จะ restart การตรวจสอบก่อนดึง image มาใช้จึงสำคัญมาก โดยเฉพาะ image จากผู้เผยแพร่ที่ไม่เป็นทางการหรือไม่รู้จัก

อีกปัญหาหนึ่งคือความล้าสมัย official image ที่คุณดึงมาเมื่อหกเดือนก่อนและไม่เคย rebuild นั้น กำลังสะสมช่องโหว่ Docker ที่ยังไม่ได้แก้ไขทุกครั้งที่มีการเปิดเผย CVE กับ package ของมัน image ไม่ได้เสียหาย เพียงแต่ไม่ทันสมัยอีกต่อไป
รายงาน Sonatype's 2024 State of the Software Supply Chain พบว่า 95% ของเวลาที่มีการใช้งาน component ที่มีช่องโหว่ จะมี version ที่แก้ไขแล้วพร้อมให้ใช้งาน และ 80% ของ dependency ในแอปพลิเคชันยังไม่ได้รับการอัปเกรดมานานกว่าหนึ่งปี รูปแบบนี้เกี่ยวข้องกับ Docker base image เช่นกัน เนื่องจากใช้ open-source package ชุดเดียวกัน
ใช้ official image จากผู้เผยแพร่ที่ผ่านการตรวจสอบและระบุ version tag ที่แน่นอนแทนการใช้ "latest" วางแผน rebuild อย่างสม่ำเสมอเพื่อให้ image ของคุณทันสมัยอยู่เสมอ
การ Hardcode Secret ใน Docker Files และ Compose Files
Credential ที่เขียนตรงลงใน ENV หรือ ARG instruction ของ Docker file, hardcode ไว้ใน Compose environment block, ส่งผ่านเป็น build argument หรือเก็บในไฟล์ .env ที่ commit เข้า version control นั้นไม่ได้หายไปพร้อมกับการหยุด container แต่ยังคงอยู่ใน image layer history หรือ source control ซึ่งใครก็ตามที่เข้าถึงสิ่งเหล่านั้นได้ก็สามารถดูข้อมูลได้

นี่คือข้อผิดพลาดด้านความปลอดภัยของ Docker ที่ถูกมองข้ามมากที่สุด เพราะในระหว่าง development จะไม่เห็นปัญหาใดๆ API key ใน ENV instruction ทำงานได้ปกติ แต่มันก็อยู่ใน repository ของคุณ ฝังอยู่ใน image และกระจายไปทุกที่ที่ image นั้นถูกใช้งาน
Docker Compose รุ่นใหม่รองรับ secrets mechanism แบบ native ที่ mount credential ในเวลา runtime โดยไม่ต้องฝังไว้ใน image Docker secrets API และ external secrets manager ก็ใช้หลักการเดียวกัน วิธีการเหล่านี้ช่วยให้ credential ไม่ปรากฏใน build artifact หรือไฟล์ที่ commit เข้า repository เลย
การใช้ environment variable ในเวลา runtime ดีกว่าการ hardcode credential แต่ยังคงถูกเปิดเผยผ่าน Docker inspect output, log และ crash dump ถือเป็นการพัฒนาขึ้นจากการฝัง secret ไว้ใน image แต่ยังไม่ใช่แนวทางที่สมบูรณ์
การไม่อัปเดต Container Image อย่างสม่ำเสมอ
การรัน image เดิมนานหลายเดือนเป็นเรื่องที่พบบ่อย ทุกวันที่ผ่านไปหลังจากมีการเปิดเผยช่องโหว่ใหม่ แต่ก่อนที่คุณจะ rebuild นั้น container ของคุณมีช่วงเวลาที่เสี่ยงต่อการถูกโจมตีและยิ่งนานขึ้นเรื่อยๆ โดยไม่มีสัญญาณใดให้เห็น
วางแผนตาราง rebuild ที่สม่ำเสมอ ทำให้เป็น automation ทุกที่ที่ทำได้ และรัน vulnerability scanner กับ image ปัจจุบันเป็นระยะ เป้าหมายไม่ใช่ความสมบูรณ์แบบ แต่คือการลดช่วงเวลาระหว่างการปล่อย patch กับการ deploy จริง
Access control และ monitoring มักถูกลดลำดับความสำคัญในการ deploy ที่รวดเร็ว และนี่คือหมวดหมู่ที่เหตุการณ์ด้านความปลอดภัยมักถูกตรวจพบช้าที่สุด
ช่องว่างด้าน Access Control และ Visibility
เมื่อ container ทำงานด้วย configuration ที่ดีและ image ที่เป็นปัจจุบันแล้ว ยังมีปัญหาสองประเภทที่หลงเหลืออยู่ ทั้งสองมองไม่เห็นโดยธรรมชาติ คุณจะไม่รู้ว่า access control อ่อนแอจนกว่าจะมีคนใช้ประโยชน์จากมัน และจะไม่รู้ว่ามี monitoring gap จนกว่าจะต้องสืบสวนกิจกรรมที่ไม่เคยถูกบันทึกไว้
เหมือนกัน งานวิจัยของ Red Hat ปี 2024 พบว่า 42% ของทีมขาดความสามารถเพียงพอในการรับมือกับความปลอดภัยของ container และภัยคุกคามที่เกี่ยวข้อง
เราพบว่า monitoring gap มักปรากฏให้เห็นในระหว่างการสืบสวนเหตุการณ์ ไม่ใช่ก่อนหน้านั้น กว่าจะตระหนักว่า visibility สำคัญ มักเป็นตอนที่กำลังตอบสนองต่อเหตุการณ์แล้ว ไม่ใช่การป้องกันล่วงหน้า
การ Authentication ที่อ่อนแอและ Management Dashboard ที่เปิดเผย
ถ้า container management dashboard วางอยู่บน public IP โดยไม่มีการ authenticate ผู้โจมตีไม่จำเป็นต้องมีทักษะสูง แค่รู้ address ก็พอ ซึ่งเป็นเงื่อนไขที่ง่ายกว่าที่หลายทีมคิด

เครื่องมือ monitoring และ management แบบ self-hosted มักมาพร้อม web interface ที่เข้าถึงได้จากทุก network interface การทิ้งไว้บน public IP โดยไม่มีการ authenticate นั้นเหมือนกับการปล่อยให้ admin panel ของ container เปิดค้างไว้โดยไม่ล็อก
Authentication, reverse proxy และการวาง management tool ไว้ใน private network คือพื้นฐานที่ต้องมี Access control เป็นขั้นตอน configuration ที่ต้องเพิ่มเองในทุก management interface ไม่ใช่สิ่งที่เปิดใช้งานมาให้แต่แรก
หลักการเดียวกันนี้ใช้กับ Docker CLI และ GUI managementการเข้าถึงระดับ admin ต่อ daemon นั้นมีความเสี่ยงเท่ากันไม่ว่าจะใช้ interface แบบใด
การไม่ตรวจสอบกิจกรรมของ Container
ถ้า container ถูก compromise กิจกรรมของผู้โจมตีจะทิ้งร่องรอยไว้เสมอ: พฤติกรรมของ process ที่เปลี่ยนไป การเชื่อมต่อเครือข่ายที่ผิดปกติ และการแก้ไขไฟล์ที่ไม่คาดคิด หากไม่มีการเก็บ log ไว้ ร่องรอยเหล่านั้นก็ไม่มีอยู่ในรูปแบบที่คุณจะนำไปใช้ดำเนินการได้
การรวบรวม log แบบรวมศูนย์ เครื่องมือตรวจสอบ audit log ของ container และการมอนิเตอร์ runtime จะให้ข้อมูลที่จำเป็นสำหรับตรวจจับพฤติกรรมผิดปกติก่อนที่จะลุกลาม เป้าหมายไม่ใช่การวิเคราะห์ทุก log line แต่คือการมีข้อมูลพร้อมเมื่อต้องสืบสวนปัญหา
Container ที่รันอยู่บน production โดยไม่มี log pipeline และไม่มี alert ไม่ได้หมายความว่าดูแลง่าย แต่หมายความว่าไม่มีใครตรวจสอบ ทั้งสองสถานะนี้แตกต่างกันโดยสิ้นเชิง
ทำไมสภาพแวดล้อมของโครงสร้างพื้นฐานจึงสำคัญ
ความปลอดภัยของ container เริ่มต้นที่การตั้งค่า แต่การตั้งค่านั้นรันอยู่บนโครงสร้างพื้นฐาน host ที่มีปัญหาด้าน network configuration, ทรัพยากรที่ใช้ร่วมกัน หรือขาด network-level filtering จะสร้างเงื่อนไขที่ส่งผลกระทบต่อทุก container ที่รันอยู่บนมัน การตั้งค่า container ให้ถูกต้องและการตั้งค่า server ให้ถูกต้องเป็นงานคนละส่วนกัน
ช่องโหว่ความปลอดภัยของ Docker หลายอย่างถูกขยายให้รุนแรงขึ้นจากเงื่อนไขที่ container รับมาโดยตรง ได้แก่
- server แบบ shared-tenancy ที่ไม่มีการแยก hardware ระหว่าง tenant
- host kernel ที่ยังไม่ได้ติดตั้ง patch
- host ที่ไม่มี network-level filtering ในตัว
สิ่งนี้ไม่ได้ตัดความจำเป็นในการทำ configuration ตามขั้นตอนข้างต้น เพราะการ harden container อย่างถูกต้องยังคงสำคัญไม่ว่า infrastructure layer จะเป็นอะไรก็ตาม การเริ่มต้นบน isolated infrastructure ช่วยตัดความกังวลออกไปหนึ่งชั้น
ที่ Cloudzy เรามีสองแนวทางให้เลือกตามความต้องการของคุณ:
- Linux VPS: สภาพแวดล้อมที่พร้อมให้ deploy Docker เองและทำ hardening ตามขั้นตอนในบทความนี้
- Portainer VPS: ตัวเลือกแบบคลิกเดียวที่ติดตั้ง Portainer มาให้แล้ว เมื่อ server บูทเสร็จคุณก็เข้า dashboard ได้เลย
ทั้งสองตัวเลือกรันบน infrastructure เดียวกัน: KVM virtualization, AMD Ryzen 9 CPU ความเร็ว boost clock สูงสุด 5.7 GHz, หน่วยความจำ DDR5, พื้นที่จัดเก็บ NVMe SSD, เครือข่ายสูงสุด 40 Gbps และการป้องกัน DDoS ฟรีผ่าน BuyVM filtering ครอบคลุม 12 ที่ตั้งทั่วโลก พร้อม uptime SLA ที่ 99.95%
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการรัน Portainer บน VPS เราได้เขียนไว้ในบทความเฉพาะแล้ว
Checklist ความปลอดภัยเชิงปฏิบัติสำหรับการ deploy Docker
ข้อผิดพลาดด้านความปลอดภัยของ Docker ส่วนใหญ่มาจากการตัดสินใจตั้งค่าครั้งเดียวแล้วไม่เคยกลับมาทบทวน การรัน checklist นี้กับ setup ที่มีอยู่จะช่วยจับช่องโหว่เหล่านั้นได้ ใช้เป็น audit ไม่ใช่คู่มือ deploy
แนวปฏิบัติด้านความปลอดภัยของ Docker เหล่านี้ครอบคลุมวิธีป้องกัน Docker container จากความผิดพลาดในการตั้งค่าที่พบบ่อยที่สุดตามที่อธิบายไว้ข้างต้น
Quick Reference: ข้อผิดพลาดทั้ง 9 ข้อ
| ความผิดพลาด | หมวดหมู่ | วิธีแก้แบบสั้น |
| ทำงานเป็น root | การตั้งค่า | เพิ่ม USER directive ใน Dockerfile ของคุณ |
| พอร์ตที่ผูกมัดกับ 0.0.0.0 | การตั้งค่า | Bind กับ 127.0.0.1 และส่งผ่าน reverse proxy |
| ไม่มีการแยกเครือข่าย | การตั้งค่า | แยก service ไปยัง network ที่กำหนดเองแต่ละอันตามสิทธิ์การเข้าถึง |
| Docker socket ที่ mount อยู่ | การตั้งค่า | ลบ mount ออก แล้วใช้ API แบบจำกัดขอบเขตหรือตัวเลือกอื่นแทน |
| image ที่ไม่น่าเชื่อถือหรือล้าสมัย | รูปภาพ | ใช้ official image พร้อมระบุ version tag แบบตรึงค่าไว้ |
| ข้อมูลลับที่ฝังแข็ง | รูปภาพ | ย้าย credentials ไปไว้ใน runtime env vars หรือ secrets manager |
| ไม่มีกำหนดการ rebuild image | รูปภาพ | กำหนด cadence การ rebuild รายเดือน และทำให้เป็นอัตโนมัติเท่าที่ทำได้ |
| แดชบอร์ดที่ไม่ได้รับการยืนยันตัวตน | การเข้าถึง | เพิ่มการยืนยันตัวตนและย้าย management UI ไปไว้ใน private network |
| ไม่มีการเก็บ container log | การเข้าถึง | ตั้งค่าระบบ logging แบบรวมศูนย์และการ monitoring แบบ runtime |
แนะนำให้ลองรันกับระบบที่ใช้งานอยู่แล้วก่อน เพราะช่องโหว่มักซ่อนอยู่ในส่วนนั้น
Container ที่รันในโหมด non-root: ตรวจสอบ Docker ไฟล์ว่ามี USER directive หรือไม่ ถ้าไม่มี container จะรันในฐานะ root
Port binding จำกัดไว้ที่ localhost หรือผ่าน proxy: รัน docker ps แล้วตรวจสอบ port binding หาก entry เป็น 0.0.0.0:PORT จะสามารถเข้าถึงได้จากภายนอก ในกรณีที่ไม่มี security group, firewall ภายนอก หรือ rule ของ DOCKER-USER chain มาบล็อกไว้
กำลังใช้งาน custom bridge network: Container บน default bridge ของ Docker สามารถติดต่อกันได้อย่างอิสระ Container บน user-defined bridge เดียวกันก็ยังคุยกันได้ ดังนั้นควรแยก service ออกเป็น network ต่างๆ ตาม trust boundary เพื่อให้ได้ isolation ที่แท้จริง
ไม่มีการ mount Docker socket เข้าไปใน container: ตรวจสอบ Compose file และ argument ที่ใช้รัน หาก /var/run/docker.sock ปรากฏเป็น volume ให้ยืนยันว่าจำเป็นและตั้งใจให้เป็นแบบนั้น
Base image จาก publisher ที่ได้รับการยืนยัน พร้อมระบุ version แบบตรึงค่าไว้: การใช้ FROM ubuntu:latest จะดึง version ที่ไม่แน่นอนและอาจล้าสมัย ควรระบุ release ที่ต้องการให้ชัดเจน
ไม่มี secret ใน Docker ไฟล์, Compose file หรือ build argument: ประวัติ layer ของ image จะเก็บ credentials ไว้แม้จะลบ container ไปแล้ว ให้ใช้ Compose secrets, Swarm secrets, build secret mount หรือ external secrets manager แทน การใช้ runtime environment variable ดีกว่าการ hardcode แต่ยังคงปรากฏใน inspect output และ log
กำหนด schedule การ rebuild image ไว้แล้ว: Image เก่าสะสม vulnerability ไปเรื่อยๆ การ rebuild รายเดือนช่วยให้ช่วงเวลาที่เสี่ยงอยู่ในระดับที่จัดการได้สำหรับส่วนใหญ่
Management interface อยู่หลังระบบยืนยันตัวตน: Dashboard ที่เปิดบน public IP โดยไม่มีการยืนยันตัวตนถือเป็นช่องทางเข้าที่เปิดโล่ง การวางไว้ใน private network จะดีกว่าเสมอหากทำได้
กำลังเก็บ container log อยู่: ถ้าไม่มี log pipeline การตรวจจับเหตุการณ์ผิดปกติจะรอให้ระบบแสดงอาการก่อน ซึ่งนั่นช้าเกินไปสำหรับการรับมือ
สรุป
การตั้งค่าเริ่มต้นของ Docker ออกแบบมาเพื่อความสะดวก ไม่ใช่ความปลอดภัย ข้อผิดพลาดส่วนใหญ่ในบทความนี้มีต้นเหตุจากการตั้งค่าที่ไม่เคยถูกเปลี่ยนหลัง deploy ครั้งแรก ไม่ใช่จากการโจมตีที่ซับซ้อนแต่อย่างใด
การแก้ไขส่วนใหญ่เป็นการตัดสินใจตั้งค่าครั้งเดียว: USER directive, การเปลี่ยน port binding, custom network, หรือกำหนดตารางการ rebuild สำหรับระบบส่วนใหญ่ไม่จำเป็นต้องใช้เครื่องมือใหม่เลย
การตั้งค่า container ให้ถูกต้องคือสิ่งแรกที่ต้องทำ โครงสร้างพื้นฐานที่รัน container นั้นคือสิ่งที่สอง ทั้งสองส่วนสำคัญเท่ากัน และไม่มีอันไหนทดแทนกันได้