xref: /unit/test/test_tls_sni.py (revision 2616:ab2896c980ab)
11848Szelenkov@nginx.comimport ssl
21843Szelenkov@nginx.comimport subprocess
31843Szelenkov@nginx.com
42478Szelenkov@nginx.comimport pytest
5*2616Szelenkov@nginx.com
62491Szelenkov@nginx.comfrom unit.applications.tls import ApplicationTLS
71843Szelenkov@nginx.comfrom unit.option import option
81843Szelenkov@nginx.com
92488Szelenkov@nginx.comprerequisites = {'modules': {'openssl': 'any'}}
102488Szelenkov@nginx.com
112491Szelenkov@nginx.comclient = ApplicationTLS()
121843Szelenkov@nginx.com
132491Szelenkov@nginx.com
142491Szelenkov@nginx.com@pytest.fixture(autouse=True)
152491Szelenkov@nginx.comdef setup_method_fixture():
162491Szelenkov@nginx.com    assert 'success' in client.conf(
172491Szelenkov@nginx.com        {
182592Szelenkov@nginx.com            "listeners": {"*:8080": {"pass": "routes"}},
192491Szelenkov@nginx.com            "routes": [{"action": {"return": 200}}],
202491Szelenkov@nginx.com            "applications": {},
212491Szelenkov@nginx.com        }
222491Szelenkov@nginx.com    )
232491Szelenkov@nginx.com
242491Szelenkov@nginx.com
252491Szelenkov@nginx.comdef add_tls(cert='default'):
262491Szelenkov@nginx.com    assert 'success' in client.conf(
272491Szelenkov@nginx.com        {"pass": "routes", "tls": {"certificate": cert}},
282592Szelenkov@nginx.com        'listeners/*:8080',
292491Szelenkov@nginx.com    )
302491Szelenkov@nginx.com
312491Szelenkov@nginx.com
322491Szelenkov@nginx.comdef check_cert(host, expect, ctx):
332491Szelenkov@nginx.com    resp, sock = client.get_ssl(
342491Szelenkov@nginx.com        headers={
352491Szelenkov@nginx.com            'Host': host,
362491Szelenkov@nginx.com            'Content-Length': '0',
372491Szelenkov@nginx.com            'Connection': 'close',
382491Szelenkov@nginx.com        },
392491Szelenkov@nginx.com        start=True,
402491Szelenkov@nginx.com        context=ctx,
412491Szelenkov@nginx.com    )
422491Szelenkov@nginx.com
432491Szelenkov@nginx.com    assert resp['status'] == 200
442491Szelenkov@nginx.com    assert sock.getpeercert()['subject'][0][0][1] == expect
452491Szelenkov@nginx.com
462491Szelenkov@nginx.com
472491Szelenkov@nginx.comdef config_bundles(bundles):
482491Szelenkov@nginx.com    client.certificate('root', False)
492491Szelenkov@nginx.com
502491Szelenkov@nginx.com    for b in bundles:
512491Szelenkov@nginx.com        client.openssl_conf(rewrite=True, alt_names=bundles[b]['alt_names'])
522491Szelenkov@nginx.com        subj = f'/CN={bundles[b]["subj"]}/' if 'subj' in bundles[b] else '/'
532491Szelenkov@nginx.com
542491Szelenkov@nginx.com        subprocess.check_output(
552491Szelenkov@nginx.com            [
562491Szelenkov@nginx.com                'openssl',
572491Szelenkov@nginx.com                'req',
582491Szelenkov@nginx.com                '-new',
592491Szelenkov@nginx.com                '-subj',
602491Szelenkov@nginx.com                subj,
612491Szelenkov@nginx.com                '-config',
622491Szelenkov@nginx.com                f'{option.temp_dir}/openssl.conf',
632491Szelenkov@nginx.com                '-out',
642491Szelenkov@nginx.com                f'{option.temp_dir}/{b}.csr',
652491Szelenkov@nginx.com                '-keyout',
662491Szelenkov@nginx.com                f'{option.temp_dir}/{b}.key',
672491Szelenkov@nginx.com            ],
682491Szelenkov@nginx.com            stderr=subprocess.STDOUT,
691843Szelenkov@nginx.com        )
701843Szelenkov@nginx.com
712491Szelenkov@nginx.com    generate_ca_conf()
722491Szelenkov@nginx.com
732491Szelenkov@nginx.com    for b in bundles:
742491Szelenkov@nginx.com        subj = f'/CN={bundles[b]["subj"]}/' if 'subj' in bundles[b] else '/'
752491Szelenkov@nginx.com
762491Szelenkov@nginx.com        subprocess.check_output(
772491Szelenkov@nginx.com            [
782491Szelenkov@nginx.com                'openssl',
792491Szelenkov@nginx.com                'ca',
802491Szelenkov@nginx.com                '-batch',
812491Szelenkov@nginx.com                '-subj',
822491Szelenkov@nginx.com                subj,
832491Szelenkov@nginx.com                '-config',
842491Szelenkov@nginx.com                f'{option.temp_dir}/ca.conf',
852491Szelenkov@nginx.com                '-keyfile',
862491Szelenkov@nginx.com                f'{option.temp_dir}/root.key',
872491Szelenkov@nginx.com                '-cert',
882491Szelenkov@nginx.com                f'{option.temp_dir}/root.crt',
892491Szelenkov@nginx.com                '-in',
902491Szelenkov@nginx.com                f'{option.temp_dir}/{b}.csr',
912491Szelenkov@nginx.com                '-out',
922491Szelenkov@nginx.com                f'{option.temp_dir}/{b}.crt',
932491Szelenkov@nginx.com            ],
942491Szelenkov@nginx.com            stderr=subprocess.STDOUT,
951843Szelenkov@nginx.com        )
961843Szelenkov@nginx.com
972491Szelenkov@nginx.com    load_certs(bundles)
982491Szelenkov@nginx.com
992491Szelenkov@nginx.com    context = ssl.create_default_context()
1002491Szelenkov@nginx.com    context.check_hostname = False
1012491Szelenkov@nginx.com    context.verify_mode = ssl.CERT_REQUIRED
1022491Szelenkov@nginx.com    context.load_verify_locations(f'{option.temp_dir}/root.crt')
1031843Szelenkov@nginx.com
1042491Szelenkov@nginx.com    return context
1052491Szelenkov@nginx.com
1062491Szelenkov@nginx.com
1072491Szelenkov@nginx.comdef generate_ca_conf():
108*2616Szelenkov@nginx.com    with open(f'{option.temp_dir}/ca.conf', 'w', encoding='utf-8') as f:
1092491Szelenkov@nginx.com        f.write(
1102491Szelenkov@nginx.com            f"""[ ca ]
1111843Szelenkov@nginx.comdefault_ca = myca
1121843Szelenkov@nginx.com
1131843Szelenkov@nginx.com[ myca ]
1142330Szelenkov@nginx.comnew_certs_dir = {option.temp_dir}
1152330Szelenkov@nginx.comdatabase = {option.temp_dir}/certindex
1161843Szelenkov@nginx.comdefault_md = sha256
1171843Szelenkov@nginx.compolicy = myca_policy
1182330Szelenkov@nginx.comserial = {option.temp_dir}/certserial
1191843Szelenkov@nginx.comdefault_days = 1
1201843Szelenkov@nginx.comx509_extensions = myca_extensions
1211843Szelenkov@nginx.comcopy_extensions = copy
1221843Szelenkov@nginx.com
1231843Szelenkov@nginx.com[ myca_policy ]
1241843Szelenkov@nginx.comcommonName = optional
1251843Szelenkov@nginx.com
1261843Szelenkov@nginx.com[ myca_extensions ]
1271843Szelenkov@nginx.combasicConstraints = critical,CA:TRUE"""
1281843Szelenkov@nginx.com        )
1291843Szelenkov@nginx.com
130*2616Szelenkov@nginx.com    with open(f'{option.temp_dir}/certserial', 'w', encoding='utf-8') as f:
1312491Szelenkov@nginx.com        f.write('1000')
1322491Szelenkov@nginx.com
133*2616Szelenkov@nginx.com    with open(f'{option.temp_dir}/certindex', 'w', encoding='utf-8') as f:
1342491Szelenkov@nginx.com        f.write('')
1352491Szelenkov@nginx.com
1362491Szelenkov@nginx.com
1372491Szelenkov@nginx.comdef load_certs(bundles):
1382491Szelenkov@nginx.com    for bname, bvalue in bundles.items():
1392491Szelenkov@nginx.com        assert 'success' in client.certificate_load(
1402491Szelenkov@nginx.com            bname, bname
1412491Szelenkov@nginx.com        ), f'certificate {bvalue["subj"]} upload'
1422491Szelenkov@nginx.com
1432491Szelenkov@nginx.com
1442491Szelenkov@nginx.comdef remove_tls():
1452592Szelenkov@nginx.com    assert 'success' in client.conf({"pass": "routes"}, 'listeners/*:8080')
1462491Szelenkov@nginx.com
1472491Szelenkov@nginx.com
1482491Szelenkov@nginx.comdef test_tls_sni():
1492491Szelenkov@nginx.com    bundles = {
1502491Szelenkov@nginx.com        "default": {"subj": "default", "alt_names": ["default"]},
1512491Szelenkov@nginx.com        "localhost.com": {
1522491Szelenkov@nginx.com            "subj": "localhost.com",
1532491Szelenkov@nginx.com            "alt_names": ["alt1.localhost.com"],
1542491Szelenkov@nginx.com        },
1552491Szelenkov@nginx.com        "example.com": {
1562491Szelenkov@nginx.com            "subj": "example.com",
1572491Szelenkov@nginx.com            "alt_names": ["alt1.example.com", "alt2.example.com"],
1582491Szelenkov@nginx.com        },
1592491Szelenkov@nginx.com    }
1602491Szelenkov@nginx.com    ctx = config_bundles(bundles)
1612491Szelenkov@nginx.com    add_tls(["default", "localhost.com", "example.com"])
1622491Szelenkov@nginx.com
1632491Szelenkov@nginx.com    check_cert('alt1.localhost.com', bundles['localhost.com']['subj'], ctx)
1642491Szelenkov@nginx.com    check_cert('alt2.example.com', bundles['example.com']['subj'], ctx)
1652491Szelenkov@nginx.com    check_cert('blah', bundles['default']['subj'], ctx)
1662491Szelenkov@nginx.com
1671843Szelenkov@nginx.com
1682491Szelenkov@nginx.comdef test_tls_sni_no_hostname():
1692491Szelenkov@nginx.com    bundles = {
1702491Szelenkov@nginx.com        "localhost.com": {"subj": "localhost.com", "alt_names": []},
1712491Szelenkov@nginx.com        "example.com": {
1722491Szelenkov@nginx.com            "subj": "example.com",
1732491Szelenkov@nginx.com            "alt_names": ["example.com"],
1742491Szelenkov@nginx.com        },
1752491Szelenkov@nginx.com    }
1762491Szelenkov@nginx.com    ctx = config_bundles(bundles)
1772491Szelenkov@nginx.com    add_tls(["localhost.com", "example.com"])
1782491Szelenkov@nginx.com
1792491Szelenkov@nginx.com    resp, sock = client.get_ssl(
1802491Szelenkov@nginx.com        headers={'Content-Length': '0', 'Connection': 'close'},
1812491Szelenkov@nginx.com        start=True,
1822491Szelenkov@nginx.com        context=ctx,
1832491Szelenkov@nginx.com    )
1842491Szelenkov@nginx.com    assert resp['status'] == 200
1852491Szelenkov@nginx.com    assert (
1862491Szelenkov@nginx.com        sock.getpeercert()['subject'][0][0][1]
1872491Szelenkov@nginx.com        == bundles['localhost.com']['subj']
1882491Szelenkov@nginx.com    )
1892491Szelenkov@nginx.com
1902491Szelenkov@nginx.com
1912491Szelenkov@nginx.comdef test_tls_sni_upper_case():
1922491Szelenkov@nginx.com    bundles = {
1932491Szelenkov@nginx.com        "localhost.com": {"subj": "LOCALHOST.COM", "alt_names": []},
1942491Szelenkov@nginx.com        "example.com": {
1952491Szelenkov@nginx.com            "subj": "example.com",
1962491Szelenkov@nginx.com            "alt_names": ["ALT1.EXAMPLE.COM", "*.ALT2.EXAMPLE.COM"],
1972491Szelenkov@nginx.com        },
1982491Szelenkov@nginx.com    }
1992491Szelenkov@nginx.com    ctx = config_bundles(bundles)
2002491Szelenkov@nginx.com    add_tls(["localhost.com", "example.com"])
2012491Szelenkov@nginx.com
2022491Szelenkov@nginx.com    check_cert('localhost.com', bundles['localhost.com']['subj'], ctx)
2032491Szelenkov@nginx.com    check_cert('LOCALHOST.COM', bundles['localhost.com']['subj'], ctx)
2042491Szelenkov@nginx.com    check_cert('EXAMPLE.COM', bundles['localhost.com']['subj'], ctx)
2052491Szelenkov@nginx.com    check_cert('ALT1.EXAMPLE.COM', bundles['example.com']['subj'], ctx)
2062491Szelenkov@nginx.com    check_cert('WWW.ALT2.EXAMPLE.COM', bundles['example.com']['subj'], ctx)
2072491Szelenkov@nginx.com
2081843Szelenkov@nginx.com
2092491Szelenkov@nginx.comdef test_tls_sni_only_bundle():
2102491Szelenkov@nginx.com    bundles = {
2112491Szelenkov@nginx.com        "localhost.com": {
2122491Szelenkov@nginx.com            "subj": "localhost.com",
2132491Szelenkov@nginx.com            "alt_names": ["alt1.localhost.com", "alt2.localhost.com"],
2142491Szelenkov@nginx.com        }
2152491Szelenkov@nginx.com    }
2162491Szelenkov@nginx.com    ctx = config_bundles(bundles)
2172491Szelenkov@nginx.com    add_tls(["localhost.com"])
2182491Szelenkov@nginx.com
2192491Szelenkov@nginx.com    check_cert('domain.com', bundles['localhost.com']['subj'], ctx)
2202491Szelenkov@nginx.com    check_cert('alt1.domain.com', bundles['localhost.com']['subj'], ctx)
2212491Szelenkov@nginx.com
2221843Szelenkov@nginx.com
2232491Szelenkov@nginx.comdef test_tls_sni_wildcard():
2242491Szelenkov@nginx.com    bundles = {
2252491Szelenkov@nginx.com        "localhost.com": {"subj": "localhost.com", "alt_names": []},
2262491Szelenkov@nginx.com        "example.com": {
2272491Szelenkov@nginx.com            "subj": "example.com",
2282491Szelenkov@nginx.com            "alt_names": ["*.example.com", "*.alt.example.com"],
2292491Szelenkov@nginx.com        },
2302491Szelenkov@nginx.com    }
2312491Szelenkov@nginx.com    ctx = config_bundles(bundles)
2322491Szelenkov@nginx.com    add_tls(["localhost.com", "example.com"])
2332491Szelenkov@nginx.com
2342491Szelenkov@nginx.com    check_cert('example.com', bundles['localhost.com']['subj'], ctx)
2352491Szelenkov@nginx.com    check_cert('www.example.com', bundles['example.com']['subj'], ctx)
2362491Szelenkov@nginx.com    check_cert('alt.example.com', bundles['example.com']['subj'], ctx)
2372491Szelenkov@nginx.com    check_cert('www.alt.example.com', bundles['example.com']['subj'], ctx)
2382491Szelenkov@nginx.com    check_cert('www.alt.example.ru', bundles['localhost.com']['subj'], ctx)
2392491Szelenkov@nginx.com
2402491Szelenkov@nginx.com
2412491Szelenkov@nginx.comdef test_tls_sni_duplicated_bundle():
2422491Szelenkov@nginx.com    bundles = {
2432491Szelenkov@nginx.com        "localhost.com": {
2442491Szelenkov@nginx.com            "subj": "localhost.com",
2452491Szelenkov@nginx.com            "alt_names": ["localhost.com", "alt2.localhost.com"],
2461922Szelenkov@nginx.com        }
2472491Szelenkov@nginx.com    }
2482491Szelenkov@nginx.com    ctx = config_bundles(bundles)
2492491Szelenkov@nginx.com    add_tls(["localhost.com", "localhost.com"])
2502491Szelenkov@nginx.com
2512491Szelenkov@nginx.com    check_cert('localhost.com', bundles['localhost.com']['subj'], ctx)
2522491Szelenkov@nginx.com    check_cert('alt2.localhost.com', bundles['localhost.com']['subj'], ctx)
2532491Szelenkov@nginx.com
2542491Szelenkov@nginx.com
2552491Szelenkov@nginx.comdef test_tls_sni_same_alt():
2562491Szelenkov@nginx.com    bundles = {
2572491Szelenkov@nginx.com        "localhost": {"subj": "subj1", "alt_names": "same.altname.com"},
2582491Szelenkov@nginx.com        "example": {"subj": "subj2", "alt_names": "same.altname.com"},
2592491Szelenkov@nginx.com    }
2602491Szelenkov@nginx.com    ctx = config_bundles(bundles)
2612491Szelenkov@nginx.com    add_tls(["localhost", "example"])
2622491Szelenkov@nginx.com
2632491Szelenkov@nginx.com    check_cert('localhost', bundles['localhost']['subj'], ctx)
2642491Szelenkov@nginx.com    check_cert('example', bundles['localhost']['subj'], ctx)
2652491Szelenkov@nginx.com
2661922Szelenkov@nginx.com
2672491Szelenkov@nginx.comdef test_tls_sni_empty_cn():
2682491Szelenkov@nginx.com    bundles = {"localhost": {"alt_names": ["alt.localhost.com"]}}
2692491Szelenkov@nginx.com    ctx = config_bundles(bundles)
2702491Szelenkov@nginx.com    add_tls(["localhost"])
2712491Szelenkov@nginx.com
2722491Szelenkov@nginx.com    resp, sock = client.get_ssl(
2732491Szelenkov@nginx.com        headers={
2742491Szelenkov@nginx.com            'Host': 'domain.com',
2752491Szelenkov@nginx.com            'Content-Length': '0',
2762491Szelenkov@nginx.com            'Connection': 'close',
2772491Szelenkov@nginx.com        },
2782491Szelenkov@nginx.com        start=True,
2792491Szelenkov@nginx.com        context=ctx,
2802491Szelenkov@nginx.com    )
2812491Szelenkov@nginx.com
2822491Szelenkov@nginx.com    assert resp['status'] == 200
2832491Szelenkov@nginx.com    assert sock.getpeercert()['subjectAltName'][0][1] == 'alt.localhost.com'
2842491Szelenkov@nginx.com
2852491Szelenkov@nginx.com
2862491Szelenkov@nginx.comdef test_tls_sni_invalid():
2872491Szelenkov@nginx.com    _ = config_bundles({"localhost": {"subj": "subj1", "alt_names": ''}})
2882491Szelenkov@nginx.com    add_tls(["localhost"])
2892491Szelenkov@nginx.com
2902491Szelenkov@nginx.com    def check_certificate(cert):
2912491Szelenkov@nginx.com        assert 'error' in client.conf(
2922491Szelenkov@nginx.com            {"pass": "routes", "tls": {"certificate": cert}},
2932592Szelenkov@nginx.com            'listeners/*:8080',
2941922Szelenkov@nginx.com        )
2951922Szelenkov@nginx.com
2962491Szelenkov@nginx.com    check_certificate('')
2972491Szelenkov@nginx.com    check_certificate('blah')
2982491Szelenkov@nginx.com    check_certificate([])
2992491Szelenkov@nginx.com    check_certificate(['blah'])
3002491Szelenkov@nginx.com    check_certificate(['localhost', 'blah'])
3012491Szelenkov@nginx.com    check_certificate(['localhost', []])
302