ใส่รหัสไว้ใน Kubernetes Secret แล้วจบหรือยัง

การย้ายรหัสออกจาก source code ไปอยู่ใน Kubernetes Secret เป็นจุดเริ่มที่ช่วยแยกข้อมูลลับจากตัวแอปได้ แต่หลังจากสร้าง object แล้ว เรายังต้องรู้ว่ามันถูกเก็บอยู่ที่ไหน ใครอ่านได้ และแอปรับค่าไปใช้อย่างไร
ถ้าหยุดที่คำว่า Secret เราอาจเผลอคิดว่าส่วนที่เหลือถูกป้องกันให้ครบแล้วครับ ทั้งที่ตัว object ยังอยู่ในระบบสิทธิ์ การจัดเก็บ และขั้นตอน deploy ที่ต้องตั้งให้ตรงกับงานเหมือนทรัพยากรสำคัญส่วนอื่น
ลองไล่เส้นทางจากคนหรือระบบที่สร้างค่า ไปถึงแอปที่ใช้ แล้วดูแต่ละขอบเขตแยกกัน จะช่วยให้เห็นว่ามีสำเนาอยู่ตรงไหนบ้างและควรเปลี่ยนค่าอย่างไรโดยไม่ทำให้บริการหลุดพร้อมกัน
base64 ช่วยแทนข้อมูล ไม่ได้ปิดความหมายให้คนมีสิทธิ์อ่าน
ข้อมูลใน field บางรูปแบบของ Secret ถูกเก็บเป็น base64 ซึ่งเป็นการเข้ารหัสแทนรูปแบบข้อมูล ไม่ใช่การเข้ารหัสเพื่อความลับ คนที่อ่านค่าชุดนั้นได้ก็สามารถนำกลับเป็นข้อมูลเดิมได้ตามวิธีที่กำหนด
ดังนั้นการนำ manifest ที่มี base64 ไปใส่ repo ก็ยังต้องถือว่าเนื้อหาข้อมูลลับอยู่ในไฟล์นั้น ไม่ทำให้มันเผยแพร่ได้เพียงเพราะคนอ่านด้วยตาไม่เห็นรหัสตรงๆ และการลบจากไฟล์ปัจจุบันก็ยังต้องพิจารณาประวัติที่เคยแจกหรือเก็บไว้แล้วด้วย

ควรแยกวิธีเก็บใน version control ออกจากวิธีส่งเข้า cluster ตาม workflow ที่เลือก หากใช้ระบบเข้ารหัสหรือเครื่องมือจัดการภายนอก ก็ต้องรู้ว่าใครถือ key และระบบใดมีสิทธิ์เปิดค่าจริง ไม่ใช้ชื่อเครื่องมือเป็นหลักฐานแทนการตรวจสิทธิ์ของมัน
log ของ pipeline กับไฟล์ชั่วคราวก็ควรถูกนับเป็นเส้นทางหนึ่ง อย่าแก้ปัญหา deploy ด้วยการพิมพ์ค่าออกมาทั้งชุดเพื่อเทียบ เพราะข้อมูลอาจถูกเก็บในระบบ log ที่มีคนเห็นมากกว่าผู้ที่มีสิทธิ์อ่าน Secret โดยตรง
ที่เก็บใน cluster ต้องตรวจการป้องกันจริง
Kubernetes ไม่ได้ทำให้ข้อมูล Secret ในที่เก็บถูกเข้ารหัสเพื่อความลับครบทุกการติดตั้งเพียงเพราะเป็น object ชนิดนี้ ผู้ดูแลต้องตรวจ encryption at rest และการตั้งค่าที่ระบบใช้จริง รวมถึงสิทธิ์กับสำเนาของ datastore ตามรูปแบบคลัสเตอร์
ถ้ามี backup ของข้อมูล cluster สำเนานั้นก็อาจมีข้อมูลสำคัญที่เกี่ยวข้อง จึงต้องวางขอบเขตเข้าถึงและวิธีเก็บให้สอดคล้องกัน ไม่ป้องกันเฉพาะ API ที่คนใช้อ่าน แต่ปล่อยสำเนาระดับระบบไว้กว้างกว่าโดยไม่รู้ตัว
การแยก namespace ช่วยจัดขอบเขตทรัพยากรและนโยบายได้ แต่ไม่ได้แทนการเข้ารหัสหรือทำให้การกำหนด RBAC กว้างๆ กลายเป็นสิทธิ์แคบเอง ต้องดูว่าตัวตนที่ใช้งานมีความสามารถใดในขอบเขตนั้นจริง
สิทธิ์อ่านกับสิทธิ์สร้างงานอาจพาไปถึงค่าได้
การตรวจ RBAC ต้องดูทั้งสิทธิ์ตรงอย่าง get, list หรือ watch ตามพฤติกรรมของ API ไม่อนุญาตกว้างเพียงเพราะคิดว่าการดูรายชื่อจะไม่เกี่ยวกับเนื้อหาข้อมูลเสมอไป
อีกทางที่ต้องคิดคือสิทธิ์สร้างหรือแก้ workload ที่นำ Secret ไปใช้ หากตัวตนสามารถสั่งรันงานที่ mount ข้อมูลหรือใช้สิทธิ์ของ service account บางชุดได้ มันก็อาจเข้าถึงข้อมูลผ่านเส้นทางนั้นตามขอบเขตระบบ จึงไม่ควรตรวจเพียงข้อห้ามอ่าน Secret โดยตรงแล้วถือว่าจบ

สำหรับแอป ให้สิทธิ์ตามสิ่งที่ต้องใช้และแยกบทบาทเท่าที่งานต้องการ ไม่ใช้บัญชีผู้ดูแล cluster ใน pipeline ทุกขั้นเพื่อให้ไม่ติด permission เพราะจะทำให้ความผิดพลาดของขั้นเล็กๆ มีขอบเขตกว้างเกินหน้าที่
ส่วนขั้นทดสอบสิทธิ์ควรทำในสภาพที่ควบคุมได้ด้วยตัวตนที่กำหนด ตรวจทั้งสิ่งที่ต้องอ่านได้กับสิ่งที่ต้องถูกปฏิเสธ ไม่ใช้การดู manifest ว่าเหมือนตัวอย่างเป็นหลักฐานเพียงอย่างเดียวว่าการผูก role ทุกจุดตรงกันแล้ว
ส่งค่าเข้าแอปแบบใด มีผลต่อการเปลี่ยนค่า
Secret อาจถูกนำไปใช้ผ่าน environment variable หรือ mount เป็นไฟล์ตามที่ workload กำหนด แต่วิธีที่แอปเห็นการอัปเดตต่างกัน การแก้ object จึงไม่ได้แปลว่า process ทุกตัวกำลังใช้ค่าใหม่ทันที
ถ้าเป็น environment variable ต้องวางแผนเริ่ม process ที่รับค่าใหม่ตามวิธีที่ใช้ ส่วนไฟล์ที่ mount ก็มีเงื่อนไขการกระจายการเปลี่ยนของ kubelet และการอ่านใหม่ของแอปเอง ไม่รับประกันว่าแก้แล้วทุกคำขอเปลี่ยนพร้อมกันในจุดเวลาเดียว
การ mount ผ่าน subPath มีข้อจำกัดเรื่องการรับการอัปเดตที่ต้องอ่านตามคู่มือด้วย และแอปที่อ่านไฟล์ครั้งเดียวตอนเริ่มก็ยังต้องมีวิธี reload หรือ restart ของมัน แม้ตัวไฟล์ในระบบจะเปลี่ยนแล้วก็ตาม
ควรทดสอบให้รู้ว่าค่าใหม่ถูกใช้จริงด้วยสถานะที่ไม่เปิดเผยความลับ เช่น รุ่นของการตั้งค่าหรือผลการเชื่อมต่อที่เกี่ยวข้อง ไม่ส่งรหัสจริงออกมาให้ดูบนหน้า status เพียงเพื่อยืนยันว่าเปลี่ยนแล้ว
หมุนรหัสให้ครบทั้งผู้ใช้และปลายทาง
หากเปลี่ยนข้อมูลที่ฝั่งบริการปลายทางทันที แต่แอปเก่าบางตัวอาจยังใช้ค่าเดิมอยู่ เราต้องมีแผนช่วงเปลี่ยนตามความสามารถของระบบ เช่น ให้ข้อมูลสองชุดอยู่ร่วมชั่วคราวหรือทยอยเปลี่ยนตามขั้นที่รองรับ โดยไม่สมมติว่าทุกบริการหมุนค่าได้แบบเดียวกัน
เก็บรายการว่า Secret ใดถูกใช้โดย workload ใด และมีขั้นตอนใดต้องรับทราบเมื่อเปลี่ยน จะช่วยไม่ให้ลืมงานเบื้องหลังหรือ Job ที่ไม่ได้รันตลอดเวลา ซึ่งอาจกลับมาใช้ค่าเก่าหลังเว็บหลักเปลี่ยนสำเร็จแล้ว
เมื่อยืนยันว่าผู้ใช้ค่าที่จำเป็นย้ายแล้ว จึงยกเลิกของเดิมตามนโยบายและตรวจผลต่อ ส่วนกรณีข้อมูลรั่วต้องมีวิธีตอบสนองที่ทันกับความเสี่ยงของงาน ไม่ใช้แผนหมุนตามรอบปกติโดยไม่ดูสถานการณ์จริง
ประวัติการเปลี่ยนควรบอกผู้ดำเนินการ เวลา ขอบเขต และรุ่นของข้อมูลตามที่ระบบใช้ โดยไม่ใส่ค่าลับจริงในข้อความ audit ที่คนเห็นกว้างกว่า การตามได้ว่าค่าเปลี่ยนเมื่อไรช่วยไล่เหตุเชื่อมต่อผิดพลาด แต่ไม่จำเป็นต้องเปิดเผยเนื้อหาของความลับเพื่อให้ได้หลักฐานนั้น
สรุป
Kubernetes Secret ช่วยจัดวางข้อมูลลับ แต่ยังต้องดู base64, ที่เก็บ, RBAC, เส้นทางเข้าแอป และการหมุนค่าร่วมกัน เมื่อรู้ว่ามีสำเนาอยู่ที่ใดและ workload ใดใช้ค่าแบบไหน เราจะตรวจขอบเขตกับเปลี่ยนข้อมูลได้ครบขึ้น โดยไม่ปล่อยให้ชื่อ Secret เป็นคำรับรองแทนการตั้งค่าจริงของระบบครับ


