Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
check_nrpe_ng: use requests package to query nrpe-ng server
Browse files Browse the repository at this point in the history
This has lots of advantages, and only one disadvantage as far as I can
tell: needing the additional dependency of the requests package. I
believe this is negligible as the requests package is pretty ubiquitous.
The advantages are:

- cleaner, more legible code, which is less likely to contain hidden
  bugs because we're not directly creating sockets, SSL contexts, and
  HTTP requests

- all of the client-side TODO items have been implemented, that is:
  * ssl_verify_server can now be disabled
  * the timeout setting is now honoured
  * timeouts return CRITICAL status by default, but can be changed back
    to UNKNOWN with the -u option as per check_nrpe
  • Loading branch information
bootc committed Aug 1, 2017
1 parent 78c1462 commit 347c7d2
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 56 deletions.
6 changes: 0 additions & 6 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,4 @@ For the server only:

* Networking timeout (`connection_timeout`)

For the client only:

* SSL server verification on/off (`ssl_verify_server`)
* Execution timeout (`--timeout`)
* Option to return UNKNOWN on timeout rather than CRITICAL (`-u`)

<!-- vim: set ft=markdown : -->
112 changes: 62 additions & 50 deletions nrpe_ng/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import argparse
import http.client
import logging
import os.path
import socket
import ssl
import requests
import sys
import urllib.parse
import warnings

from requests.packages.urllib3.exceptions import SubjectAltNameWarning

from .config import NrpeConfig, ConfigError
from .defaults import CLIENT_CONFIG, CLIENT_CONFIG_PATH
Expand Down Expand Up @@ -104,13 +104,19 @@ def reload_config(self):

self.cfg = cfg

def format_request(self):
def make_request(self):
req = {
'url': '/v1/check/{}'.format(self.cfg.command),
'url': "https://{host}:{port}/v1/check/{command}".format(
host=self.cfg.host,
port=self.cfg.port,
command=self.cfg.command),
'headers': {
'User-Agent': '{prog}/{ver}'.format(
prog=self.argparser.prog, ver=__version__),
}
'User-Agent': "{prog}/{ver}".format(
prog=self.argparser.prog,
ver=__version__),
},
'timeout': self.cfg.timeout,
'allow_redirects': False,
}

if self.cfg.args:
Expand All @@ -130,14 +136,25 @@ def format_request(self):
args[key] = kv[1]

req['method'] = 'POST'
req['body'] = urllib.parse.urlencode(args)
req['headers']['Content-Length'] = len(req['body'])
req['headers']['Content-Type'] = \
'application/x-www-form-urlencoded'
req['data'] = args
else:
req['method'] = 'GET'

return req
# Configure SSL server certificate verification
if self.cfg.ssl_verify_server:
if self.cfg.ssl_ca_file:
req['verify'] = self.cfg.ssl_ca_file
else:
req['verify'] = True
else:
# Do not verify server certificates
req['verify'] = False

# Configure SSL client certificates
if self.cfg.ssl_key_file and self.cfg.ssl_cert_file:
req['cert'] = (self.cfg.ssl_cert_file, self.cfg.ssl_key_file)

return requests.request(**req)

def run(self):
self.setup_logging()
Expand All @@ -156,52 +173,47 @@ def run(self):
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(self.cfg._get_kwargs())

# Set up the SSLContext
try:
ssl_context = ssl.create_default_context(
ssl.Purpose.SERVER_AUTH, cafile=self.cfg.ssl_ca_file)
except IOError as e:
log.error('cannot read ssl_ca_file: {}'.format(e.args[1]))
sys.exit(1)

# Load our own key and certificate into the client
if self.cfg.ssl_key_file and self.cfg.ssl_cert_file:
try:
ssl_context.load_cert_chain(
certfile=self.cfg.ssl_cert_file,
keyfile=self.cfg.ssl_key_file)
except IOError as e:
log.error('cannot read ssl_cert_file or ssl_key_file: {}'
.format(e.args[1]))
sys.exit(1)

conn = http.client.HTTPSConnection(
self.cfg.host, self.cfg.port, context=ssl_context)

req = self.format_request()

try:
conn.request(**req)
except socket.gaierror as e:
log.error('{host}: {err}'.format(
host=self.cfg.host, err=e.args[1]))
with warnings.catch_warnings():
# Ignore urllib3's (embedded into requests) subjectAltName
# warning unconditionally. It is highly likely that the
# certificates used with nrpe-ng lack a subjectAltName so just
# allow them to keep working silently; if/when requests or
# urllib3 makes this fail, people will realise quickly enough.
warnings.simplefilter('ignore', category=SubjectAltNameWarning)

r = self.make_request()

except requests.exceptions.Timeout:
log.error("{host}: Request timed out".format(
host=self.cfg.host))

if self.cfg.timeout_unknown:
sys.exit(NAGIOS_UNKNOWN)
else:
sys.exit(NAGIOS_CRITICAL)

except requests.exceptions.RequestException as e:
log.error("{host}: {err}".format(
host=self.cfg.host, err=e.args[0]))
sys.exit(NAGIOS_UNKNOWN)

response = conn.getresponse()
data = response.read()
conn.close()

if response.status != 200:
print(response.reason)
if r.status_code != 200:
print(r.reason)
sys.exit(NAGIOS_UNKNOWN)

result = int(response.getheader('X-NRPE-Result', NAGIOS_UNKNOWN))
sys.stdout.write(data.decode("utf-8")) # FIXME: use Content-Type?
try:
result = int(r.headers['X-NRPE-Result'])
except:
result = NAGIOS_UNKNOWN

sys.stdout.write(r.text)
sys.exit(result)


def main():
return Client().run()


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,7 @@
'nrpe-ng = nrpe_ng.server:main',
],
},
install_requires=[
'requests',
],
)

0 comments on commit 347c7d2

Please sign in to comment.