Hướng dẫn cấu hình LUKS và Clevis/Tang trên CentOS Stream 9: Mã hóa ổ đĩa tự động (NBDE)

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

Sau cái vụ CentOS 8 EOL, mình phải migrate gấp 5 server sang Rocky Linux trong vòng 1 tuần. Một trong những thứ mình tiếc nhất là không kịp setup NBDE trước khi chuyển — kết quả là phải làm lại từ đầu trên môi trường mới. Rút kinh nghiệm, lần này mình ghi lại đầy đủ cho CentOS Stream 9.

Bối cảnh: Vấn đề thực tế với LUKS truyền thống

Bạn có một server bare-metal ở datacenter, ổ đĩa được mã hóa bằng LUKS. Mỗi lần server restart — dù là do update kernel, mất điện, hay maintenance — bạn phải ngồi chờ console để nhập passphrase thủ công. Với 5–10 server thì đã mệt, chứ đừng nói đến fleet lớn hơn hay môi trường không có người trực đêm.

NBDE (Network-Bound Disk Encryption) giải quyết đúng vấn đề này: ổ đĩa tự động unlock khi server boot trong network nội bộ (có Tang server), và vẫn yêu cầu passphrase thủ công nếu server bị mang ra khỏi mạng. Đây là cái hay — bảo mật vật lý mà không cản trở automation.

NBDE phù hợp nhất với:

  • Server database chứa dữ liệu nhạy cảm (PII, financial data)
  • Bare-metal hoặc VM ở co-location datacenter
  • Yêu cầu compliance: HIPAA, PCI-DSS, ISO 27001
  • Server tự reboot sau unattended-upgrades mà không cần người trực

Kiến trúc gồm hai thành phần chính:

  • Tang server: Server chuyên dụng giữ key, dùng McTLS protocol. Tang không lưu key của client — chỉ tham gia vào quá trình derive key theo Diffie-Hellman. Server bị compromise cũng không expose LUKS key.
  • Clevis: Framework phía client, hỗ trợ nhiều "pin" (tang, tpm2, sss). Bind LUKS volume với Tang hoặc TPM.

Luồng boot: Dracut initramfs → Clevis kết nối Tang qua network → Tang trả dữ liệu để derive key → LUKS unlock → boot tiếp tục.

Cài đặt Tang và Clevis

1. Cài đặt Tang Server

Tang nên chạy trên server riêng hoặc VM khác — không phải chính cái server cần unlock. Trên CentOS Stream 9:

dnf install tang -y
systemctl enable --now tangd.socket
firewall-cmd --add-service=tangd --permanent
firewall-cmd --reload

Kiểm tra Tang đang lắng nghe và lấy thumbprint (cần dùng khi bind Clevis):

systemctl status tangd.socket

