-
@ VΔz
2024-06-23 10:15:02
## 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 ของเราได้
```sh
❯ 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 เต็มบ่อย ๆ ฮาาา)
```sh
❯ 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](https://voidlinux.org/) ซี่งเป็นระบบง่าย ๆ ไม่มี systemd ให้กวนใจ
ส่วนของ software ที่ใช้จะมี
1. grub
2. sbctl
และ bash script เล็กน้อย ในการทำ kernel hook สำหรับการ Sign ไฟล์ boot ของเราอัตโนมัติหลังจาก Update
# เริ่มกันเลย!
อย่างแรก ตั้งค่า SecureBoot ใน Bios เป็น Setup-Mode ให้เรียบร้อยก่อน
จากนั้นต้องตั้งค่า Grub กันใหม่ โดยเพิ่ม module `tpm` และ `--disable-shim-lock` (สำหรับ Microsoft's CA) เผื่อกรณีที่อยากทำ Dual Boot เข้า Windows ด้วย
```sh
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=void --modules="tpm" --disable-shim-lock
```
จากนั้นก็มา Gen ไฟล์ตั้งค่าที่เพิ่งทำไปด้วยคำสั่ง
```sh
sudo grub-mkconfig -o /boot/grub/grub.cfg
```
แล้วก็ติดตั้ง sbctl ให้เรียบร้อย
```sh
sudo xbps-install sbctl
```
จากนั้นลองเช็คด้วยคำสั่ง `sbctl status` ก็จะแสดงผลประมาณนี้
```sh
❯ sbctl status
Installed: ✓ sbctl is installed
.....
```
จากนั้นเราก็จะสร้าง Key สำหรับการ Sign ไฟล์ Boot ของเรา
```sh
sudo sbctl create-keys
```
พอได้คีย์แล้ว `enroll-keys` ต่อ คำสั่งนี้จะสร้างรหัสสำหรับ Verify ใน Bios ให้เราหลังจากการตั้ง Setup-Mode ใน Secure Boot (`-m` คือการเพิ่ม Cert ของ Microsoft ไปด้วย เผื่อ Dual Boot)
```sh
sudo sbctl enroll-keys -m
```
หลังจากขั้นตอนนี้ก็ใช้คำสั่งดู status อีกรอบ
และใช้คำสั่ง `sudo sbctl verify` เพื่อดูว่าเราต้อง Sign ไฟล์ไหนบ้าง
```sh
❯ 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 /ไฟล์พาท` เช่น
```sh
sudo sbctl sign -s /boot/efi/EFI/void/grubx64.efi
```
แล้วก็ลอง `sudo sbctl verify` ดูว่า sign เรียบร้อยดีไหม
ถ้าเรียบร้อยแล้วก็สามารถ reboot ไปเปิด SecureBoot ดูได้เลย ว่า Boot เข้าระบบได้หรือไม่
และบางระบบก็ต้อง sign ไฟล์ kernel ด้วยเหมือนกัน อย่างระบบผม ต้อง sign `/boot/vmlinuz-{version}` ด้วย ไม่งั้น Boot ไม่ได้
```sh
❯ 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
```sh
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 นี้นั้น จะนำหน้าด้วยตัวเลข ซึ่งเป็นลำดับของการทำงานนั่นเอง โดยที่เลขน้อยกว่า จะถูกรันก่อน จนครบ
```sh
❯ 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 เป็นลำดับสุดท้าย
```sh
sudo cp /etc/kernel.d/post-install/50-grub /etc/kernel.d/post-install/60-sign
```
แล้วก็เข้าไปแก้ไขไฟล์
```sh
sudo vi /etc/kernel.d/post-install/60-sign
```
แล้วก็ปรับคำสั่งเป็น sbctl ให้เรียบร้อย
จะสังเกตุว่าผมได้ใช้ sign สองครั้ง คือ `grub config` และไฟล์ kernel `vmlinuz-` ที่มีเวอร์ชั่นตามท้าย
```bash
#!/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 หรือว่าให้โปรแกรมเรียยกทำงานได้
```sh
sudo chmod +x /etc/kernel.d/post-install/60-sign
```
เนื่องจากว่าบางระบบจะไม่ให้ไฟล์ script ใดๆ มาทำงานได้ตามใจชอบ
เราต้องควบคุมเองว่าไฟล์ไหนมี `สิทธ์` ที่จะทำงานได้ นั่นเอง
เพียงเท่านี้ เราก็จะมีระบบเข้ารหัสที่ปลอดภัยสุด ๆ ใครจะมาแอบเสียบแฟรชไดร์มาแก้ไขไฟล์ Boot วางยาเราไม่ได้ แน่นอน