Mã hóa trong MySQL: Keystore

Dự đoán về việc bắt đầu tuyển sinh mới cho khóa học "Cơ sở dữ liệu" Chúng tôi đã chuẩn bị bản dịch một bài viết hữu ích cho bạn.

Mã hóa trong MySQL: Keystore

Mã hóa dữ liệu trong suốt (TDE) xuất hiện trong Máy chủ Percona cho MySQL và MySQL trong một thời gian khá lâu. Nhưng bạn đã bao giờ nghĩ về cách thức hoạt động của nó và TDE có thể có tác động gì đến máy chủ của bạn chưa? Trong loạt bài viết này, chúng ta sẽ xem xét cách TDE hoạt động nội bộ. Hãy bắt đầu với việc lưu trữ khóa vì điều này là bắt buộc để mọi mã hóa hoạt động. Sau đó, chúng ta sẽ xem xét kỹ hơn cách hoạt động của mã hóa trong Percona Server for MySQL/MySQL và những tính năng bổ sung mà Percona Server for MySQL có.

Móc khóa MySQL

Khóa là các plugin cho phép máy chủ truy vấn, tạo và xóa khóa trong tệp cục bộ (keyring_file) hoặc trên máy chủ từ xa (chẳng hạn như HashiCorp Vault). Các khóa luôn được lưu vào bộ nhớ đệm cục bộ để tăng tốc độ truy xuất.

Các plugin có thể được chia thành hai loại:

  • Lưu trữ cục bộ. Ví dụ: một tệp cục bộ (chúng tôi gọi đây là khóa dựa trên tệp).
  • Lưu trữ từ xa. Ví dụ: Máy chủ Vault (chúng tôi gọi đây là khóa dựa trên máy chủ).

Sự phân tách này rất quan trọng vì các loại lưu trữ khác nhau hoạt động hơi khác nhau, không chỉ khi lưu trữ và truy xuất khóa mà còn khi chạy chúng.

Khi sử dụng bộ lưu trữ tệp, khi khởi động, toàn bộ nội dung của bộ lưu trữ sẽ được tải vào bộ đệm: id khóa, người dùng khóa, loại khóa và chính khóa đó.

Trong trường hợp cửa hàng phía máy chủ (chẳng hạn như Vault Server), chỉ id khóa và người dùng khóa được tải khi khởi động, do đó việc lấy tất cả các khóa không làm chậm quá trình khởi động. Các phím được tải một cách lười biếng. Nghĩa là, bản thân khóa chỉ được tải từ Vault khi thực sự cần thiết. Sau khi tải xuống, khóa sẽ được lưu vào bộ nhớ để không cần truy cập thông qua kết nối TLS đến Máy chủ Vault trong tương lai. Tiếp theo, chúng ta hãy xem thông tin nào có trong kho khóa.

Thông tin chính có chứa những điều sau đây:

  • mã khóa - mã định danh khóa, ví dụ:
    INNODBKey-764d382a-7324-11e9-ad8f-9cb6d0d5dc99-1
  • loại chính - loại khóa dựa trên thuật toán mã hóa được sử dụng, các giá trị có thể có: “AES”, “RSA” hoặc “DSA”.
  • độ dài chìa khóa — độ dài khóa tính bằng byte, AES: 16, 24 hoặc 32, RSA 128, 256, 512 và DSA 128, 256 hoặc 384.
  • người sử dụng - chủ nhân của chìa khóa. Nếu khóa là hệ thống, chẳng hạn như Master Key, thì trường này trống. Nếu khóa được tạo bằng keyring_udf thì trường này sẽ xác định chủ sở hữu của khóa.
  • chính chiếc chìa khóa

Khóa được xác định duy nhất bởi cặp: key_id, user.

Cũng có sự khác biệt trong việc lưu trữ và xóa khóa.

Lưu trữ tập tin nhanh hơn. Bạn có thể nghĩ rằng kho khóa chỉ đơn giản là ghi khóa vào một tệp một lần, nhưng không, ở đây còn nhiều điều hơn thế nữa. Bất cứ khi nào thực hiện sửa đổi bộ nhớ tệp, bản sao lưu của tất cả nội dung sẽ được tạo trước tiên. Giả sử tệp có tên my_biggest_secrets thì bản sao lưu sẽ là my_biggest_secrets.backup. Tiếp theo, bộ đệm được thay đổi (các khóa được thêm hoặc xóa) và nếu mọi thứ thành công, bộ đệm sẽ được đặt lại thành một tệp. Trong một số trường hợp hiếm gặp, chẳng hạn như lỗi máy chủ, bạn có thể thấy tệp sao lưu này. Tệp sao lưu sẽ bị xóa vào lần tải khóa tiếp theo (thường là sau khi máy chủ khởi động lại).