# Lấy thumbprint của key
jose jwk thp -i /var/db/tang/*.jwk

Lưu thumbprint lại — ví dụ: abc123XYZdef456. Sẽ dùng ở bước bind.

2. Cài đặt Clevis phía Client

Trên server CentOS Stream 9 cần mã hóa tự động:

dnf install clevis clevis-luks clevis-dracut -y

# Nếu muốn hỗ trợ thêm TPM2
dnf install clevis-tpm2 -y

Cấu hình chi tiết

Bước 1: Xác định LUKS volume cần bind

# Kiểm tra các LUKS device
lsblk -f | grep crypto
blkid | grep LUKS

# Xem thông tin LUKS header (số slot đang dùng, UUID...)
cryptsetup luksDump /dev/sda3

Mình hay dùng UUID thay vì device name để tránh nhầm khi thêm disk mới. Lưu UUID từ output blkid.

Bước 2: Bind LUKS với Tang

# Thay TANG_SERVER_IP và THUMBPRINT bằng giá trị thực
clevis luks bind -d /dev/sda3 tang \
  '{"url":"http://TANG_SERVER_IP","thp":"THUMBPRINT"}'

Clevis hỏi passphrase LUKS hiện tại để thêm slot mới. Sau khi thành công, LUKS có 2 slot: slot 0 (passphrase gốc) và slot 1 (Clevis/Tang). Kiểm tra:

clevis luks list -d /dev/sda3
# Output:
# 1: tang '{"url":"http://192.168.1.50","thp":"abc123..."}'

Bước 3: Rebuild initramfs (bước hay bị bỏ qua nhất)

Đây là bước mình từng bỏ qua, và kết quả là ngồi chờ passphrase lúc 2 giờ sáng sau khi reboot production server. Phải rebuild initramfs để đưa Clevis vào bootloader:

dracut -fv --regenerate-all

Xác nhận Clevis đã có trong initramfs:

lsinitrd /boot/initramfs-$(uname -r).img | grep clevis

Bước 4: Cập nhật /etc/crypttab

Với data volume (không phải root), thêm vào /etc/crypttab:

# Format: name  device  keyfile  options
data-encrypted  UUID=your-luks-uuid  none  _netdev,x-initrd.attach

Option _netdev báo systemd cần network trước khi mount. x-initrd.attach đảm bảo được xử lý trong initrd. Thiếu hai option này, volume sẽ không unlock đúng thứ tự boot.

Bước 5 (Tùy chọn): HA với SSS — 2 Tang server

Nếu cần high availability, SSS (Shamir’s Secret Sharing) cho phép kết hợp nhiều pin với threshold. Ví dụ cần ít nhất 1/2 Tang server:

clevis luks bind -d /dev/sda3 sss \
  '{"t":1,"pins":{"tang":[{"url":"http://tang1.internal","thp":"THUMB1"},{"url":"http://tang2.internal","thp":"THUMB2"}]}}'

Setup này giúp server vẫn boot được kể cả khi 1 trong 2 Tang server bị down.

Kiểm tra và Monitoring

Test không cần reboot

# Test Clevis decrypt được không
clevis luks unlock -d /dev/sda3 -n test-unlock
# Nếu thành công → tạo /dev/mapper/test-unlock

# Dọn dẹp sau khi test
cryptsetup close test-unlock

# Test kết nối Tang trực tiếp
curl -sS http://TANG_SERVER_IP/adv | jose fmt -j- -g payload -y -o-
# Output là JSON chứa keys — nếu empty, check firewall

Xem log khi boot

# Log Clevis trong lần boot hiện tại
journalctl -b 0 | grep -i clevis

# Log Tang server theo real-time
journalctl -u tangd -f

Monitor Tang với Prometheus Blackbox

Tang không có built-in metrics. Mình dùng Blackbox Exporter để check endpoint /adv:

# blackbox.yml
modules:
  tang_check:
    prober: http
    http:
      valid_status_codes: [200]
      method: GET
      fail_if_body_not_matches_regexp:
        - "keys"
# prometheus.yml
- job_name: 'tang'
  metrics_path: /probe
  params:
    module: [tang_check]
  static_configs:
    - targets:
      - http://TANG_SERVER_IP/adv
  relabel_configs:
    - source_labels: [__address__]
      target_label: __param_target
    - target_label: __address__
      replacement: localhost:9115

Alert quan trọng: nếu Tang down quá 5 phút, page oncall ngay — vì lần reboot tiếp theo sẽ require passphrase thủ công.

Rotate key Tang định kỳ

# Trên Tang server: generate key mới
tangd-keygen /var/db/tang

# Trên client: re-bind với key mới (slot 1)
clevis luks regen -d /dev/sda3 -s 1

Mình đặt lịch rotate key mỗi 6 tháng, trigger re-bind qua Ansible playbook để không cần SSH thủ công vào từng server.

Backup LUKS header — bắt buộc

Sau cái vụ migrate CentOS 8 sang Rocky Linux hồi trước, mình học bài học cứng: backup LUKS header ngay sau khi cấu hình xong. Mất header = mất data vĩnh viễn, không có cách recovery:

cryptsetup luksHeaderBackup /dev/sda3 \
  --header-backup-file /root/luks-header-sda3-backup.img

# Lưu file này ra nơi khác (không phải cùng server)
scp /root/luks-header-sda3-backup.img backup-server:/secure-backups/

Xử lý lỗi dracut timeout

Nếu boot bị timeout vì Clevis không tìm được Tang kịp, tăng timeout trong dracut config:

# /etc/dracut.conf.d/clevis.conf
kernel_cmdline="rd.timeout=90 rd.neednet=1"

# Rebuild lại
dracut -fv --regenerate-all

Setup NBDE mất khoảng 1–2 tiếng cho lần đầu, nhưng sau đó toàn bộ fleet restart tự động không cần ai trực — và đội bảo mật cũng yên tâm hơn vì data-at-rest được encrypt đúng chuẩn.

Share: