@cryptotaxi247 / netdata-1 / commits / d0291aa7e

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