From e96bba0eb8f38a2328adff1827182f29cdd41356 Mon Sep 17 00:00:00 2001 From: Markus Opolka Date: Mon, 18 Mar 2024 09:32:31 +0100 Subject: [PATCH] Refactor for a leaner main function - Also added tests for TLS options --- check_http_json.py | 134 +++++++++++++++++++++++-------------------- test/test_main.py | 34 +++++++++++ test/tls/ca-root.pem | 21 +++++++ test/tls/cert.pem | 19 ++++++ test/tls/key.pem | 27 +++++++++ 5 files changed, 172 insertions(+), 63 deletions(-) create mode 100644 test/tls/ca-root.pem create mode 100644 test/tls/cert.pem create mode 100644 test/tls/key.pem diff --git a/check_http_json.py b/check_http_json.py index 3eb7c50..1313f5e 100755 --- a/check_http_json.py +++ b/check_http_json.py @@ -528,6 +528,75 @@ def debugPrint(debug_flag, message, pretty_flag=False): print(message) +def prepare_context(args): + """ + Prepare TLS Context + """ + nagios = NagiosHelper() + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.options |= ssl.OP_NO_SSLv2 + context.options |= ssl.OP_NO_SSLv3 + + if args.insecure: + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + else: + context.verify_mode = ssl.CERT_OPTIONAL + context.load_default_certs() + if args.cacert: + try: + context.load_verify_locations(args.cacert) + except ssl.SSLError: + nagios.append_unknown('Error loading SSL CA cert "%s"!' % args.cacert) + if args.cert: + try: + context.load_cert_chain(args.cert, keyfile=args.key) + except ssl.SSLError: + if args.key: + nagios.append_unknown('Error loading SSL cert. Make sure key "%s" belongs to cert "%s"!' % (args.key, args.cert)) + else: + nagios.append_unknown('Error loading SSL cert. Make sure "%s" contains the key as well!' % (args.cert)) + + if nagios.getCode() != OK_CODE: + print(nagios.getMessage()) + sys.exit(nagios.getCode()) + + return context + + +def make_request(args, url, context): + """ + Performs the actual request to the given URL + """ + req = urllib.request.Request(url, method=args.method) + req.add_header("User-Agent", "check_http_json") + if args.auth: + authbytes = str(args.auth).encode() + base64str = base64.encodebytes(authbytes).decode().replace('\n', '') + req.add_header('Authorization', 'Basic %s' % base64str) + if args.headers: + headers = json.loads(args.headers) + debugPrint(args.debug, "Headers:\n %s" % headers) + for header in headers: + req.add_header(header, headers[header]) + if args.timeout and args.data: + databytes = str(args.data).encode() + response = urllib.request.urlopen(req, timeout=args.timeout, + data=databytes, context=context) + elif args.timeout: + response = urllib.request.urlopen(req, timeout=args.timeout, + context=context) + elif args.data: + databytes = str(args.data).encode() + response = urllib.request.urlopen(req, data=databytes, context=context) + else: + # pylint: disable=consider-using-with + response = urllib.request.urlopen(req, context=context) + + return response.read() + + def main(cliargs): """ Main entrypoint for CLI @@ -543,42 +612,7 @@ def main(cliargs): if args.ssl: url = "https://%s" % args.host - - context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - context.options |= ssl.OP_NO_SSLv2 - context.options |= ssl.OP_NO_SSLv3 - - if args.insecure: - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - else: - context.verify_mode = ssl.CERT_OPTIONAL - context.load_default_certs() - if args.cacert: - try: - context.load_verify_locations(args.cacert) - except ssl.SSLError: - nagios.append_unknown( - 'Error loading SSL CA cert "%s"!' - % args.cacert) - - if args.cert: - try: - context.load_cert_chain(args.cert, keyfile=args.key) - except ssl.SSLError: - if args.key: - nagios.append_unknown( - 'Error loading SSL cert. Make sure key "%s" belongs to cert "%s"!' - % (args.key, args.cert)) - else: - nagios.append_unknown( - 'Error loading SSL cert. Make sure "%s" contains the key as well!' - % (args.cert)) - - if nagios.getCode() != OK_CODE: - print(nagios.getMessage()) - sys.exit(nagios.getCode()) - + context = prepare_context(args) else: url = "http://%s" % args.host if args.port: @@ -590,33 +624,7 @@ def main(cliargs): json_data = '' try: - req = urllib.request.Request(url, method=args.method) - req.add_header("User-Agent", "check_http_json") - if args.auth: - authbytes = str(args.auth).encode() - base64str = base64.encodebytes(authbytes).decode().replace('\n', '') - req.add_header('Authorization', 'Basic %s' % base64str) - if args.headers: - headers = json.loads(args.headers) - debugPrint(args.debug, "Headers:\n %s" % headers) - for header in headers: - req.add_header(header, headers[header]) - if args.timeout and args.data: - databytes = str(args.data).encode() - response = urllib.request.urlopen(req, timeout=args.timeout, - data=databytes, context=context) - elif args.timeout: - response = urllib.request.urlopen(req, timeout=args.timeout, - context=context) - elif args.data: - databytes = str(args.data).encode() - response = urllib.request.urlopen(req, data=databytes, context=context) - else: - # pylint: disable=consider-using-with - response = urllib.request.urlopen(req, context=context) - - json_data = response.read() - + json_data = make_request(args, url, context) except HTTPError as e: # Try to recover from HTTP Error, if there is JSON in the response if "json" in e.info().get_content_subtype(): diff --git a/test/test_main.py b/test/test_main.py index a531af7..0949f2f 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -95,3 +95,37 @@ class MainTest(unittest.TestCase): main(args) self.assertEqual(test.exception.code, 0) + + @mock.patch('builtins.print') + def test_main_with_tls(self, mock_print): + args = ['-H', 'localhost', + '--ssl', + '--cacert', + 'test/tls/ca-root.pem', + '--cert', + 'test/tls/cert.pem', + '--key', + 'test/tls/key.pem'] + + with self.assertRaises(SystemExit) as test: + main(args) + + self.assertTrue('https://localhost' in str(mock_print.call_args)) + self.assertEqual(test.exception.code, 3) + + @mock.patch('builtins.print') + def test_main_with_tls_wrong_ca(self, mock_print): + args = ['-H', 'localhost', + '--ssl', + '--cacert', + 'test/tls/key.pem', + '--cert', + 'test/tls/cert.pem', + '--key', + 'test/tls/key.pem'] + + with self.assertRaises(SystemExit) as test: + main(args) + + self.assertTrue('Error loading SSL CA' in str(mock_print.call_args)) + self.assertEqual(test.exception.code, 3) diff --git a/test/tls/ca-root.pem b/test/tls/ca-root.pem new file mode 100644 index 0000000..c09e8ec --- /dev/null +++ b/test/tls/ca-root.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIUB6EZDl3ajJgJsoLzyC9DrOQQpKowDQYJKoZIhvcNAQEN +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yNDAzMTgwODE5MDhaGA8yMDUx +MDgwMzA4MTkwOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALVxioj+6zw6Snr+B1JOivC8Of6YptVYym5ICiHX +wjpbSVVe+Py/P2LDb/uQ1QkAENlpvChFqSaRBZU5keXYS/DaFb2Evb2/zf5qIdWU +2ju8B5V13gXSeaNNetyEn1Ivvk0lOCQo2RwEZXuStpLS4Q32rkRBvkoL+RXDc1NX +c3RwcU1p9ybgBqAC7FYdV82sgHGugIrbzkjfFREJXp1AnqvKAdk39b1CnPxfmPZC +nzPPetfr3iivH8yVO5rodU/LDtQNph22JR94YvPB89QO+bZ9bw2GHtPdAKFew9HF +UxM1fmy381Mq2iS3KUq5vsC1jMe8slUAIFYEDzoPvOz+MpcCAwEAAaNTMFEwHQYD +VR0OBBYEFOmCb+JnMzX29hwgtXSzrN+m6mTDMB8GA1UdIwQYMBaAFOmCb+JnMzX2 +9hwgtXSzrN+m6mTDMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQENBQADggEB +AAkTD8K4UO4uO4i6p2BCofbhVm9LYA0ulmLO8Uro0U491TeEDOQpgMFAK+b2gZIU +zvDHoCMn3UPVxHKl7XzDgLZVkYYEc2s9vArxk5vSnFmh3XvlDu2SO5gSLB2sf68A +2+Jz2x6z9tjWWdZCGJWU/iwMbG2Y3JMHyv1NMF8cyOclJaSDNBAwF5c5sdlGTLKb +WHGXzVqHSAFlGcHtQrcEKclHiuzw2G3LZzwghGk0XzxwvyKrnAEy408RY0mfNLtz +32KHqYtrip0RYlGWKP7/7q6i0D8muEFW/I4emFI0z0I/1CcYZZS8tQkWaPf/wCN0 +llTD1kKJACsIMaqkkyy+EZM= +-----END CERTIFICATE----- diff --git a/test/tls/cert.pem b/test/tls/cert.pem new file mode 100644 index 0000000..3ae00a0 --- /dev/null +++ b/test/tls/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDzCCAfcCFBOrBcHIH2x9xcUyUeDid0cvBxWtMA0GCSqGSIb3DQEBDQUAMEUx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjQwMzE4MDgxOTM1WhgPMjA1MTA4MDMw +ODE5MzVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEeMA0GCSqGSIb3DQEBAQUA +A4IBCwAwggEGAoH+ALuzyIhEATF5YyAOsXKfr2mttF2HyJvEscGcoA7YetT57bjJ +5lg944kc3QH/wTEdrGda3cwh3OXdUuyR7Wrm9jPw38hMArx/fWPkiISOShrUSHGd +Qyy2bT+zxBaUo+pomyrlqlgwGlbxuwTAlTSFcI+i7yXrckl2HRj40EW4FNsYpPzv +maxRXs0kg0J2JLTYF+fHlqlYbSX/hRU9wz2DYfkRSS0+OYJNSmqK0jayUsdZYurG +gbPwOCgQ0QxLLh7P8z4sOanRowqUzqTI77cyUugEJRyoi+LJr4r0EwMTBX3STgPh +S9B78+LNvwOrLrZFUhr144RfO9QPLnz0uWcCAwEAATANBgkqhkiG9w0BAQ0FAAOC +AQEAeIR21PfLzgpL7WDBE2KgwI78nVc1wY9nwoAxSBzHjS0Olve3r9MaVzAKn5ZS +xHtv8oroXjhTcczCIzxii6Imp6u0iIr3QVBIceofxrH3aWmICURcC9l+dIiY6sk9 +Ct8P8gm/Erv2iF/7bnsARwDnw0f41fC9eXtHZ7WLRQrc7tLHpjL0Z7bT77ysQJVK +C1SWtBnq3afmwH3R1wVHENn0JVFQpBp+vqWU5KIlvjcz49yPU+aNODk1rJsHMlgS +x2iddwF31GNOxNfXtw8fdw4UDUl2wYoZ45w2e2pXt4pbN43m0Wys1eQZdk3tyR6G +AZOLP05073mLtbVlFRmcTdXIGg== +-----END CERTIFICATE----- diff --git a/test/tls/key.pem b/test/tls/key.pem new file mode 100644 index 0000000..b2b18bb --- /dev/null +++ b/test/tls/key.pem @@ -0,0 +1,27 @@ +-----BEGIN PRIVATE KEY----- +MIIEqAIBADANBgkqhkiG9w0BAQEFAASCBJIwggSOAgEAAoH+ALuzyIhEATF5YyAO +sXKfr2mttF2HyJvEscGcoA7YetT57bjJ5lg944kc3QH/wTEdrGda3cwh3OXdUuyR +7Wrm9jPw38hMArx/fWPkiISOShrUSHGdQyy2bT+zxBaUo+pomyrlqlgwGlbxuwTA +lTSFcI+i7yXrckl2HRj40EW4FNsYpPzvmaxRXs0kg0J2JLTYF+fHlqlYbSX/hRU9 +wz2DYfkRSS0+OYJNSmqK0jayUsdZYurGgbPwOCgQ0QxLLh7P8z4sOanRowqUzqTI +77cyUugEJRyoi+LJr4r0EwMTBX3STgPhS9B78+LNvwOrLrZFUhr144RfO9QPLnz0 +uWcCAwEAAQKB/UQAYzMy5/fDkWzoxdLQFV3E56ZG7h+4x+lr0/Ts6rtD/KLIyqHH +ciqXgV4bCSPBK1eabOZqkjvYzhUU3R2wpRu2NWy8VPVzfrr07ZyQbDqCE+jNX6vQ +P44nk2/W0/e1hBmrcOZYLwK2utmC58tKWLhBAEENpq8EkpAcfF/1y9aRHKYwNnH7 +vouoQibN5NTs5m8s0VyjRTDwRZja98eWnn5NfU3orqYO8fSlF6CyzDtoyhMco6zR +0skBgMzRYCRTuJpV+KekC7XFYyiJ6XZN5DKLbbqP6Y7YR8wjyFEruoGCS0mZH2H0 +9/rhTsJram1B2zohXHPsHJGGGv12/7kCfw5C7yda+8Yv0NmRp1F+EJYb75SCAWIP +kzN/xvjP2bMKa6oSzU0DOga3Wc4ijJHDaND8rqdPqQe3zXFr1nPdBrybLSJ6k5CN +4Dd6ENJWVWino0L460kpLtlBG6TsgmB8bkwhjWVE6Vgt4Vila+a3TGRXeniaRzdw +icNOtMrjYlUCfw0pWEvO2uFq0DbNZbmzC2j5ClFcU96CAl4AqKG2PiGnuSy9TKVZ +c5OiXFmyoig7v4LJzaKLSqVIN4hVBU80/MlhvG+dpeimvLaQKNtlZQethIs5hXlB +R1XfaPhq6BQiYmQ3tufyS/0Es2OY+Cs3LU1uDB8qVzonlmnIi69OwMsCfwRPISfJ +C+4UIIy8v8uVxbk1c6xxo61Xe2jCIQKo+uRoL6PRzoqIgQ3qdI4eTk70tkT/NF6F +aVNVrBOrO78Cd7ihQn/6fX/d/nOExHRpdaELlf70a1NNyEQIsiug8rvonQMP2ENT +ERZ9tmssgG/Tzpc6/1xVcVNFA7spmuL61YkCfwnu2zGTc0PO7kd96rkktIbL9YqD +6NQ0QH8bdildtjSGNc3bLB5ajUytq48Sryk4NogJr8Vt5K8q+qZMrE4kCmgd+C4w +x4b3V9Ncp0k1k/MgdLjyd5aUurbHfpyFapPPg3xpRAR3q/vP8WdIintrECiw1jsr +JFvChtVdQnbTM9MCfw41RcjNwCaIG+uXc8bD6Yf+NyXD8zP6ZDywmBlkMWlGSzx4 +xM8J+wQiQsNWthDBbF7inJc+lbtJiEe4YOPkbjCYVZRHribL65HKJlEUv6M9bvQo +3P1DS5tDrwo6z9UPs4tD1SgF9fDu/xA7fwPF1RTvuW07MhFJWlDo4FSWS9c= +-----END PRIVATE KEY-----