Skip to content

Commit 62a009c

Browse files
chungeun-choiwhy-arongBeautterLife
authored
Developed the 'MariadbStartEncryptionEvent' (#415)
* implement StartEncryptionEvent * Completed setup of MariaDB test environment. * Fix: Correct assertion in test_basic:_allowed_event_list * Fixed docker-compose.yaml * Fix: Correct assertion in test_basic:_allowed_event_list * Add unit tests for MariadbStartEncryptionEvent class * Fix:find file from current directroy * Fix: find the file path * Rename StartEncryptionEvent to MariadbStartEncryptionEvent * add description for MariadbStartEncruptionEvent * fix description for MariadbStartEncryptionEvent --------- Co-authored-by: Pilmo <[email protected]> Co-authored-by: BeautterLife <[email protected]>
1 parent 986bf5c commit 62a009c

File tree

7 files changed

+106
-9
lines changed

7 files changed

+106
-9
lines changed

Diff for: .mariadb/my.cnf

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[client-server]
2+
# Port or socket location where to connect
3+
# port = 3306
4+
socket = /run/mysqld/mysqld.sock
5+
6+
# Import all .cnf files from configuration directory
7+
8+
!includedir /etc/mysql/mariadb.conf.d/
9+
!includedir /etc/mysql/conf.d/
10+
11+
12+
[mariadb]
13+
plugin_load_add = file_key_management
14+
# Key files that are not encrypted
15+
loose_file_key_management_filename = /opt/key_file/no_encryption_key.key
16+
17+
# Encrypted key file
18+
# loose_file_key_management_filename=/opt/key_file/keyfile.enc
19+
# loose_file_key_management_filekey=FILE:/opt/key_file/no_encryption_key.key
20+
# file_key_management_encryption_algorithm=aes_ctr
21+
22+
# Set encrypt_binlog
23+
encrypt_binlog=ON

Diff for: .mariadb/no_encryption_key.key

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1;dda0ccb18a28b0b4c2448b5f0217a134

Diff for: docker-compose.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,10 @@ services:
2828
--log-bin=master-bin
2929
--binlog-format=row
3030
--log-slave-updates=on
31-
31+
volumes:
32+
- type: bind
33+
source: ./.mariadb
34+
target: /opt/key_file
35+
- type: bind
36+
source: ./.mariadb/my.cnf
37+
target: /etc/mysql/my.cnf

Diff for: pymysqlreplication/binlogstream.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
XidEvent, GtidEvent, StopEvent, XAPrepareEvent,
1616
BeginLoadQueryEvent, ExecuteLoadQueryEvent,
1717
HeartbeatLogEvent, NotImplementedEvent, MariadbGtidEvent,
18-
MariadbAnnotateRowsEvent, RandEvent)
18+
MariadbAnnotateRowsEvent, RandEvent, MariadbStartEncryptionEvent)
1919
from .exceptions import BinLogNotEnabled
2020
from .row_event import (
2121
UpdateRowsEvent, WriteRowsEvent, DeleteRowsEvent, TableMapEvent)
@@ -622,7 +622,8 @@ def _allowed_event_list(self, only_events, ignored_events,
622622
NotImplementedEvent,
623623
MariadbGtidEvent,
624624
MariadbAnnotateRowsEvent,
625-
RandEvent
625+
RandEvent,
626+
MariadbStartEncryptionEvent
626627
))
627628
if ignored_events is not None:
628629
for e in ignored_events:

Diff for: pymysqlreplication/event.py

+29
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,35 @@ def _dump(self):
488488
print("seed1: %d" % (self.seed1))
489489
print("seed2: %d" % (self.seed2))
490490

491+
class MariadbStartEncryptionEvent(BinLogEvent):
492+
"""
493+
Since MariaDB 10.1.7,
494+
the START_ENCRYPTION event is written to every binary log file
495+
if encrypt_binlog is set to ON. Prior to enabling this setting,
496+
additional configuration steps are required in MariaDB.
497+
(Link: https://mariadb.com/kb/en/encrypting-binary-logs/)
498+
499+
This event is written just once, after the Format Description event
500+
501+
Attributes:
502+
schema: The Encryption scheme, always set to 1 for system files.
503+
key_version: The Encryption key version.
504+
nonce: Nonce (12 random bytes) of current binlog file.
505+
"""
506+
507+
def __init__(self, from_packet, event_size, table_map, ctl_connection, **kwargs):
508+
super(MariadbStartEncryptionEvent, self).__init__(from_packet, event_size, table_map, ctl_connection, **kwargs)
509+
510+
self.schema = self.packet.read_uint8()
511+
self.key_version = self.packet.read_uint32()
512+
self.nonce = self.packet.read(12)
513+
514+
def _dump(self):
515+
print("Schema: %d" % self.schema)
516+
print("Key version: %d" % self.key_version)
517+
print(f"Nonce: {self.nonce}")
518+
519+
491520
class NotImplementedEvent(BinLogEvent):
492521
def __init__(self, from_packet, event_size, table_map, ctl_connection, **kwargs):
493522
super(NotImplementedEvent, self).__init__(

Diff for: pymysqlreplication/packet.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class BinLogPacketWrapper(object):
8888
constants.MARIADB_BINLOG_CHECKPOINT_EVENT: event.NotImplementedEvent,
8989
constants.MARIADB_GTID_EVENT: event.MariadbGtidEvent,
9090
constants.MARIADB_GTID_GTID_LIST_EVENT: event.NotImplementedEvent,
91-
constants.MARIADB_START_ENCRYPTION_EVENT: event.NotImplementedEvent
91+
constants.MARIADB_START_ENCRYPTION_EVENT: event.MariadbStartEncryptionEvent
9292
}
9393

