diff --git a/tests/staging_test_acme_dns_tiny.py b/tests/staging_test_acme_dns_tiny.py index 7b245a150ff6e404de58fa896ccaa27996bb812c..cfc3f44a7fca03dee851fac4851eab73a7caf928 100644 --- a/tests/staging_test_acme_dns_tiny.py +++ b/tests/staging_test_acme_dns_tiny.py @@ -1,200 +1,216 @@ -import unittest, sys, os, subprocess, time, configparser +"""Tests for acme_dns_tiny script to be run with real ACME server""" +import unittest +import sys +import os +import subprocess +import configparser from io import StringIO import dns.version import acme_dns_tiny from tests.config_factory import generate_acme_dns_tiny_config from tools.acme_account_deactivate import account_deactivate -ACMEDirectory = os.getenv("GITLABCI_ACMEDIRECTORY_V2", "https://acme-staging-v02.api.letsencrypt.org/directory") +ACME_DIRECTORY = os.getenv("GITLABCI_ACMEDIRECTORY_V2", + "https://acme-staging-v02.api.letsencrypt.org/directory") + +def _openssl(command, options, communicate=None): + """Helper function to run openssl command""" + openssl = subprocess.Popen(["openssl", command] + options, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = openssl.communicate(communicate) + if openssl.returncode != 0: + raise IOError("OpenSSL Error: {0}".format(err)) + return out.decode("utf8") class TestACMEDNSTiny(unittest.TestCase): "Tests for acme_dns_tiny.get_crt()" @classmethod - def setUpClass(self): + def setUpClass(cls): print("Init acme_dns_tiny with python modules:") print(" - python: {0}".format(sys.version)) print(" - dns python: {0}".format(dns.version.version)) - self.configs = generate_acme_dns_tiny_config() + cls.configs = generate_acme_dns_tiny_config() sys.stdout.flush() - super(TestACMEDNSTiny, self).setUpClass() + super(TestACMEDNSTiny, cls).setUpClass() # To clean ACME staging server and close correctly temporary files + #pylint: disable=bare-except @classmethod - def tearDownClass(self): + def tearDownClass(cls): # close temp files correctly - for conffile in self.configs: + for conffile in cls.configs: parser = configparser.ConfigParser() parser.read(conffile) try: os.remove(parser["acmednstiny"]["AccountKeyFile"]) + except: + pass + try: os.remove(parser["acmednstiny"]["CSRFile"]) - # for each configuraiton, deactivate the account key - if conffile != "cnameCSR": - account_deactivate(parser["acmednstiny"]["AccountKeyFile"], ACMEDirectory) + except: + pass + try: + # for each configuration, deactivate the account key + if conffile != "cname_csr": + account_deactivate(parser["acmednstiny"]["AccountKeyFile"], ACME_DIRECTORY) + except: + pass + try: os.remove(conffile) except: pass - super(TestACMEDNSTiny, self).tearDownClass() - - # helper function to run openssl command - def openssl(self, command, options, communicate=None): - openssl = subprocess.Popen(["openssl", command] + options, - stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = openssl.communicate(communicate) - if openssl.returncode != 0: - raise IOError("OpenSSL Error: {0}".format(err)) - return out.decode("utf8") + super(TestACMEDNSTiny, cls).tearDownClass() # helper function to valid success by making assertion on returned certificate chain - def assertCertificateChain(self, certificateChain): + def _assert_certificate_chain(self, cert_chain): # Output have to contains two certiicates - certlist = certificateChain.split("-----BEGIN CERTIFICATE-----") + certlist = cert_chain.split("-----BEGIN CERTIFICATE-----") self.assertEqual(3, len(certlist)) self.assertEqual('', certlist[0]) self.assertIn("-----END CERTIFICATE-----{0}".format(os.linesep), certlist[1]) self.assertIn("-----END CERTIFICATE-----{0}".format(os.linesep), certlist[2]) # Use openssl to check validity of chain and simple test of readability - readablecertchain = self.openssl("x509", ["-text", "-noout"], certificateChain.encode("utf8")) + readablecertchain = _openssl("x509", ["-text", "-noout"], + cert_chain.encode("utf8")) self.assertIn("Issuer", readablecertchain) def test_success_cn(self): """ Successfully issue a certificate via common name """ old_stdout = sys.stdout sys.stdout = StringIO() - - acme_dns_tiny.main([self.configs['goodCName'], "--verbose"]) + + acme_dns_tiny.main([self.configs['good_cname'], "--verbose"]) certchain = sys.stdout.getvalue() - + sys.stdout.close() sys.stdout = old_stdout - - self.assertCertificateChain(certchain) + + self._assert_certificate_chain(certchain) def test_success_cn_without_contacts(self): """ Successfully issue a certificate via CN, but without Contacts field """ old_stdout = sys.stdout sys.stdout = StringIO() - acme_dns_tiny.main([self.configs['goodCNameWithoutContacts'], "--verbose"]) + acme_dns_tiny.main([self.configs['good_cname_without_contacts'], "--verbose"]) certchain = sys.stdout.getvalue() sys.stdout.close() sys.stdout = old_stdout - self.assertCertificateChain(certchain) + self._assert_certificate_chain(certchain) def test_success_cn_with_csr_option(self): """ Successfully issue a certificate using CSR option outside from the config file""" old_stdout = sys.stdout sys.stdout = StringIO() - acme_dns_tiny.main(["--csr", self.configs['cnameCSR'], self.configs['goodCNameWithoutCSR'], "--verbose"]) + acme_dns_tiny.main(["--csr", self.configs['cname_csr'], + self.configs['good_cname_without_csr'], "--verbose"]) certchain = sys.stdout.getvalue() sys.stdout.close() sys.stdout = old_stdout - self.assertCertificateChain(certchain) + self._assert_certificate_chain(certchain) def test_success_wild_cn(self): """ Successfully issue a certificate via a wildcard common name """ old_stdout = sys.stdout sys.stdout = StringIO() - acme_dns_tiny.main([self.configs['wildCName'], "--verbose"]) + acme_dns_tiny.main([self.configs['wild_cname'], "--verbose"]) certchain = sys.stdout.getvalue() sys.stdout.close() sys.stdout = old_stdout - self.assertCertificateChain(certchain) + self._assert_certificate_chain(certchain) def test_success_dnshost_ip(self): """ When DNS Host is an IP, DNS resolution have to fail without error """ old_stdout = sys.stdout sys.stdout = StringIO() - + with self.assertLogs(level='INFO') as adnslog: - acme_dns_tiny.main([self.configs['dnsHostIP'], "--verbose"]) - self.assertIn("INFO:acme_dns_tiny:A and/or AAAA DNS resources not found for configured dns host: we will use either resource found if one exists or directly the DNS Host configuration.", - adnslog.output) + acme_dns_tiny.main([self.configs['dns_host_ip'], + "--verbose"]) + self.assertIn("INFO:acme_dns_tiny:A and/or AAAA DNS resources not found for configured dns \ +host: we will use either resource found if one exists or directly the DNS Host configuration.", + adnslog.output) certchain = sys.stdout.getvalue() - + sys.stdout.close() sys.stdout = old_stdout - - self.assertCertificateChain(certchain) + + self._assert_certificate_chain(certchain) def test_success_san(self): """ Successfully issue a certificate via subject alt name """ old_stdout = sys.stdout sys.stdout = StringIO() - - acme_dns_tiny.main([self.configs['goodSAN'], "--verbose"]) + + acme_dns_tiny.main([self.configs['good_san'], "--verbose"]) certchain = sys.stdout.getvalue() - + sys.stdout.close() sys.stdout = old_stdout - - self.assertCertificateChain(certchain) + + self._assert_certificate_chain(certchain) def test_success_wildsan(self): """ Successfully issue a certificate via wildcard in subject alt name """ old_stdout = sys.stdout sys.stdout = StringIO() - acme_dns_tiny.main([self.configs['wildSAN']]) + acme_dns_tiny.main([self.configs['wild_san']]) certchain = sys.stdout.getvalue() sys.stdout.close() sys.stdout = old_stdout - self.assertCertificateChain(certchain) + self._assert_certificate_chain(certchain) def test_success_cli(self): """ Successfully issue a certificate via command line interface """ - certout, err = subprocess.Popen([ - "python3", "acme_dns_tiny.py", self.configs['goodCName'], "--verbose" + certout, _ = subprocess.Popen([ + "python3", "acme_dns_tiny.py", self.configs['good_cname'], "--verbose" ], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() - + certchain = certout.decode("utf8") - - self.assertCertificateChain(certchain) + + self._assert_certificate_chain(certchain) def test_success_cli_with_csr_option(self): """ Successfully issue a certificate via command line interface using CSR option""" - certout, err = subprocess.Popen([ - "python3", "acme_dns_tiny.py", "--csr", self.configs['cnameCSR'], self.configs['goodCNameWithoutCSR'], "--verbose" + certout, _ = subprocess.Popen([ + "python3", "acme_dns_tiny.py", "--csr", self.configs['cname_csr'], + self.configs['good_cname_without_csr'], "--verbose" ], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() certchain = certout.decode("utf8") - self.assertCertificateChain(certchain) + self._assert_certificate_chain(certchain) def test_weak_key(self): """ Let's Encrypt rejects weak keys """ self.assertRaisesRegex(ValueError, "key too small", - acme_dns_tiny.main, [self.configs['weakKey'], "--verbose"]) + acme_dns_tiny.main, [self.configs['weak_key'], "--verbose"]) def test_account_key_domain(self): """ Can't use the account key for the CSR """ self.assertRaisesRegex(ValueError, "certificate public key must be different than account key", - acme_dns_tiny.main, [self.configs['accountAsDomain'], "--verbose"]) + acme_dns_tiny.main, [self.configs['account_as_domain'], "--verbose"]) def test_failure_dns_update_tsigkeyname(self): """ Fail to update DNS records by invalid TSIG Key name """ self.assertRaisesRegex(ValueError, "Error updating DNS", - acme_dns_tiny.main, [self.configs['invalidTSIGName'], "--verbose"]) - - def test_failure_notcompleted_configuration(self): - """ Configuration file have to be completed """ - self.assertRaisesRegex(ValueError, - "Some required settings are missing\.", - acme_dns_tiny.main, [self.configs['missingDNS'], "--verbose"]) + acme_dns_tiny.main, [self.configs['invalid_tsig_name'], "--verbose"]) if __name__ == "__main__": unittest.main()