summaryrefslogtreecommitdiffstats
path: root/tests/test_acme_dns_tiny.py
blob: 9a2562d7051e67c6e6ec39c046a762eda6ea5e16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!python3


import collections
import os
import sys
import tempfile
import pytest


# append cwd to module importable paths (pytest-3 should be executed from main
# directory).
sys.path.append(os.path.realpath("."))
import acme_dns_tiny


DOMAIN = os.getenv('TEST_DOMAIN')
ACME_STAGING_DIRECTORY = \
        'https://acme-staging-v02.api.letsencrypt.org/directory'
CONTACT = os.getenv('TEST_CONTACT') # form: mailto:name@domain


def test_sanity_env():
    assert DOMAIN
    assert CONTACT


@pytest.fixture(scope='module')
def casemap(tmpdir_factory):

    tmpdir = tmpdir_factory.mktemp("data")

    def _gen_key(name, size):
        path = str(tmpdir.join(name))
        acme_dns_tiny.openssl(['genrsa', '-out', path, str(size)])
        return path

    account_key         = _gen_key('account_key', 4096)
    domain_key          = _gen_key('domain_key', 4096)
    weak_account_key    = _gen_key('weak_account_key', 1024)
    weak_domain_key     = _gen_key('weak_domain_key', 1024)

    default_args = {
            '--acme-directory': ACME_STAGING_DIRECTORY,
            '--script': 'tests/test_script.sh',
            '--account-key': account_key,
            #'--ttl': 60, # already the default
            '--ttl': None,
            '--contact': None,
            #'--contact': [CONTACT], # not supported by staging directory
            '--verbose': None,
            '--quiet': None,
    }

    def _csr(name, domain_key, subj=f'/CN={DOMAIN}'):
        path = str(tmpdir.join(name))
        acme_dns_tiny.openssl([
            'req', '-new', '-key', domain_key, '-subj', subj, '-out', path
        ])
        return {**default_args, '--csr': path}

    def _san_csr(name, subj='/', san=None):
        path = str(tmpdir.join(name))
        with tempfile.NamedTemporaryFile('w', encoding='utf8') as conf, \
                open('/etc/ssl/openssl.cnf', 'r', encoding='utf8') as orig:
            conf.write(orig.read())
            conf.write(f'\n[SAN]\nsubjectAltName={san}\n')
            conf.flush()
            acme_dns_tiny.openssl([
                'req', '-new', '-key', domain_key, '-subj', subj,
                '-reqexts', 'SAN', '-config', conf.name, '-out', path
            ])
        return {**default_args, '--csr': path}

    return {
            # single domain
            'single': _csr('csr_single', domain_key),

            # single, but weak account key used
            'weakaccountkey': {
                    **_csr('csr_simple', domain_key),
                    '--account-key': weak_account_key,
            },

            # single, but weak domain key used to sign csr
            'weakdomainkey': _csr('csr_weakdomain', weak_domain_key),

            # single, csr wrongly signed by account key
            'csrbyaccount': _csr('csr_byaccount', account_key),

            # wildcard domain in CN
            'wildcard': _csr('csr_wildcard', domain_key, f'/CN=*.{DOMAIN}'),

            # san only
            'sanonly': _san_csr('csr_sanonly',
                san=f'DNS:{DOMAIN},DNS:www.{DOMAIN}'),

            # san and cn
            'san': _san_csr('csr_san', subj=f'/CN={DOMAIN}',
                san=f'DNS:www.{DOMAIN}'),

            # san wildcard (domain + *.domain, contrary to wildcard in cn)
            'wildcardsan': _san_csr('csr_wildcardsan',
                    san=f'DNS:{DOMAIN},DNS:*.{DOMAIN}')
    }


def assert_certificate_chain(captured):
    #assert not captured.err
    certlist = captured.out.split('-----BEGIN CERTIFICATE-----')
    assert len(certlist) == 3
    assert certlist[0] == ''
    assert '-----END CERTIFICATE-----\n' in certlist[1]
    assert '-----END CERTIFICATE-----\n' in certlist[2]
    textchain = acme_dns_tiny.openssl(['x509', '-text', '-noout'],
            stdin=captured.out)
    assert 'Issuer' in textchain


def test_success(capsys, casemap):
    for name in ('single', 'wildcard', 'sanonly', 'san', 'wildcardsan'):
        print(f'test_success case: {name}', file=sys.stderr)
        acme_dns_tiny.main(casemap[name])
        captured = capsys.readouterr()
        assert_certificate_chain(captured)


def test_assert(casemap):
    for name in ('weakaccountkey', 'weakdomainkey', 'csrbyaccount'):
        print(f'test_assert case: {name}', file=sys.stderr)
        with pytest.raises(ValueError):
            acme_dns_tiny.main(casemap[name])