Module: check_mk
Branch: master
Commit: 00af5a219e7136f38b3b765cd5f6387e1cca58bb
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=00af5a219e7136…
Author: Sebastian Herbord <sh(a)mathias-kettner.de>
Date: Mon Feb 22 11:44:44 2016 +0100
#3081 mk_jolokia: plugin now supports setting custom CAs for verifying server certificate
as well as sending a client certificate
The CA can be set through the new configuration parameter "cert_path". This can
also be set to None,
in which case the server certificate is not verified.
If cert_path is not set, the behaviour is identical to the old behaviour which is
dependent on the
python version. Old python versions (not sure exactly, probably up to at least 2.5)
ignored server
certificates, later versions checked against system-wide trusted CAs.
To send a client certificate, set "client_cert" and "client_key". The
key can't be password
protected.
"mode" also supports a new option called "https". If this is set, the
client certificate parameters
need to be set and no further authentication should be expected by the server.
---
.werks/3081 | 19 +++++++
ChangeLog | 1 +
agents/plugins/mk_jolokia | 124 ++++++++++++++++++++++++++++++++++-----------
3 files changed, 115 insertions(+), 29 deletions(-)
diff --git a/.werks/3081 b/.werks/3081
new file mode 100644
index 0000000..6a7cd61
--- /dev/null
+++ b/.werks/3081
@@ -0,0 +1,19 @@
+Title: mk_jolokia: plugin now supports setting custom CAs for verifying server
certificate as well as sending a client certificate
+Level: 1
+Component: checks
+Compatible: compat
+Version: 1.2.9i1
+Date: 1456137267
+Class: feature
+
+The CA can be set through the new configuration parameter "cert_path". This can
also be set to None,
+in which case the server certificate is not verified.
+If cert_path is not set, the behaviour is identical to the old behaviour which is
dependent on the
+python version. Old python versions (not sure exactly, probably up to at least 2.5)
ignored server
+certificates, later versions checked against system-wide trusted CAs.
+
+To send a client certificate, set "client_cert" and "client_key". The
key can't be password
+protected.
+
+"mode" also supports a new option called "https". If this is set, the
client certificate parameters
+need to be set and no further authentication should be expected by the server.
diff --git a/ChangeLog b/ChangeLog
index f2aa51c..438bda7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -13,6 +13,7 @@
* 2995 esx_vsphere_licenses: now compatible with esx 6.0...
* 2996 websphere_mq_queues: now able to configure levels for used percentage of total
queues
* 3190 entity_sensors, entity_sensors.fan: new checks monitoring temperature and fan
sensors of devices such as Palo Alto Networks which support the ENTITY-SENSORS-MIB
+ * 3081 mk_jolokia: plugin now supports setting custom CAs for verifying server
certificate as well as sending a client certificate...
* 3073 FIX: windows agent: relative paths to mrpe scripts are now treated as relative
to the agent installation directory...
* 3061 FIX: mk_jolokia: Fixed debugging of the agent plugin
* 3074 FIX: windows agent: fixed incorrect values for 32-bit performance counters
diff --git a/agents/plugins/mk_jolokia b/agents/plugins/mk_jolokia
index d90a7f0..451f588 100755
--- a/agents/plugins/mk_jolokia
+++ b/agents/plugins/mk_jolokia
@@ -24,7 +24,16 @@
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
-import urllib2, sys, os, socket, pprint, base64
+import urllib2, sys, os, socket, pprint, base64, ssl
+from httplib import HTTPConnection, HTTPSConnection
+
+try:
+ from simplejson import json
+except ImportError:
+ try:
+ import json
+ except ImportError:
+ json = None
opt_verbose = '--verbose' in sys.argv
opt_debug = '--debug' in sys.argv
@@ -48,34 +57,72 @@ class PreemptiveBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
https_request = http_request
-def fetch_var(protocol, server, port, path, suburi, itemspec):
+class HTTPSValidatingConnection(HTTPSConnection):
+ def __init__(self, host, ca_file, key_file, cert_file):
+ HTTPSConnection.__init__(self, host, key_file=key_file, cert_file=cert_file)
+ self.__ca_file = ca_file
+ self.__key_file = key_file
+ self.__cert_file = cert_file
+
+ def connect(self):
+ HTTPConnection.connect(self)
+ if self.__ca_file:
+ self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file,
certfile=self.cert_file,
+ ca_certs=self.__ca_file,
cert_reqs=ssl.CERT_REQUIRED)
+ else:
+ self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file,
certfile=self.cert_file,
+ ca_certs=self.__ca_file,
cert_reqs=ssl.CERT_NONE)
+
+
+class HTTPSAuthHandler(urllib2.HTTPSHandler):
+ def __init__(self, ca_file, key, cert):
+ urllib2.HTTPSHandler.__init__(self)
+ self.__ca_file = ca_file
+ self.__key = key
+ self.__cert = cert
+
+ def https_open(self, req):
+ # do_open expects a class as the first parameter but getConnection will act
+ # as a facotry function
+ return self.do_open(self.getConnection, req)
+
+ def getConnection(self, host, timeout):
+ return HTTPSValidatingConnection(host, ca_file=self.__ca_file,
+ key_file=self.__key, cert_file=self.__cert)
+
+
+def fetch_var(protocol, server, port, path, suburi, cert_path, itemspec):
url = "%s://%s:%d/%s/%s" % (protocol, server, port, suburi, path)
if opt_verbose:
sys.stderr.write("DEBUG: Fetching: %s\n" % url)
try:
- json = urllib2.urlopen(url).read()
+ conn = urllib2.urlopen(url)
+ data = conn.read()
+
if opt_verbose:
- sys.stderr.write("DEBUG: Result: %s\n\n" % json)
+ sys.stderr.write("DEBUG: Result: %s\n\n" % data)
except Exception, e:
if opt_debug:
raise
sys.stderr.write("ERROR: %s\n" % e)
return []
-
try:
true = True
false = False
null = None
- obj = eval(json)
+ if json:
+ obj = json.loads(data)
+ else:
+ obj = eval(data)
except Exception, e:
sys.stderr.write('ERROR: Invalid json code (%s)\n' % e)
- sys.stderr.write(' Response %s\n' % json)
+ sys.stderr.write(' Response %s\n' % data)
return []
if obj.get('status', 200) != 200:
sys.stderr.write('ERROR: Invalid response when fetching url %s\n' % url)
- sys.stderr.write(' Response: %s\n' % json)
+ sys.stderr.write(' Response: %s\n' % data)
return []
# Only take the value of the object. If the value is an object
@@ -140,24 +187,35 @@ def extract_item(key, itemspec):
def query_instance(inst):
# Prepare user/password authentication via HTTP Auth
+ password_mngr = urllib2.HTTPPasswordMgrWithDefaultRealm()
if inst.get("password"):
- password_mngr = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_mngr.add_password(None, "%s://%s:%d/" %
(inst["protocol"], inst["server"],
inst["port"]), inst["user"], inst["password"])
- if inst["mode"] == 'digest':
- authhandler = urllib2.HTTPDigestAuthHandler(password_mngr)
- elif inst["mode"] == "basic_preemptive":
- authhandler = PreemptiveBasicAuthHandler(password_mngr)
- elif inst["mode"] == "basic" and inst["protocol"]
!= "https":
- authhandler = urllib2.HTTPBasicAuthHandler(password_mngr)
-
- opener = urllib2.build_opener(authhandler)
+ handlers = []
+ if inst["protocol"] == "https":
+ if inst["mode"] == 'https' and (inst["client_key"] is
None or
+ inst["client_cert"] is None):
+ sys.stdout.write('<<<jolokia_info>>>\n')
+ sys.stderr.write("ERROR: https set up as authentication method but
certificate "
+ "wasn't provided\n")
+ return
+ handlers.append(HTTPSAuthHandler(inst["cert_path"],
+ inst["client_key"],
inst["client_cert"]))
+ if inst["mode"] == 'digest':
+ handlers.append(urllib2.HTTPDigestAuthHandler(password_mngr))
+ elif inst["mode"] == "basic_preemptive":
+ handlers.append(PreemptiveBasicAuthHandler(password_mngr))
+ elif inst["mode"] == "basic" and inst["protocol"] !=
"https":
+ handlers.append(urllib2.HTTPBasicAuthHandler(password_mngr))
+
+ if handlers:
+ opener = urllib2.build_opener(*handlers)
urllib2.install_opener(opener)
# Determine type of server
- server_info = fetch_var(inst["protocol"], inst["server"],
inst["port"], "", inst["suburi"], "")
-
+ server_info = fetch_var(inst["protocol"], inst["server"],
inst["port"], "", inst["suburi"],
+ inst["cert_path"], "")
sys.stdout.write('<<<jolokia_info>>>\n')
if server_info:
d = dict(server_info)
@@ -177,7 +235,8 @@ def query_instance(inst):
# Fetch the general information first
for path, title, itemspec in global_vars + specific_vars.get(product, []):
try:
- values = fetch_var(inst["protocol"], inst["server"],
inst["port"], "read/" + path, inst["suburi"], itemspec)
+ values = fetch_var(inst["protocol"], inst["server"],
inst["port"], "read/" + path,
+ inst["suburi"], inst["cert_path"],
itemspec)
# In case of network errors skip this server
except IOError:
@@ -214,14 +273,17 @@ def query_instance(inst):
# Default configuration for all instances
-protocol = "http"
-server = "localhost"
-port = 8080
-user = "monitoring"
-password = None
-mode = "digest"
-suburi = "jolokia"
-instance = None
+protocol = "http"
+server = "localhost"
+port = 8080
+user = "monitoring"
+password = None
+mode = "digest"
+suburi = "jolokia"
+instance = None
+cert_path = "_default"
+client_cert = None
+client_key = None
global_vars = [
( "java.lang:type=Memory/NonHeapMemoryUsage/used",
"NonHeapMemoryUsage", [] ),
@@ -304,7 +366,11 @@ for inst in instances:
( "password", password ),
( "mode", mode ),
( "suburi", suburi ),
- ( "instance", instance )]:
+ ( "instance", instance ),
+ ( "cert_path", cert_path ),
+ ( "client_cert", client_cert ),
+ ( "client_key", client_key ),
+ ]:
if varname not in inst:
inst[varname] = value
if not inst["instance"]: