Protect your data with MariaDB Server and Intel® TDX

In 2023, Intel launched Intel® Trust Domain Extensions (Intel® TDX), their latest Confidential Computing technology that ensures data in use protection for hardware-isolated virtual machines and verified hardware and software integrity through attestation.

In this blog post, we will show that MariaDB Server can run correctly on an Intel® TDX enabled VM and that the in-memory data is protected from outside actors, even from the host OS running the VM. By coupling Intel’s Confidential Computing technology with MariaDB Server build in data-at-rest and data-in-transit protection, customers can gain an end-to-end protected solution.

This advancement is crucial as the growing use of cloud services is inscreasing the need to protect sensitive data. Cloud providers now offer Confidential Computing services based on Intel® TDX. For more technology specific details, see Intel’s website.

Prologue

This year, we engaged in discussions with Intel representatives at SUSECON, leading us to collaborate on testing MariaDB Server with Intel® TDX. As part of this partnership we will publish several blog posts highlighting the combination of MariaDB Server and Intel® TDX.

This specific post will demonstrate how data stored in the server’s memory is protected from unauthorized access, even from attackers exploiting kernel vulnerabilities or rogue administrative users.

We will make use of Intel® TDX from two perspectives:

  • A DBA configuring MariaDB on their own VM to store sensitive banking information.
  • A third-party hosting provider.

Now, let’s keep the introduction brief and delve into the technical details!

Host Configuration

We’re working on a Ubuntu 24.04 OS installed on top of a system equipped with a CPU from the Xeon Scalable 5th Gen Emerald Rapids family.

~/tdx$ cat /etc/os-release 
PRETTY_NAME="Ubuntu 24.04 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04 LTS (Noble Numbat)"

~/tdx$ lscpu
Architecture:             x86_64
  CPU op-mode(s):         32-bit, 64-bit
  Address sizes:          45 bits physical, 57 bits virtual
  Byte Order:             Little Endian
CPU(s):                   224
  On-line CPU(s) list:    0-223
Vendor ID:                GenuineIntel
  Model name:             INTEL(R) XEON(R) PLATINUM 8570
    CPU family:           6
    Model:                207
    Thread(s) per core:   2
    Core(s) per socket:   56
    Socket(s):            2
    Stepping:             2

We’re following Canonical’s instructions for setting up the host OS and guest OS. If you’re looking for a more detailed alternative, Intel offers excellent documentation on setting up Intel® TDX.

On the host side, the BIOS settings were already in place for us as described in Canonical’s instructions. If you’re setting up your own bare-metal instance, remember to enable TDX in the BIOS settings.

We just need to run the configuration script.

# Clone the Canonical's Intel® TDX repository
git clone -b noble-24.04 https://github.com/canonical/tdx.git

# Setup Intel® TDX in host OS
cd tdx
sudo ./setup-tdx-host.sh

# Reboot
sudo reboot

# Verify that Intel® TDX is enabled
$ sudo dmesg | grep -i tdx
[    5.340815] virt/tdx: BIOS enabled: private KeyID range [64, 128)
...
...
[   21.984224] virt/tdx: module initialized

Configure VMs

First, we need to generate the guest images that are later used by QEMU. Canonical provides a script to generate guest images with Intel® TDX tools inside, with the baseline image being an ubuntu-24.04 cloud image, which does not require any kernel changes.

# Creating an Intel® TDX qcow2 disk image
cd tdx/guest-tools/image/
sudo ./create-td-image.sh

To later compare the VM protection provided by Intel® TDX with a regular VM, we also create a regular disk image, by simply removing Intel® TDX tooling from the image creation script.

We will end up with two disk images, under the repository tree.

:~/tdx/guest-tools/image$ ls -l *qcow*
-rwxrwxrwx 1 root root 1903362048 Oct 17 23:30 regular-guest-ubuntu-24.04-generic.qcow2
-rwxrwxrwx 1 root root 4116250624 Oct 17 06:23 tdx-guest-ubuntu-24.04-generic.qcow2

In the Canonical TDX repository we can also find two sample domain XML’s for booting both an Intel® TDX protected VM (also called Trust Domain) and a regular VM. I’ve added hostfwd=tcp::0-:3306 to the XML’s to let QEMU assign a random port for the client-server connection.

The recommended way for booting both VM’s is using the libvirt wrapper tdvirsh provided in the repository.

./tdvirsh new \
    --td-image ./image/regular-guest-ubuntu-24.04-generic.qcow2 \
    --xml-template ./regular_vm.xml.template

./tdvirsh new \
    --td-image ./image/tdx-guest-ubuntu-24.04-generic.qcow2 \
    --xml-template ./trust_domain.xml.template

As a result both VM’s will start and both will be assigned random ports for SSH and MariaDB Server in the example below.

./tdvirsh list

Id   Name                                                        State 
--------------------------------------------------------------------------- 
27   tdvirsh-trust_domain-ae41a94c-43f3-4bf0-9c30-b66a13ca6b42   running (ssh:38231 45017, cid:3)
30   tdvirsh-regular_vm-00c643ab-205d-41b5-873d-a60800b284f3     running (ssh:45545 38053, cid:4)

