Secure Boot trên Linux: Cách tôi tự ký Kernel để loại bỏ Bootkit

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Cuộc gọi lúc 2:14 sáng: Khi phần cứng ngừng tin tưởng phần mềm

Thông báo từ Slack vang lên lúc 2:14 sáng. Một node cơ sở dữ liệu chính đã không thể khởi động lại sau bản vá bảo mật định kỳ. Khi tôi mở bảng điều khiển từ xa, không có lời nhắc đăng nhập nào hiện ra. Thay vào đó, một dòng lỗi trắng trên nền đen lạnh lùng hiện lên: Security Violation. Hệ thống từ chối tải hệ điều hành. Suy nghĩ đầu tiên của tôi là ổ NVMe đã hỏng, nhưng sự thật còn nghiêm trọng hơn: Secure Boot đang làm đúng nhiệm vụ của nó và từ chối một kernel mà nó không nhận diện được.

Trong một cuộc khủng hoảng hệ thống, nhiều quản trị viên sẽ chọn giải pháp tạm thời là tắt Secure Boot trong cài đặt UEFI. Đó là cách khắc phục chỉ mất 30 giây nhưng lại để ngỏ cửa cho mọi cuộc tấn công. Nếu không có nó, bạn sẽ dễ bị tấn công bởi bootkit và UEFI rootkit—loại mã độc xâm nhập vào quá trình khởi động trước khi hệ điều hành thức dậy. Những mối đe dọa này vô hình trước các phần mềm diệt virus tiêu chuẩn. Tôi quyết định giải quyết tận gốc vấn đề bằng cách thiết lập chuỗi tin cậy của riêng mình.

Chuỗi tin cậy (Chain of Trust) thực sự hoạt động như thế nào?

Secure Boot về cơ bản là một người gác cổng. Trong quá trình khởi động, firmware UEFI sẽ kiểm tra chữ ký số của bootloader (thường là GRUB). Nếu chữ ký đó khớp với một khóa đáng tin cậy trong cơ sở dữ liệu của firmware, nó sẽ được chạy. Bootloader sau đó sẽ xác minh chữ ký của kernel Linux. Điều này tạo ra một “Chuỗi Tin cậy” liên tục từ phần cứng đến không gian người dùng.

Hầu hết các bo mạch chủ chỉ đi kèm với các khóa của Microsoft. Các bản phân phối phổ biến như Ubuntu hay Fedora vượt qua điều này bằng cách sử dụng một shim đã được Microsoft ký. Tuy nhiên, chuỗi này sẽ bị đứt ngay khi bạn biên dịch một kernel tùy chỉnh hoặc sử dụng các module ngoài như ZFS và Nvidia. Nếu bản phân phối của bạn không cung cấp các tệp nhị phân đã được ký, bạn sẽ gặp rắc rối. Để giữ cho máy chủ an toàn trong khi vẫn chạy mã tùy chỉnh, tôi phải trở thành Cơ quan cấp chứng chỉ (Certificate Authority) của chính mình.

Xây dựng lòng tin của riêng bạn: Quy trình từng bước

Để giải quyết tình trạng bị khóa khởi động, tôi đã triển khai Machine Owner Key (MOK). Đây là một khóa do người dùng tạo mà bạn yêu cầu firmware UEFI tin tưởng một cách thủ công. Dưới đây là quy trình chính xác mà tôi đã sử dụng để đưa máy chủ đó hoạt động trở lại.

1. Chẩn đoán lỗi khởi động

Trước khi thay đổi bất cứ điều gì, tôi phải xác nhận Secure Boot chính là điểm nghẽn. Tôi đã sử dụng mokutil, công cụ tiêu chuẩn để quản lý các khóa này trên Linux.

# Kiểm tra trạng thái Secure Boot
mokutil --sb-state

Nếu kết quả hiển thị SecureBoot enabled, firmware sẽ chặn mọi tiến trình không có chữ ký. Nhiệm vụ của tôi là thêm chữ ký cụ thể của mình vào danh sách trắng (whitelist).

2. Tạo khóa ký 2048-bit

