Skip to content

Commit fab076c

Browse files
authored
feat: mTLS support for the InfluxDB Python client (#509)
1 parent 75b7df1 commit fab076c

10 files changed

+189
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
## 1.34.0 [unreleased]
22

3+
### Breaking Changes
4+
1. [#509](https://github.com/influxdata/influxdb-client-python/pull/509): Rename `key_file` to `cert_key_file` inside the central [configuration class](https://github.com/influxdata/influxdb-client-python/blob/d011df72b528a45d305aa8accbe879b31be3280e/influxdb_client/configuration.py#L92)
5+
36
### Features
47
1. [#510](https://github.com/influxdata/influxdb-client-python/pull/510): Allow to use client's optional configs for initialization from file or environment properties
8+
2. [#509](https://github.com/influxdata/influxdb-client-python/pull/509): MTLS support for the InfluxDB Python client
59

610
### Bug Fixes
711
1. [#512](https://github.com/influxdata/influxdb-client-python/pull/512): Exception propagation for asynchronous `QueryApi` [async/await]

README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ The following options are supported:
197197
- ``timeout`` - socket timeout in ms (default value is 10000)
198198
- ``verify_ssl`` - set this to false to skip verifying SSL certificate when calling API from https server
199199
- ``ssl_ca_cert`` - set this to customize the certificate file to verify the peer
200+
- ``cert_file`` - path to the certificate that will be used for mTLS authentication
201+
- ``cert_key_file`` - path to the file contains private key for mTLS certificate
202+
- ``cert_key_password`` - string or function which returns password for decrypting the mTLS private key
200203
- ``connection_pool_maxsize`` - set the number of connections to save that can be reused by urllib3
201204
- ``auth_basic`` - enable http basic authentication when talking to a InfluxDB 1.8.x without authentication but is accessed via reverse proxy with basic authentication (defaults to false)
202205
- ``profilers`` - set the list of enabled `Flux profilers <https://docs.influxdata.com/influxdb/v2.0/reference/flux/stdlib/profiler/>`_
@@ -226,6 +229,9 @@ Supported properties are:
226229
- ``INFLUXDB_V2_TIMEOUT`` - socket timeout in ms (default value is 10000)
227230
- ``INFLUXDB_V2_VERIFY_SSL`` - set this to false to skip verifying SSL certificate when calling API from https server
228231
- ``INFLUXDB_V2_SSL_CA_CERT`` - set this to customize the certificate file to verify the peer
232+
- ``INFLUXDB_V2_CERT_FILE`` - path to the certificate that will be used for mTLS authentication
233+
- ``INFLUXDB_V2_CERT_KEY_FILE`` - path to the file contains private key for mTLS certificate
234+
- ``INFLUXDB_V2_CERT_KEY_PASSWORD`` - string or function which returns password for decrypting the mTLS private key
229235
- ``INFLUXDB_V2_CONNECTION_POOL_MAXSIZE`` - set the number of connections to save that can be reused by urllib3
230236
- ``INFLUXDB_V2_AUTH_BASIC`` - enable http basic authentication when talking to a InfluxDB 1.8.x without authentication but is accessed via reverse proxy with basic authentication (defaults to false)
231237
- ``INFLUXDB_V2_PROFILERS`` - set the list of enabled `Flux profilers <https://docs.influxdata.com/influxdb/v2.0/reference/flux/stdlib/profiler/>`_

influxdb_client/_async/rest.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,19 @@ def __init__(self, configuration, pools_size=4, maxsize=None, **kwargs):
8383
if maxsize is None:
8484
maxsize = configuration.connection_pool_maxsize
8585

86-
ssl_context = ssl.create_default_context(cafile=configuration.ssl_ca_cert)
87-
if configuration.cert_file:
88-
ssl_context.load_cert_chain(
89-
configuration.cert_file, keyfile=configuration.key_file
90-
)
91-
92-
if not configuration.verify_ssl:
93-
ssl_context.check_hostname = False
94-
ssl_context.verify_mode = ssl.CERT_NONE
86+
if configuration.ssl_context is None:
87+
ssl_context = ssl.create_default_context(cafile=configuration.ssl_ca_cert)
88+
if configuration.cert_file:
89+
ssl_context.load_cert_chain(
90+
certfile=configuration.cert_file, keyfile=configuration.cert_key_file,
91+
password=configuration.cert_key_password
92+
)
93+
94+
if not configuration.verify_ssl:
95+
ssl_context.check_hostname = False
96+
ssl_context.verify_mode = ssl.CERT_NONE
97+
else:
98+
ssl_context = configuration.ssl_context
9599

96100
connector = aiohttp.TCPConnector(
97101
limit=maxsize,

influxdb_client/_sync/rest.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,11 @@ def __init__(self, configuration, pools_size=4, maxsize=None, retries=False):
101101
cert_reqs=cert_reqs,
102102
ca_certs=ca_certs,
103103
cert_file=configuration.cert_file,
104-
key_file=configuration.key_file,
104+
key_file=configuration.cert_key_file,
105+
key_password=configuration.cert_key_password,
105106
proxy_url=configuration.proxy,
106107
proxy_headers=configuration.proxy_headers,
108+
ssl_context=configuration.ssl_context,
107109
**addition_pool_args
108110
)
109111
else:
@@ -113,7 +115,9 @@ def __init__(self, configuration, pools_size=4, maxsize=None, retries=False):
113115
cert_reqs=cert_reqs,
114116
ca_certs=ca_certs,
115117
cert_file=configuration.cert_file,
116-
key_file=configuration.key_file,
118+
key_file=configuration.cert_key_file,
119+
key_password=configuration.cert_key_password,
120+
ssl_context=configuration.ssl_context,
117121
**addition_pool_args
118122
)
119123

influxdb_client/client/_base.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ def __init__(self, url, token, debug=None, timeout=10_000, enable_gzip=False, or
6060
self.conf.enable_gzip = enable_gzip
6161
self.conf.verify_ssl = kwargs.get('verify_ssl', True)
6262
self.conf.ssl_ca_cert = kwargs.get('ssl_ca_cert', None)
63+
self.conf.cert_file = kwargs.get('cert_file', None)
64+
self.conf.cert_key_file = kwargs.get('cert_key_file', None)
65+
self.conf.cert_key_password = kwargs.get('cert_key_password', None)
66+
self.conf.ssl_context = kwargs.get('ssl_context', None)
6367
self.conf.proxy = kwargs.get('proxy', None)
6468
self.conf.proxy_headers = kwargs.get('proxy_headers', None)
6569
self.conf.connection_pool_maxsize = kwargs.get('connection_pool_maxsize', self.conf.connection_pool_maxsize)
@@ -142,6 +146,18 @@ def _has_section(key: str):
142146
if _has_option('ssl_ca_cert'):
143147
ssl_ca_cert = _config_value('ssl_ca_cert')
144148

149+
cert_file = None
150+
if _has_option('cert_file'):
151+
cert_file = _config_value('cert_file')
152+
153+
cert_key_file = None
154+
if _has_option('cert_key_file'):
155+
cert_key_file = _config_value('cert_key_file')
156+
157+
cert_key_password = None
158+
if _has_option('cert_key_password'):
159+
cert_key_password = _config_value('cert_key_password')
160+
145161
connection_pool_maxsize = None
146162
if _has_option('connection_pool_maxsize'):
147163
connection_pool_maxsize = _config_value('connection_pool_maxsize')
@@ -168,6 +184,7 @@ def _has_section(key: str):
168184

169185
return cls(url, token, debug=debug, timeout=_to_int(timeout), org=org, default_tags=default_tags,
170186
enable_gzip=enable_gzip, verify_ssl=_to_bool(verify_ssl), ssl_ca_cert=ssl_ca_cert,
187+
cert_file=cert_file, cert_key_file=cert_key_file, cert_key_password=cert_key_password,
171188
connection_pool_maxsize=_to_int(connection_pool_maxsize), auth_basic=_to_bool(auth_basic),
172189
profilers=profilers, proxy=proxy, **kwargs)
173190

@@ -179,6 +196,9 @@ def _from_env_properties(cls, debug=None, enable_gzip=False, **kwargs):
179196
org = os.getenv('INFLUXDB_V2_ORG', "my-org")
180197
verify_ssl = os.getenv('INFLUXDB_V2_VERIFY_SSL', "True")
181198
ssl_ca_cert = os.getenv('INFLUXDB_V2_SSL_CA_CERT', None)
199+
cert_file = os.getenv('INFLUXDB_V2_CERT_FILE', None)
200+
cert_key_file = os.getenv('INFLUXDB_V2_CERT_KEY_FILE', None)
201+
cert_key_password = os.getenv('INFLUXDB_V2_CERT_KEY_PASSWORD', None)
182202
connection_pool_maxsize = os.getenv('INFLUXDB_V2_CONNECTION_POOL_MAXSIZE', None)
183203
auth_basic = os.getenv('INFLUXDB_V2_AUTH_BASIC', "False")
184204

@@ -195,6 +215,7 @@ def _from_env_properties(cls, debug=None, enable_gzip=False, **kwargs):
195215

196216
return cls(url, token, debug=debug, timeout=_to_int(timeout), org=org, default_tags=default_tags,
197217
enable_gzip=enable_gzip, verify_ssl=_to_bool(verify_ssl), ssl_ca_cert=ssl_ca_cert,
218+
cert_file=cert_file, cert_key_file=cert_key_file, cert_key_password=cert_key_password,
198219
connection_pool_maxsize=_to_int(connection_pool_maxsize), auth_basic=_to_bool(auth_basic),
199220
profilers=profilers, **kwargs)
200221

influxdb_client/client/influxdb_client.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ def __init__(self, url, token: str = None, debug=None, timeout=10_000, enable_gz
4040
:param org: organization name (used as a default in Query, Write and Delete API)
4141
:key bool verify_ssl: Set this to false to skip verifying SSL certificate when calling API from https server.
4242
:key str ssl_ca_cert: Set this to customize the certificate file to verify the peer.
43+
:key str cert_file: Path to the certificate that will be used for mTLS authentication.
44+
:key str cert_key_file: Path to the file contains private key for mTLS certificate.
45+
:key str cert_key_password: String or function which returns password for decrypting the mTLS private key.
46+
:key ssl.SSLContext ssl_context: Specify a custom Python SSL Context for the TLS/ mTLS handshake.
47+
Be aware that only delivered certificate/ key files or an SSL Context are
48+
possible.
4349
:key str proxy: Set this to configure the http proxy to be used (ex. http://localhost:3128)
4450
:key str proxy_headers: A dictionary containing headers that will be sent to the proxy. Could be used for proxy
4551
authentication.
@@ -89,6 +95,9 @@ def from_config_file(cls, config_file: str = "config.ini", debug=None, enable_gz
8995
authentication.
9096
:key urllib3.util.retry.Retry retries: Set the default retry strategy that is used for all HTTP requests
9197
except batching writes. As a default there is no one retry strategy.
98+
:key ssl.SSLContext ssl_context: Specify a custom Python SSL Context for the TLS/ mTLS handshake.
99+
Be aware that only delivered certificate/ key files or an SSL Context are
100+
possible.
92101
93102
The supported formats:
94103
- https://docs.python.org/3/library/configparser.html
@@ -102,6 +111,9 @@ def from_config_file(cls, config_file: str = "config.ini", debug=None, enable_gz
102111
- timeout,
103112
- verify_ssl
104113
- ssl_ca_cert
114+
- cert_file
115+
- cert_key_file
116+
- cert_key_password
105117
- connection_pool_maxsize
106118
- auth_basic
107119
- profilers
@@ -177,6 +189,9 @@ def from_env_properties(cls, debug=None, enable_gzip=False, **kwargs):
177189
authentication.
178190
:key urllib3.util.retry.Retry retries: Set the default retry strategy that is used for all HTTP requests
179191
except batching writes. As a default there is no one retry strategy.
192+
:key ssl.SSLContext ssl_context: Specify a custom Python SSL Context for the TLS/ mTLS handshake.
193+
Be aware that only delivered certificate/ key files or an SSL Context are
194+
possible.
180195
181196
Supported environment properties:
182197
- INFLUXDB_V2_URL
@@ -185,6 +200,9 @@ def from_env_properties(cls, debug=None, enable_gzip=False, **kwargs):
185200
- INFLUXDB_V2_TIMEOUT
186201
- INFLUXDB_V2_VERIFY_SSL
187202
- INFLUXDB_V2_SSL_CA_CERT
203+
- INFLUXDB_V2_CERT_FILE
204+
- INFLUXDB_V2_CERT_KEY_FILE
205+
- INFLUXDB_V2_CERT_KEY_PASSWORD
188206
- INFLUXDB_V2_CONNECTION_POOL_MAXSIZE
189207
- INFLUXDB_V2_AUTH_BASIC
190208
- INFLUXDB_V2_PROFILERS

influxdb_client/client/influxdb_client_async.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ def __init__(self, url, token: str = None, org: str = None, debug=None, timeout=
3232
supports the Gzip compression.
3333
:key bool verify_ssl: Set this to false to skip verifying SSL certificate when calling API from https server.
3434
:key str ssl_ca_cert: Set this to customize the certificate file to verify the peer.
35+
:key str cert_file: Path to the certificate that will be used for mTLS authentication.
36+
:key str cert_key_file: Path to the file contains private key for mTLS certificate.
37+
:key str cert_key_password: String or function which returns password for decrypting the mTLS private key.
38+
:key ssl.SSLContext ssl_context: Specify a custom Python SSL Context for the TLS/ mTLS handshake.
39+
Be aware that only delivered certificate/ key files or an SSL Context are
40+
possible.
3541
:key str proxy: Set this to configure the http proxy to be used (ex. http://localhost:3128)
3642
:key str proxy_headers: A dictionary containing headers that will be sent to the proxy. Could be used for proxy
3743
authentication.
@@ -105,6 +111,9 @@ def from_config_file(cls, config_file: str = "config.ini", debug=None, enable_gz
105111
authentication.
106112
:key urllib3.util.retry.Retry retries: Set the default retry strategy that is used for all HTTP requests
107113
except batching writes. As a default there is no one retry strategy.
114+
:key ssl.SSLContext ssl_context: Specify a custom Python SSL Context for the TLS/ mTLS handshake.
115+
Be aware that only delivered certificate/ key files or an SSL Context are
116+
possible.
108117
109118
The supported formats:
110119
- https://docs.python.org/3/library/configparser.html
@@ -118,6 +127,9 @@ def from_config_file(cls, config_file: str = "config.ini", debug=None, enable_gz
118127
- timeout,
119128
- verify_ssl
120129
- ssl_ca_cert
130+
- cert_file
131+
- cert_key_file
132+
- cert_key_password
121133
- connection_pool_maxsize
122134
- auth_basic
123135
- profilers
@@ -193,6 +205,10 @@ def from_env_properties(cls, debug=None, enable_gzip=False, **kwargs):
193205
authentication.
194206
:key urllib3.util.retry.Retry retries: Set the default retry strategy that is used for all HTTP requests
195207
except batching writes. As a default there is no one retry strategy.
208+
:key ssl.SSLContext ssl_context: Specify a custom Python SSL Context for the TLS/ mTLS handshake.
209+
Be aware that only delivered certificate/ key files or an SSL Context are
210+
possible.
211+
196212
197213
Supported environment properties:
198214
- INFLUXDB_V2_URL
@@ -201,6 +217,9 @@ def from_env_properties(cls, debug=None, enable_gzip=False, **kwargs):
201217
- INFLUXDB_V2_TIMEOUT
202218
- INFLUXDB_V2_VERIFY_SSL
203219
- INFLUXDB_V2_SSL_CA_CERT
220+
- INFLUXDB_V2_CERT_FILE
221+
- INFLUXDB_V2_CERT_KEY_FILE
222+
- INFLUXDB_V2_CERT_KEY_PASSWORD
204223
- INFLUXDB_V2_CONNECTION_POOL_MAXSIZE
205224
- INFLUXDB_V2_AUTH_BASIC
206225
- INFLUXDB_V2_PROFILERS

influxdb_client/configuration.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,15 @@ def __init__(self):
8989
# client certificate file
9090
self.cert_file = None
9191
# client key file
92-
self.key_file = None
92+
self.cert_key_file = None
93+
# client key file password
94+
self.cert_key_password = None
9395
# Set this to True/False to enable/disable SSL hostname verification.
9496
self.assert_hostname = None
9597

98+
# Set this to specify a custom ssl context to inject this context inside the urllib3 connection pool.
99+
self.ssl_context = None
100+
96101
# urllib3 connection pool's maximum number of connections saved
97102
# per pool. urllib3 uses 1 connection as default value, but this is
98103
# not the best value when you are making a lot of possibly parallel

tests/config-ssl-mtls-certs.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[influx2]
2+
url=http://localhost:8086
3+
org=my-org
4+
token=my-token
5+
timeout=6000
6+
ssl_ca_cert=/path/to/my/cert
7+
cert_file=/path/to/my/cert
8+
cert_key_file=/path/to/my/key
9+
cert_key_password=test
10+
11+
[tags]
12+
id = 132-987-655
13+
customer = California Miner
14+
data_center = ${env.data_center}

0 commit comments

Comments
 (0)