Using SSH to connect into the Intel® TDX protected VM, we can easily verify that Intel® TDX is enabled inside this VM.

root@tdx-guest:~# sudo dmesg | grep -i tdx
[    0.000000] tdx: Guest detected
[    0.000000] DMI: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 2024.02-3+tdx1.0 07/03/2024
[    0.278473] process: using TDX aware idle routine
[    0.330149] Memory Encryption Features active: Intel TDX
[    6.454503] systemd[1]: Detected confidential virtualization tdx.
[    6.470694] systemd[1]: Hostname set to <tdx-guest>.

Configure MariaDB server

We will configure MariaDB Server 11.4.3 on both VM’s. Since 11.4.1 the connection between the client and the server has SSL enabled by default, which automatically brings us data-in transit protection. The server will generate certificates in memory and use them. For further reading we’ve blog posted about it.

Note that Intel® TDX does not inherently protect data that is written to disk, i.e. data-at-rest. There are two options to protect data in this state: (1) application-level file encryption and (2) guest OS-based file encryption. We opt for the first option as this is already provided by MariaDB Server.

In more details, we will set up the server with data-at-rest protection for the InnoDB storage engine using the file-key-management plugin.

Overall, we perform the following steps:

  • Install the server. You can download the latest MariaDB Server 11.4 from the downloads page.
  • Generate a keyfile for data-at-rest protection
  • Enable data-at-rest protection in the server configuration file
  • Bind to a public interface to allow remote connections
  • Restart the server to apply changes

Steps executed inside both VM’s after installing MariaDB Server:

# Keyfile generation
root@tdx-guest:~# mkdir /etc/mysql/encryption
root@tdx-guest:~# (echo -n "1;" ; openssl rand -hex 32 ) | sudo tee -a  /etc/mysql/encryption/keyfile
1;56f9e484de0016b19c4de543430e729367838033db1d3ea724dc9624f1f3ae4b

# Server configuration
root@tdx-guest:~# cat <<EOL >> /etc/mysql/my.cnf

[mariadb]
plugin_load_add = file_key_management
loose_file_key_management_filename = /etc/mysql/encryption/keyfile
innodb_encrypt_log = ON
innodb_encrypt_temporary_tables = ON
innodb_encryption_threads = 4
bind-address = 0.0.0.0
EOL

root@tdx-guest:~# systemctl restart mariadb

The last step is to create a user for the remote connection.

CREATE USER 'user12'@'%' IDENTIFIED BY 'password123';
GRANT ALL PRIVILEGES ON *.* TO 'user12'@'%' WITH GRANT OPTION;

Dataset preparation.

For our deployment, we will use the mariadb command-line client installed in the host OS to interact with the database server. In an actual production environment, the client would be installed on another machine.

The following steps will be repeated for both VM’s. First, let’s verify that MariaDB Server is configured to protect data in transit and at rest.

# Connect to the server
mariadb -uuser12 -ppassword123 -h localhost -P #FW_PORT#

# Verify that the connection is secured (data-in-transit protection)
MariaDB [(none)]> SHOW STATUS LIKE 'Ssl_cipher';
+---------------+------------------------+
| Variable_name | Value                  |
+---------------+------------------------+
| Ssl_cipher    | TLS_AES_256_GCM_SHA384 |
+---------------+------------------------+

# Check that the encryption plugin is ON (data-at-rest protection)
MariaDB [(none)]> SHOW VARIABLES LIKE '%encryption%';
+------------------------------------------+---------+
| Variable_name                            | Value   |
+------------------------------------------+---------+
| file_key_management_encryption_algorithm | aes_cbc |
| innodb_default_encryption_key_id         | 1       |
| innodb_encryption_rotate_key_age         | 1       |
| innodb_encryption_rotation_iops          | 100     |
| innodb_encryption_threads                | 4       |
+------------------------------------------+---------+

MariaDB [(none)]> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE "%key%";
+---------------------+---------------+
| PLUGIN_NAME         | PLUGIN_STATUS |
+---------------------+---------------+
| file_key_management | ACTIVE        |
+---------------------+---------------+

While setting up our database, we create encrypted tables to ensure data-at-rest protection and add some dummy data to the tables. We execute the following from the client:

# Prepare dataset
CREATE DATABASE Customers;
USE Customers;

CREATE TABLE Individuals (
    ID INT AUTO_INCREMENT PRIMARY KEY,
    Name VARCHAR(100) NOT NULL,
    Email VARCHAR(100),
    PhoneNumber VARCHAR(15) NOT NULL,
    Address VARCHAR(255)
) ENGINE=InnoDB, ENCRYPTED='YES';

INSERT INTO Individuals (Name, Email, PhoneNumber, Address)
VALUES ('Razvan Varzaru', 'razvan@provider.com', '+40700060000', '456 Real Rd, Bucharest, Romania');