Khi lưu hoặc xóa khóa trong bộ lưu trữ máy chủ, bộ lưu trữ phải kết nối với máy chủ MySQL bằng lệnh “gửi khóa” / “yêu cầu xóa khóa”.

Hãy quay lại tốc độ khởi động máy chủ. Ngoài thực tế là tốc độ khởi chạy bị ảnh hưởng bởi chính vault, còn có vấn đề là cần lấy bao nhiêu chìa khóa từ vault khi khởi động. Tất nhiên, điều này đặc biệt quan trọng đối với việc lưu trữ máy chủ. Khi khởi động, máy chủ sẽ kiểm tra khóa nào được yêu cầu cho các bảng/không gian bảng được mã hóa và yêu cầu khóa từ bộ lưu trữ. Trên máy chủ “sạch” có mã hóa Master Key, phải có một Master Key, phải được lấy từ bộ lưu trữ. Tuy nhiên, có thể cần số lượng khóa lớn hơn, chẳng hạn như khi máy chủ dự phòng đang khôi phục bản sao lưu từ máy chủ chính. Trong những trường hợp như vậy, nên cung cấp tính năng xoay Khóa chính. Điều này sẽ được đề cập chi tiết hơn trong các bài viết sau, mặc dù ở đây tôi muốn lưu ý rằng máy chủ sử dụng nhiều Khóa chính có thể mất nhiều thời gian hơn để khởi động, đặc biệt là khi sử dụng kho khóa phía máy chủ.

Bây giờ hãy nói thêm một chút về keyring_file. Khi tôi đang phát triển keyring_file, tôi cũng lo lắng về cách kiểm tra các thay đổi của keyring_file trong khi máy chủ đang chạy. Trong 5.7, việc kiểm tra được thực hiện dựa trên số liệu thống kê tệp, đây không phải là giải pháp lý tưởng và trong 8.0, nó được thay thế bằng tổng kiểm tra SHA256.

Lần đầu tiên bạn chạy keyring_file, số liệu thống kê tệp và tổng kiểm tra sẽ được tính toán, được máy chủ ghi nhớ và các thay đổi chỉ được áp dụng nếu chúng khớp. Khi tập tin thay đổi, tổng kiểm tra sẽ được cập nhật.

Chúng tôi đã giải đáp nhiều câu hỏi về kho tiền chính. Tuy nhiên, có một chủ đề quan trọng khác thường bị lãng quên hoặc hiểu sai: chia sẻ khóa giữa các máy chủ.

Ý tôi là gì? Mỗi máy chủ (ví dụ: Máy chủ Percona) trong cụm phải có một vị trí riêng trên Máy chủ Vault trong đó Máy chủ Percona phải lưu trữ các khóa của nó. Mỗi Khóa chính được lưu trong bộ lưu trữ đều chứa GUID của Máy chủ Percona trong mã nhận dạng của nó. Tại sao nó lại quan trọng? Hãy tưởng tượng rằng bạn chỉ có một Máy chủ Vault và tất cả Máy chủ Percona trong cụm đều sử dụng Máy chủ Vault đó. Vấn đề có vẻ hiển nhiên. Nếu tất cả Máy chủ Percona sử dụng Khóa chính mà không có mã định danh duy nhất, chẳng hạn như id = 1, id = 2, v.v. thì tất cả các máy chủ trong cụm sẽ sử dụng cùng một Khóa chính. Những gì GUID cung cấp là sự khác biệt giữa các máy chủ. Tại sao lại nói về việc chia sẻ khóa giữa các máy chủ nếu đã tồn tại một GUID duy nhất? Có một plugin khác - keyring_udf. Với plugin này, người dùng máy chủ của bạn có thể lưu trữ khóa của họ trên máy chủ Vault. Sự cố xảy ra khi người dùng tạo khóa trên server1 chẳng hạn, sau đó cố gắng tạo khóa có cùng ID trên server2, ví dụ:

--server1:
select keyring_key_store('ROB_1','AES',"123456789012345");
1
--1 значит успешное завершение
--server2:
select keyring_key_store('ROB_1','AES',"543210987654321");
1

Chờ đợi. Cả hai máy chủ đều đang sử dụng cùng một Máy chủ Vault, chức năng keyring_key_store có bị lỗi trên server2 không? Điều thú vị là nếu bạn cố gắng thực hiện tương tự trên một máy chủ, bạn sẽ gặp lỗi:

--server1:
select keyring_key_store('ROB_1','AES',"123456789012345");
1
select keyring_key_store('ROB_1','AES',"543210987654321");
0