9494
def __init__(self, from_packet, table_map,

Diff for: pymysqlreplication/tests/test_basic.py

+42-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from pymysqlreplication.exceptions import TableMetadataUnavailableError
1919
from pymysqlreplication.constants.BINLOG import *
2020
from pymysqlreplication.row_event import *
21+
from pathlib import Path
2122

2223
__all__ = ["TestBasicBinLogStreamReader", "TestMultipleRowBinLogStreamReader", "TestCTLConnectionSettings", "TestGtidBinLogStreamReader", "TestMariadbBinlogStreamReader", "TestStatementConnectionSetting"]
2324

@@ -27,9 +28,9 @@ def ignoredEvents(self):
2728
return [GtidEvent]
2829

2930
def test_allowed_event_list(self):
30-
self.assertEqual(len(self.stream._allowed_event_list(None, None, False)), 18)
31-
self.assertEqual(len(self.stream._allowed_event_list(None, None, True)), 17)
32-
self.assertEqual(len(self.stream._allowed_event_list(None, [RotateEvent], False)), 17)
31+
self.assertEqual(len(self.stream._allowed_event_list(None, None, False)), 19)
32+
self.assertEqual(len(self.stream._allowed_event_list(None, None, True)), 18)
33+
self.assertEqual(len(self.stream._allowed_event_list(None, [RotateEvent], False)), 18)
3334
self.assertEqual(len(self.stream._allowed_event_list([RotateEvent], None, False)), 1)
3435

3536
def test_read_query_event(self):
@@ -1036,6 +1037,42 @@ def test_annotate_rows_event(self):
10361037
self.assertEqual(event.sql_statement,insert_query)
10371038
self.assertIsInstance(event,MariadbAnnotateRowsEvent)
10381039

1040+
def test_start_encryption_event(self):
1041+
query = "CREATE TABLE test (id INT NOT NULL AUTO_INCREMENT, data VARCHAR (50) NOT NULL, PRIMARY KEY (id))"
1042+
self.execute(query)
1043+
query = "INSERT INTO test (data) VALUES('Hello World')"
1044+
self.execute(query)
1045+
self.execute("COMMIT")
1046+
1047+
self.assertIsInstance(self.stream.fetchone(), RotateEvent)
1048+
self.assertIsInstance(self.stream.fetchone(), FormatDescriptionEvent)
1049+
1050+
start_encryption_event = self.stream.fetchone()
1051+
self.assertIsInstance(start_encryption_event, MariadbStartEncryptionEvent)
1052+
1053+
schema = start_encryption_event.schema
1054+
key_version = start_encryption_event.key_version
1055+
nonce = start_encryption_event.nonce
1056+
1057+
from pathlib import Path
1058+
1059+
encryption_key_file_path = Path(__file__).parent.parent.parent
1060+
1061+
try:
1062+
with open(f"{encryption_key_file_path}/.mariadb/no_encryption_key.key", "r") as key_file:
1063+
first_line = key_file.readline()
1064+
key_version_from_key_file = int(first_line.split(";")[0])
1065+
except Exception as e:
1066+
self.fail("raised unexpected exception: {exception}".format(exception=e))
1067+
finally:
1068+
self.resetBinLog()
1069+
1070+
# schema is always 1
1071+
self.assertEqual(schema, 1)
1072+
self.assertEqual(key_version, key_version_from_key_file)
1073+
self.assertEqual(type(nonce), bytes)
1074+
self.assertEqual(len(nonce), 12)
1075+
10391076
class TestStatementConnectionSetting(base.PyMySQLReplicationTestCase):
10401077
def setUp(self):
10411078
super(TestStatementConnectionSetting, self).setUp()
@@ -1065,8 +1102,8 @@ def test_rand_event(self):
10651102
def tearDown(self):
10661103
self.execute("SET @@binlog_format='ROW'")
10671104
self.assertEqual(self.bin_log_format(), "ROW")
1068-
super(TestStatementConnectionSetting, self).tearDown()
1069-
1105+
super(TestStatementConnectionSetting, self).tearDown()
1106+
10701107

10711108
if __name__ == "__main__":
10721109
import unittest

0 commit comments

Comments
 (0)