INSERT INTO Individuals (Name, Email, PhoneNumber, Address)
VALUES ('John Doe', 'john.doe@provider.com', '+40700060001', '123 Fake St, Springfield, USA');

CREATE TABLE Accounts (
    AccountID INT AUTO_INCREMENT PRIMARY KEY,
    CustomerID INT,
    AccountNumber VARCHAR(50) NOT NULL,
    Currency VARCHAR(3) NOT NULL,
    Balance DECIMAL(15, 2) NOT NULL,
    FOREIGN KEY (CustomerID) REFERENCES Individuals(ID)
) ENGINE=InnoDB, ENCRYPTED='YES';

INSERT INTO Accounts (CustomerID, AccountNumber, Currency, Balance)
VALUES (1, 'ACC123456789', 'EUR', 1000.00);

INSERT INTO Accounts (CustomerID, AccountNumber, Currency, Balance)
VALUES (2, 'ACC444555666', 'GBP', 1500.00);

If we examine the data, we see that sensitive account information is present:

MariaDB [Customers]> SELECT * FROM Individuals;
+----+----------------+-----------------------+--------------+---------------------------------+
| ID | Name           | Email                 | PhoneNumber  | Address                         |
+----+----------------+-----------------------+--------------+---------------------------------+
|  1 | Razvan Varzaru | razvan@provider.com   | +40700060000 | 456 Real Rd, Bucharest, Romania |
|  2 | John Doe       | john.doe@provider.com | +40700060001 | 123 Fake St, Springfield, USA   |
+----+----------------+-----------------------+--------------+---------------------------------+

MariaDB [Customers]> SELECT * FROM Accounts;
+-----------+------------+---------------+----------+---------+
| AccountID | CustomerID | AccountNumber | Currency | Balance |
+-----------+------------+---------------+----------+---------+
|         1 |          1 | ACC123456789  | EUR      | 1000.00 |
|         2 |          2 | ACC444555666  | GBP      | 1500.00 |
+-----------+------------+---------------+----------+---------+

Memory dump

Now it’s time to check out how this setup protects data-in-transit, data-at-rest and data-in-use. To show this, we perform a strong attack assuming a malicious administrator with full host OS privileges. Such an administrator can perform a memory dump of every running VM.

First, the regular VM without Intel® TDX protection:

# Memory dump of the regular domain
:~/memdumps$ virsh dump tdvirsh-regular_vm-00c643ab-205d-41b5-873d-a60800b284f3 ./regular-dom-mem-dump.file --memory-only

# Suppose ACC123 it's just a standard pattern for identifying the bank and the account type.

:~/memdumps$ strings regular-dom-mem-dump.file | grep -aE -B 5 -A 5 'ACC123'

Balance
Razvan Varzaru
razvan@provider.com
+40700060000
456 Real Rd, Bucharest, Romania
ACC123456789
1000.00i
John Doe
john.doe@provider.com
+40700060001
123 Fake St, Springfield, USA
--
AUE1
default-character-set=utf8mb3
default-collation=utf8mb3_general_ci
infimum
supremum
ACC123456789EUR
ACC444555666GBP
I:6769510
E:ID_MM_CANDIDATE=1
G:systemd
Q:systemd

...
...
# and there's more output to it

With enough patience and further investigation into the results, we can discover that Razvan has the account number ACC123456789 and a balance of 1,000.00 EUR.

As we can see, an administrator can basically read all the sensitive data, even with data-at-rest and data-in-transit protection.

Now let’s try the same on the Intel® TDX protected VM.

# Memory dump of the TD
:~/memdumps$ virsh dump tdvirsh-trust_domain-ae41a94c-43f3-4bf0-9c30-b66a13ca6b42 ./tdx-dom-mem-dump.file --memory-only

:~/memdumps$ strings tdx-dom-mem-dump.file | grep -aE -B 5 -A 5 'ACC123'
# Nothing!
:~/memdumps$ strings tdx-dom-mem-dump.file | grep -aE -B 5 -A 5 'Razvan'
:~/memdumps$
# Nothing x2!

Indeed, it works seamlessly. With Intel® TDX and a properly configured MariaDB Server installation, data protection is effectively ensured, at rest, in transit and in use. This setup ensures that no external actor can access or read the sensitive information stored within the Intel® TDX protected MariaDB Server.

Still, there are certain limits of the state we are in now:

  • we say that data is protected in use by doing a main memory dump but if you deploy the same workload at any external infrastructure provider, you normally do not have host OS access and thus cannot perform this test. Therefore, to address this, MariaDB Server can be combined with attestation to remotely verify the protection guarantees.
  • the key used for data-at-rest protection is created inside the TD. As a result, this requires a VM administrator that is allowed to login. Additionally, this key is written to a file which is not protected by Intel® TDX.

In conclusion, by leveraging the features of Intel® TDX, we can fully protect our data from unauthorized access and maintain the integrity of our information assets. We will explore in a future blog post how we can address the limits mentioned above.