Commit 5ff96857 authored by Adrien Dorsaz's avatar Adrien Dorsaz

Merge branch 'v2' into 'master'

V2 : Support of Let's Encrypt API v2

See merge request !12
parents 4e83caf6 d952255e
Pipeline #206 failed with stage
in 2 minutes and 59 seconds
This diff is collapsed.
[acmednstiny]
# Required readable ACME account key
AccountKeyFile = account.key
# Required readable CSR file
# Note: if you use the "--csr" optional argument, this setting is not read and can be omitted
CSRFile = domain.csr
# Optional ACME directory url (default: https://acme-staging.api.letsencrypt.org/directory)
ACMEDirectory = https://acme-staging.api.letsencrypt.org/directory
# Optional time in seconds to wait between DNS update and challenge check (default: 3)
CheckChallengeDelay = 3
# Optional Contact info to send to the ACME provider
MailContact = mail@example.com
# Note that Let's Encrypt servers disallow use of phone numbers
PhoneContact = +11111111111
# Optional ACME directory url
# Default: https://acme-staging-v02.api.letsencrypt.org/directory
ACMEDirectory = https://acme-staging-v02.api.letsencrypt.org/directory
# Optional To be able to be reached by ACME provider (e.g. to warn about
# certificate expicration), you can provide some contact informations.
# Contacts setting is a list of contact URI separated by semicolon (;).
# If ACME provider support contact informations, it must at least support mailto
# URI and can support more of contact.
# For the mailto URI, the email address part must contains only one address
# without header fields (see [RFC6068]).
# Default: none
Contacts = mailto:mail@example.com;mailto:mail2@example.org
# Optional to give hint to the ACME server about your prefered language for errors given by their server
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language for more informations
# Default: en
Language = en
[TSIGKeyring]
# Required TSIG key name
KeyName = host-example
# Required TSIG key value in base64
KeyValue = XXXXXXXXXXX==
# Required TSIG algorithm
Algorithm = hmac-sha256
[DNS]
# Required name of zone to update
Zone = dnszone
# Required name or IP of DNS server
Host = dnsserver
# Optional port to connect on DNS server (default: 53)
Port = 53
# Optional time to live (TTL) value used to add DNS entries
# For each domain registered in the CSR, at least 1 TTL is waited before certificate creation.
# If an error occurs while looking for TXT records, we wait up to 10 TTLs by domain.
# That's why the default is only of 10 seconds, to avoid having too long time to wait to receive a new certificate.
# Default: 10 seconds
TTL = 10
FROM debian:jessie-backports
RUN apt-get update
RUN apt-get upgrade -y
# Minimal tools required by acme-dns-tiny CI
RUN apt-get install -y \
python3-coverage \
python3-pip
RUN apt-get install -y \
-t jessie-backports \
python3-configargparse \
python3-dnspython
# Allows run python3-coverage with same command than manual install by pip
RUN update-alternatives --install \
/usr/bin/coverage \
coverage \
/usr/bin/python3-coverage \
1
FROM debian:jessie
RUN apt-get update
RUN apt-get upgrade -y
# Minimal tools required by acme-dns-tiny CI
RUN apt-get install -y \
python3-dnspython \
python3-coverage \
python3-pip
# Allows run python3-coverage with same command than manual install by pip
RUN update-alternatives --install \
/usr/bin/python3-coverage \
coverage \
/usr/bin/python3.4-coverage \
1
RUN ln -s /etc/alternatives/coverage /usr/bin/coverage
FROM debian:stretch
RUN apt-get update
RUN apt-get upgrade -y
# Minimal tools required by acme-dns-tiny CI
RUN apt-get install -y \
python3-dnspython \
python3-coverage \
python3-configargparse \
python3-pip
# Allows run python3-coverage with same command than manual install by pip
RUN update-alternatives --install \
/usr/bin/coverage \
coverage \
/usr/bin/python3-coverage \
1
after_script:
- sleep 10
jessie:
image: adt-jessie_dnspython3_1.11
before_script:
- pip3 install --upgrade -r tests/requirements.txt
script:
- coverage run --source ./ -m unittest -v tests.test_acme_dns_tiny tests.test_acme_account_rollover tests.test_acme_account_delete
- coverage report --include=acme_dns_tiny.py,tools/acme_account_rollover.py,tools/acme_account_delete.py
- coverage run --source ./ -m unittest -v tests.test_acme_dns_tiny tests.test_acme_account_rollover tests.test_acme_account_deactivate
- coverage report --include=acme_dns_tiny.py,tools/acme_account_rollover.py,tools/acme_account_deactivate.py
- coverage html
jessie_backport:
......@@ -15,6 +12,18 @@ jessie_backport:
before_script:
- pip3 install --upgrade -r tests/requirements.txt
script:
- coverage run --source ./ -m unittest -v tests.test_acme_dns_tiny tests.test_acme_account_rollover tests.test_acme_account_delete
- coverage report --include=acme_dns_tiny.py,tools/acme_account_rollover.py,tools/acme_account_delete.py
- coverage run --source ./ -m unittest -v tests.test_acme_dns_tiny tests.test_acme_account_rollover tests.test_acme_account_deactivate
- coverage report --include=acme_dns_tiny.py,tools/acme_account_rollover.py,tools/acme_account_deactivate.py
- coverage html
stretch:
image: adt-stretch_dnspython3_1.15
before_script:
- pip3 install --upgrade -r tests/requirements.txt
script:
- coverage run --source ./ -m unittest -v tests.test_acme_dns_tiny tests.test_acme_account_rollover tests.test_acme_account_deactivate
- coverage report --include=acme_dns_tiny.py,tools/acme_account_rollover.py,tools/acme_account_deactivate.py
- coverage html
artifacts:
paths:
- htmlcov
......@@ -21,7 +21,7 @@ explains how to setup and test acme-tiny yourself.
## List of environment variables
* `GITLABCI_CAURL`: URL of a staging ACME server
* `GITLABCI_ACMEDIRECTORY_V2`: URL of a staging V2 ACME server
* `GITLABCI_CHALLENGEDELAY`: time to wait between dns update and self-check (set it to `0` to cover a bit more code)
* `GITLABCI_DNSHOST`: domain name to reach of your DNS server (e.g. `adorsaz.ch`)
* `GITLABCI_DNSHOSTIP`: IP address to reach of your DNS server
......
......@@ -4,97 +4,136 @@ from subprocess import Popen
# domain with server.py running on it for testing
DOMAIN = os.getenv("GITLABCI_DOMAIN")
ACMEDIRECTORY = os.getenv("GITLABCI_ACMEDIRECTORY", "https://acme-staging.api.letsencrypt.org/directory")
CHALLENGEDELAY = os.getenv("GITLABCI_CHALLENGEDELAY", "3")
ACMEDIRECTORY = os.getenv("GITLABCI_ACMEDIRECTORY_V2", "https://acme-staging-v02.api.letsencrypt.org/directory")
DNSHOST = os.getenv("GITLABCI_DNSHOST")
DNSHOSTIP = os.getenv("GITLABCI_DNSHOSTIP")
DNSZONE = os.getenv("GITLABCI_DNSZONE")
DNSPORT = os.getenv("GITLABCI_DNSPORT", "53")
DNSTTL = os.getenv("GITLABCI_DNSTTL", "10")
TSIGKEYNAME = os.getenv("GITLABCI_TSIGKEYNAME")
TSIGKEYVALUE = os.getenv("GITLABCI_TSIGKEYVALUE")
TSIGALGORITHM = os.getenv("GITLABCI_TSIGALGORITHM")
# generate simple config
def generate_config():
# Account key
account_key = NamedTemporaryFile(delete=False)
Popen(["openssl", "genrsa", "-out", account_key.name, "2048"]).wait()
# Domain key and CSR
domain_key = NamedTemporaryFile(delete=False)
domain_csr = NamedTemporaryFile(delete=False)
Popen(["openssl", "req", "-newkey", "rsa:2048", "-nodes", "-keyout", domain_key.name,
"-subj", "/CN={0}".format(DOMAIN), "-out", domain_csr.name]).wait()
# acme-dns-tiny configuration
parser = configparser.ConfigParser()
parser.read("./example.ini")
parser["acmednstiny"]["AccountKeyFile"] = account_key.name
parser["acmednstiny"]["CSRFile"] = domain_csr.name
parser["acmednstiny"]["ACMEDirectory"] = ACMEDIRECTORY
parser["acmednstiny"]["Contacts"] = "mailto:mail@example.com"
parser["TSIGKeyring"]["KeyName"] = TSIGKEYNAME
parser["TSIGKeyring"]["KeyValue"] = TSIGKEYVALUE
parser["TSIGKeyring"]["Algorithm"] = TSIGALGORITHM
parser["DNS"]["Host"] = DNSHOST
parser["DNS"]["Port"] = DNSPORT
parser["DNS"]["Zone"] = DNSZONE
parser["DNS"]["TTL"] = DNSTTL
config = NamedTemporaryFile(delete=False)
with open(config.name, 'w') as configfile:
parser.write(configfile)
return account_key.name, domain_key.name, domain_csr.name, config.name
# generate account and domain keys
def generate_acme_dns_tiny_config():
# good account key
account_key = NamedTemporaryFile()
Popen(["openssl", "genrsa", "-out", account_key.name, "2048"]).wait()
# Simple good configuration
account_key, domain_key, domain_csr, goodCName = generate_config();
# CSR for good configuration with wildcard domain
wilddomain_csr = NamedTemporaryFile(delete=False)
Popen(["openssl", "req", "-newkey", "rsa:2048", "-nodes", "-keyout", domain_key,
"-subj", "/CN=*.{0}".format(DOMAIN), "-out", wilddomain_csr.name]).wait()
# weak 1024 bit account key
weak_key = NamedTemporaryFile()
weak_key = NamedTemporaryFile(delete=False)
Popen(["openssl", "genrsa", "-out", weak_key.name, "1024"]).wait()
# good domain key
domain_key = NamedTemporaryFile()
domain_csr = NamedTemporaryFile()
Popen(["openssl", "req", "-newkey", "rsa:2048", "-nodes", "-keyout", domain_key.name,
"-subj", "/CN={0}".format(DOMAIN), "-out", domain_csr.name]).wait()
# subject alt-name domain
san_csr = NamedTemporaryFile()
san_conf = NamedTemporaryFile()
# CSR using subject alt-name domain instead of CN (common name)
san_csr = NamedTemporaryFile(delete=False)
san_conf = NamedTemporaryFile(delete=False)
san_conf.write(open("/etc/ssl/openssl.cnf").read().encode("utf8"))
san_conf.write("\n[SAN]\nsubjectAltName=DNS:{0},DNS:www.{0}\n".format(DOMAIN).encode("utf8"))
san_conf.seek(0)
Popen(["openssl", "req", "-new", "-sha256", "-key", domain_key.name,
Popen(["openssl", "req", "-new", "-sha256", "-key", domain_key,
"-subj", "/", "-reqexts", "SAN", "-config", san_conf.name,
"-out", san_csr.name]).wait()
# account-signed domain csr
account_csr = NamedTemporaryFile()
Popen(["openssl", "req", "-new", "-sha256", "-key", account_key.name,
# CSR using wildcard in subject alt-name domain
wildsan_csr = NamedTemporaryFile(delete=False)
wildsan_conf = NamedTemporaryFile(delete=False)
wildsan_conf.write(open("/etc/ssl/openssl.cnf").read().encode("utf8"))
wildsan_conf.write("\n[SAN]\nsubjectAltName=DNS:{0},DNS:*.{0}\n".format(DOMAIN).encode("utf8"))
wildsan_conf.seek(0)
Popen(["openssl", "req", "-new", "-sha256", "-key", domain_key,
"-subj", "/", "-reqexts", "SAN", "-config", wildsan_conf.name,
"-out", wildsan_csr.name]).wait()
# CSR signed with the account key
account_csr = NamedTemporaryFile(delete=False)
Popen(["openssl", "req", "-new", "-sha256", "-key", account_key,
"-subj", "/CN={0}".format(DOMAIN), "-out", account_csr.name]).wait()
# Default test configuration
# Create config parser from the good default config to generate custom configs
config = configparser.ConfigParser()
config.read("./example.ini".format(DOMAIN))
config["acmednstiny"]["ACMEDirectory"] = ACMEDIRECTORY
config["acmednstiny"]["CheckChallengeDelay"] = CHALLENGEDELAY
config["acmednstiny"]["MailContact"] = "mail@example.com"
del config["acmednstiny"]["PhoneContact"]
config["TSIGKeyring"]["KeyName"] = TSIGKEYNAME
config["TSIGKeyring"]["KeyValue"] = TSIGKEYVALUE
config["TSIGKeyring"]["Algorithm"] = TSIGALGORITHM
config["DNS"]["Host"] = DNSHOST
config["DNS"]["Port"] = DNSPORT
config["DNS"]["Zone"] = DNSZONE
config.read(goodCName)
goodCNameWithoutCSR = NamedTemporaryFile(delete=False)
config.remove_option("acmednstiny", "CSRFile")
with open(goodCNameWithoutCSR.name, 'w') as configfile:
config.write(configfile)
goodCName = NamedTemporaryFile()
config["acmednstiny"]["AccountKeyFile"] = account_key.name
config["acmednstiny"]["CSRFile"] = domain_csr.name
with open(goodCName.name, 'w') as configfile:
wildCName = NamedTemporaryFile(delete=False)
config["acmednstiny"]["CSRFile"] = wilddomain_csr.name
with open(wildCName.name, 'w') as configfile:
config.write(configfile)
dnsHostIP = NamedTemporaryFile()
dnsHostIP = NamedTemporaryFile(delete=False)
config["DNS"]["Host"] = DNSHOSTIP
with open(dnsHostIP.name, 'w') as configfile:
config.write(configfile)
config["DNS"]["Host"] = DNSHOST
goodSAN = NamedTemporaryFile()
config["acmednstiny"]["AccountKeyFile"] = account_key.name
goodSAN = NamedTemporaryFile(delete=False)
config["acmednstiny"]["CSRFile"] = san_csr.name
with open(goodSAN.name, 'w') as configfile:
config.write(configfile)
weakKey = NamedTemporaryFile()
wildSAN = NamedTemporaryFile(delete=False)
config["acmednstiny"]["CSRFile"] = wildsan_csr.name
with open(wildSAN.name, 'w') as configfile:
config.write(configfile)
weakKey = NamedTemporaryFile(delete=False)
config["acmednstiny"]["AccountKeyFile"] = weak_key.name
config["acmednstiny"]["CSRFile"] = domain_csr.name
config["acmednstiny"]["CSRFile"] = domain_csr
with open(weakKey.name, 'w') as configfile:
config.write(configfile)
accountAsDomain = NamedTemporaryFile()
config["acmednstiny"]["AccountKeyFile"] = account_key.name
accountAsDomain = NamedTemporaryFile(delete=False)
config["acmednstiny"]["AccountKeyFile"] = account_key
config["acmednstiny"]["CSRFile"] = account_csr.name
with open(accountAsDomain.name, 'w') as configfile:
config.write(configfile)
invalidTSIGName = NamedTemporaryFile()
invalidTSIGName = NamedTemporaryFile(delete=False)
config["TSIGKeyring"]["KeyName"] = "{0}.invalid".format(TSIGKEYNAME)
with open(invalidTSIGName.name, 'w') as configfile:
config.write(configfile)
missingDNS = NamedTemporaryFile()
missingDNS = NamedTemporaryFile(delete=False)
config["DNS"] = {}
with open(missingDNS.name, 'w') as configfile:
config.write(configfile)
......@@ -102,39 +141,43 @@ def generate_acme_dns_tiny_config():
return {
# configs
"goodCName": goodCName,
"dnsHostIP": dnsHostIP,
"goodSAN": goodSAN,
"weakKey": weakKey,
"accountAsDomain": accountAsDomain,
"invalidTSIGName": invalidTSIGName,
"missingDNS": missingDNS,
# keys (returned to keep files on system)
"goodCNameWithoutCSR": goodCNameWithoutCSR.name,
"wildCName": wildCName.name,
"dnsHostIP": dnsHostIP.name,
"goodSAN": goodSAN.name,
"wildSAN": wildSAN.name,
"weakKey": weakKey.name,
"accountAsDomain": accountAsDomain.name,
"invalidTSIGName": invalidTSIGName.name,
"missingDNS": missingDNS.name,
# key (just to simply remove the account from staging server)
"accountkey": account_key,
"weakkey": weak_key,
"domainkey": domain_key,
# csr (returned to keep files on system)
"domaincsr": domain_csr,
"sancsr": san_csr,
"accountcsr": account_csr
# CName CSR file to use with goodCNameWithoutCSR
"cnameCSR": domain_csr,
}
# generate two account keys to roll over them
def generate_acme_account_rollover_config():
# Old account key
old_account_key = NamedTemporaryFile()
Popen(["openssl", "genrsa", "-out", old_account_key.name, "2048"]).wait()
# Old account is directly created by the config generator
old_account_key, domain_key, domain_csr, config = generate_config()
# New account key
new_account_key = NamedTemporaryFile()
new_account_key = NamedTemporaryFile(delete=False)
Popen(["openssl", "genrsa", "-out", new_account_key.name, "2048"]).wait()
return {
# keys (returned to keep files on system)
# config and keys (returned to keep files on system)
"config": config,
"oldaccountkey": old_account_key,
"newaccountkey": new_account_key
"newaccountkey": new_account_key.name
}
# generate an account key to delete it
def generate_acme_account_delete_config():
# account key
account_key = NamedTemporaryFile()
Popen(["openssl", "genrsa", "-out", account_key.name, "2048"]).wait()
return account_key
def generate_acme_account_deactivate_config():
# Account key is created by the by the config generator
account_key, domain_key, domain_csr, config = generate_config()
return {
"config": config,
"key": account_key
}
coverage
argparse
configparser
\ No newline at end of file
configparser
requests
import unittest, os, time
import acme_dns_tiny
from tests.config_factory import generate_acme_account_deactivate_config
import tools.acme_account_deactivate
ACMEDirectory = os.getenv("GITLABCI_ACMEDIRECTORY_V2", "https://acme-staging-v02.api.letsencrypt.org/directory")
class TestACMEAccountDeactivate(unittest.TestCase):
"Tests for acme_account_deactivate"
@classmethod
def setUpClass(self):
self.configs = generate_acme_account_deactivate_config()
try:
acme_dns_tiny.main([self.configs['config']])
except ValueError as err:
if str(err).startswith("Error register"):
raise ValueError("Fail test as account has not been registered correctly: {0}".format(err))
super(TestACMEAccountDeactivate, self).setUpClass()
# To clean ACME staging server and close correctly temporary files
@classmethod
def tearDownClass(self):
# Remove temporary files
os.remove(self.configs['config'])
os.remove(self.configs['key'])
super(TestACMEAccountDeactivate, self).tearDownClass()
def test_success_account_deactivate(self):
""" Test success account key deactivate """
with self.assertLogs(level='INFO') as accountdeactivatelog:
tools.acme_account_deactivate.main(["--account-key", self.configs['key'],
"--acme-directory", ACMEDirectory])
self.assertIn("INFO:acme_account_deactivate:Account key deactivated !",
accountdeactivatelog.output)
if __name__ == "__main__":
unittest.main()
import unittest, os
import acme_dns_tiny
from tests.config_factory import generate_acme_account_delete_config
import tools.acme_account_delete
ACMEDirectory = os.getenv("GITLABCI_ACMEDIRECTORY", "https://acme-staging.api.letsencrypt.org/directory")
class TestACMEAccountDelete(unittest.TestCase):
"Tests for acme_account_delete"
@classmethod
def setUpClass(self):
self.accountkey = generate_acme_account_delete_config()
super(TestACMEAccountDelete, self).setUpClass()
# To clean ACME staging server and close correctly temporary files
@classmethod
def tearDownClass(self):
# close temp files correctly
self.accountkey.close()
super(TestACMEAccountDelete, self).tearDownClass()
def test_success_account_delete(self):
""" Test success account key delete """
with self.assertLogs(level='INFO') as accountdeletelog:
tools.acme_account_delete.main(["--account-key", self.accountkey.name,
"--acme-directory", ACMEDirectory])
self.assertIn("INFO:acme_account_delete:Account key deleted !",
accountdeletelog.output)
if __name__ == "__main__":
unittest.main()
import unittest, os
import unittest, os, time
import acme_dns_tiny
from tests.config_factory import generate_acme_account_rollover_config
from tools.acme_account_delete import account_delete
from tools.acme_account_deactivate import account_deactivate
import tools.acme_account_rollover
ACMEDirectory = os.getenv("GITLABCI_ACMEDIRECTORY", "https://acme-staging.api.letsencrypt.org/directory")
ACMEDirectory = os.getenv("GITLABCI_ACMEDIRECTORY_V2", "https://acme-staging-v02.api.letsencrypt.org/directory")
class TestACMEAccountRollover(unittest.TestCase):
"Tests for acme_account_rollover"
......@@ -12,23 +12,24 @@ class TestACMEAccountRollover(unittest.TestCase):
@classmethod
def setUpClass(self):
self.configs = generate_acme_account_rollover_config()
acme_dns_tiny.main([self.configs['config']])
super(TestACMEAccountRollover, self).setUpClass()
# To clean ACME staging server and close correctly temporary files
@classmethod
def tearDownClass(self):
# delete account key registration at end of tests
account_delete(self.configs["newaccountkey"].name, ACMEDirectory)
# deactivate account key registration at end of tests
account_deactivate(self.configs["oldaccountkey"], ACMEDirectory)
# close temp files correctly
for tmpfile in self.configs:
self.configs[tmpfile].close()
os.remove(self.configs[tmpfile])
super(TestACMEAccountRollover, self).tearDownClass()
def test_success_account_rollover(self):
""" Test success account key rollover """
with self.assertLogs(level='INFO') as accountrolloverlog:
tools.acme_account_rollover.main(["--current", self.configs['oldaccountkey'].name,
"--new", self.configs['newaccountkey'].name,
tools.acme_account_rollover.main(["--current", self.configs['oldaccountkey'],
"--new", self.configs['newaccountkey'],
"--acme-directory", ACMEDirectory])
self.assertIn("INFO:acme_account_rollover:Account keys rolled over !",
accountrolloverlog.output)
......
import unittest, sys, os, subprocess
import unittest, sys, os, subprocess, time
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_delete import account_delete
from tools.acme_account_deactivate import account_deactivate
ACMEDirectory = os.getenv("GITLABCI_ACMEDIRECTORY", "https://acme-staging.api.letsencrypt.org/directory")
ACMEDirectory = os.getenv("GITLABCI_ACMEDIRECTORY_V2", "https://acme-staging-v02.api.letsencrypt.org/directory")
class TestACMEDNSTiny(unittest.TestCase):
"Tests for acme_dns_tiny.get_crt()"
......@@ -22,11 +22,11 @@ class TestACMEDNSTiny(unittest.TestCase):
# To clean ACME staging server and close correctly temporary files
@classmethod
def tearDownClass(self):
# delete account key registration at end of tests
account_delete(self.configs["accountkey"].name, ACMEDirectory)
# deactivate account key registration at end of tests
account_deactivate(self.configs["accountkey"], ACMEDirectory)
# close temp files correctly
for tmpfile in self.configs:
self.configs[tmpfile].close()
os.remove(self.configs[tmpfile])
super(TestACMEDNSTiny, self).tearDownClass()
# helper function to run openssl command
......@@ -55,7 +55,7 @@ class TestACMEDNSTiny(unittest.TestCase):
old_stdout = sys.stdout
sys.stdout = StringIO()
acme_dns_tiny.main([self.configs['goodCName'].name])
acme_dns_tiny.main([self.configs['goodCName'], "--verbose"])
certchain = sys.stdout.getvalue()
sys.stdout.close()
......@@ -63,14 +63,40 @@ class TestACMEDNSTiny(unittest.TestCase):
self.assertCertificateChain(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"])
certchain = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = old_stdout
self.assertCertificateChain(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"])
certchain = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = old_stdout
self.assertCertificateChain(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'].name])
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 exists or directly the DNS Host configuration.",
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)
certchain = sys.stdout.getvalue()
......@@ -84,7 +110,7 @@ class TestACMEDNSTiny(unittest.TestCase):
old_stdout = sys.stdout
sys.stdout = StringIO()
acme_dns_tiny.main([self.configs['goodSAN'].name])