unbound: fix init (#7112)
* SocketService: fix error handling in connect and more debug output * SocketService: use ssl.PROTOCOL_SSLv23 instead of ssl.PROTOCOL_TLS for py2 * unbound: conf parse fix
Ilya Mashchenko committed
Oct 17, 2019 at 14:45 UTC
d0291aa7e69c2020121864229cc6f542bcbd5db9
2 files changed
+66
-30
collectors/python.d.plugin/python_modules/bases/FrameworkServices/SocketService.py
+14
-4
@@ -14,6 +14,12 @@ except ImportError:
14
else:
15
_TLS_SUPPORT = True
16
17
+if _TLS_SUPPORT:
18
+ try:
19
+ PROTOCOL_TLS = ssl.PROTOCOL_TLS
20
+ except AttributeError:
21
+ PROTOCOL_TLS = ssl.PROTOCOL_SSLv23
22
+
23
from bases.FrameworkServices.SimpleService import SimpleService
24
25
@@ -80,15 +86,18 @@ class SocketService(SimpleService):
86
if self.tls:
87
try:
88
self.debug('Encapsulating socket with TLS')
89
+ self.debug('Using keyfile: {0}, certfile: {1}, cert_reqs: {2}, ssl_version: {3}'.format(
90
+ self.key, self.cert, ssl.CERT_NONE, PROTOCOL_TLS
91
+ ))
92
self._sock = ssl.wrap_socket(self._sock,
93
keyfile=self.key,
94
certfile=self.cert,
95
server_side=False,
96
cert_reqs=ssl.CERT_NONE,
88
- ssl_version=ssl.PROTOCOL_TLS,
97
+ ssl_version=PROTOCOL_TLS,
98
)
90
- except (socket.error, ssl.SSLError) as error:
91
- self.error('failed to wrap socket : {0}'.format(error))
99
+ except (socket.error, ssl.SSLError, IOError, OSError) as error:
100
+ self.error('failed to wrap socket : {0}'.format(repr(error)))
101
self._disconnect()
102
self.__socket_config = None
103
return False
@@ -167,7 +176,8 @@ class SocketService(SimpleService):
176
if self._connect2socket(res):
177
break
178
170
- except Exception:
179
+ except Exception as error:
180
+ self.error('unhandled exception during connect : {0}'.format(repr(error)))
181
self._sock = None
182
self.__socket_config = None
183
collectors/python.d.plugin/unbound/unbound.chart.py
+52
-26
@@ -103,7 +103,6 @@ PER_THREAD_CHARTS = {
103
}
104
}
105
106
-
106
# This maps the Unbound stat names to our names and precision requiremnets.
107
STAT_MAP = {
108
'total.num.queries_ip_ratelimited': ('ratelimit', 1),
@@ -147,6 +146,10 @@ PER_THREAD_STAT_MAP = {
146
}
147
148
149
+def is_readable(name):
150
+ return os.access(name, os.R_OK)
151
+
152
+
153
# Used to actually generate per-thread charts.
154
def _get_perthread_info(thread):
155
sname = 'thread{0}'.format(thread)
@@ -203,25 +206,8 @@ class Service(SocketService):
206
self.debug('Using certificate: {0}'.format(self.cert))
207
208
def _auto_config(self):
206
- if self.ubconf and os.access(self.ubconf, os.R_OK):
207
- self.debug('Unbound config: {0}'.format(self.ubconf))
208
- conf = dict()
209
- try:
210
- conf = load_config(self.ubconf)
211
- except Exception as error:
212
- self.error("error on loading '{0}' : {1}".format(self.ubconf, error))
213
- if self.ext is None:
214
- if 'extended-statistics' in conf['server']:
215
- self.ext = conf['server']['extended-statistics']
216
- if 'remote-control' in conf:
217
- if conf['remote-control'].get('control-use-cert', False):
218
- self.key = self.key or conf['remote-control'].get('control-key-file')
219
- self.cert = self.cert or conf['remote-control'].get('control-cert-file')
220
- self.port = self.port or conf['remote-control'].get('control-port')
221
- else:
222
- self.unix_socket = self.unix_socket or conf['remote-control'].get('control-interface')
223
- else:
224
- self.debug('Unbound configuration not found.')
209
+ self.load_unbound_config()
210
+
211
if not self.key:
212
self.key = '/etc/unbound/unbound_control.key'
213
if not self.cert:
@@ -229,6 +215,38 @@ class Service(SocketService):
215
if not self.port:
216
self.port = 8953
217
218
+ def load_unbound_config(self):
219
+ if not (self.ubconf and is_readable(self.ubconf)):
220
+ self.debug('Unbound configuration not found.')
221
+ return
222
+
223
+ self.debug('Loading Unbound config: {0}'.format(self.ubconf))
224
+
225
+ try:
226
+ conf = load_config(self.ubconf)
227
+ except Exception as error:
228
+ self.error("error on loading '{0}' : {1}".format(self.ubconf, error))
229
+ return
230
+
231
+ srv = conf.get('server')
232
+ if self.ext is None:
233
+ if srv and 'extended-statistics' in srv:
234
+ self.ext = srv['extended-statistics']
235
+
236
+ rc = conf.get('remote-control')
237
+ if not (rc and isinstance(rc, dict)):
238
+ return
239
+
240
+ if rc.get('control-use-cert', False):
241
+ self.key = self.key or rc.get('control-key-file')
242
+ self.cert = self.cert or rc.get('control-cert-file')
243
+ self.port = self.port or rc.get('control-port')
244
+ else:
245
+ ci = rc.get('control-interface', str())
246
+ is_socket = '/' in ci
247
+ if is_socket:
248
+ self.unix_socket = ci
249
+
250
def _generate_perthread_charts(self):
251
tmporder = list()
252
for thread in range(0, self.threads):
@@ -239,6 +257,14 @@ class Service(SocketService):
257
self.order.extend(sorted(tmporder))
258
259
def check(self):
260
+ if not is_readable(self.key):
261
+ self.error("ssl key '{0}' is not readable".format(self.key))
262
+ return False
263
+
264
+ if not is_readable(self.cert):
265
+ self.error("ssl certificate '{0}' is not readable".format(self.certificate))
266
+ return False
267
+
268
# Check if authentication is working.
269
self._connect()
270
result = bool(self._sock)
@@ -268,12 +294,6 @@ class Service(SocketService):
294
self.request = tmp
295
return result
296
271
- @staticmethod
272
- def _check_raw_data(data):
273
- # The server will close the connection when it's done sending
274
- # data, so just keep looping until that happens.
275
- return False
276
-
297
def _get_data(self):
298
raw = self._get_raw_data()
299
data = dict()
@@ -288,3 +308,9 @@ class Service(SocketService):
308
else:
309
self.warning('Received no data from socket.')
310
return data
311
+
312
+ @staticmethod
313
+ def _check_raw_data(data):
314
+ # The server will close the connection when it's done sending
315
+ # data, so just keep looping until that happens.
316
+ return False