Tôi cần hai thứ: một khóa riêng (private key) để ký kernel và một chứng chỉ công khai cho firmware. Tôi đặt chúng trong một thư mục bảo mật. Hãy nhớ rằng, nếu những khóa này ai cũng có thể đọc được, thì hệ thống bảo mật của bạn chỉ là hình thức.

sudo mkdir -p /var/lib/shim-signed/mok
sudo chmod 700 /var/lib/shim-signed/mok
cd /var/lib/shim-signed/mok

# Tạo chứng chỉ có thời hạn 100 năm
sudo openssl req -new -x509 -newkey rsa:2048 -nodes -days 36500 -outform DER -keyout MOK.priv -out MOK.der

Khi được hỏi về Common Name, hãy sử dụng một cái tên gợi nhớ như “Ops-Prod-Kernel-Signer”. Điều này giúp bạn nhận diện khóa dễ dàng hơn trong menu UEFI sau này.

3. Đăng ký khóa vào Firmware

Khóa sẽ vô dụng nếu phần cứng không nhận diện được chúng. Tôi đã sử dụng mokutil để đưa tệp .der công khai vào danh sách chờ đăng ký. Với kinh nghiệm quản lý hơn 15 node lưu lượng cao, tôi đã học được cách kiểm tra kỹ các lệnh này—một lỗi đánh máy nhỏ có thể khiến bạn mất thêm hàng giờ trong trung tâm dữ liệu.

sudo mokutil --import MOK.der

Công cụ này yêu cầu một mật khẩu tạm thời. Đây là mã sử dụng một lần cho lần khởi động tiếp theo. Tôi đã viết nó vào một mẩu giấy chú thích và nhấn nút reset.

Trong khi khởi động lại, một màn hình xanh có tiêu đề “MOK Management” xuất hiện. Tôi chọn Enroll MOK, xác nhận dấu vân tay (fingerprints) của khóa khớp nhau và nhập mật khẩu tạm thời. Sau một lần khởi động lại nữa, firmware đã chính thức tin tưởng CA tùy chỉnh của tôi.

4. Ký tệp nhị phân Kernel

Rào cản cuối cùng là ký tệp kernel thực tế. Ngay cả khi khóa đã được đăng ký, một kernel chưa ký vẫn sẽ không được tin tưởng. Tôi đã sử dụng sbsign để bao bọc tệp nhị phân bằng chữ ký mới của mình.

# Xác định đường dẫn kernel mục tiêu
KERNEL_PATH="/boot/vmlinuz-$(uname -r)"

# Áp dụng chữ ký
sudo sbsign --key MOK.priv --cert MOK.der --output $KERNEL_PATH.signed $KERNEL_PATH

# Ghi đè lên phiên bản chưa được ký
sudo mv $KERNEL_PATH.signed $KERNEL_PATH

Đối với các driver như Nvidia, bạn sẽ cần các script ký nội bộ của kernel. Hầu hết các hệ thống hiện đại có thể tự động hóa việc này thông qua cấu hình DKMS trong /etc/dkms/, đảm bảo các bản cập nhật mới được ký tự động.

Củng cố thành quả

Đến 4 giờ sáng, cơ sở dữ liệu đã quay trở lại cụm (cluster), Secure Boot đã hoạt động và lỗi Security Violation đã biến mất. Sự yên tâm có được là hoàn toàn xứng đáng với công sức bỏ ra. Bây giờ bạn đã có một hệ thống từ chối khởi động bất cứ thứ gì mà bạn chưa đích thân kiểm chứng.

Một cảnh báo cho môi trường production: hãy bảo vệ tệp MOK.priv của bạn như mạng sống của mình. Nếu kẻ tấn công lấy được khóa riêng đó, họ có thể ký các kernel độc hại của riêng mình và vượt qua toàn bộ hệ thống phòng thủ. Tôi lưu trữ khóa của mình trong một kho mã hóa (vault), chỉ gắn kết (mount) nó trong các đợt bảo trì.

Cấu hình Secure Boot thủ công có thể giống như một cuộc chiến với các menu BIOS khó hiểu, nhưng đó là một thắng lợi quyết định for tính toàn vẹn của hệ thống. Một khi chuỗi tin cậy đó đã được khóa lại, bạn có thể ngủ ngon hơn—ngay cả khi các cảnh báo bắt đầu vang lên lúc 2 giờ sáng.

Share: