Hướng dẫn cấu hình NBDE trên Fedora với Clevis và Tang: Tự động mở khóa LUKS trong mạng nội bộ

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

Chạy server Fedora với ổ đĩa mã hóa LUKS là điều mình làm ngay từ đầu. Bảo mật dữ liệu khi máy bị lấy cắp vật lý là yêu cầu tối thiểu — không thương lượng. Nhưng vấn đề nảy sinh khi cần reboot: phải gõ passphrase thủ công qua console hoặc SSH với dropbear, rất bất tiện nếu không có ai trực. Sau khi tìm hiểu, mình phát hiện NBDE với Clevis + Tang giải quyết đúng vấn đề này — tự động mở khóa ổ đĩa khi máy khởi động trong mạng nội bộ an toàn.

Mình đã chạy setup này trên homelab 6 tháng, rồi áp dụng lên 2 server production. Bài này ghi lại đúng những gì mình làm, kể cả những chỗ bị vấp.

NBDE hoạt động như thế nào

NBDE (Network-Bound Disk Encryption) là mô hình mã hóa ổ đĩa “gắn với mạng” — ổ đĩa chỉ tự mở khóa được khi máy đang kết nối vào mạng nội bộ có Tang server. Mang máy ra ngoài mạng, hoặc cắt cable — ổ đĩa vẫn được bảo vệ hoàn toàn.

Ba thành phần chính:

  • Tang: Server HTTP đơn giản, cung cấp key material để mở khóa ổ đĩa. Tang không lưu bất kỳ secret nào của client — thiết kế theo giao thức McCallum-Relyea.
  • Clevis: Client-side tool tích hợp với LUKS. Clevis “pin” ổ đĩa vào Tang server (hoặc TPM, hoặc kết hợp cả hai) và xử lý việc mở khóa tự động lúc boot.
  • LUKS: Cơ chế mã hóa ổ đĩa của Linux, hỗ trợ nhiều keyslot — Clevis thêm một keyslot mới mà không đụng đến passphrase gốc.

Điểm quan trọng cần hiểu: Tang không biết passphrase LUKS của bạn. Quá trình mở khóa dùng ECDH (Elliptic Curve Diffie-Hellman) — Tang chỉ cần “tồn tại và phản hồi” trong mạng. Không lưu secret, không có điểm rò rỉ tập trung.

Chuẩn bị môi trường

Mình dùng 2 máy trong cùng mạng LAN:

  • Tang server: Fedora Server, IP 192.168.1.10 — có thể là Raspberry Pi, VM, hoặc máy cũ nằm trong mạng nội bộ.
  • Client: Fedora Workstation/Server với ổ đĩa LUKS, IP 192.168.1.20.

Yêu cầu: Fedora 36+ (mình dùng Fedora 40), ổ đĩa đã mã hóa LUKS từ trước, 2 máy trong cùng subnet.

Cài đặt Tang Server

Trên máy Tang server (192.168.1.10):

sudo dnf install tang -y
sudo systemctl enable --now tangd.socket

Tang chạy qua socket activation — không cần service thường trực, chỉ “thức dậy” khi có request đến. Mặc định lắng nghe port 7500.

Mở firewall cho Tang:

sudo firewall-cmd --add-service=tangd --permanent
sudo firewall-cmd --reload

Từ máy client, test nhanh xem Tang đang sống:

curl http://192.168.1.10:7500/adv

Nhận được JSON với trường payload là Tang đang chạy đúng. Đây là advertisement — public key của Tang server mà Clevis dùng để binding.

Lấy thumbprint của Tang

Thumbprint dùng để verify Tang server khi binding, tránh man-in-the-middle. Chạy trên máy Tang:

sudo tang-show-keys

Output là chuỗi base64, kiểu: 3DqH5h1TxhJGRD9pZm.... Ghi lại — cần dùng ở bước bind.

Cài đặt Clevis trên Client

Trên máy client (192.168.1.20), cài Clevis và dracut module để tích hợp vào initramfs:

sudo dnf install clevis clevis-luks clevis-dracut -y

Bind ổ đĩa LUKS với Tang

Xác định partition LUKS:

lsblk -f | grep crypto_LUKS

Giả sử partition là /dev/sda2. Bind với Tang, thay thumbprint bằng giá trị vừa lấy từ bước trên:

sudo clevis luks bind -d /dev/sda2 tang '{"url":"http://192.168.1.10:7500","thp":"3DqH5h1TxhJGRD9pZm"}'

Clevis hỏi passphrase LUKS để thêm keyslot mới — nhập vào là xong. Passphrase gốc vẫn còn nguyên, Clevis chỉ thêm slot mới, không xóa cái cũ.

Kiểm tra keyslot sau khi bind:

sudo cryptsetup luksDump /dev/sda2 | grep -E "Keyslot|ENABLED"

Regenerate initramfs — bước hay bị quên nhất

