Skip to content

Commit

Permalink
Allow using client cert and key with mutual TLS proxy (#373)
Browse files Browse the repository at this point in the history
  • Loading branch information
genzgd authored Jul 1, 2024
1 parent 801539f commit 61b4430
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 13 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ release (0.8.0), unrecognized arguments/keywords for these methods of creating a
instead of being passed as ClickHouse server settings. This is in conjunction with some refactoring in Client construction.
The supported method of passing ClickHouse server settings is to prefix such arguments/query parameters with`ch_`.

## 0.7.15, 2024-07-01
### Bug Fix
- If the ClickHouse server was behind an https proxy that required mutual TLS authentication, the client would incorrectly
attempt to use ClickHouse mutual TLS instead and authentication would fail. It should now be possible to authenticate
correctly in this situation by settings the `verify` parameter to `proxy`. This should close https://github.com/ClickHouse/clickhouse-connect/issues/370

## 0.7.14, 2024-06-24
### Bug Fix
- Fix insert of UUID strings including dashes. Closes #368
Expand Down
2 changes: 1 addition & 1 deletion clickhouse_connect/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = '0.7.14'
version = '0.7.15'
6 changes: 6 additions & 0 deletions clickhouse_connect/driver/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ def dict_copy(source: Dict = None, update: Optional[Dict] = None) -> Dict:
return copy


def dict_add(source: Dict, key: str, value: any) -> Dict:
if value is not None:
source[key] = value
return source


def empty_gen():
yield from ()

Expand Down
25 changes: 13 additions & 12 deletions clickhouse_connect/driver/httpclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from clickhouse_connect.datatypes import registry
from clickhouse_connect.datatypes.base import ClickHouseType
from clickhouse_connect.driver.client import Client
from clickhouse_connect.driver.common import dict_copy, coerce_bool, coerce_int
from clickhouse_connect.driver.common import dict_copy, coerce_bool, coerce_int, dict_add
from clickhouse_connect.driver.compression import available_compression
from clickhouse_connect.driver.ctypes import RespBuffCls
from clickhouse_connect.driver.exceptions import DatabaseError, OperationalError, ProgrammingError
Expand Down Expand Up @@ -58,7 +58,7 @@ def __init__(self,
connect_timeout: int = 10,
send_receive_timeout: int = 300,
client_name: Optional[str] = None,
verify: bool = True,
verify: Union[bool, str] = True,
ca_cert: Optional[str] = None,
client_cert: Optional[str] = None,
client_cert_key: Optional[str] = None,
Expand All @@ -81,22 +81,23 @@ def __init__(self,
if interface == 'https':
if not https_proxy:
https_proxy = check_env_proxy('https', host, port)
if client_cert:
if https_proxy and isinstance(verify, str) and verify.lower() == 'proxy':
verify = 'proxy'
else:
verify = coerce_bool(verify)
if client_cert and verify != 'proxy':
if not username:
raise ProgrammingError('username parameter is required for Mutual TLS authentication')
self.headers['X-ClickHouse-User'] = username
self.headers['X-ClickHouse-SSL-Certificate-Auth'] = 'on'
verify = coerce_bool(verify)
# pylint: disable=too-many-boolean-expressions
if not self.http and (server_host_name or ca_cert or client_cert or not verify or https_proxy):
options = {
'ca_cert': ca_cert,
'client_cert': client_cert,
'verify': verify,
'client_cert_key': client_cert_key
}
options = {'verify': verify is not False}
dict_add(options,'ca_cert', ca_cert)
dict_add(options, 'client_cert', client_cert)
dict_add(options, 'client_cert_key', client_cert_key)
if server_host_name:
if verify:
if options['verify']:
options['assert_hostname'] = server_host_name
options['server_hostname'] = server_host_name
self.http = get_pool_manager(https_proxy=https_proxy, **options)
Expand All @@ -109,7 +110,7 @@ def __init__(self,
else:
self.http = default_pool_manager()

if not client_cert and username:
if (not client_cert or verify == 'proxy') and username:
self.headers['Authorization'] = 'Basic ' + b64encode(f'{username}:{password}'.encode()).decode()
self.headers['User-Agent'] = common.build_client_name(client_name)
self._read_format = self._write_format = 'Native'
Expand Down

0 comments on commit 61b4430

Please sign in to comment.