diff options
author | Pavlo Shchelokovskyy <shchelokovskyy@gmail.com> | 2021-07-27 12:39:19 +0000 |
---|---|---|
committer | Pavlo Shchelokovskyy <shchelokovskyy@gmail.com> | 2021-11-08 19:41:16 +0200 |
commit | 81c629676c06e798e5073b4361cffd269876c553 (patch) | |
tree | 3b1e127cce35dccd009891481e58a8b8fd36f9b1 | |
parent | 292d768b8019abed2dfe97c9cdd8e491f4b8167f (diff) | |
download | tooz-81c629676c06e798e5073b4361cffd269876c553.tar.gz |
Add TLS support for MySQL driver
use pymysql TLS-related arguments as query params in DB connection URL.
Change-Id: I3136df2a9ac4e05daa46f33e47c1438e3b02a321
-rw-r--r-- | releasenotes/notes/mysql-tls-support-88941e2ebaf938b4.yaml | 33 | ||||
-rw-r--r-- | tooz/drivers/mysql.py | 38 | ||||
-rw-r--r-- | tooz/tests/test_mysql.py | 33 |
3 files changed, 103 insertions, 1 deletions
diff --git a/releasenotes/notes/mysql-tls-support-88941e2ebaf938b4.yaml b/releasenotes/notes/mysql-tls-support-88941e2ebaf938b4.yaml new file mode 100644 index 0000000..e9bc306 --- /dev/null +++ b/releasenotes/notes/mysql-tls-support-88941e2ebaf938b4.yaml @@ -0,0 +1,33 @@ +--- +features: + - | + Added TLS support for MySQL driver. + + The following TLS-related options now can be specifed in the MySQL + connection URL as query parameters + + ``ssl_ca`` + path to the CA bundle to use for verifying server certificate + + ``ssl_capath`` + path to folder with CA bundle files + + ``ssl_cert`` + path to client public key certificate file + + ``ssl_key`` + path to client private key file + + ``ssl_check_hostname`` + verify server hostname against its certificate, + accepted values are "true", "1", "yes" or "false", "0", "no" + (default is "true") + + ``ssl_verify_mode`` + whether to verify TLS connection + accepted values are "true", "1", "yes", "required" or + "false", "0", "no", "none", or + "optional" + + ``ssl_cipher`` + list of permissible ciphers for connection encryption diff --git a/tooz/drivers/mysql.py b/tooz/drivers/mysql.py index 5bc8892..491eecc 100644 --- a/tooz/drivers/mysql.py +++ b/tooz/drivers/mysql.py @@ -116,9 +116,23 @@ class MySQLDriver(coordination.CoordinationDriver): The MySQL driver connection URI should look like:: - mysql://USERNAME:PASSWORD@HOST[:PORT]/DBNAME[?unix_socket=SOCKET_PATH] + mysql://USERNAME:PASSWORD@HOST[:PORT]/DBNAME[?OPTION1=VALUE1[&OPTION2=VALUE2[&...]]] If not specified, PORT defaults to 3306. + Available options are: + + ================== ======= + Name Default + ================== ======= + ssl_ca None + ssl_capath None + ssl_cert None + ssl_key None + ssl_cipher None + ssl_verify_mode None + ssl_check_hostname True + unix_socket None + ================== ======= .. _MySQL: http://dev.mysql.com/ """ @@ -182,6 +196,26 @@ class MySQLDriver(coordination.CoordinationDriver): username = parsed_url.username password = parsed_url.password unix_socket = options.get("unix_socket") + ssl_opt_names = ( + "ca", + "capath", + "cert", + "key", + "cipher", + "verify_mode", + ) + ssl_args = {} + for o in ssl_opt_names: + value = options.get("ssl_" + o) + if value: + ssl_args[o] = value + check_hostname = options.get("ssl_check_hostname") + if check_hostname is not None: + check_hostname = check_hostname.lower() + if check_hostname in ("true", "1", "yes"): + ssl_args["check_hostname"] = True + elif check_hostname in ("false", "0", "no"): + ssl_args["check_hostname"] = False try: if unix_socket: @@ -190,6 +224,7 @@ class MySQLDriver(coordination.CoordinationDriver): user=username, passwd=password, database=dbname, + ssl=ssl_args, defer_connect=defer_connect) else: return pymysql.Connect(host=host, @@ -197,6 +232,7 @@ class MySQLDriver(coordination.CoordinationDriver): user=username, passwd=password, database=dbname, + ssl=ssl_args, defer_connect=defer_connect) except (pymysql.err.OperationalError, pymysql.err.InternalError) as e: utils.raise_with_cause(coordination.ToozConnectionError, diff --git a/tooz/tests/test_mysql.py b/tooz/tests/test_mysql.py index 291f44a..735d9d0 100644 --- a/tooz/tests/test_mysql.py +++ b/tooz/tests/test_mysql.py @@ -14,6 +14,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from unittest import mock from oslo_utils import encodeutils from testtools import testcase @@ -52,3 +53,35 @@ class TestMySQLDriver(testcase.TestCase): def test_connect_failure_invalid_hostname_and_port_provided(self): c = self._create_coordinator("mysql://invalidhost:54/test") self.assertRaises(coordination.ToozConnectionError, c.start) + + @mock.patch("pymysql.Connect") + def test_parsing_tls_settings(self, sql_mock): + c = self._create_coordinator( + "mysql://invalidhost:54/test" + "?ssl_ca=/ca/not/there" + "&ssl_capath=/capath/not/there" + "&ssl_check_hostname=False" + "&ssl_verify_mode=yes" + "&ssl_cert=/cert/not/there" + "&ssl_key=/key/not/there" + "&ssl_cipher=spam,ham" + ) + c.start() + sql_mock.assert_called_once_with( + host="invalidhost", + port=54, + user=None, + passwd=None, + database="test", + defer_connect=False, + ssl=dict( + ca="/ca/not/there", + capath="/capath/not/there", + check_hostname=False, + verify_mode="yes", + cert="/cert/not/there", + key="/key/not/there", + cipher="spam,ham" + ) + + ) |