24
24
import socket
25
25
import struct
26
26
import re
27
+ import functools
28
+ import ssl
29
+ import sslpsk
27
30
28
31
# For python 2 and 3 compatibility
29
32
try :
@@ -187,11 +190,25 @@ def __init__(self,
187
190
self .chunk_size = chunk_size
188
191
self .timeout = timeout
189
192
190
- self .socket_wrapper = socket_wrapper
191
193
if use_config :
192
194
self .zabbix_uri = self ._load_from_config (use_config )
195
+ psk_identity = self ._load_from_config (use_config ,'TLSPSKIdentity' )
196
+ psk_file = self ._load_from_config (use_config ,'TLSPSKFile' )
197
+ if psk_identity and psk_file :
198
+ with open (psk_file , "r" ) as psk_data :
199
+ psk_txt = psk_data .readlines ()[0 ]
200
+ self .socket_wrapper = functools .partial (
201
+ PyZabbixPSKSocketWrapper ,
202
+ identity = psk_identity , # your PSK identity
203
+ psk = bytes .fromhex (
204
+ psk_txt # your PSK
205
+ )
206
+ )
193
207
else :
194
208
self .zabbix_uri = [(zabbix_server , zabbix_port )]
209
+ self .psk_identity = None
210
+ self .psk_file = None
211
+ self .socket_wrapper = socket_wrapper
195
212
196
213
def __repr__ (self ):
197
214
"""Represent detailed ZabbixSender view."""
@@ -201,7 +218,7 @@ def __repr__(self):
201
218
202
219
return result
203
220
204
- def _load_from_config (self , config_file ):
221
+ def _load_from_config (self , config_file , return_param = 'ServerActive' ):
205
222
"""Load zabbix server IP address and port from zabbix agent config
206
223
file.
207
224
@@ -240,22 +257,28 @@ def _load_from_config(self, config_file):
240
257
config_file_fp = StringIO (config_file_data )
241
258
config = configparser .RawConfigParser (** params )
242
259
config .readfp (config_file_fp )
243
- # Prefer ServerActive, then try Server and fallback to defaults
244
- if config .has_option ('root' , 'ServerActive' ):
245
- zabbix_serveractives = config .get ('root' , 'ServerActive' )
246
- elif config .has_option ('root' , 'Server' ):
247
- zabbix_serveractives = config .get ('root' , 'Server' )
248
- else :
249
- zabbix_serveractives = '127.0.0.1:10051'
250
260
251
- result = []
252
- for serverport in zabbix_serveractives .split (',' ):
253
- if ':' not in serverport :
254
- serverport = "%s:%s" % (serverport .strip (), 10051 )
255
- server , port = serverport .split (':' )
256
- serverport = (server , int (port ))
257
- result .append (serverport )
258
- logger .debug ("Loaded params: %s" , result )
261
+ result = ''
262
+ if return_param == 'ServerActive' :
263
+ # Prefer ServerActive, then try Server and fallback to defaults
264
+ if config .has_option ('root' , 'ServerActive' ):
265
+ zabbix_serveractives = config .get ('root' , 'ServerActive' )
266
+ elif config .has_option ('root' , 'Server' ):
267
+ zabbix_serveractives = config .get ('root' , 'Server' )
268
+ else :
269
+ zabbix_serveractives = '127.0.0.1:10051'
270
+
271
+ result = []
272
+ for serverport in zabbix_serveractives .split (',' ):
273
+ if ':' not in serverport :
274
+ serverport = "%s:%s" % (serverport .strip (), 10051 )
275
+ server , port = serverport .split (':' )
276
+ serverport = (server , int (port ))
277
+ result .append (serverport )
278
+ logger .debug ("Loaded params: %s" , result )
279
+ else :
280
+ if config .has_option ('root' , return_param ):
281
+ result = config .get ('root' , return_param )
259
282
260
283
return result
261
284
@@ -442,3 +465,34 @@ def send(self, metrics):
442
465
for m in range (0 , len (metrics ), self .chunk_size ):
443
466
result .parse (self ._chunk_send (metrics [m :m + self .chunk_size ]))
444
467
return result
468
+
469
+
470
+ class PyZabbixPSKSocketWrapper :
471
+ """Implements ssl.wrap_socket with PSK instead of certificates.
472
+
473
+ Proxies calls to a `socket` instance.
474
+ """
475
+
476
+ def __init__ (self , sock , * , identity , psk ):
477
+ self .__sock = sock
478
+ self .__identity = identity
479
+ self .__psk = psk
480
+
481
+ def connect (self , * args , ** kwargs ):
482
+ # `sslpsk.wrap_socket` must be called *after* socket.connect,
483
+ # while the `ssl.wrap_socket` must be called *before* socket.connect.
484
+ self .__sock .connect (* args , ** kwargs )
485
+
486
+ # `sslv3 alert bad record mac` exception means incorrect PSK
487
+ self .__sock = sslpsk .wrap_socket (
488
+ self .__sock ,
489
+ # https://github.com/zabbix/zabbix/blob/f0a1ad397e5653238638cd1a65a25ff78c6809bb/src/libs/zbxcrypto/tls.c#L3231
490
+ ssl_version = ssl .PROTOCOL_TLSv1_2 ,
491
+ # https://github.com/zabbix/zabbix/blob/f0a1ad397e5653238638cd1a65a25ff78c6809bb/src/libs/zbxcrypto/tls.c#L3179
492
+ ciphers = "PSK-AES128-CBC-SHA" ,
493
+ psk = (self .__psk , self .__identity ),
494
+ )
495
+
496
+ def __getattr__ (self , name ):
497
+ return getattr (self .__sock , name )
498
+
0 commit comments