• systemd มี ความสามารถในการจัดการบริการ ที่ทรงพลัง แต่ค่าตั้งต้นถูกปรับให้เหมาะกับการใช้งานมากกว่าความปลอดภัย จึงควรมีการใช้ ตัวเลือก hardening เพิ่มเติม
  • สามารถใช้คำสั่ง systemd-analyze security เพื่อวิเคราะห์ ตัวชี้วัดการเปิดเผยด้านความปลอดภัย ของทุกบริการหรือบริการเฉพาะ และใช้จัดลำดับความสำคัญได้
  • มี ตัวเลือกด้านความปลอดภัย หลายแบบที่ใช้ได้ในระดับ service unit และสามารถแก้ไขแยกเป็นรายตัวผ่าน /etc/systemd/system/ServiceName.service.d/override.conf เป็นต้น
  • ตัวเลือกสำคัญมีทั้ง ProtectSystem, PrivateTmp, NoNewPrivileges, SystemCallFilter, MemoryDenyWriteExecute เป็นต้น ซึ่งใช้จำกัดสิทธิ์ของโปรเซสและการเข้าถึงทรัพยากร
  • แทนที่จะมุ่งเป้าไปที่ความปลอดภัยสมบูรณ์แบบ ควร harden บริการที่เปิดเผยสู่ภายนอก ก่อนเพื่อลดความเสี่ยง และยังให้ผลดีมากในสภาพแวดล้อมแบบ self-hosting

ภาพรวมของ systemd

  • systemd มอบแนวทางการจัดการบริการที่สมบูรณ์และแข็งแกร่งมาก
  • แต่เนื่องจากให้ความสำคัญกับความพร้อมใช้งานทันทีมากกว่าความปลอดภัย ค่าตั้งต้นจึงค่อนข้างผ่อนปรน
  • เอกสารนี้แนะนำตัวเลือกเสริมความปลอดภัยหลายแบบที่ใช้กับ systemd service unit และ podman quadlet เพื่อลดโอกาสถูกเจาะ และลดขอบเขตความเสียหายหากเกิดการเจาะระบบ
  • นี่ไม่ใช่คู่มือสำหรับนำไปใช้แบบเหมารวมกับทุกบริการ แต่ต้องมีการทดลอง ตรวจสอบล็อก และปรับแต่งเป็นรายบริการตามลักษณะและความต้องการของแต่ละตัว
  • ความรับผิดชอบด้านความปลอดภัยของโครงสร้างพื้นฐานเป็นของผู้ดูแลทั้งหมด และเอกสารนี้เป็นเพียงเครื่องมืออ้างอิง

การวิเคราะห์ความปลอดภัยของ systemd

  • ใช้คำสั่ง systemd-analyze security เพื่อตรวจสอบสถานะความปลอดภัยของทุกบริการ หรือวิเคราะห์การตั้งค่าอย่างละเอียดของบริการเฉพาะ (เช่น sshd.service) ได้
    • เอาต์พุตจะมีสถานะการตรวจสอบ ชื่อฟีเจอร์ คำอธิบาย และ คะแนน Exposure โดยยิ่ง Exposure สูง ความเสี่ยงก็ยิ่งมาก
  • สามารถเพิ่มการตั้งค่าตัวเลือกความปลอดภัยในส่วน [Service] (systemd) หรือ [Container] (podman quadlet) ได้
  • แนะนำให้ใช้ systemctl edit ServiceName.service เพื่อสร้างไฟล์ override และหากล้มเหลวควรตรวจสอบสิทธิ์ที่จำเป็นแล้วปรับแก้

