Mã hóa một VPS trông như một lệnh duy nhất — cryptsetup luksFormat, xong — rồi âm thầm không mang lại thứ người ta tưởng mình đang mua. Trên một cỗ máy bạn đi thuê, một hypervisor nằm bên dưới kernel của bạn, nên mô hình đe dọa không còn giống với mô hình mà mã hóa toàn ổ đĩa từng được thiết kế để đối phó. Bài hướng dẫn này là phiên bản thành thật: LUKS đánh bại được điều gì trên một VPS, điều gì thì không, hai phương án triển khai thực sự hiệu quả, và cách mở khóa một root đã mã hóa qua SSH để bạn không bao giờ bị khóa khỏi chính máy chủ của mình.
Mã hóa trên một máy chủ đi thuê thực sự đem lại điều gì
Mã hóa toàn ổ đĩa bảo vệ dữ liệu ở trạng thái nghỉ. Cụm từ đó gánh khá nhiều ý nghĩa, và trên một VPS thì đáng để nói rõ đâu là khoảnh khắc được tính là nghỉ và đâu thì không — khoảng cách giữa hai điều đó chính là nơi phần lớn kỳ vọng bị vỡ.
Phần cứng bị loại bỏ
Ổ đĩa hỏng, được thay thế, và rời khỏi rack. Một volume LUKS trên một đĩa đã chết chỉ là một khối nhiễu đối với bất kỳ ai xử lý nó sau đó. Mã hóa giải quyết trọn vẹn trường hợp này.
Một volume bị tháo ra hoặc bị sao chép
Nếu một volume bị gỡ khỏi instance của bạn, bị nhân bản, hoặc bị tạo image trong lúc máy đang tắt, kết quả chỉ là dữ liệu đã mã hóa cùng một header — không phải các file của bạn.
Một máy đang chạy
Một khi volume đã mở, khóa chủ nằm trong bộ nhớ kernel. Về nguyên tắc, quyền truy cập ở tầng hypervisor có thể chạm tới nó. Mã hóa nâng cao chi phí tấn công ở đây; nó không đóng hẳn cánh cửa.
Sai lầm của chính bạn
Swap chưa mã hóa, các bản sao lưu chưa mã hóa, và nhật ký được ghi trước khi mount đều nằm ngoài container mã hóa. Phần lớn rò rỉ thực tế từ các máy chủ đã mã hóa xảy ra ở đây, không phải qua thuật toán mã hóa.
Hãy nói thẳng ra giới hạn này để bạn còn lên kế hoạch quanh nó: trong lúc volume đang mở khóa, khóa nằm trong RAM trên phần cứng không thuộc sở hữu của bạn. LUKS trên một VPS là câu trả lời vững chắc cho truy cập ngoại tuyến và chỉ là câu trả lời một phần cho truy cập khi máy đang chạy. Hãy kết hợp nó với một khu vực pháp lý bạn thực sự tin tưởng — xem /offshore-hosting — thay vì xem nó như một thứ thay thế cho điều đó.
Chọn hình thức trước khi gõ bất kỳ lệnh nào
Bốn kiểu triển khai sau bao trùm gần như mọi nhu cầu thực tế. Chọn sai sẽ khiến bạn phải cài lại từ đầu, nên hãy quyết định ngay tại đây thay vì làm được nửa chừng mới nhận ra.
Phương án A — mã hóa một volume dữ liệu trên máy chủ bạn đang chạy
Đây là phiên bản mà hầu hết mọi người thực sự muốn: mười phút thao tác, không cần cài lại, và mọi thứ quan trọng đều nằm gọn bên trong container mã hóa. Trước tiên hãy gắn thêm một volume thứ hai — trên một gói /storage đó chính là ổ đĩa lớn; trên một /vps tiêu chuẩn bạn có thể thêm một ổ ngay lúc triển khai.
- 1
Xác định đúng thiết bị mục tiêu
Chạy lsblk và xác nhận thiết bị bạn sắp format chính là ổ trống. luksFormat sẽ xóa sạch mọi thứ có trên đó và không thể hoàn tác.
- 2
Tạo container LUKS2
cryptsetup luksFormat --type luks2 /dev/vdb sẽ yêu cầu bạn gõ YES bằng chữ in hoa, sau đó đặt một cụm mật khẩu. Hãy chọn một cụm mà bạn có thể gõ lại chính xác trên một console không lưu lịch sử gõ và không hiện ký tự.
- 3
Mở nó ra và tạo filesystem
cryptsetup open /dev/vdb cryptdata tạo ra /dev/mapper/cryptdata. Sau đó chạy mkfs.ext4 /dev/mapper/cryptdata và mount nó vào nơi dữ liệu sẽ nằm, ví dụ /srv/data.
- 4
Quyết định cách nó mở khóa lúc khởi động
Hoặc gõ cụm mật khẩu sau mỗi lần khởi động lại, hoặc thêm một file khóa bằng cryptsetup luksAddKey và lưu nó trên root. File khóa tiện lợi hơn nhưng chắc chắn kém an toàn hơn — hãy hiểu rõ bạn đang đánh đổi điều gì.
- 5
Khai báo trong crypttab và fstab
Thêm ánh xạ vào /etc/crypttab bằng UUID= thay vì tên thiết bị, sau đó mount từ /etc/fstab với cờ nofail để một lần mở khóa thất bại không bao giờ làm treo quá trình boot.
Nếu file khóa nằm trên root chưa mã hóa, hãy hiểu chính xác bạn đang có gì: volume được bảo vệ trước một ổ đĩa rời khỏi rack hoặc bị gỡ khỏi instance của bạn, chứ không được bảo vệ trước một bản sao của toàn bộ VM — vì bản sao đó chứa luôn cả khóa. Bất cứ điều gì mạnh hơn đều cần cụm mật khẩu đến từ bên ngoài máy chủ.
Phương án B — root được mã hóa từ một ISO tùy chỉnh
Khi yêu cầu đặt ra là không có gì đọc được tồn tại trên máy trong lúc nó đang tắt, chính root cũng phải nằm bên trong LUKS. Điều đó có nghĩa là một lần cài đặt mới điều khiển từ console, trên một nhà cung cấp cho phép bạn boot ISO của riêng mình.
- 1
Boot trình cài đặt từ console
Gắn một ISO netinst của Debian hoặc Ubuntu làm ISO tùy chỉnh và điều khiển quá trình cài đặt qua VNC hoặc serial. Không bước nào trong số này chạy được qua SSH — vì chưa có hệ thống nào để kết nối tới cả.
- 2
Dùng phân vùng có hướng dẫn với LVM đã mã hóa
Trình cài đặt để lại một /boot nhỏ ở dạng chưa mã hóa, vì phải có thứ gì đó chạy được trước khi volume mở ra, và đặt root cùng swap vào chung một container LUKS.
- 3
Chọn một cụm mật khẩu bạn có thể gõ mà không cần nhìn
Bạn sẽ phải gõ lại nó qua SSH ở mỗi lần khởi động lại, không hiện ký tự và không gợi ý tự động. Một cụm mật khẩu gồm năm từ ở đây tốt hơn hẳn một chuỗi ký hiệu lộn xộn.
- 4
Thiết lập mở khóa từ xa trước khi đăng xuất
Một root vừa được mã hóa sẽ đứng yên mãi ở màn hình nhập cụm mật khẩu sau lần khởi động lại tiếp theo. Hãy cấu hình mở khóa từ xa ngay trong cùng phiên làm việc, khi console vẫn còn đang mở trước mặt bạn.
Trên các gói có 1–2 GB RAM, quá trình dẫn xuất khóa Argon2id mặc định có thể đòi hỏi nhiều bộ nhớ hơn mức initramfs có sẵn, khiến việc mở khóa thất bại lúc boot dù cụm mật khẩu hoàn toàn đúng. Hãy giới hạn nó ngay lúc format bằng cryptsetup luksFormat --pbkdf-memory 262144, hoặc kiểm tra một header đã có sẵn bằng cryptsetup luksDump trước khi tin tưởng giao một instance nhỏ cho việc khởi động lại không người giám sát.
Mở khóa từ xa qua SSH bằng dropbear-initramfs
Một root đã mã hóa cần một cụm mật khẩu trước khi hệ điều hành tồn tại. dropbear-initramfs đặt một SSH server nhỏ xíu bên trong initramfs để bạn có thể nhập nó từ bất cứ đâu — đó là khác biệt giữa một máy chủ đã mã hóa và một cục gạch đã mã hóa.
- 1
Cài gói
apt install dropbear-initramfs. Nó móc vào quá trình tạo initramfs và tự động được build lại vào mọi kernel image trong tương lai.
- 2
Cấp quyền cho một khóa, và chỉ đúng một lệnh
Đặt khóa công khai của bạn vào /etc/dropbear/initramfs/authorized_keys — trên Debian 11 trở về trước là /etc/dropbear-initramfs/authorized_keys. Thêm tiền tố no-port-forwarding,no-agent-forwarding,no-x11-forwarding,command="cryptroot-unlock" vào đầu dòng để một khóa bị đánh cắp cũng chỉ đổi lấy đúng một lời nhắc nhập cụm mật khẩu, không gì khác.
- 3
Cấp mạng cho initramfs
DHCP hoạt động ở những nơi nhà cung cấp hỗ trợ; nếu không, hãy thêm một tham số tĩnh ip=ADDRESS::GATEWAY:NETMASK::eth0:off vào GRUB_CMDLINE_LINUX và chạy update-grub. Kết thúc bằng update-initramfs -u -k all để thay đổi được đưa vào image.
- 4
Kiểm thử một lần khởi động lại đầy đủ trước khi tin dùng
Khởi động lại với console của nhà cung cấp mở sẵn ở một cửa sổ khác, kết nối, nhập cụm mật khẩu và quan sát quá trình boot tiếp diễn. Một đường mở khóa chưa từng được kiểm thử không phải là một tính năng, mà là một lần sập máy trong tương lai.
initramfs mang theo một host key SSH riêng, khác với host key mà hệ thống sau khi boot xong trình ra, nên client của bạn sẽ cảnh báo về một host key bị thay đổi ở mỗi lần khởi động lại. Hãy chạy dropbear trên một cổng riêng thông qua DROPBEAR_OPTIONS trong /etc/dropbear/initramfs/dropbear.conf, hoặc kết nối bằng ssh -o HostKeyAlias=box-initramfs, và ghi lại cả hai dấu vân tay.
Quản lý khóa chính là nơi mã hóa ổ đĩa thường thất bại
Thuật toán mã hóa không phải là điểm yếu, và chưa bao giờ là điểm yếu. Mọi thảm họa còn có thể cứu vãn liên quan tới LUKS đều quy về một khóa hoặc một header, nên hãy đối xử với cả hai như hạ tầng thực thụ.
- Sao lưu header ngay lập tức — cryptsetup luksHeaderBackup /dev/vdb --header-backup-file luks-header.img — và lưu nó ở một nơi khác ngoài máy chủ. Một header bị hỏng chỉ vì một lệnh dd bất cẩn là mất dữ liệu vĩnh viễn, cho dù cụm mật khẩu có hoàn hảo đến đâu.
- Dùng hai slot. LUKS2 cho bạn tới 32 slot: cụm mật khẩu của bạn ở một slot, một khóa khôi phục ngẫu nhiên và dài ở slot khác, lưu trong một trình quản lý mật khẩu. Chỉ dùng một slot duy nhất là tự rước lấy một điểm lỗi duy nhất cho chính mình.
- Xoay vòng khóa đúng thứ tự trên một máy từ xa — thêm khóa mới bằng luksAddKey, xác nhận nó mở được volume, rồi mới xóa khóa cũ bằng luksKillSlot. Không bao giờ làm ngược lại.
- Giữ cụm mật khẩu và thông tin kết nối của máy chủ ở những nơi tách biệt nhau. Một ghi chú bị lộ không nên trao luôn cả địa chỉ lẫn khóa.
Cái giá mã hóa phải trả về hiệu năng
Chi phí phát sinh là có thật nhưng thường không thấy được. Hãy đo trên chính cỗ máy bạn đang có, thay vì tin vào một benchmark từ một thập kỷ khác.
Những phần người ta hay quên
Một volume đã mã hóa nhưng vẫn để rò rỉ dữ liệu chưa mã hóa ở xung quanh chỉ tạo ra một cảm giác an toàn giả tạo, mà điều đó còn tệ hơn cả việc không mã hóa gì cả. Hãy khép kín năm điểm sau trước khi coi công việc đã xong.
- Swap. Một phân vùng swap chưa mã hóa có thể giữ lại mảnh vụn của bất cứ thứ gì từng đi qua bộ nhớ. Hãy mã hóa nó bằng một khóa ngẫu nhiên mới ở mỗi lần khởi động, thông qua một dòng /dev/urandom trong /etc/crypttab.
- Nhật ký ghi trước khi mount. Bất cứ điều gì được ghi vào nhật ký trong lúc volume mã hóa vẫn còn đóng sẽ rơi thẳng vào root chưa mã hóa. Hãy trỏ nhật ký của ứng dụng và cơ sở dữ liệu vào một đường dẫn bên trong container mã hóa.
- Bản sao lưu. Sao chép nội dung của một volume đã mã hóa sang một object storage chưa mã hóa sẽ xóa sạch mọi công sức mã hóa. Hãy mã hóa bản sao lưu một cách độc lập bằng restic, borg hoặc age, và giữ các khóa đó ở một nơi khác.
- Snapshot. Một snapshot của nhà cung cấp chụp lại ổ đĩa, chứ không chụp RAM của bạn, nên volume LUKS bên trong vẫn ở dạng đã mã hóa — nhưng mọi thứ còn sót lại trên root chưa mã hóa sẽ được chụp lại nguyên vẹn như nó vốn có.
- Discard. Bật cờ discard xuyên qua LUKS giữ cho trim của NVMe tiếp tục hoạt động, nhưng cũng để lộ block nào đang không được dùng, tức là làm rò rỉ mức độ đầy của filesystem và hình dạng đại khái của nó. Hãy chọn dùng nó một cách có chủ đích, không phải vì sao chép lại một file cấu hình có sẵn.
Chỗ mà nhà cung cấp vẫn còn quan trọng
Mã hóa là lớp mà bạn kiểm soát: nó quyết định việc đọc dữ liệu của bạn tốn kém đến mức nào một khi máy đã tắt hoặc một ổ đĩa đã rời khỏi tòa nhà. Lớp bao quanh nó — ai có thể ép buộc quyền truy cập vào phần cứng, và những hồ sơ nào gắn máy chủ với bạn — thuộc về nhà cung cấp và khu vực pháp lý của họ. Sáu trong số mười lăm khu vực của chúng tôi là các khu vực pháp lý ưu tiên quyền riêng tư; /locations liệt kê chúng, và /offshore-hosting nói rõ điều đó thực sự thay đổi những gì.
Ba năng lực của nhà cung cấp quyết định các phương án ở trên có khả thi hay không: khả năng boot ISO tùy chỉnh, thiếu nó thì Phương án B là bất khả thi; quyền truy cập console ngoài băng thông cho những lần khởi động lại mà việc mở khóa từ xa không quay trở lại được; và một quy trình đăng ký chưa từng gắn cỗ máy với danh tính pháp lý của bạn ngay từ đầu. Mọi gói /offshore-vps và /storage ở đây đều boot được ISO tùy chỉnh, đi kèm quyền truy cập console, và tính phí từ một số dư tiền mã hóa trả trước, không yêu cầu KYC — nên một ổ đĩa đã mã hóa không nằm trên một dấu vết giấy tờ vốn đã gắn tên bạn vào đó. /guides có một bài trình bày thành thật về những gì một nhà cung cấp có thể và không thể nhìn thấy.
Danh sách kiểm tra khi triển khai
- Xác định rõ mối đe dọa trước tiên: ổ đĩa bị loại bỏ, volume bị tháo rời và image ngoại tuyến là những gì mã hóa trả lời được — một hypervisor đang chạy thì không.
- Mã hóa một volume dữ liệu (Phương án A) nếu máy chủ đã đang chạy; chỉ cài lại từ một ISO tùy chỉnh (Phương án B) khi chính root bắt buộc phải được mã hóa.
- Dùng LUKS2 với aes-xts-plain64, và giới hạn bộ nhớ Argon2id trên bất kỳ gói nào dưới 2 GB RAM.
- Cụm mật khẩu ở một slot, một khóa khôi phục ngẫu nhiên và dài ở slot khác, cả hai đều được lưu ở nơi khác ngoài máy chủ.
- Sao lưu LUKS header trước khi ghi byte dữ liệu thật đầu tiên.
- dropbear-initramfs trên một cổng không mặc định, chỉ xác thực bằng khóa, giới hạn chỉ đúng lệnh cryptroot-unlock.
- Kiểm thử trọn một chu trình khởi động lại và mở khóa với console của nhà cung cấp vẫn đang mở bên cạnh bạn.
- Bản sao lưu đã mã hóa đặt ở nơi khác, cộng thêm một lần diễn tập khôi phục — một bản sao lưu chưa từng được kiểm thử chỉ là một hy vọng, không phải một bản sao lưu.
Mã hóa ổ đĩa có ngăn được nhà cung cấp hosting đọc dữ liệu của tôi không?
Ở trạng thái nghỉ thì có: một khi máy đã tắt, volume chỉ còn là dữ liệu đã mã hóa và cụm mật khẩu chưa bao giờ rời khỏi đầu bạn. Còn trong lúc máy chủ đang chạy với volume đã mở, khóa chủ nằm trong RAM trên phần cứng do nhà cung cấp vận hành. Mã hóa đóng kín mọi con đường ngoại tuyến — ổ đĩa bị loại bỏ, volume bị tháo rời, image chụp lúc máy tắt — và nâng cao chi phí cho phần còn lại. Nó bổ sung cho một khu vực pháp lý đáng tin cậy, chứ không thay thế được điều đó.
Tôi có thể mã hóa một VPS đang chạy sẵn mà không cần cài lại không?
Với một volume dữ liệu riêng thì có, mất khoảng mười phút — đó chính là Phương án A ở trên. Còn với root filesystem thì thực tế là không. cryptsetup reencrypt có thể chuyển đổi một filesystem ngay tại chỗ, nhưng một kết nối bị rớt hay một sự cố mất điện giữa chừng trên một máy từ xa sẽ để lại một máy chủ không boot được và một đêm dài vất vả. Cài lại từ một ISO tùy chỉnh nhanh hơn và an toàn hơn nhiều.
LUKS tốn bao nhiêu hiệu năng?
Ít hơn phần lớn người ta tưởng. Với AES-NI, AES-XTS chỉ tốn vài phần trăm trên các workload hỗn hợp thông thường, và cryptsetup benchmark trên một nhân EPYC hiện đại báo cáo tới vài GB/s. Trường hợp dễ thấy nhất là I/O tuần tự đơn luồng trên NVMe tốc độ cao, nơi một nhân CPU có thể bão hòa trước cả khi ổ đĩa chạm ngưỡng. Hãy tự benchmark trên chính instance của bạn thay vì đoán mò.
Điều gì xảy ra nếu tôi quên cụm mật khẩu?
Dữ liệu mất trắng. Không có cơ chế khôi phục, không có khóa chủ phía nhà cung cấp, và không có ticket hỗ trợ nào cứu vãn được điều đó — đặc tính đó chính là mục đích của thiết kế này. Hãy tự bảo vệ mình bằng một slot thứ hai chứa một khóa khôi phục ngẫu nhiên, và một bản sao lưu LUKS header được lưu ở một nơi khác ngoài máy chủ.
Chạy SSH bên trong initramfs có rủi ro không?
Đó là một bề mặt lộ diện nhỏ và đã được hiểu rõ. dropbear chỉ chạy trong vài giây trước khi root filesystem của bạn tồn tại, chỉ chấp nhận khóa công khai, và có thể bị giới hạn chỉ chạy đúng một lệnh bắt buộc mà việc duy nhất nó làm là hỏi cụm mật khẩu. Trên một cổng không mặc định, với host key của initramfs được ghi lại riêng, rủi ro thực tế là rất nhỏ so với việc chắc chắn sẽ bị khóa ngoài máy chủ nếu không có nó.


