Commit 01eb11c6 authored by Adrien Dorsaz's avatar Adrien Dorsaz

tests: lint staging_test_acme_dns_tiny

parent 1808bd9d
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 from io import StringIO
import dns.version import dns.version
import acme_dns_tiny import acme_dns_tiny
from tests.config_factory import generate_acme_dns_tiny_config from tests.config_factory import generate_acme_dns_tiny_config
from tools.acme_account_deactivate import account_deactivate 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): class TestACMEDNSTiny(unittest.TestCase):
"Tests for acme_dns_tiny.get_crt()" "Tests for acme_dns_tiny.get_crt()"
@classmethod @classmethod
def setUpClass(self): def setUpClass(cls):
print("Init acme_dns_tiny with python modules:") print("Init acme_dns_tiny with python modules:")
print(" - python: {0}".format(sys.version)) print(" - python: {0}".format(sys.version))
print(" - dns python: {0}".format(dns.version.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() sys.stdout.flush()
super(TestACMEDNSTiny, self).setUpClass() super(TestACMEDNSTiny, cls).setUpClass()
# To clean ACME staging server and close correctly temporary files # To clean ACME staging server and close correctly temporary files
#pylint: disable=bare-except
@classmethod @classmethod
def tearDownClass(self): def tearDownClass(cls):
# close temp files correctly # close temp files correctly
for conffile in self.configs: for conffile in cls.configs:
parser = configparser.ConfigParser() parser = configparser.ConfigParser()
parser.read(conffile) parser.read(conffile)
try: try:
os.remove(parser["acmednstiny"]["AccountKeyFile"]) os.remove(parser["acmednstiny"]["AccountKeyFile"])
except:
pass
try:
os.remove(parser["acmednstiny"]["CSRFile"]) os.remove(parser["acmednstiny"]["CSRFile"])
# for each configuraiton, deactivate the account key except:
if conffile != "cnameCSR": pass
account_deactivate(parser["acmednstiny"]["AccountKeyFile"], ACMEDirectory) 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) os.remove(conffile)
except: except:
pass pass
super(TestACMEDNSTiny, self).tearDownClass() super(TestACMEDNSTiny, cls).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")
# helper function to valid success by making assertion on returned certificate chain # 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 # Output have to contains two certiicates
certlist = certificateChain.split("-----BEGIN CERTIFICATE-----") certlist = cert_chain.split("-----BEGIN CERTIFICATE-----")
self.assertEqual(3, len(certlist)) self.assertEqual(3, len(certlist))
self.assertEqual('', certlist[0]) self.assertEqual('', certlist[0])
self.assertIn("-----END CERTIFICATE-----{0}".format(os.linesep), certlist[1]) self.assertIn("-----END CERTIFICATE-----{0}".format(os.linesep), certlist[1])
self.assertIn("-----END CERTIFICATE-----{0}".format(os.linesep), certlist[2]) self.assertIn("-----END CERTIFICATE-----{0}".format(os.linesep), certlist[2])
# Use openssl to check validity of chain and simple test of readability # 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) self.assertIn("Issuer", readablecertchain)
def test_success_cn(self): def test_success_cn(self):
""" Successfully issue a certificate via common name """ """ Successfully issue a certificate via common name """
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = StringIO() sys.stdout = StringIO()
acme_dns_tiny.main([self.configs['goodCName'], "--verbose"]) acme_dns_tiny.main([self.configs['good_cname'], "--verbose"])
certchain = sys.stdout.getvalue() certchain = sys.stdout.getvalue()
sys.stdout.close() sys.stdout.close()
sys.stdout = old_stdout sys.stdout = old_stdout
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_cn_without_contacts(self): def test_success_cn_without_contacts(self):
""" Successfully issue a certificate via CN, but without Contacts field """ """ Successfully issue a certificate via CN, but without Contacts field """
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = StringIO() 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() certchain = sys.stdout.getvalue()
sys.stdout.close() sys.stdout.close()
sys.stdout = old_stdout sys.stdout = old_stdout
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_cn_with_csr_option(self): def test_success_cn_with_csr_option(self):
""" Successfully issue a certificate using CSR option outside from the config file""" """ Successfully issue a certificate using CSR option outside from the config file"""
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = StringIO() 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() certchain = sys.stdout.getvalue()
sys.stdout.close() sys.stdout.close()
sys.stdout = old_stdout sys.stdout = old_stdout
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_wild_cn(self): def test_success_wild_cn(self):
""" Successfully issue a certificate via a wildcard common name """ """ Successfully issue a certificate via a wildcard common name """
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = StringIO() sys.stdout = StringIO()
acme_dns_tiny.main([self.configs['wildCName'], "--verbose"]) acme_dns_tiny.main([self.configs['wild_cname'], "--verbose"])
certchain = sys.stdout.getvalue() certchain = sys.stdout.getvalue()
sys.stdout.close() sys.stdout.close()
sys.stdout = old_stdout sys.stdout = old_stdout
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_dnshost_ip(self): def test_success_dnshost_ip(self):
""" When DNS Host is an IP, DNS resolution have to fail without error """ """ When DNS Host is an IP, DNS resolution have to fail without error """
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = StringIO() sys.stdout = StringIO()
with self.assertLogs(level='INFO') as adnslog: with self.assertLogs(level='INFO') as adnslog:
acme_dns_tiny.main([self.configs['dnsHostIP'], "--verbose"]) acme_dns_tiny.main([self.configs['dns_host_ip'],
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.", "--verbose"])
adnslog.output) 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() certchain = sys.stdout.getvalue()
sys.stdout.close() sys.stdout.close()
sys.stdout = old_stdout sys.stdout = old_stdout
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_san(self): def test_success_san(self):
""" Successfully issue a certificate via subject alt name """ """ Successfully issue a certificate via subject alt name """
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = StringIO() sys.stdout = StringIO()
acme_dns_tiny.main([self.configs['goodSAN'], "--verbose"]) acme_dns_tiny.main([self.configs['good_san'], "--verbose"])
certchain = sys.stdout.getvalue() certchain = sys.stdout.getvalue()
sys.stdout.close() sys.stdout.close()
sys.stdout = old_stdout sys.stdout = old_stdout
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_wildsan(self): def test_success_wildsan(self):
""" Successfully issue a certificate via wildcard in subject alt name """ """ Successfully issue a certificate via wildcard in subject alt name """
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = StringIO() sys.stdout = StringIO()
acme_dns_tiny.main([self.configs['wildSAN']]) acme_dns_tiny.main([self.configs['wild_san']])
certchain = sys.stdout.getvalue() certchain = sys.stdout.getvalue()
sys.stdout.close() sys.stdout.close()
sys.stdout = old_stdout sys.stdout = old_stdout
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_cli(self): def test_success_cli(self):
""" Successfully issue a certificate via command line interface """ """ Successfully issue a certificate via command line interface """
certout, err = subprocess.Popen([ certout, _ = subprocess.Popen([
"python3", "acme_dns_tiny.py", self.configs['goodCName'], "--verbose" "python3", "acme_dns_tiny.py", self.configs['good_cname'], "--verbose"
], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() ], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
certchain = certout.decode("utf8") certchain = certout.decode("utf8")
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_success_cli_with_csr_option(self): def test_success_cli_with_csr_option(self):
""" Successfully issue a certificate via command line interface using CSR option""" """ Successfully issue a certificate via command line interface using CSR option"""
certout, err = subprocess.Popen([ certout, _ = subprocess.Popen([
"python3", "acme_dns_tiny.py", "--csr", self.configs['cnameCSR'], self.configs['goodCNameWithoutCSR'], "--verbose" "python3", "acme_dns_tiny.py", "--csr", self.configs['cname_csr'],
self.configs['good_cname_without_csr'], "--verbose"
], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() ], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
certchain = certout.decode("utf8") certchain = certout.decode("utf8")
self.assertCertificateChain(certchain) self._assert_certificate_chain(certchain)
def test_weak_key(self): def test_weak_key(self):
""" Let's Encrypt rejects weak keys """ """ Let's Encrypt rejects weak keys """
self.assertRaisesRegex(ValueError, self.assertRaisesRegex(ValueError,
"key too small", "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): def test_account_key_domain(self):
""" Can't use the account key for the CSR """ """ Can't use the account key for the CSR """
self.assertRaisesRegex(ValueError, self.assertRaisesRegex(ValueError,
"certificate public key must be different than account key", "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): def test_failure_dns_update_tsigkeyname(self):
""" Fail to update DNS records by invalid TSIG Key name """ """ Fail to update DNS records by invalid TSIG Key name """
self.assertRaisesRegex(ValueError, self.assertRaisesRegex(ValueError,
"Error updating DNS", "Error updating DNS",
acme_dns_tiny.main, [self.configs['invalidTSIGName'], "--verbose"]) acme_dns_tiny.main, [self.configs['invalid_tsig_name'], "--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"])
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment