2424import socket
2525import struct
2626import re
27+ import functools
28+ import ssl
29+ import sslpsk
2730
2831# For python 2 and 3 compatibility
2932try :
@@ -187,11 +190,25 @@ def __init__(self,
187190 self .chunk_size = chunk_size
188191 self .timeout = timeout
189192
190- self .socket_wrapper = socket_wrapper
191193 if use_config :
192194 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+ )
193207 else :
194208 self .zabbix_uri = [(zabbix_server , zabbix_port )]
209+ self .psk_identity = None
210+ self .psk_file = None
211+ self .socket_wrapper = socket_wrapper
195212
196213 def __repr__ (self ):
197214 """Represent detailed ZabbixSender view."""
@@ -201,7 +218,7 @@ def __repr__(self):
201218
202219 return result
203220
204- def _load_from_config (self , config_file ):
221+ def _load_from_config (self , config_file , return_param = 'ServerActive' ):
205222 """Load zabbix server IP address and port from zabbix agent config
206223 file.
207224
@@ -240,22 +257,28 @@ def _load_from_config(self, config_file):
240257 config_file_fp = StringIO (config_file_data )
241258 config = configparser .RawConfigParser (** params )
242259 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'
250260
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 )
259282
260283 return result
261284
@@ -442,3 +465,34 @@ def send(self, metrics):
442465 for m in range (0 , len (metrics ), self .chunk_size ):
443466 result .parse (self ._chunk_send (metrics [m :m + self .chunk_size ]))
444467 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