มาทำ Secure Boot บน Linux กันเถอะ
Secure Boot คืออะไร?
Secure Boot เป็นฟังก์ชั่นของระบบไบออส UEFI ที่ป้องกันการติดตั้งไฟล์ระบบที่ไม่ได้รับการอนุมัติจากผู้ผลิดเมนบอร์ด
โดยเมื่อระบบบูต, ไบออส UEFI จะตรวจหาลายเซ็นดิจิตอลในไฟล์บูตของระบบปฏิบัติการ
หากตรวจพบและเช็คความถูกต้องผ่านหมด ก็จะทำการบูตระบบปฏิบัติการนั้นขึ้นมา
แต่ถ้าไม่พบ, ไม่ถูกต้อง หรือลายเซ็นมาจากเจ้าที่ไม่รู้จัก ก็จะปฏิเสธการบูตระบบนั้น
ฟีเจอร์นี้มีไว้ในกรณีที่ระบบปฏิบัติการที่มีรูโหว่แล้วถูกไวรัสเขียนทับไฟล์ระบบที่ใช้บูต หรือถูกผู้อื่นเข้าถึงคอมของเราแล้ววางไฟล์บูตที่มีช่องโหว่ให้โจมตีได้
เมื่อผู้ใช้รีสตาร์ต ระบบจะหยุดทำงานที่จอดำเพื่อไม่ให้ไวรัสก่อความเสียหายเพิ่มเติม หรือถูกโจมตีระบบ
จำเป็นแค่ไหน?
หากว่าระบของเราไม่ได้เข้ารหัสแบบ Full-disk encryption แล้วนั้น ตัว secure boot ไม่ได้จำเป็น หรือไม่มีประโยชน์ใด ๆ เลย นอกจากให้ Microsoft บังคับห้ามเราลง ระบบประติบัติการที่เราต้องการเท่านั้น (ปิด secure boot ใน bios ก็สามารถเข้าระบบได้เลย)
แต่สำหรับระบบที่เข้ารหัสไว้นั้น จะช่วยเพิ่มความปลอดภัยไปอีกระดับ เช่นหากโดยขโมยคอมไป แม้เขาจะพยายามแก้ไขไฟล์ boot ก็ไม่สามารถเข้าถึงระบบเราได้อยู่ดี มีเดียวทางเดียวคือล้างข้อมูลทั้งหมดทิ้งไปเท่านั้น ถึงจะใช้งานคอมเครื่องนั้น ๆ ได้ แม้จะปิด secure boot ใน bios แล้วก็ตาม
ทำให้ระบบ Linux ที่เข้ารหัสไว้ ปลอดภัยขึ้นอีกชั้นนึง
Boot Loader
BIOS > BOOT LOADER > KERNEL > INIT SYSTEM
ในระบบประติบัติการ Linux นั้น มีตัวเลือกสำหรับ Boot Loader ที่หลากหลาย อย่างระบบใหม่ ๆ จะใช้ systemd-boot ที่สะดวกใช้งานง่าย ปรับอะไรได้ไม่มากนัก
โดยตัวที่ใช้เยอะที่สุดคงจะเป็น GRUB ซึ่งสามารถปรับแต่งได้แทบทุก logic ของการบูตเข้าระบบของเรา และในบทความนี้ เราจะใช้ GRUB ในการทำ Secure Boot
โดยก่อนหน้านั้นเราต้องแบ่ง partition ส่วนนึงไปให้ /boot/efi
ซึ่งเป็น filesystem แบบ fat ทั่ว ๆ ไป สำหรับ ให้ bios อ่านไฟล์ boot ของเราได้
❯ sudo fdisk -l
Disk /dev/nvme0n1: 476.94 GiB, 512110190592 bytes, 1000215216 sectors
Disk model: WD PC SN735 SDBPNHH-512G-1002
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 9A5798B5-DC04-4770-A26B-3A489642CF32
Device Start End Sectors Size Type
/dev/nvme0n1p1 2048 2099199 2097152 1G EFI System
/dev/nvme0n1p2 2099200 1000214527 998115328 475.9G Linux filesystem
และอีกส่วน จะใช้ LVM (Logical Volume Management) ในการสร้าง Volume Group (VG) บน SSD ของผม สำหรับติดตั้งระบบที่เข้ารหัสไว้ โดยจะแบ่งพื้นที่ส่วนนึงไปเป็น swap
partition ใช้สำหรับการเก็บข้อมูลตอนที่เราพับจอ หรือเข้าโหมด sleep
ที่เหลือทั้งหมด ก็จะเป็นพื้นที่ติดตั้ง Linux ของผม (disk นี้ผมไม่ได้แยก /home
(user's home) กับ /
(root) ไว้คนละ Partition เพราะว่าผมชอบทำ root เต็มบ่อย ๆ ฮาาา)
❯ sudo vgscan
Found volume group "vg0" using metadata type lvm2
❯ sudo lvscan
ACTIVE '/dev/vg0/swap' [17.00 GiB] inherit
ACTIVE '/dev/vg0/void' [458.93 GiB] inherit
สำหรับวิธีการติดตั้ง Linux แบบเข้ารหัสนั้น มีอยู่ตามคู่มือตอนติดตั้ง Linux เกือบทุกตัวอยู่แล้ว ไม่ต้อง งง ยิ่งพวกที่เป็น GUI install อย่าง Fedora, Ubuntu บลา ๆ นั้นแค่คลิก ๆ ก็ทำได้แล้ว (แถมทำ Secure Boot ได้เลยอีกต่างหาก)
โดยที่ระบบที่ผมใช้ในบทความนี้ คือ VoidLinux ซี่งเป็นระบบง่าย ๆ ไม่มี systemd ให้กวนใจ
ส่วนของ software ที่ใช้จะมี
- grub
- sbctl
และ bash script เล็กน้อย ในการทำ kernel hook สำหรับการ Sign ไฟล์ boot ของเราอัตโนมัติหลังจาก Update
เริ่มกันเลย!
อย่างแรก ตั้งค่า SecureBoot ใน Bios เป็น Setup-Mode ให้เรียบร้อยก่อน
จากนั้นต้องตั้งค่า Grub กันใหม่ โดยเพิ่ม module tpm
และ --disable-shim-lock
(สำหรับ Microsoft's CA) เผื่อกรณีที่อยากทำ Dual Boot เข้า Windows ด้วย
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=void --modules="tpm" --disable-shim-lock
จากนั้นก็มา Gen ไฟล์ตั้งค่าที่เพิ่งทำไปด้วยคำสั่ง
sudo grub-mkconfig -o /boot/grub/grub.cfg
แล้วก็ติดตั้ง sbctl ให้เรียบร้อย
sudo xbps-install sbctl
จากนั้นลองเช็คด้วยคำสั่ง sbctl status
ก็จะแสดงผลประมาณนี้
❯ sbctl status
Installed: ✓ sbctl is installed
.....
จากนั้นเราก็จะสร้าง Key สำหรับการ Sign ไฟล์ Boot ของเรา
sudo sbctl create-keys
พอได้คีย์แล้ว enroll-keys
ต่อ คำสั่งนี้จะสร้างรหัสสำหรับ Verify ใน Bios ให้เราหลังจากการตั้ง Setup-Mode ใน Secure Boot (-m
คือการเพิ่ม Cert ของ Microsoft ไปด้วย เผื่อ Dual Boot)
sudo sbctl enroll-keys -m
หลังจากขั้นตอนนี้ก็ใช้คำสั่งดู status อีกรอบ
และใช้คำสั่ง sudo sbctl verify
เพื่อดูว่าเราต้อง Sign ไฟล์ไหนบ้าง
❯ sbctl status
Installed: ✓ sbctl is installed
Owner GUID: 61bb7cf9-38bb-4ef5-94f6-960b095c9322
Setup Mode: ✓ Disabled
Secure Boot: ✓ Enabled
Vendor Keys: microsoft
❯ sudo sbctl verify
Verifying file database and EFI images in /boot/efi..
✓ /boot/efi/EFI/void/grubx64.efi is signed
อย่างตัวอย่างข้างบนคือไฟล์เหล่านี้ได้ถูก sign เรียบร้อยแล้ว ก็สามารถ Boot ด้วย Secure Boot ได้เลย
หากไฟล์ไหนยังไม่ได้ Sign ก็จัดการด้วยคำสั่ง sudo sbctl sign -s /ไฟล์พาท
เช่น
sudo sbctl sign -s /boot/efi/EFI/void/grubx64.efi
แล้วก็ลอง sudo sbctl verify
ดูว่า sign เรียบร้อยดีไหม
ถ้าเรียบร้อยแล้วก็สามารถ reboot ไปเปิด SecureBoot ดูได้เลย ว่า Boot เข้าระบบได้หรือไม่
และบางระบบก็ต้อง sign ไฟล์ kernel ด้วยเหมือนกัน อย่างระบบผม ต้อง sign /boot/vmlinuz-{version}
ด้วย ไม่งั้น Boot ไม่ได้
❯ sudo sbctl sign -s /boot/vmlinuz-6.9.6_1
❯ sudo sbctl verify
Verifying file database and EFI images in /boot/efi...
✓ /boot/vmlinuz-6.9.6_1 is signed
✓ /boot/efi/EFI/void/grubx64.efi is signed
หลังจาก reboot ด้วย SecureBoot ได้เรียบร้อย เป็นอันเสร็จ
แต่อย่าลืมว่าถ้าระบบ Update เราต้องกลับมา Sign เองใหม่หรือเปล่า??
Kernel Hook
โดยทั่วไป ระบบ Linux จะมี Directory /etc/kernel.d/post-install/
ซึ่งจะรวบรวมเอา Script สำหรับใช้หลังจาก Update ระบบเช่น ตัว Grub เอง ก็สร้างไฟล์ config ใหม่ทุกครั้งหลังจาก update kernel
cat /etc/kernel.d/post-install/50-grub
#!/bin/sh
#
# Kernel hook for GRUB 2.
#
# Arguments passed to this script: $1 pkgname, $2 version.
#
PKGNAME="$1"
VERSION="$2"
export ZPOOL_VDEV_NAME_PATH=YES
if command -v grub-mkconfig >/dev/null 2>&1; then
if [ -d $ROOTDIR/boot/grub ]; then
grub-mkconfig -o $ROOTDIR/boot/grub/grub.cfg
exit $?
fi
fi
exit 0
และสังเกตุว่า ไฟล์ทั้งหมดใน Directory นี้นั้น จะนำหน้าด้วยตัวเลข ซึ่งเป็นลำดับของการทำงานนั่นเอง โดยที่เลขน้อยกว่า จะถูกรันก่อน จนครบ
❯ sudo ls /etc/kernel.d/post-install/ -l
10-dkms
20-initramfs -> ../../../usr/libexec/dracut/kernel-hook-postinst
50-bootsize
50-efibootmgr
50-grub
เราก็จะทำการ Copy ไฟล์ 50-grub
มาเป็น templates ในการทำ kernel hook และตั้งชื่อว่า 60-sign
เพื่อให้แน่ใจว่า ทุกอย่างถูกติดตั้งไปก่อน แล้ว sign เป็นลำดับสุดท้าย
sudo cp /etc/kernel.d/post-install/50-grub /etc/kernel.d/post-install/60-sign
แล้วก็เข้าไปแก้ไขไฟล์
sudo vi /etc/kernel.d/post-install/60-sign
แล้วก็ปรับคำสั่งเป็น sbctl ให้เรียบร้อย
จะสังเกตุว่าผมได้ใช้ sign สองครั้ง คือ grub config
และไฟล์ kernel vmlinuz-
ที่มีเวอร์ชั่นตามท้าย
#!/bin/sh
#
# Kernel post-install hook for surcue boot.
#
# Arguments passed to this script: $1 pkgname, $2 version.
#
PKGNAME="$1"
VERSION="$2"
if command -v sbctl >/dev/null 2>&1; then
if [ -d $ROOTDIR/boot/efi ]; then
echo "Signing new packages v.$VERSION"
sbctl sign -s /boot/efi/EFI/void/grubx64.efi
sbctl sign -s /boot/vmlinuz-${VERSION}
exit $?
fi
fi
exit 0
เซฟไฟล์ และสุดท้าย ทำ permission ให้มัน excuteable หรือว่าให้โปรแกรมเรียยกทำงานได้
sudo chmod +x /etc/kernel.d/post-install/60-sign
เนื่องจากว่าบางระบบจะไม่ให้ไฟล์ script ใดๆ มาทำงานได้ตามใจชอบ
เราต้องควบคุมเองว่าไฟล์ไหนมี สิทธ์
ที่จะทำงานได้ นั่นเอง
เพียงเท่านี้ เราก็จะมีระบบเข้ารหัสที่ปลอดภัยสุด ๆ ใครจะมาแอบเสียบแฟรชไดร์มาแก้ไขไฟล์ Boot วางยาเราไม่ได้ แน่นอน