Tutorial

How to design a secure Docker image

By Arnon Puitrakul - 26 พฤษภาคม 2025

How to design a secure Docker image

เมื่อไม่กี่วันก่อน มีคนมาถามประเด็นที่น่าสนใจมาก ๆ ว่า ถ้าเราอยากจะใช้งาน Container ให้ปลอดภัย เราจะมี Best Practice อะไรบ้างที่จะเข้ามาช่วย วันนี้เราจะมาสรุปสิ่งที่เราได้อ่านจากหนังสือ, ประสบการณ์ และได้ปรึกษากับหลาย ๆ คนมากันว่า เขามีทริกอะไรที่ทำให้มันปลอดภัยมากขึ้น

ใช้ Base Image ที่เป็น Official Image

ส่วนผสมแรกของ Docker Image ที่เราใช้งานกันคือ Base Image หรือ Image ที่อยู่ด้านล่างสุด แนะนำว่าเราควรจะใช้ Base Image ที่เป็น Official Image ด้วย 2 เหตุผลด้วยกัน

อย่างแรกคือ Official Image มี Maintainer ที่ดูแล Update Dependencies อย่างสม่ำเสมอ ทำให้หากเกิดช่องโหว่ขึ้นมา ก็สามารถถูกแก้ไขได้อย่างทันท่วงที กับอีกเหตุผลคือ Image พวกนี้ถูกตรวจสอบโดยคนจำนวนมาก ทำให้การที่จะมีการสอดไส้ช่องโหว่ หรือ Attack Vector ต่าง ๆ ย่อมทำได้ยากกว่าแน่นอน (ถามว่าเกิดได้มั้ย ได้ เราก็เห็นมาแล้วในเคสที่เกิดไม่นานมานี้ ซึ่งมันต้องอาศัยการโจมตีวิธีการอื่น ๆ ร่วมด้วย เช่น Supply-Chain Attack เป็นต้น)

แต่หากเราไม่ได้ใช้ Official Image จริง ๆ แนะนำว่า ให้ใช้ Base Image ที่ได้รับการรับรอง หรือ เราเชื่อว่ามันปลอดภัย และอย่าลืม..... อัพเดท Base Image ของเราอย่างสม่ำเสมอด้วย

เปิดการใช้งาน Docker Content Trust

Docker Content Trust (DCT) ทำให้เรามั่นใจว่า Image ที่เราดึงมาใช้งานนั้นถูก Signed และสามารถตรวจสอบความถูกต้องของ Image ได้ เพื่อป้องกันการปลอม Image ขึ้นมา โดยอาจจะปลอมเป็น Repository Server แล้วโยน Image ปลอมที่แอบเสียบของน่ากลัวออกมาให้รัน ก็คือชิบหายการช่างได้เลย

ดังนั้นการเปิดใช้งาน DCT ด้วยการ Sign Image ของเราและมีการ Verify Key ตอนที่ Pull Image ด้วย ก็ย่อมทำให้เราปลอดภัยขึ้นมาอีกขั้นหนึ่ง

Rootless Please

สิ่งนึงที่ Podman มี และเราค่อนข้างชอบมาก ๆ คือ การที่มันทำงานแบบ Rootless ทำให้ ไม่ว่ายังไง มันก็จะไม่สามารถเข้าถึงสิทธิ์ของ Root และยากที่จะทำ Privilege Escalation ได้ แต่ในกรณีที่แย่ที่สุด เมื่อมันเกิดปัญหาขึ้นจริง ๆ อย่างน้อย เราสามารถ Damage Control ที่ผู้โจมตีสามารถทำได้

RUN useradd -r -s /bin/false app_user
USER app_user

วิธีการก็คือ ใน DockerFile แทนที่เราจะรันด้วยสิทธิ์ Root ที่ Docker มี เราทำการสร้าง User ขึ้นมา ตัวนึงขึ้นมาเพื่อรันแทน และแนะนำให้มีการจัดการสิทธิ์การเข้าถึงของ User นี้อย่างชัดเจน เพื่อทำให้เรามั่นใจได้ว่า หากเกิดการโจมตีขึ้นมาจริง ๆ User นี้จะไม่สามารถเข้าถึงสิ่งที่มันไม่ควรเข้าถึงได้ พูดง่าย ๆ คือ เราพยายามเอาหลักการ The Principle of Least Privilege (POLP) เข้ามาใช้นั่นเอง

ลด Attack Surface

หนึ่งในพื้นฐานของ Cyber Security เพื่อทำให้เราปลอดภัย ที่สำคัญคือ การลด Attack Surface ที่อาจจะเกิดขึ้นให้ได้มากที่สุด แน่นอนว่า Image ที่มี Dependencies มากขึ้น ก็ย่อมสร้าง Attack Surface ที่มากขึ้น ดังนั้น วิธีการที่จะลดได้นั้น ก็คือ การลด Dependencies ที่ไม่จำเป็นให้ได้มากที่สุด พูดง่าย ๆ คือพยายามทำให้ Image ของเรา Lean ให้ได้มากที่สุด

อย่างแรกคือ ตัดสิ่งที่ไม่จำเป็นออกไปก่อน พวก Software ที่เราใช้ในการ Build ต่าง ๆ ที่ไม่ได้จำเป็นต่อ Runtime แนะนำว่า ใช้งานเสร็จตอน Build แล้วก็ให้เอาออกไปเลย และอีกอย่างที่ทำได้คือให้เราใช้ Base Image ที่มัน Slim บางเบาอยู่แล้ว เช่นแทนที่เราจะใช้ Ubuntu ที่มี Dependencies ติดมาเยอะ ๆ ครบ ๆ เราลองไปใช้พวก Alpine ที่เล็กลงมา และมีแค่ส่วนที่เราต้องใช้เท่านั้น เอาจริง ๆ คือ ถ้าให้ดีที่สุดเลย เหลือแค่ Runtime หรือ Executable ที่เราต้องใช้ก็พอแล้ว อย่าให้เหลืออย่างอื่น

Secret Management using Docker Secret

บางครั้งใน Image ของเราอาจจะประกอบด้วยข้อมูลที่ Sensitive ต่าง ๆ เช่นพวก API Key, Password และพวก Token ที่เราต้องใช้สำหรับการ Authenticate ทำงานกับ Service ต่าง ๆ ที่เราใช้งานร่วมกัน ซึ่งการ Publish Secret พวกนี้เข้าไปสามารถสร้างความชิบหายกับเราได้ ดังนั้น เราจะต้องมีวิธีการบางอย่าง นั่นคือการใช้งาน Docker Secret ที่เข้ามาช่วยเราจัดการพวก Sensitive Information ต่าง ๆ ได้ สำหรับวิธีการก็ไปอ่านใน Documentation เอาละกันนะ

สรุป

ทั้งหมดนี้ก็คือทริกเบื้องต้นสำหรับการสร้าง Docker Image ที่ปลอดภัยมากขึ้น เรื่องนึงที่เราอยากจะให้เป็น Key Takeaway คือ เราอาจจะคิดว่า Docker มันก็รันแค่ Container ตัวนึงทำอะไรไม่ได้มาก แต่จริง ๆ แล้ว มันรันอยู่บน Root หลุดได้แรง กว้าง และหนักกว่าที่เราคิดเยอะมาก ๆ การพยายามปิดช่องโหว่เพื่อให้ระบบปลอดภัยมากขึ้นจึงเป็นสิ่งสำคัญที่ควรคำนึงถึงเป็นอันดับแรก ๆ เสมอ Safety First เนอะ