ตัวเลือกความปลอดภัยของบริการ

  • systemd มี คีย์เวิร์ดตัวเลือกความปลอดภัย หลายแบบ และสามารถดูรายละเอียดได้จาก man systemd.exec 5, man capabilities 7 เป็นต้น
  • ตัวเลือกด้านความปลอดภัยที่พบได้บ่อย
    • ProtectSystem → ตัวเลือกสำหรับจำกัดไฟล์ซิสเต็มให้อ่านได้อย่างเดียว
    • ProtectHome → ตัวเลือกสำหรับบล็อกการเข้าถึง /home, /root, /run/user
    • PrivateDevices → ตัวเลือกสำหรับบล็อกการเข้าถึงอุปกรณ์จริง และอนุญาตเฉพาะอุปกรณ์เสมือนอย่าง /dev/null
    • ProtectKernelTunables, ProtectKernelModules, ProtectKernelLogs → ตัวเลือกสำหรับบล็อกการเข้าถึงทรัพยากรของเคอร์เนล
    • NoNewPrivileges → ตัวเลือกสำหรับป้องกันการได้สิทธิ์ใหม่ผ่าน setuid/setgid เป็นต้น
    • MemoryDenyWriteExecute → บล็อกการใช้หน่วยความจำที่เขียนและรันได้พร้อมกัน ซึ่งอาจมีปัญหากับภาษาแบบ JIT บางตัว
    • SystemCallFilter → ตัวเลือกสำหรับจำกัด system call ที่อนุญาต โดยเมื่อฝ่าฝืนอาจจบโปรเซสหรือคืนค่า EPERM ได้

คำอธิบายของแต่ละตัวเลือก

  • ProtectSystem: หากตั้งเป็น strict จะเมานต์ทั้งไฟล์ซิสเต็มเป็นแบบอ่านอย่างเดียว โดย /dev, /proc, /sys ต้องใช้ตัวเลือกป้องกันเพิ่มเติมแยกต่างหาก
  • ReadWritePaths: ตั้งค่าให้บางพาธกลับมาเขียนได้อีกครั้ง
  • ProtectHome: บล็อกการเข้าถึง /home, /root, /run/user
  • PrivateDevices: ปิดการเข้าถึงอุปกรณ์จริง และอนุญาตเฉพาะอุปกรณ์ pseudo อย่าง /dev/null
  • ProtectKernelTunables: ทำให้ /proc, /sys เป็นแบบอ่านอย่างเดียว
  • ProtectControlGroups: อนุญาตเฉพาะการเข้าถึง cgroup แบบอ่านอย่างเดียว
  • ProtectKernelModules: ห้ามโหลดโมดูลเคอร์เนลแบบชัดแจ้ง
  • ProtectKernelLogs: จำกัดการเข้าถึงบัฟเฟอร์ล็อกของเคอร์เนล
  • ProtectProc: หากตั้งเป็น invisible จะซ่อนโปรเซสของผู้ใช้อื่นจาก /proc/
  • ProcSubset: บล็อกเนื้อหาใน /proc นอกเหนือจากรายการที่เกี่ยวข้องกับ PID ที่กำหนด
  • NoNewPrivileges: บล็อกการยกระดับสิทธิ์ใหม่ผ่าน setuid, setgid และ file system capability
  • ProtectClock: บล็อกการเขียนค่า system/hardware clock
  • SystemCallArchitectures: หากตั้งเป็น native จะอนุญาตเฉพาะ native syscall เช่น x86-64
  • RestrictNamespaces: จำกัด namespace ที่ใช้กับคอนเทนเนอร์โดยเฉพาะ
  • RestrictSUIDSGID: บล็อกการตั้งบิต setuid, setgid ให้กับไฟล์
  • LockPersonality: ป้องกันการเปลี่ยน execution domain (มักจำเป็นเฉพาะกับแอปพลิเคชันรุ่นเก่า)
  • RestrictRealtime: จำกัดการจัดตารางแบบ real-time (จำเป็นเฉพาะกับบริการเฉพาะทางบางตัว)
  • RestrictAddressFamilies: จำกัดตระกูล socket address ที่อนุญาต (เช่น ระบุ AF_INET, AF_INET6, AF_UNIX เป็นต้น)
  • MemoryDenyWriteExecute: บล็อกการสร้างพื้นที่หน่วยความจำใหม่ที่เขียนได้+รันได้ (บริการที่ใช้ JIT ต้องระวัง)
  • ProtectHostname: ห้ามใช้ syscall sethostname, setdomainname
  • SystemCallFilter: ตั้งค่าอนุญาต/บล็อก syscall รายบริการ และกรองได้ละเอียด
    • ปรับได้ทั้งแบบกลุ่ม, syscall รายตัว, วิธีอนุญาต/บล็อก เป็นต้น
    • รองรับการตั้งค่าให้คืนรหัสผิดพลาดอย่าง EPERM แทนการจบโปรเซสเมื่อมีการฝ่าฝืน
    • ดูรายการทั้งหมดได้จาก systemd-analyze syscall-filter หรือ man systemd.exec(5)
    • ใช้คำนำหน้า ~ เพื่อกลับความหมายของทั้งลิสต์ได้ (เช่น CapabilityBoundingSet=~CAP_SETUID เป็นต้น)

