Skip to content

Commit dd7bd76

Browse files
authored
Merge pull request #1073 from meldafert/option-tls-version
Add cli option --tls-version to control tls version used
2 parents 707871a + f1bd580 commit dd7bd76

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ $ sudo apt-get install mycli # Only on debian or ubuntu
7676
--ssl-cert PATH X509 cert in PEM format.
7777
--ssl-key PATH X509 key in PEM format.
7878
--ssl-cipher TEXT SSL cipher to use.
79+
--tls-version [TLSv1|TLSv1.1|TLSv1.2|TLSv1.3]
80+
TLS protocol version for secure connection.
81+
7982
--ssl-verify-server-cert Verify server's "Common Name" in its cert
8083
against hostname used when connecting. This
8184
option is disabled by default.

changelog.md

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ Features:
5555
* Add prettify/unprettify keybindings to format the current statement using `sqlglot`.
5656

5757

58+
Features:
59+
---------
60+
* Add `--tls-version` option to control the tls version used.
61+
5862
Internal:
5963
---------
6064
* Pin `cryptography` to suppress `paramiko` warning, helping CI complete and presumably affecting some users.

mycli/main.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,9 @@ def get_last_query(self):
11371137
@click.option('--ssl-key', help='X509 key in PEM format.',
11381138
type=click.Path(exists=True))
11391139
@click.option('--ssl-cipher', help='SSL cipher to use.')
1140+
@click.option('--tls-version',
1141+
type=click.Choice(['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'], case_sensitive=False),
1142+
help='TLS protocol version for secure connection.')
11401143
@click.option('--ssl-verify-server-cert', is_flag=True,
11411144
help=('Verify server\'s "Common Name" in its cert against '
11421145
'hostname used when connecting. This option is disabled '
@@ -1188,8 +1191,8 @@ def cli(database, user, host, port, socket, password, dbname,
11881191
version, verbose, prompt, logfile, defaults_group_suffix,
11891192
defaults_file, login_path, auto_vertical_output, local_infile,
11901193
ssl_enable, ssl_ca, ssl_capath, ssl_cert, ssl_key, ssl_cipher,
1191-
ssl_verify_server_cert, table, csv, warn, execute, myclirc, dsn,
1192-
list_dsn, ssh_user, ssh_host, ssh_port, ssh_password,
1194+
tls_version, ssl_verify_server_cert, table, csv, warn, execute,
1195+
myclirc, dsn, list_dsn, ssh_user, ssh_host, ssh_port, ssh_password,
11931196
ssh_key_filename, list_ssh_config, ssh_config_path, ssh_config_host,
11941197
init_command, charset, password_file):
11951198
"""A MySQL terminal client with auto-completion and syntax highlighting.
@@ -1248,6 +1251,7 @@ def cli(database, user, host, port, socket, password, dbname,
12481251
'key': ssl_key and os.path.expanduser(ssl_key),
12491252
'capath': ssl_capath,
12501253
'cipher': ssl_cipher,
1254+
'tls_version': tls_version,
12511255
'check_hostname': ssl_verify_server_cert,
12521256
}
12531257

mycli/sqlexecute.py

+42-1
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,15 @@ def connect(self, database=None, user=None, password=None, host=None,
176176
if init_command and len(list(special.split_queries(init_command))) > 1:
177177
client_flag |= pymysql.constants.CLIENT.MULTI_STATEMENTS
178178

179+
ssl_context = None
180+
if ssl:
181+
ssl_context = self._create_ssl_ctx(ssl)
182+
179183
conn = pymysql.connect(
180184
database=db, user=user, password=password, host=host, port=port,
181185
unix_socket=socket, use_unicode=True, charset=charset,
182186
autocommit=True, client_flag=client_flag,
183-
local_infile=local_infile, conv=conv, ssl=ssl, program_name="mycli",
187+
local_infile=local_infile, conv=conv, ssl=ssl_context, program_name="mycli",
184188
defer_connect=defer_connect, init_command=init_command
185189
)
186190

@@ -354,3 +358,40 @@ def reset_connection_id(self):
354358
def change_db(self, db):
355359
self.conn.select_db(db)
356360
self.dbname = db
361+
362+
def _create_ssl_ctx(self, sslp):
363+
import ssl
364+
365+
ca = sslp.get("ca")
366+
capath = sslp.get("capath")
367+
hasnoca = ca is None and capath is None
368+
ctx = ssl.create_default_context(cafile=ca, capath=capath)
369+
ctx.check_hostname = not hasnoca and sslp.get("check_hostname", True)
370+
ctx.verify_mode = ssl.CERT_NONE if hasnoca else ssl.CERT_REQUIRED
371+
if "cert" in sslp:
372+
ctx.load_cert_chain(sslp["cert"], keyfile=sslp.get("key"))
373+
if "cipher" in sslp:
374+
ctx.set_ciphers(sslp["cipher"])
375+
376+
# raise this default to v1.1 or v1.2?
377+
ctx.minimum_version = ssl.TLSVersion.TLSv1
378+
379+
if "tls_version" in sslp:
380+
tls_version = sslp["tls_version"]
381+
382+
if tls_version == "TLSv1":
383+
ctx.minimum_version = ssl.TLSVersion.TLSv1
384+
ctx.maximum_version = ssl.TLSVersion.TLSv1
385+
elif tls_version == "TLSv1.1":
386+
ctx.minimum_version = ssl.TLSVersion.TLSv1_1
387+
ctx.maximum_version = ssl.TLSVersion.TLSv1_1
388+
elif tls_version == "TLSv1.2":
389+
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
390+
ctx.maximum_version = ssl.TLSVersion.TLSv1_2
391+
elif tls_version == "TLSv1.3":
392+
ctx.minimum_version = ssl.TLSVersion.TLSv1_3
393+
ctx.maximum_version = ssl.TLSVersion.TLSv1_3
394+
else:
395+
_logger.error('Invalid tls version: %s', tls_version)
396+
397+
return ctx

0 commit comments

Comments
 (0)