- 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 จะได้ประโยชน์มากในการยกระดับความปลอดภัย
- ให้ความสำคัญกับ “ความใช้งานได้จริงมากกว่าความสมบูรณ์แบบ” และแนะนำให้นำไปใช้แบบบางส่วนตามงานและสภาพแวดล้อมที่เหมาะสม
ยังไม่มีความคิดเห็น