summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--conf/glances.conf11
-rw-r--r--docs/aoa/header.rst19
-rw-r--r--glances/compat.py4
-rw-r--r--glances/plugins/glances_ip.py111
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)