Đúng vậy, ROB_1 đã tồn tại.

Trước tiên hãy thảo luận về ví dụ thứ hai. Như chúng tôi đã nói trước đó, keyring_vault hoặc bất kỳ plugin tạo khóa nào khác sẽ lưu trữ tất cả ID khóa trong bộ nhớ. Vì vậy, sau khi tạo khóa mới, ROB_1 sẽ được thêm vào server1 và ngoài việc gửi khóa này đến Vault, khóa này còn được thêm vào bộ đệm. Bây giờ, khi chúng tôi cố gắng thêm cùng một khóa lần thứ hai, keyring_vault sẽ kiểm tra xem khóa đó có tồn tại trong bộ đệm hay không và đưa ra lỗi.

Trong trường hợp đầu tiên, tình hình sẽ khác. Server1 và server2 có bộ đệm riêng biệt. Sau khi thêm ROB_1 vào bộ đệm khóa trên máy chủ1 và máy chủ Vault, bộ đệm khóa trên máy chủ2 không đồng bộ. Không có khóa ROB_2 trong bộ đệm trên server1. Do đó, khóa ROB_1 được ghi vào keyring_key_store và vào máy chủ Vault, thực tế sẽ ghi đè (!) Giá trị trước đó. Bây giờ khóa ROB_1 trên máy chủ Vault là 543210987654321. Điều thú vị là máy chủ Vault không chặn những hành động như vậy và dễ dàng ghi đè giá trị cũ.

Bây giờ chúng ta có thể hiểu tại sao việc phân vùng máy chủ trong Vault lại quan trọng - khi bạn đang sử dụng keyring_udf và muốn lưu trữ khóa trong Vault. Làm cách nào để đạt được sự tách biệt này trên máy chủ Vault?

Có hai cách để phân vùng vào Vault. Bạn có thể tạo các điểm gắn kết khác nhau cho mỗi máy chủ hoặc sử dụng các đường dẫn khác nhau trong cùng một điểm gắn kết. Điều này được minh họa tốt nhất bằng các ví dụ. Vì vậy, trước tiên hãy xem xét các điểm gắn kết riêng lẻ:

--server1:
vault_url = http://127.0.0.1:8200
secret_mount_point = server1_mount
token = (...)
vault_ca = (...)

--server2:
vault_url = http://127.0.0.1:8200
secret_mount_point = sever2_mount
token = (...)
vault_ca = (...)

Ở đây bạn có thể thấy server1 và server2 đang sử dụng các điểm gắn kết khác nhau. Khi chia đường dẫn thì cấu hình sẽ như sau:

--server1:
vault_url = http://127.0.0.1:8200
secret_mount_point = mount_point/server1
token = (...)
vault_ca = (...)
--server2:
vault_url = http://127.0.0.1:8200
secret_mount_point = mount_point/sever2
token = (...)
vault_ca = (...)

Trong trường hợp này, cả hai máy chủ đều sử dụng cùng một điểm gắn kết "mount_point", nhưng các đường dẫn khác nhau. Khi bạn tạo bí mật đầu tiên trên server1 bằng đường dẫn này, máy chủ Vault sẽ tự động tạo thư mục “server1”. Đối với server2 mọi thứ đều tương tự. Khi bạn xóa bí mật cuối cùng trong mount_point/server1 hoặc mount_point/server2, máy chủ Vault cũng xóa các thư mục đó. Trong trường hợp sử dụng tính năng phân tách đường dẫn, bạn chỉ phải tạo một điểm gắn kết và thay đổi các tệp cấu hình để các máy chủ sử dụng các đường dẫn riêng biệt. Điểm gắn kết có thể được tạo bằng yêu cầu HTTP. Sử dụng CURL, việc này có thể được thực hiện như thế này:

curl -L -H "X-Vault-Token: TOKEN" –cacert VAULT_CA
--data '{"type":"generic"}' --request POST VAULT_URL/v1/sys/mounts/SECRET_MOUNT_POINT

Tất cả các trường (TOKEN, VAULT_CA, VAULT_URL, SECRET_MOUNT_POINT) tương ứng với các tham số của tệp cấu hình. Tất nhiên, bạn có thể sử dụng tiện ích Vault để làm điều tương tự. Nhưng việc tự động hóa việc tạo điểm gắn kết sẽ dễ dàng hơn. Tôi hy vọng bạn thấy thông tin này hữu ích và chúng tôi sẽ gặp lại bạn trong các bài viết tiếp theo của loạt bài này.

Mã hóa trong MySQL: Keystore

Đọc thêm:

Nguồn: www.habr.com

Thêm một lời nhận xét