Skip to content

Commit 3cab2cd

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

File tree

4 files changed

+80
-18
lines changed

4 files changed

+80
-18
lines changed

pyzabbix/sender.py

+71-17
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
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:
3032
from StringIO import StringIO
3133
import ConfigParser as configparser
34+
import sslpsk
3235
except ImportError:
3336
from io import StringIO
3437
import configparser
@@ -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+

pyzabbix/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '1.1.7'
1+
__version__ = '1.1.8'

setup.cfg

+3
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

+5
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)