Skip to content

Commit c781f83

Browse files
author
dmitry.duka
committed
fix for #114
1 parent 8d2b855 commit c781f83

File tree

4 files changed

+84
-18
lines changed

4 files changed

+84
-18
lines changed

pyzabbix/sender.py

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import socket
2525
import struct
2626
import re
27+
import functools
28+
import ssl
2729

2830
# For python 2 and 3 compatibility
2931
try:
@@ -33,6 +35,11 @@
3335
from io import StringIO
3436
import configparser
3537

38+
try:
39+
import sslpsk
40+
except ImportError:
41+
pass
42+
3643
from .logger import NullHandler
3744

3845
null_handler = NullHandler()
@@ -187,11 +194,25 @@ def __init__(self,
187194
self.chunk_size = chunk_size
188195
self.timeout = timeout
189196

190-
self.socket_wrapper = socket_wrapper
191197
if use_config:
192198
self.zabbix_uri = self._load_from_config(use_config)
199+
psk_identity = self._load_from_config(use_config,'TLSPSKIdentity')
200+
psk_file = self._load_from_config(use_config,'TLSPSKFile')
201+
if psk_identity and psk_file:
202+
with open(psk_file, "r") as psk_data:
203+
psk_txt = psk_data.readlines()[0]
204+
self.socket_wrapper = functools.partial(
205+
PyZabbixPSKSocketWrapper,
206+
identity=psk_identity, # your PSK identity
207+
psk=bytes.fromhex(
208+
psk_txt # your PSK
209+
)
210+
)
193211
else:
194212
self.zabbix_uri = [(zabbix_server, zabbix_port)]
213+
self.psk_identity = None
214+
self.psk_file = None
215+
self.socket_wrapper = socket_wrapper
195216

196217
def __repr__(self):
197218
"""Represent detailed ZabbixSender view."""
@@ -201,7 +222,7 @@ def __repr__(self):
201222

202223
return result
203224

204-
def _load_from_config(self, config_file):
225+
def _load_from_config(self, config_file, return_param='ServerActive'):
205226
"""Load zabbix server IP address and port from zabbix agent config
206227
file.
207228
@@ -240,22 +261,28 @@ def _load_from_config(self, config_file):
240261
config_file_fp = StringIO(config_file_data)
241262
config = configparser.RawConfigParser(**params)
242263
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'
250264

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)
265+
result = ''
266+
if return_param == 'ServerActive':
267+
# Prefer ServerActive, then try Server and fallback to defaults
268+
if config.has_option('root', 'ServerActive'):
269+
zabbix_serveractives = config.get('root', 'ServerActive')
270+
elif config.has_option('root', 'Server'):
271+
zabbix_serveractives = config.get('root', 'Server')
272+
else:
273+
zabbix_serveractives = '127.0.0.1:10051'
274+
275+
result = []
276+
for serverport in zabbix_serveractives.split(','):
277+
if ':' not in serverport:
278+
serverport = "%s:%s" % (serverport.strip(), 10051)
279+
server, port = serverport.split(':')
280+
serverport = (server, int(port))
281+
result.append(serverport)
282+
logger.debug("Loaded params: %s", result)
283+
else:
284+
if config.has_option('root', return_param):
285+
result = config.get('root', return_param)
259286

260287
return result
261288

@@ -442,3 +469,34 @@ def send(self, metrics):
442469
for m in range(0, len(metrics), self.chunk_size):
443470
result.parse(self._chunk_send(metrics[m:m + self.chunk_size]))
444471
return result
472+
473+
474+
class PyZabbixPSKSocketWrapper:
475+
"""Implements ssl.wrap_socket with PSK instead of certificates.
476+
477+
Proxies calls to a `socket` instance.
478+
"""
479+
480+
def __init__(self, sock, *, identity, psk):
481+
self.__sock = sock
482+
self.__identity = identity
483+
self.__psk = psk
484+
485+
def connect(self, *args, **kwargs):
486+
# `sslpsk.wrap_socket` must be called *after* socket.connect,
487+
# while the `ssl.wrap_socket` must be called *before* socket.connect.
488+
self.__sock.connect(*args, **kwargs)
489+
490+
# `sslv3 alert bad record mac` exception means incorrect PSK
491+
self.__sock = sslpsk.wrap_socket(
492+
self.__sock,
493+
# https://github.com/zabbix/zabbix/blob/f0a1ad397e5653238638cd1a65a25ff78c6809bb/src/libs/zbxcrypto/tls.c#L3231
494+
ssl_version=ssl.PROTOCOL_TLSv1_2,
495+
# https://github.com/zabbix/zabbix/blob/f0a1ad397e5653238638cd1a65a25ff78c6809bb/src/libs/zbxcrypto/tls.c#L3179
496+
ciphers="PSK-AES128-CBC-SHA",
497+
psk=(self.__psk, self.__identity),
498+
)
499+
500+
def __getattr__(self, name):
501+
return getattr(self.__sock, name)
502+

pyzabbix/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '1.1.7'
1+
__version__ = '1.1.8'

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[metadata]
2+
requires-dist =
3+
sslpsk>=1.0.0,<2.0.0'

setup.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
from setuptools import setup
33
from pyzabbix.version import __version__
44

5+
requires = [
6+
'sslpsk>=1.0.0,<2.0.0',
7+
]
8+
59
setup(name='py-zabbix',
610
version=__version__,
711
description='Python module to work with zabbix.',
@@ -13,6 +17,7 @@
1317
test_suite='tests',
1418
packages=['pyzabbix', 'zabbix'],
1519
tests_require=['mock'],
20+
install_requires=requires,
1621
classifiers=[
1722
'Development Status :: 5 - Production/Stable',
1823
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',

0 commit comments

Comments
 (0)