Bind xong là phải regenerate initramfs ngay — bỏ qua bước này thì Clevis không chạy được lúc boot:

sudo dracut --force

Mình đã bị vấp đúng chỗ này lần đầu — bind xong nhưng quên dracut, reboot vào vẫn phải gõ passphrase thủ công như cũ. Mất 30 phút debug mới tìm ra.

Cấu hình Network trong initramfs

Vấn đề tinh tế ở đây: client cần network để liên hệ Tang server, nhưng root filesystem đang bị LUKS khóa — phải mở khóa trước. Classic chicken-and-egg. Clevis-dracut giải quyết bằng cách nhúng network stack thẳng vào initramfs.

Thêm tham số kernel để bật network trong initramfs (DHCP):

sudo grubby --update-kernel=ALL --args="rd.neednet=1 ip=dhcp"

Nếu dùng static IP (phổ biến với server):

sudo grubby --update-kernel=ALL --args="rd.neednet=1 ip=192.168.1.20::192.168.1.1:255.255.255.0:myhostname:eth0:none"

Regenerate initramfs một lần nữa để nhận cấu hình mới:

sudo dracut --force

Test trước khi reboot thật

Unlock thủ công để xác nhận binding — không cần passphrase nếu Tang đang chạy:

sudo clevis luks unlock -d /dev/sda2

Không hỏi passphrase là setup đúng. Lúc này reboot thật là an toàn.

Xử lý khi Tang server không khả dụng

Câu hỏi mình lo nhất trước khi deploy production: Tang server down thì máy client có boot được không?

Câu trả lời: có, nhưng phải gõ passphrase thủ công. Clevis thử liên hệ Tang trong initramfs, timeout sau ~30 giây, rồi fallback về prompt nhập passphrase LUKS. Passphrase gốc vẫn hoạt động bình thường.

Ngồi chờ 30 giây ngắm màn hình đen khi Tang down khá khó chịu. Rút ngắn timeout:

sudo grubby --update-kernel=ALL --args="rd.clevis.tang.timeout=10"
sudo dracut --force

Rotate key của Tang định kỳ

Sau khoảng 6 tháng, nên rotate key Tang để giảm rủi ro nếu key bị lộ. Fedora 40 dùng Tang 14+ — không còn lệnh tangd-keygen nữa. Key được sinh tự động khi service khởi động lần đầu và lưu tại /var/db/tang/.

Thêm key exchange mới bằng jose (đi kèm package tang), rồi reload service:

# Xem keys hiện tại
ls /var/db/tang/
sudo tang-show-keys

# Sinh key exchange mới
sudo jose jwk gen -i '{"alg":"EC","crv":"P-521","key_ops":["deriveKey"]}' \
  | sudo tee /var/db/tang/rotate-exc.jwk > /dev/null
sudo systemctl reload tangd.socket

Trên từng client, re-bind với key mới (thay SLOT bằng số keyslot Clevis đang dùng):

# Xem slot nào Clevis đang dùng
sudo clevis luks list -d /dev/sda2

# Re-bind slot đó
sudo clevis luks regen -d /dev/sda2 -s SLOT
sudo dracut --force

Key cũ vẫn giữ trong /var/db/tang/ cho đến khi bạn xóa thủ công — safety net để client chưa kịp re-bind vẫn hoạt động.

Những gì mình học được sau 6 tháng

Cái hay của NBDE là bạn không nghĩ đến nó nữa sau khi setup xong — server tự reboot sau kernel update mà không cần thức đến nửa đêm chờ gõ passphrase. Đây là thứ mình hài lòng nhất về mặt vận hành.

Một vài điều rút ra qua thực tế:

  • Tang server phải boot trước client: Nếu cả hai reboot cùng lúc sau mất điện, client sẽ timeout và cần passphrase thủ công. UPS cho máy Tang là đầu tư đáng giá.
  • Monitor Tang uptime riêng: Mình thêm HTTP check đơn giản vào Uptime Kuma — nếu Tang down là cảnh báo ngay, không để server client reboot vào lúc đó.
  • Tang phải là máy riêng biệt: Nghe hiển nhiên nhưng vẫn có người hỏi. Tang và client cùng máy thì mã hóa mất hoàn toàn ý nghĩa.
  • Test fallback định kỳ: Mỗi quý mình tắt Tang server rồi reboot client để xác nhận fallback passphrase vẫn hoạt động. Đừng để đến lúc thật sự cần mới phát hiện đã quên passphrase.

Kết luận

NBDE với Clevis + Tang cho phép bạn có cả hai cùng lúc: ổ đĩa mã hóa đủ mạnh và không phải gõ passphrase mỗi lần reboot. Tradeoff là phụ thuộc vào Tang server availability — nhưng fallback passphrase vẫn hoạt động, nên rủi ro này nằm trong tầm kiểm soát.

Toàn bộ stack nằm trong repo Fedora chính thức — không cần thêm repo bên thứ ba nào, khác với nhiều giải pháp tự chế khác.

Share: