

เมื่อ CI เริ่มต้องใช้ข้อมูลลับจากหลายบริการ เราอาจอยากต่อ Vault ให้สำเร็จก่อน แล้วใช้ตัวตนเดียวอ่านค่าทุกอย่างเพื่อให้งานเดินได้ง่าย แต่พอมีหลายโปรเจกต์กับหลาย environment สิทธิ์ชุดเดียวก็เริ่มกว้างกว่างานที่ job หนึ่งควรทำ
งานดาวน์โหลด package ส่วนตัวอาจไม่ต้องรู้รหัสฐานข้อมูล ส่วนงาน deploy ไป UAT ก็ไม่ควรได้รับค่าของ prod เพียงเพราะรันอยู่บนระบบ CI เดียวกันครับ การเชื่อมต่อได้จึงควรถูกตามด้วยคำถามว่างานนั้นจำเป็นต้องอ่านอะไรจริง
Vault policy กับวิธียืนยันตัวตนต้องถูกออกแบบคู่กัน เพื่อให้รู้ทั้งว่าใครขอสิทธิ์และได้รับการทำงานใดบนข้อมูลชุดไหน ไม่จบที่เอา secret ออกจากไฟล์ใน repo แล้วถือว่าขอบเขตพร้อมทั้งหมด
เริ่มจากรายการงาน เช่น ติดตั้ง dependency ทดสอบ integration หรือส่งชิ้นงานไป environment ที่กำหนด แล้วเขียนว่าต้องอ่านข้อมูลใด ใช้ตอนไหน และจำเป็นต้องเขียนกลับหรือไม่ งานจำนวนหนึ่งต้องอ่านเพียงชุดเล็ก ไม่ต้องมีสิทธิ์จัดการ secret ของทั้งโครงการ
โครง path ควรช่วยแยกโปรเจกต์ environment และหน้าที่ที่มีสิทธิ์ต่างกันได้ ไม่เก็บทุกความลับขององค์กรใน entry เดียวแล้วหวังว่าการเลือก field ตอนเรียกจะทำหน้าที่เป็น ACL ให้แต่ละ job

โดยเฉพาะ KV การให้สิทธิ์อ่าน path หนึ่งต้องเข้าใจขอบเขตข้อมูลที่ path นั้นคืนมา หากงานควรเห็นคนละชุดจริง ก็ควรแยก entry ตามขอบเขตที่บังคับได้ ไม่เพียงตั้งชื่อ field ให้ดูเหมือนแยกแล้ว
เมื่อรายการนี้ชัด เราจะอธิบายได้ว่าทำไม policy ของ build กับ deploy ต่างกัน และรู้ว่าจะเพิ่มสิทธิ์ที่ใดเมื่อมีความต้องการใหม่ แทนการขยาย wildcard ครั้งเดียวเพื่อให้ job ทุกตัวหยุดขึ้น error
Vault กำหนด policy ตาม path กับ capability และมีหลัก deny by default ตัวอย่างสมมติของ KV v2 ที่ mount ชื่อ kv สำหรับอ่าน entry ของงานหนึ่งอาจเป็นแบบนี้:
path "kv/data/web/uat/package-reader" {
capabilities = ["read"]
}
ตัวอย่างนี้แสดงเพียง rule หนึ่ง ไม่ใช่การตั้งระบบครบ ทั้ง mount path วิธี auth และ policy อื่นที่ติดมากับ token ยังต้องถูกตรวจร่วมกัน อย่าถือว่าการมีไฟล์ rule แคบหนึ่งใบทำให้สิทธิ์จริงแคบตาม หากยังมีอีก policy ที่เปิดกว้างอยู่
สำหรับ KV v1 กับ v2 เส้นทาง API และข้อมูลประกอบต่างกัน ต้องใช้รูปแบบให้ตรงกับ engine จริง ไม่แก้ path ด้วยการเติม * จนอ่านได้แล้วข้ามคำถามว่ากำลังให้สิทธิ์ครอบคลุมส่วนใดเพิ่มไปบ้าง
สิทธิ์ read กับ list ก็มีหน้าที่ต่างกัน งานที่รู้ path ที่จะใช้แล้วอาจไม่ต้องมีสิทธิ์สำรวจรายชื่อทั้งหมด และงานอ่านค่าทั่วไปไม่จำเป็นต้องได้ capability ลบหรือแก้ข้อมูลเพียงเพื่อใช้ค่าระหว่าง build
ถ้าใช้ GitLab ID token เชื่อมกับ Vault ผ่าน JWT ต้องตั้งการตรวจ issuer กับ audience ตามระบบจริง พร้อมกำหนด role ให้ผูกกับ claim ที่เหมาะ เช่น โครงการ reference และสถานะ protected ที่เกี่ยวข้อง ไม่รับ token ใดก็ได้จาก instance เดียวกันแล้วให้ role ของ production

ชื่อ environment ที่ job ส่งมาเองไม่ควรถูกใช้เป็นหลักฐานสิทธิ์ทั้งหมด ต้องดูว่าผู้ออก token รับรองข้อมูลใดและ Vault ตรวจอย่างไร รวมถึงใครแก้ pipeline หรือเรียกใช้งานจาก branch ที่กำหนดได้
แยก role สำหรับบริบทที่มีสิทธิ์ต่างกันให้ชัด และตรวจรุ่นกับความสามารถของ GitLab ที่ติดตั้งก่อนเลือกวิธี integration เพราะตัวอย่าง keyword กับการเชื่อมในตัวมีเงื่อนไขของผลิตภัณฑ์ ไม่ใช่เพียงคัด YAML แล้วทุกชุดจะรองรับเหมือนกัน
สำหรับ runner ต้องรู้ด้วยว่างานจากที่มาใดรันร่วมกันและแยกสภาพแวดล้อมอย่างไร เพราะเมื่อ job ได้อ่าน secret แล้ว โค้ดภายใน job มีโอกาสจัดการข้อมูลนั้น การตั้ง policy ถูกจึงต้องอยู่กับขอบเขตคนแก้โค้ดและตัว runner ที่เหมาะสมด้วย
เลือกอายุและเงื่อนไข token ให้พอกับงานที่ตั้งใจ โดยไม่ใช้ตัวตนถาวรสิทธิ์กว้างร่วมกันเพียงเพราะสะดวกกับการตั้งครั้งแรก และกำหนดวิธีจัดการเมื่องานนานกว่าที่คาดหรือสิทธิ์หมดระหว่างทาง
แต่ token หมดอายุไม่ได้ทำให้ค่าที่ถูกคัดออกไปก่อนหน้านั้นหายไป หากงานเผลอเขียน secret ลง artifact log หรืออิมเมจ ก็ต้องดูผลข้างเคียงที่เกิดแล้วและจัดการความลับนั้นตามเหตุ ไม่จบที่ลบตัวแปรจากหน้าตั้ง CI
สำหรับข้อมูลที่บริการรองรับการออก credential แบบมีอายุ ก็ควรอ่าน lifecycle ของ credential นั้นแยกจากอายุ Vault token อีกครั้ง เพราะทั้งสองอาจเป็นคนละวัตถุที่ต้องควบคุม ไม่ใช้ TTL ค่าเดียวแทนคำตอบทั้งหมด
ในขั้น build เลือกวิธีส่ง secret ที่ไม่ฝังลงชิ้นงาน และตรวจ output ที่เก็บจริง การอ่านค่าจาก Vault ช่วยเรื่องต้นทาง แต่ปลายทางของค่าหลังอ่านยังอยู่ในความรับผิดชอบของ workflow ที่เราออกแบบ
ใช้ข้อมูลทดสอบในขอบเขตที่จัดไว้ ยืนยันว่า job ของ UAT อ่าน entry ที่กำหนดได้ และ job จากบริบทที่ไม่ตรงถูกปฏิเสธตามกติกา โดยไม่ต้องใช้ production secret เป็นข้อมูลตัวอย่างหรือพิมพ์ค่าจริงลง log เพื่อพิสูจน์ว่าต่อสำเร็จ
ตรวจนโยบายรวมกับ role ที่ token ได้รับจริง รวมถึงการเปลี่ยน branch โครงการ หรือ claim สำคัญตามสิ่งที่ต้องแยก หากทดสอบเฉพาะกรณีที่อ่านสำเร็จ เราจะยังไม่รู้ว่าขอบเขตที่ตั้งใจปิดทำงานหรือเปล่า
เก็บข้อมูล audit ที่ช่วยตามว่า role ใดเรียก path ใดและเกิดผลอย่างไรตามการตั้งที่รองรับ โดยให้ผู้ดูแลเข้าถึงอย่างเหมาะสม ไม่คัดค่าลับออกมาไว้ในบันทึกทั่วไปเพื่อทำรายงานให้ดูครบ
เมื่อเพิ่ม job ใหม่ ให้ทบทวนจากหน้าที่ของมันอีกครั้ง ไม่อาศัยว่าคัดมาจาก job เดิมแล้วต้องใช้สิทธิ์เดิมทั้งหมด การรักษารายการเหตุผลของแต่ละสิทธิ์จะทำให้ลดสิทธิ์หรือเปลี่ยนโครงในภายหลังได้โดยไม่ต้องเดาว่ารายการไหนยังมีคนใช้อยู่
CI ไม่จำเป็นต้องเห็นทุก secret เพื่อใช้ Vault ได้ เริ่มจากข้อมูลที่แต่ละ job ต้องใช้ แยก path และ capability ตามขอบเขตจริง แล้วผูก role กับที่มาของงานที่ตรวจได้ พร้อมดูอายุสิทธิ์ runner และ output หลังอ่านค่า เมื่อทดสอบทั้งกรณีอนุญาตกับปฏิเสธ เราจะได้การเข้าถึงที่อธิบายได้ว่างานใดใช้ความลับชุดใดเพื่ออะไรครับ