Nguyên lý hoạt động của TDE và vai trò của Master Key
Transparent Data Encryption (TDE) mã hóa dữ liệu vật lý trên disk mà không cần thay đổi ứng dụng. Khi dữ liệu được ghi vào disk, nó đã ở dạng mã hóa; khi đọc, kernel hoặc engine database giải mã trong bộ nhớ RAM.
Vai trò của Master Key (File Encryption Key) là khóa mã hóa các file dữ liệu. Master Key này lại được bảo vệ bởi một chứng chỉ (Certificate) hoặc một khóa mã hóa khác (KEK - Key Encryption Key) lưu trữ bên ngoài. Nếu file dữ liệu bị đánh cắp nhưng không có Master Key hoặc chứng chỉ, dữ liệu vẫn là văn bản ngẫu nhiên (ciphertext).
Trong môi trường phân tán, việc quản lý chứng chỉ là bắt buộc để đảm bảo tính nhất quán giữa các node khi thực hiện failover.
Verify kết quả phần này
Đảm bảo bạn đã nắm rõ luồng: App -> RAM (Plain) -> Disk (Encrypted) sử dụng Master Key được bảo vệ bởi Certificate.
Tạo và quản lý chứng chỉ SSL/TLS cho giao tiếp nội bộ
Bước đầu tiên là tạo một Certificate Authority (CA) nội bộ và chứng chỉ server cho từng node. Chứng chỉ này sẽ được dùng để mã hóa Master Key của database, đảm bảo chỉ node có private key mới giải mã được.
Tạo thư mục chứa chứng chỉ và tạo CA root với thuật toán RSA 4096 bit, thời hạn 10 năm.
mkdir -p /etc/db-ssl/ca && cd /etc/db-ssl/ca
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem -subj "/CN=InternalDB_CA/O=MyCompany/C=VN"
Kết quả: File ca-key.pem và ca-cert.pem được tạo trong thư mục /etc/db-ssl/ca.
Tạo private key và CSR (Certificate Signing Request) cho node hiện tại (ví dụ: node1). Đặt mật khẩu cho private key để bảo vệ khi lưu trên disk.
mkdir -p /etc/db-ssl/node1 && cd /etc/db-ssl/node1
openssl genrsa -aes256 -out node1-key.pem 4096
openssl req -new -key node1-key.pem -out node1.csr -subj "/CN=node1.local/O=MyCompany/C=VN"
Kết quả: File node1-key.pem (có mật khẩu) và node1.csr được tạo.
Ký CSR bằng CA root để tạo chứng chỉ final. Cấp quyền đọc private key cho user chạy database (ví dụ: user 'postgres' hoặc 'mysql').
openssl x509 -req -days 3650 -in node1.csr -CA /etc/db-ssl/ca/ca-cert.pem -CAkey /etc/db-ssl/ca/ca-key.pem -set_serial 01 -out node1-cert.pem
chmod 600 node1-key.pem
chown root:root node1-key.pem
chown root:root node1-cert.pem
Kết quả: File node1-cert.pem được tạo, quyền truy cập file key được hạn chế chỉ root đọc được (database sẽ cần root hoặc sudo để khởi động, hoặc cấu hình systemd để read key).
Verify kết quả phần này
Kiểm tra tính hợp lệ của chứng chỉ và thời hạn.
openssl verify -CAfile /etc/db-ssl/ca/ca-cert.pem /etc/db-ssl/node1/node1-cert.pem
Kết quả mong đợi: "node1-cert.pem: OK"
Cấu hình file cấu hình để kích hoạt TDE trên Database
Chúng ta sẽ sử dụng PostgreSQL làm ví dụ cho phần cấu hình TDE, vì nó hỗ trợ TDE qua extension hoặc thông qua cơ chế TDE tích hợp trong các bản phân phối enterprise (như EDB Postgres Advanced Server) hoặc sử dụng pg_tde. Ở đây, giả sử chúng ta đang dùng Postgres với module TDE hoặc cấu hình tương đương cho MySQL (TDE native). Ví dụ này dùng PostgreSQL cấu hình để load key từ file chứng chỉ.
Chỉnh sửa file cấu hình chính của PostgreSQL (postgresql.conf) để bật TDE và chỉ định đường dẫn đến chứng chỉ đã tạo.
Đường dẫn file: /var/lib/pgsql/data/postgresql.conf
echo "
# Enable TDE
tde.enable = on
# Path to the certificate used to encrypt the master key
tde.certificate_path = '/etc/db-ssl/node1/node1-cert.pem'
# Path to the private key (optional if using cert with embedded key, but recommended separate)
tde.private_key_path = '/etc/db-ssl/node1/node1-key.pem'
# Encryption algorithm
tde.algorithm = 'AES-256-CBC'
# Force encryption on startup
tde.force_encrypt = on
" >> /var/lib/pgsql/data/postgresql.conf
Kết quả: Các tham số TDE được thêm vào cuối file postgresql.conf.
Đối với MySQL (MariaDB/Percona), cấu hình nằm trong my.cnf. Nếu bạn dùng MySQL, hãy chỉnh sửa như sau:
Đường dẫn file: /etc/my.cnf.d/tde.cnf
cat > /etc/my.cnf.d/tde.cnf
Kết quả: File cấu hình TDE riêng biệt được tạo trong thư mục cấu hình của MySQL.
Verify kết quả phần này
Khởi động lại dịch vụ database để áp dụng cấu hình.
systemctl restart postgresql
# hoặc
systemctl restart mysqld
Kiểm tra log khởi động để đảm bảo không có lỗi liên quan đến key hoặc certificate.
journalctl -u postgresql -n 50 --no-pager | grep -i "tde\|encrypt\|key"
Kết quả mong đợi: Log hiển thị "TDE initialized successfully" hoặc "Encryption enabled" mà không có lỗi "Permission denied" hay "Invalid key".
Lưu trữ an toàn key mã hóa ngoài hệ thống file
Lưu key mã hóa trực tiếp trên disk cùng với dữ liệu là rủi ro cao. Nếu attacker có quyền root, họ có thể lấy cả key và data. Giải pháp là sử dụng Hardware Security Module (HSM) hoặc Key Management Service (KMS) như HashiCorp Vault hoặc AWS KMS. Ở đây, chúng ta mô phỏng việc lưu trữ an toàn bằng cách sử dụng một volume mạng (NFS/Samba) riêng biệt hoặc một partition đã được mount với quyền truy cập hạn chế cực độ, hoặc sử dụng LUKS để mã hóa thư mục chứa key.
Tạo một partition riêng biệt để lưu trữ key, mã hóa partition này bằng LUKS trước khi mount.
mkfs.ext4 /dev/sdb1
cryptsetup luksFormat /dev/sdb1
cryptsetup open /dev/sdb1 db-key-store
mkfs.ext4 /dev/mapper/db-key-store -m 0
mount /dev/mapper/db-key-store /mnt/db-keys
chmod 700 /mnt/db-keys
chown root:root /mnt/db-keys
Kết quả: Partition /dev/sdb1 được mã hóa, mount vào /mnt/db-keys với quyền 700.
Di chuyển các file key và certificate vào thư mục an toàn này. Cập nhật lại đường dẫn trong file cấu hình database (postgresql.conf hoặc my.cnf) trỏ đến /mnt/db-keys.
mv /etc/db-ssl/node1/node1-key.pem /mnt/db-keys/
mv /etc/db-ssl/node1/node1-cert.pem /mnt/db-keys/
chmod 600 /mnt/db-keys/node1-key.pem
chown root:root /mnt/db-keys/node1-key.pem
Kết quả: Key được di chuyển vào vùng mã hóa riêng, không nằm cùng partition với dữ liệu database.
Cấu hình systemd để tự động unlock partition khi khởi động service database, sử dụng script hoặc systemd-cryptsetup-generator. Ở đây dùng unit file đơn giản để mount trước khi start DB.
Đường dẫn file: /etc/systemd/system/db-keys.mount
cat > /etc/systemd/system/db-keys.mount
Kết quả: Unit file được tạo để mount key store trước khi database khởi động.
Để đơn giản hóa trong môi trường không có HSM, chúng ta có thể sử dụng công cụ `age` hoặc `gpg` để mã hóa key trước khi lưu, và sử dụng script khởi động để giải mã key vào RAM (không lưu trên disk) trước khi truyền cho database. Tuy nhiên, cách chuẩn nhất là cấu hình database đọc key từ LUKS mount point như trên.
Verify kết quả phần này
Unmount partition và thử khởi động database. Database phải báo lỗi không tìm thấy key hoặc không thể khởi động.
cryptsetup close db-key-store
systemctl restart postgresql
journalctl -u postgresql -n 20 --no-pager | grep -i "error\|key\|tde"
Kết quả mong đợi: Log báo lỗi "Cannot open encryption key" hoặc "TDE initialization failed".
Mount lại và khởi động lại database để xác nhận hoạt động bình thường.
cryptsetup open /dev/sdb1 db-key-store
systemctl start postgresql
journalctl -u postgresql -n 20 --no-pager | grep -i "TDE\|encrypt"
Kết quả mong đợi: Database khởi động thành công với TDE active.
Kiểm tra xác minh dữ liệu đã được mã hóa trên Disk
Cách chắc chắn nhất để xác minh TDE đang hoạt động là kiểm tra nội dung raw của file dữ liệu trên disk khi database đang chạy. Dữ liệu phải hiển thị là binary ngẫu nhiên (entropy cao), không thể đọc được bằng các công cụ text hoặc hex editor thông thường.
Tạo một bảng và chèn dữ liệu nhạy cảm vào database để làm mẫu kiểm tra.
psql -U postgres -c "CREATE TABLE secure_data (id int, secret text);"
psql -U postgres -c "INSERT INTO secure_data VALUES (1, 'SUPER_SECRET_PASSWORD_12345');"
psql -U postgres -c "SELECT pg_sleep(1);"
Kết quả: Dữ liệu đã được ghi vào file data của PostgreSQL.
Xác định file data vật lý chứa dữ liệu mới ghi. Trong PostgreSQL, có thể dùng `pg_ls_dir` hoặc tìm file lớn nhất trong thư mục data, hoặc dùng `strace` để xem file nào được mở ghi (write).
Sử dụng `hexdump` hoặc `xxd` để đọc trực tiếp file dữ liệu từ disk. Nếu TDE hoạt động, bạn sẽ thấy dữ liệu mã hóa, không phải chuỗi "SUPER_SECRET_PASSWORD_12345".
find /var/lib/pgsql/data/base -name "*secure_data*" -exec xxd -l 1024 {} \; | head -n 20
Kết quả mong đợi: Đầu ra là các hex ngẫu nhiên (ví dụ: 4a 5b 2c 9f...), không có pattern ASCII của chuỗi bí mật.
So sánh với trường hợp TDE tắt (nếu có). Nếu TDE tắt, bạn sẽ thấy chuỗi "SUPER_SECRET_PASSWORD_12345" rõ ràng trong hex dump.
Đối với MySQL, file dữ liệu nằm trong thư mục database của schema. Kiểm tra file .ibd hoặc .MYD.
xxd -l 1024 /var/lib/mysql/mydb/secure_data.ibd | grep -i "SUPER"
Kết quả mong đợi: Không tìm thấy chuỗi "SUPER" nào (lệnh grep trả về exit code 1).
Thêm một bước kiểm tra bằng cách copy file dữ liệu ra một thư mục khác và cố gắng mount hoặc đọc nó như một file database bình thường. Nó sẽ bị lỗi checksum hoặc không thể mở do thiếu key giải mã.
cp /var/lib/pgsql/data/base/*/12345 /tmp/test_copy_file
psql -U postgres -c "COPY (SELECT * FROM secure_data) TO STDOUT;" 2>&1 | head
Kết quả mong đợi: Query SELECT chạy thành công (vì DB giải mã trong RAM), nhưng file copy ra /tmp là dữ liệu mã hóa không thể đọc trực tiếp.
Verify kết quả phần này
Chạy công cụ kiểm tra entropy để đảm bảo dữ liệu trên disk có độ ngẫu nhiên cao.
file /var/lib/pgsql/data/base/*/12345
hexdump -C /var/lib/pgsql/data/base/*/12345 | head -n 5
Kết quả mong đợi:
1. Lệnh `file` báo: "data" hoặc "data 12345" (không báo là text).
2. Lệnh `hexdump` hiển thị các byte ngẫu nhiên, không có cấu trúc ASCII rõ ràng.
Điều hướng series:
Mục lục: Series: Triển khai Database phân tán an toàn với Raft, TDE và Linux Kernel Audit
« Phần 2: Triển khai cơ chế đồng thuận Raft cho Database
Phần 4: Thiết lập Linux Kernel Audit để giám sát truy cập »