diff options
-rw-r--r-- | conf/glances.conf | 11 | ||||
-rw-r--r-- | docs/aoa/header.rst | 19 | ||||
-rw-r--r-- | glances/compat.py | 4 | ||||
-rw-r--r-- | glances/plugins/glances_ip.py | 111 |
4 files changed, 134 insertions, 11 deletions
diff --git a/conf/glances.conf b/conf/glances.conf index 09cb5c68..a8974185 100644 --- a/conf/glances.conf +++ b/conf/glances.conf @@ -175,6 +175,17 @@ tx_critical=90 [ip] disable=False +public_refresh_interval=300 +public_ip_disabled=False +# Configuration for the Censys online service +# Need to create an aacount: https://censys.io/login +censys_url=https://search.censys.io/api +# Get your own credential here: https://search.censys.io/account/api +# Enter and uncomment the following lines to use your credentials +censys_username=13718bd6-b0c4-4c21-ae63-2d13da0b2dd8 +censys_password=hnjdSOukHM84tgx1Oo9Zzv4Pmz2E5bXH +# List of fields to be displayed in user interface (comma separated) +censys_fields=location:continent,location:country,autonomous_system:name [connections] # Display additional information about TCP connections diff --git a/docs/aoa/header.rst b/docs/aoa/header.rst index 2e723413..a4719ef3 100644 --- a/docs/aoa/header.rst +++ b/docs/aoa/header.rst @@ -17,12 +17,29 @@ file under the ``[ip]`` section: .. code-block:: ini [ip] - public_refresh_interval=240 + public_refresh_interval=300 public_ip_disabled=True + **NOTE:** Setting low values for `public_refresh_interval` will result in frequent HTTP requests to the IP detection servers. Recommended range: 120-600 seconds +If the Censys options are configured, the public IP address is also analysed (with the same interval) +and additional information is displayed. + +.. code-block:: ini + [ip] + public_refresh_interval=300 + public_ip_disabled=True + censys_url=https://search.censys.io/api + # Get your own credential here: https://search.censys.io/account/api + censys_username=CENSYS_API_ID + censys_password=CENSYS_API_SECRET + # List of fields to be displayed in user interface (comma separated) + censys_fields=location:continent,location:country,autonomous_system:name + +**Note:** Access to the Censys Search API need an account (https://censys.io/login). + **Connected**: .. image:: ../_static/connected.png diff --git a/glances/compat.py b/glances/compat.py index 73b52256..a193fb49 100644 --- a/glances/compat.py +++ b/glances/compat.py @@ -31,7 +31,7 @@ if PY3: from statistics import mean from xmlrpc.client import Fault, ProtocolError, ServerProxy, Transport, Server from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer - from urllib.request import urlopen + from urllib.request import Request, urlopen, base64 from urllib.error import HTTPError, URLError from urllib.parse import urlparse @@ -126,7 +126,7 @@ else: from ConfigParser import SafeConfigParser as ConfigParser, NoOptionError, NoSectionError from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer from xmlrpclib import Fault, ProtocolError, ServerProxy, Transport, Server - from urllib2 import urlopen, HTTPError, URLError + from urllib2 import Request, urlopen, HTTPError, URLError, base64 from urlparse import urlparse # Correct issue #1025 by monkey path the xmlrpc lib diff --git a/glances/plugins/glances_ip.py b/glances/plugins/glances_ip.py index 17e425f7..6799e272 100644 --- a/glances/plugins/glances_ip.py +++ b/glances/plugins/glances_ip.py @@ -10,6 +10,7 @@ """IP plugin.""" import threading +import urllib from json import loads from glances.compat import iterkeys, urlopen, queue @@ -65,6 +66,13 @@ class Plugin(GlancesPlugin): public_ip_disabled = self.get_conf_value("public_ip_disabled", default=self._default_public_ip_disabled) self.public_ip_disabled = True if public_ip_disabled == ["True"] else False + # For the Censys options (see issue #2105) + self.public_info = "" + self.censys_url = self.get_conf_value("censys_url", default=[None])[0] + self.censys_username = self.get_conf_value("censys_username", default=[None])[0] + self.censys_password = self.get_conf_value("censys_password", default=[None])[0] + self.censys_fields = self.get_conf_value("censys_fields", default=[None]) + @GlancesPlugin._check_decorator @GlancesPlugin._log_result_decorator def update(self): @@ -77,30 +85,53 @@ class Plugin(GlancesPlugin): if self.input_method == 'local' and not import_error_tag: # Update stats using the netifaces lib + # Start with the default IP gateway try: default_gw = netifaces.gateways()['default'][netifaces.AF_INET] except (KeyError, AttributeError) as e: - logger.debug("Cannot grab the default gateway ({})".format(e)) + logger.debug("Cannot grab default gateway IP address ({})".format(e)) return {} + else: + stats['gateway'] = default_gw[0] + # Then the private IP address try: address = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['addr'] mask = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['netmask'] + except (KeyError, AttributeError) as e: + logger.debug("Cannot grab private IP address ({})".format(e)) + return {} + else: + stats['address'] = address + stats['mask'] = mask + stats['mask_cidr'] = self.ip_to_cidr(stats['mask']) - time_since_update = getTimeSinceLastUpdate('public-ip') + # Continue with the public IP address + time_since_update = getTimeSinceLastUpdate('public-ip') + try: if not self.public_ip_disabled and ( - self.stats.get('address') != address or time_since_update > self.public_address_refresh_interval + self.stats.get('address') != address + or time_since_update > self.public_address_refresh_interval ): self.public_address = PublicIpAddress().get() except (KeyError, AttributeError) as e: - logger.debug("Cannot grab IP information: {}".format(e)) + logger.debug("Cannot grab public IP address ({})".format(e)) else: - stats['address'] = address - stats['mask'] = mask - stats['mask_cidr'] = self.ip_to_cidr(stats['mask']) - stats['gateway'] = default_gw[0] stats['public_address'] = self.public_address + # Finally the Censys information + if ( + self.public_address + and not self.public_ip_disabled + and (self.stats.get('address') != address + or time_since_update > self.public_address_refresh_interval) + ): + self.public_info = PublicIpInfo(self.public_address, + self.censys_url, + self.censys_username, + self.censys_password).get() + stats['public_info'] = self.public_info + elif self.input_method == 'snmp': # Not implemented yet pass @@ -151,6 +182,14 @@ class Plugin(GlancesPlugin): msg = ' Pub ' ret.append(self.curse_add_line(msg, 'TITLE')) ret.append(self.curse_add_line(msg_pub)) + if 'public_info' in self.stats: + for f in self.censys_fields: + field = f.split(':') + if len(field) == 1 and field[0] in self.stats['public_info']: + msg = '{}'.format(self.stats['public_info'][field[0]]) + elif len(field) == 2 and field[0] in self.stats['public_info'] and field[1] in self.stats['public_info'][field[0]]: + msg = '{}'.format(self.stats['public_info'][field[0]][field[1]]) + ret.append(self.curse_add_line(msg)) return ret @@ -211,3 +250,59 @@ class PublicIpAddress(object): queue_target.put(loads(response)[key]) except ValueError: queue_target.put(None) + + +class PublicIpInfo(object): + """Get public IP information from Censys online service.""" + + def __init__(self, ip, url, username, password, timeout=2): + """Init the class.""" + self.ip = ip + self.url = url + self.username = username + self.password = password + self.timeout = timeout + + def get(self): + """Return the public IP information returned by one of the online service.""" + q = queue.Queue() + + t = threading.Thread(target=self._get_ip_public_info, args=(q, + self.ip, + self.url, + self.username, + self.password)) + t.daemon = True + t.start() + + timer = Timer(self.timeout) + info = None + while not timer.finished() and info is None: + if q.qsize() > 0: + info = q.get() + + if info is None: + return None + + return info + + def _get_ip_public_info(self, queue_target, ip, url, username, password): + """Request the url service and put the result in the queue_target.""" + request_url = "{}/v2/hosts/{}".format(url, ip) + try: + # Python 3 code only + # https://stackoverflow.com/questions/24635064/how-to-use-urllib-with-username-password-authentication-in-python-3/24648149#24648149 + request = urllib.request.Request(request_url) + base64string = urllib.request.base64.b64encode(bytes('%s:%s' % (username, password), 'ascii')) + request.add_header("Authorization", "Basic %s" % base64string.decode('utf-8')) + result = urllib.request.urlopen(request) + response = result.read() + except Exception as e: + logger.debug("IP plugin - Cannot open URL {} ({})".format(request_url, e)) + queue_target.put(None) + else: + try: + queue_target.put(loads(response)['result']) + except (ValueError, KeyError) as e: + logger.debug("IP plugin - Cannot get result field from {} ({})".format(request_url, e)) + queue_target.put(None) |