Starting in version 8.0.14, MySQL server can encrypt all new binary and relay log files on disk. In order to do so, you just need to enable the new binlog_encryption
option (and also ensure that you have a keyring).
Once enabling the option in a client session, the server will rotate both binary and relay logs to start using a new encrypted binary log file format. Only the data at rest is encrypted. The replication stream sent to replicas (and to mysqlbinlog
client program) wasn’t changed.
Using binary log encryption
First of all, you need to have keyring enabled on MySQL before using binary log encryption. Trying to enable binary log encryption without a keyring will result in the following error:
1
2
|
mysqld>SET GLOBAL binlog_encryption = ON; ERROR 3794 (HY000): Unable to recover binlog encryption master key, please check if keyring plugin is loaded. |
For more information about how to install keyring, please check MySQL manual pages.
Once having an operational keyring in the MySQL server, all you have to do is enable binlog_encryption
option. In order to have your MySQL server always generating encrypted binary and relay log files you must set the option in any startup configuration file. You can do it by adding a line as below to a configuration file.
1 |
binlog_encryption=ON |
You can also use SET PERSIST
when enabling binary log encryption for the first time in a client session to ensure that your MySQL server will always generate encrypted binary and relay log files, event after a server restart.
1
2
|
mysqld>SET PERSIST binlog_encryption = ON; Query OK, 0 rows affected (0.01 sec) |
It is possible to check whether the binary log encryption feature is enabled by consulting the binlog_encryption
global variable value, either directly or using a performance schema table.
1
2
3
4
5
6
7
8
9
|
mysqld>SELECT * -> FROM performance_schema.global_variables -> WHERE VARIABLE_NAME = 'binlog_encryption'; +-------------------+----------------+ | VARIABLE_NAME | VARIABLE_VALUE | +-------------------+----------------+ | binlog_encryption | ON | +-------------------+----------------+ 1 row in set (0.01 sec) |
When listing the existing binary log files, the MySQL server will now tell which files are encrypted and which files are not encrypted:
1
2
3
4
5
6
7
8
|
mysqld>SHOW BINARY LOGS; +-------------------+-----------+-----------+ | Log_name | File_size | Encrypted | +-------------------+-----------+-----------+ | master-bin.000001 | 203 | No | | master-bin.000002 | 667 | Yes | +-------------------+-----------+-----------+ 2 rows in set (0.00 sec) |
The file size displayed in SHOW BINARY LOGS
output takes into account a 512 bytes header for encrypted files. But, besides when dealing with files sizes, this 512 bytes header of the encrypted files is never accounted (i.e., it is not taken into account for event positions in a file). The 512 bytes header of encrypted files is local and it is never replicated.
1
2
3
4
5
6
7
8
|
mysqld>SHOW BINLOG EVENTS IN 'master-bin.000002'; +-------------------+-----+----------------+-----------+-------------+-----------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +-------------------+-----+----------------+-----------+-------------+-----------------------------------------+ | master-bin.000002 | 4 | Format_desc | 1 | 124 | Server ver: 8.0.14-debug, Binlog ver: 4 | | master-bin.000002 | 124 | Previous_gtids | 1 | 155 | | +-------------------+-----+----------------+-----------+-------------+-----------------------------------------+ 2 rows in set (0.00 sec) |
Note that doing some math with the examples above we can deduce the encrypted binary log file header size:
encrypted binary log file header size
= File_size - End_log_pos (of last event)
= 667 - 155
= 512
Two tiers encryption
The binary logs encryption feature was designed to use two tiers:
A per file password, used to encrypt/decrypt binary log file content, is stored in the encrypted binary log file header.
A binary log encryption key, used to encrypt/decrypt sensitive data (i.e., the file password) in the encrypted binary log file header, is stored in the keyring.
While a given binary log encryption key may be used to encrypt/decrypt many binary and relay log files passwords, a file password is intended to encrypt/decrypt a single binary or relay log file.
The server automatically generates a binary log encryption key to protect file passwords when the binary log encryption feature is enabled for the first time. The key used to protect new binary and relay log files is called the master key.
If the option is disabled and then enabled again, the server will not generate a new master key, as it shall be able to fetch the existing master key from keyring.
When generating new encrypted binary or relay log files, the server generates a new file password for the new file, encrypt it using the binary log encryption master key, and store the encrypted file password and the master key identification (not the master key itself) in the encrypted file header.
Then, when the server needs to read from a binary or relay log file, the server first reads the encrypted file header, fetches the binary log encryption key required to decrypt the file password, decrypts the file password and then it has access to the a plain binary log data.
Using mysqlbinlog client program
As mysqlbinlog
client program does not have access to the keyring, it is not able to dump an encrypted binary or relay log file directly.
Let’s show an example with the two files our server has:
1
2
3
4
|
/var/mysqld/data: ls -l master-bin* -rw-rw---- 1 mysql mysql 203 Jan 15 13:26 master-bin.000001 -rw-rw---- 1 mysql mysql 667 Jan 15 13:26 master-bin.000002 -rw-rw---- 1 mysql mysql 40 Jan 15 13:26 master-bin.index |
Showing the content of the first (unencrypted) binary log file directly is fine:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/var/mysqld/data: mysqlbinlog master-bin.000001 /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #190115 13:23:03 server id 1 end_log_pos 124 CRC32 0x3ed15040 Start: binlog v 4, server v 8.0.14-debug created 190115 13:23:03 at startup ROLLBACK/*!*/; BINLOG ' p9A9XA8BAAAAeAAAAHwAAAAAAAQAOC4wLjE0LWRlYnVnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAACn0D1cEwANAAgAAAAABAAEAAAAYAAEGggAAAAICAgCAAAACgoKKioAEjQA CgFAUNE+ '/*!*/; # at 124 #190115 13:23:03 server id 1 end_log_pos 155 CRC32 0x598da946 Previous-GTIDs # [empty] # at 155 #190115 13:26:42 server id 1 end_log_pos 203 CRC32 0x9922228e Rotate to master-bin.000002 pos: 4 SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; |
But when we try to show the content of the second (encrypted) binary log directly, mysqlbinlog
complains:
1
2
3
4
5
6
7
8
9
10
|
/var/mysqld/data: mysqlbinlog master-bin.000002 /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; ERROR: Reading encrypted log files directly is not supported. SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; |
But you can still get access to the encrypted binary log file by requesting it to the server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/var/mysqld/data: mysqlbinlog -R --protocol TCP --host localhost --user root master-bin.000002 /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #190115 13:26:42 server id 1 end_log_pos 124 CRC32 0x3745257e Start: binlog v 4, server v 8.0.14-debug created 190115 13:26:42 BINLOG ' gtE9XA8BAAAAeAAAAHwAAAAAAAQAOC4wLjE0LWRlYnVnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEwANAAgAAAAABAAEAAAAYAAEGggAAAAICAgCAAAACgoKKioAEjQA CgF+JUU3 '/*!*/; # at 124 #190115 13:26:42 server id 1 end_log_pos 155 CRC32 0x0db4297f Previous-GTIDs # [empty] SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; |
It is possible to know if a given binary or relay log file is encrypted or not by consulting the file “magic header” (file’s first four bytes). While unencrypted files has 0xFE62696E
as magic header, encrypted files has 0xFD62696E
:
1
2
3
4
|
/var/mysqld/data: echo $( hexdump -v -e '/1 "%02X"' master-bin.000001 -n 4 ) FE62696E /var/mysqld/data: echo $( hexdump -v -e '/1 "%02X"' master-bin.000002 -n 4 ) FD62696E |
This is how MySQL server and mysqlbinlog
differentiate encrypted and plain binary and relay log files. Notice that using older than 8.0.14 binaries to access encrypted binary or relay log files directly will lead the programs to not recognize the encrypted files as of binary log format.
1
2
3
4
5
6
7
8
9
10
|
/var/mysqld/data: /opt/mysql-5.7/bin/mysqlbinlog master-bin.000002 /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; ERROR: File is not a binary log file. SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; |
Rotating binary log master key
In MySQL version 8.0.14 it is also possible to rotate the binary log master key by enabling binlog_rotate_encryption_master_key_at_startup
option before (re)starting the server.
Setting the option will make the server to generate a new binary log master key to protect new binary and relay log files. Old binary log encryption keys on keyring are kept as they may protect existing binary and relay log files still needed for server operation.
How to decrypt an encrypted binary log file
The encrypted binary log file format was designed to allow a “manual” decryption of the file data when the value of the key that encrypted its file password is known. If you want to know more about how to do it, please take a look at this blog post.
About binary log storage access API
One of the challenges for implementing the binary log encryption functionality into MySQL server was to ensure that adding a new way of storing binary and relay logs on disk would not break anything related to the binary log (i.e.: backup, replication, Group Replication).
MySQL 8.0.13 carried a work that refactored the binary log storage access API. This work implemented a storage layer abstraction interface and made all the server (and mysqlbinlog client program) code related to binary and relay log files accesses to use it.
With this new binary log storage access API, the coding part of the binary log encryption functionality could focust mostly in what really matter (encryption and decryption of binary and relay log files).