ขั้นตอนการปรับข้อจำกัดของ SystemCallFilter

  • ใช้ auditd เพื่อตรวจดูล็อกได้ว่าเมื่อบริการล้มเหลว มี syscall ใดถูกบล็อก
    • รัน sudo ausearch -i -m SECCOMP -ts recent แล้วตรวจค่าของ syscall
    • จากนั้นเพิ่ม syscall นั้นหรือกลุ่มที่เกี่ยวข้องลงใน SystemCallFilter เพื่อแก้ปัญหาไปทีละขั้น

ลำดับความสำคัญในการทำ hardening และเคล็ดลับการใช้งาน

  • ไม่จำเป็นต้องใช้กับทุกบริการทั้งหมด
  • threat model และการจัดการความเสี่ยงคือหัวใจสำคัญ โดยเฉพาะ บริการที่เปิดเผยสู่ภายนอก (httpd, nginx, ssh เป็นต้น) ควรพิจารณาเป็นพิเศษ
  • คำสั่งแบบกำหนดเอง, timer unit (ตัวแทน cron แบบเดิม) เป็นต้น ก็ให้ผลดีหากนำไปใช้เชิงรุก
  • ยิ่งเป็นบริการที่ไม่ซับซ้อนมาก ก็ยิ่งมีโอกาสปรับจูนแบบละเอียดได้มาก

เช็กลิสต์: ชุดตัวเลือกความปลอดภัยที่แนะนำ (ลำดับความสำคัญเริ่มต้น)

  • ProtectSystem=strict
  • PrivateTmp=yes
  • ProtectHome=yes หรือ ProtectHome=tmpfs
  • ProtectClock=yes, ProtectKernelLogs=yes, ProtectKernelModules=yes
  • RestrictSUIDGUID=yes
  • UMask=0077
  • LockPersonality=yes
  • RestrictRealtime=yes
  • MemoryDenyWriteExecute=yes
  • DynamicUser=yes หรือระบุ User เป็นผู้ใช้เฉพาะที่ไม่ใช่ root
  • รายการข้างต้นโดยทั่วไปเป็นชุดที่มักใช้ได้โดยแทบไม่กระทบต่อบริการ
  • หากต้องการเพิ่มการกรอง syscall (SystemCallFilter) ด้วย จะต้องมีการทดสอบอย่างละเอียด

ตัวอย่างการตั้งค่า Traefik

  • เป็นกรณีตัวอย่างของบริการ Traefik แบบคอนเทนเนอร์ที่รันด้วย systemd quadlet และใช้ตัวเลือกด้านความปลอดภัยหลายรายการ
    • ใช้ ProtectSystem=full, ProtectHome=yes, SystemCallFilter=@system-service @mount @privileged เป็นต้น
    • ลบสิทธิ์บางอย่างด้วย CapabilityBoundingSet=~CAP_SETUID CAP_SETPCAP
    • ใช้ข้อจำกัดการเข้าถึงเครือข่าย เช่น RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK

บทสรุป

  • ตัวเลือกเสริมความปลอดภัยของ systemd เป็นวิธีที่ใช้งานได้จริงและควรมีติดกล่องเครื่องมือไว้สำหรับผู้ดูแลระบบสายยูนิกซ์
  • ไม่ใช่มาตรการความปลอดภัยที่สมบูรณ์แบบ แต่ควรใช้เป็น เครื่องมือเพื่อลดความเสี่ยง และไม่จำเป็นต้องยัดการตั้งค่าความปลอดภัยใส่ทุกบริการแบบไม่เลือก
  • โดยเฉพาะผู้ดูแลในสภาพแวดล้อมแบบ self-hosting จะได้ประโยชน์มากในการยกระดับความปลอดภัย
  • ให้ความสำคัญกับ “ความใช้งานได้จริงมากกว่าความสมบูรณ์แบบ” และแนะนำให้นำไปใช้แบบบางส่วนตามงานและสภาพแวดล้อมที่เหมาะสม

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น