xref: /unit/test/test_tls_sni.py (revision 2073:bc6ad31ce286)
11848Szelenkov@nginx.comimport ssl
21843Szelenkov@nginx.comimport subprocess
31843Szelenkov@nginx.com
41843Szelenkov@nginx.comfrom unit.applications.tls import TestApplicationTLS
51843Szelenkov@nginx.comfrom unit.option import option
61843Szelenkov@nginx.com
71843Szelenkov@nginx.com
81843Szelenkov@nginx.comclass TestTLSSNI(TestApplicationTLS):
91843Szelenkov@nginx.com    prerequisites = {'modules': {'openssl': 'any'}}
101843Szelenkov@nginx.com
111843Szelenkov@nginx.com    def setup_method(self):
121843Szelenkov@nginx.com        self._load_conf(
131843Szelenkov@nginx.com            {
141843Szelenkov@nginx.com                "listeners": {"*:7080": {"pass": "routes"}},
151843Szelenkov@nginx.com                "routes": [{"action": {"return": 200}}],
161843Szelenkov@nginx.com                "applications": {},
171843Szelenkov@nginx.com            }
181843Szelenkov@nginx.com        )
191843Szelenkov@nginx.com
201843Szelenkov@nginx.com    def openssl_date_to_sec_epoch(self, date):
211843Szelenkov@nginx.com        return self.date_to_sec_epoch(date, '%b %d %H:%M:%S %Y %Z')
221843Szelenkov@nginx.com
231843Szelenkov@nginx.com    def add_tls(self, cert='default'):
241843Szelenkov@nginx.com        assert 'success' in self.conf(
251848Szelenkov@nginx.com            {"pass": "routes", "tls": {"certificate": cert}},
261843Szelenkov@nginx.com            'listeners/*:7080',
271843Szelenkov@nginx.com        )
281843Szelenkov@nginx.com
291843Szelenkov@nginx.com    def remove_tls(self):
301843Szelenkov@nginx.com        assert 'success' in self.conf({"pass": "routes"}, 'listeners/*:7080')
311843Szelenkov@nginx.com
321843Szelenkov@nginx.com    def generate_ca_conf(self):
331843Szelenkov@nginx.com        with open(option.temp_dir + '/ca.conf', 'w') as f:
341843Szelenkov@nginx.com            f.write(
351843Szelenkov@nginx.com                """[ ca ]
361843Szelenkov@nginx.comdefault_ca = myca
371843Szelenkov@nginx.com
381843Szelenkov@nginx.com[ myca ]
391843Szelenkov@nginx.comnew_certs_dir = %(dir)s
401843Szelenkov@nginx.comdatabase = %(database)s
411843Szelenkov@nginx.comdefault_md = sha256
421843Szelenkov@nginx.compolicy = myca_policy
431843Szelenkov@nginx.comserial = %(certserial)s
441843Szelenkov@nginx.comdefault_days = 1
451843Szelenkov@nginx.comx509_extensions = myca_extensions
461843Szelenkov@nginx.comcopy_extensions = copy
471843Szelenkov@nginx.com
481843Szelenkov@nginx.com[ myca_policy ]
491843Szelenkov@nginx.comcommonName = optional
501843Szelenkov@nginx.com
511843Szelenkov@nginx.com[ myca_extensions ]
521843Szelenkov@nginx.combasicConstraints = critical,CA:TRUE"""
531843Szelenkov@nginx.com                % {
541843Szelenkov@nginx.com                    'dir': option.temp_dir,
551843Szelenkov@nginx.com                    'database': option.temp_dir + '/certindex',
561843Szelenkov@nginx.com                    'certserial': option.temp_dir + '/certserial',
571843Szelenkov@nginx.com                }
581843Szelenkov@nginx.com            )
591843Szelenkov@nginx.com
601843Szelenkov@nginx.com        with open(option.temp_dir + '/certserial', 'w') as f:
611843Szelenkov@nginx.com            f.write('1000')
621843Szelenkov@nginx.com
631843Szelenkov@nginx.com        with open(option.temp_dir + '/certindex', 'w') as f:
641843Szelenkov@nginx.com            f.write('')
651843Szelenkov@nginx.com
661843Szelenkov@nginx.com    def config_bundles(self, bundles):
671843Szelenkov@nginx.com        self.certificate('root', False)
681843Szelenkov@nginx.com
691843Szelenkov@nginx.com        for b in bundles:
701843Szelenkov@nginx.com            self.openssl_conf(rewrite=True, alt_names=bundles[b]['alt_names'])
711843Szelenkov@nginx.com            subj = (
721843Szelenkov@nginx.com                '/CN={}/'.format(bundles[b]['subj'])
731843Szelenkov@nginx.com                if 'subj' in bundles[b]
741843Szelenkov@nginx.com                else '/'
751843Szelenkov@nginx.com            )
761843Szelenkov@nginx.com
772004Szelenkov@nginx.com            subprocess.check_output(
781843Szelenkov@nginx.com                [
791843Szelenkov@nginx.com                    'openssl',
801843Szelenkov@nginx.com                    'req',
811843Szelenkov@nginx.com                    '-new',
821843Szelenkov@nginx.com                    '-subj',
831843Szelenkov@nginx.com                    subj,
841843Szelenkov@nginx.com                    '-config',
851843Szelenkov@nginx.com                    option.temp_dir + '/openssl.conf',
861843Szelenkov@nginx.com                    '-out',
871843Szelenkov@nginx.com                    option.temp_dir + '/{}.csr'.format(b),
881843Szelenkov@nginx.com                    '-keyout',
891843Szelenkov@nginx.com                    option.temp_dir + '/{}.key'.format(b),
901843Szelenkov@nginx.com                ],
911843Szelenkov@nginx.com                stderr=subprocess.STDOUT,
921843Szelenkov@nginx.com            )
931843Szelenkov@nginx.com
941843Szelenkov@nginx.com        self.generate_ca_conf()
951843Szelenkov@nginx.com
961843Szelenkov@nginx.com        for b in bundles:
971843Szelenkov@nginx.com            subj = (
981843Szelenkov@nginx.com                '/CN={}/'.format(bundles[b]['subj'])
991843Szelenkov@nginx.com                if 'subj' in bundles[b]
1001843Szelenkov@nginx.com                else '/'
1011843Szelenkov@nginx.com            )
1021843Szelenkov@nginx.com
1032004Szelenkov@nginx.com            subprocess.check_output(
1041843Szelenkov@nginx.com                [
1051843Szelenkov@nginx.com                    'openssl',
1061843Szelenkov@nginx.com                    'ca',
1071843Szelenkov@nginx.com                    '-batch',
1081843Szelenkov@nginx.com                    '-subj',
1091843Szelenkov@nginx.com                    subj,
1101843Szelenkov@nginx.com                    '-config',
1111843Szelenkov@nginx.com                    option.temp_dir + '/ca.conf',
1121843Szelenkov@nginx.com                    '-keyfile',
1131843Szelenkov@nginx.com                    option.temp_dir + '/root.key',
1141843Szelenkov@nginx.com                    '-cert',
1151843Szelenkov@nginx.com                    option.temp_dir + '/root.crt',
1161843Szelenkov@nginx.com                    '-in',
1171843Szelenkov@nginx.com                    option.temp_dir + '/{}.csr'.format(b),
1181843Szelenkov@nginx.com                    '-out',
1191843Szelenkov@nginx.com                    option.temp_dir + '/{}.crt'.format(b),
1201843Szelenkov@nginx.com                ],
1211843Szelenkov@nginx.com                stderr=subprocess.STDOUT,
1221843Szelenkov@nginx.com            )
1231843Szelenkov@nginx.com
1241843Szelenkov@nginx.com        self.context = ssl.create_default_context()
1251843Szelenkov@nginx.com        self.context.check_hostname = False
1261843Szelenkov@nginx.com        self.context.verify_mode = ssl.CERT_REQUIRED
1271843Szelenkov@nginx.com        self.context.load_verify_locations(option.temp_dir + '/root.crt')
1281843Szelenkov@nginx.com
1291843Szelenkov@nginx.com        self.load_certs(bundles)
1301843Szelenkov@nginx.com
1311843Szelenkov@nginx.com    def load_certs(self, bundles):
1321843Szelenkov@nginx.com        for bname, bvalue in bundles.items():
1331843Szelenkov@nginx.com            assert 'success' in self.certificate_load(
1341843Szelenkov@nginx.com                bname, bname
1351843Szelenkov@nginx.com            ), 'certificate {} upload'.format(bvalue['subj'])
1361843Szelenkov@nginx.com
1371843Szelenkov@nginx.com    def check_cert(self, host, expect):
1381843Szelenkov@nginx.com        resp, sock = self.get_ssl(
1391843Szelenkov@nginx.com            headers={
1401843Szelenkov@nginx.com                'Host': host,
1411843Szelenkov@nginx.com                'Content-Length': '0',
1421843Szelenkov@nginx.com                'Connection': 'close',
1431843Szelenkov@nginx.com            },
1441843Szelenkov@nginx.com            start=True,
1451843Szelenkov@nginx.com        )
1461843Szelenkov@nginx.com
1471843Szelenkov@nginx.com        assert resp['status'] == 200
1481843Szelenkov@nginx.com        assert sock.getpeercert()['subject'][0][0][1] == expect
1491843Szelenkov@nginx.com
1501843Szelenkov@nginx.com    def test_tls_sni(self):
1511843Szelenkov@nginx.com        bundles = {
1521848Szelenkov@nginx.com            "default": {"subj": "default", "alt_names": ["default"]},
1531843Szelenkov@nginx.com            "localhost.com": {
1541843Szelenkov@nginx.com                "subj": "localhost.com",
1551843Szelenkov@nginx.com                "alt_names": ["alt1.localhost.com"],
1561843Szelenkov@nginx.com            },
1571843Szelenkov@nginx.com            "example.com": {
1581843Szelenkov@nginx.com                "subj": "example.com",
1591843Szelenkov@nginx.com                "alt_names": ["alt1.example.com", "alt2.example.com"],
1601843Szelenkov@nginx.com            },
1611843Szelenkov@nginx.com        }
1621843Szelenkov@nginx.com        self.config_bundles(bundles)
1631843Szelenkov@nginx.com        self.add_tls(["default", "localhost.com", "example.com"])
1641843Szelenkov@nginx.com
1651843Szelenkov@nginx.com        self.check_cert('alt1.localhost.com', bundles['localhost.com']['subj'])
1661843Szelenkov@nginx.com        self.check_cert('alt2.example.com', bundles['example.com']['subj'])
1671843Szelenkov@nginx.com        self.check_cert('blah', bundles['default']['subj'])
1681843Szelenkov@nginx.com
1691922Szelenkov@nginx.com    def test_tls_sni_no_hostname(self):
1701922Szelenkov@nginx.com        bundles = {
1711922Szelenkov@nginx.com            "localhost.com": {"subj": "localhost.com", "alt_names": []},
1721922Szelenkov@nginx.com            "example.com": {
1731922Szelenkov@nginx.com                "subj": "example.com",
1741922Szelenkov@nginx.com                "alt_names": ["example.com"],
1751922Szelenkov@nginx.com            },
1761922Szelenkov@nginx.com        }
1771922Szelenkov@nginx.com        self.config_bundles(bundles)
1781922Szelenkov@nginx.com        self.add_tls(["localhost.com", "example.com"])
1791922Szelenkov@nginx.com
1801922Szelenkov@nginx.com        resp, sock = self.get_ssl(
181*2073Szelenkov@nginx.com            headers={'Content-Length': '0', 'Connection': 'close'},
182*2073Szelenkov@nginx.com            start=True,
1831922Szelenkov@nginx.com        )
1841922Szelenkov@nginx.com        assert resp['status'] == 200
1851922Szelenkov@nginx.com        assert (
1861922Szelenkov@nginx.com            sock.getpeercert()['subject'][0][0][1]
1871922Szelenkov@nginx.com            == bundles['localhost.com']['subj']
1881922Szelenkov@nginx.com        )
1891922Szelenkov@nginx.com
1901843Szelenkov@nginx.com    def test_tls_sni_upper_case(self):
1911843Szelenkov@nginx.com        bundles = {
1921843Szelenkov@nginx.com            "localhost.com": {"subj": "LOCALHOST.COM", "alt_names": []},
1931843Szelenkov@nginx.com            "example.com": {
1941843Szelenkov@nginx.com                "subj": "example.com",
1951843Szelenkov@nginx.com                "alt_names": ["ALT1.EXAMPLE.COM", "*.ALT2.EXAMPLE.COM"],
1961843Szelenkov@nginx.com            },
1971843Szelenkov@nginx.com        }
1981843Szelenkov@nginx.com        self.config_bundles(bundles)
1991843Szelenkov@nginx.com        self.add_tls(["localhost.com", "example.com"])
2001843Szelenkov@nginx.com
2011843Szelenkov@nginx.com        self.check_cert('localhost.com', bundles['localhost.com']['subj'])
2021843Szelenkov@nginx.com        self.check_cert('LOCALHOST.COM', bundles['localhost.com']['subj'])
2031843Szelenkov@nginx.com        self.check_cert('EXAMPLE.COM', bundles['localhost.com']['subj'])
2041843Szelenkov@nginx.com        self.check_cert('ALT1.EXAMPLE.COM', bundles['example.com']['subj'])
2051843Szelenkov@nginx.com        self.check_cert('WWW.ALT2.EXAMPLE.COM', bundles['example.com']['subj'])
2061843Szelenkov@nginx.com
2071843Szelenkov@nginx.com    def test_tls_sni_only_bundle(self):
2081843Szelenkov@nginx.com        bundles = {
2091843Szelenkov@nginx.com            "localhost.com": {
2101843Szelenkov@nginx.com                "subj": "localhost.com",
2111843Szelenkov@nginx.com                "alt_names": ["alt1.localhost.com", "alt2.localhost.com"],
2121843Szelenkov@nginx.com            }
2131843Szelenkov@nginx.com        }
2141843Szelenkov@nginx.com        self.config_bundles(bundles)
2151843Szelenkov@nginx.com        self.add_tls(["localhost.com"])
2161843Szelenkov@nginx.com
2171843Szelenkov@nginx.com        self.check_cert('domain.com', bundles['localhost.com']['subj'])
2181843Szelenkov@nginx.com        self.check_cert('alt1.domain.com', bundles['localhost.com']['subj'])
2191843Szelenkov@nginx.com
2201843Szelenkov@nginx.com    def test_tls_sni_wildcard(self):
2211843Szelenkov@nginx.com        bundles = {
2221848Szelenkov@nginx.com            "localhost.com": {"subj": "localhost.com", "alt_names": []},
2231843Szelenkov@nginx.com            "example.com": {
2241843Szelenkov@nginx.com                "subj": "example.com",
2251843Szelenkov@nginx.com                "alt_names": ["*.example.com", "*.alt.example.com"],
2261843Szelenkov@nginx.com            },
2271843Szelenkov@nginx.com        }
2281843Szelenkov@nginx.com        self.config_bundles(bundles)
2291843Szelenkov@nginx.com        self.add_tls(["localhost.com", "example.com"])
2301843Szelenkov@nginx.com
2311843Szelenkov@nginx.com        self.check_cert('example.com', bundles['localhost.com']['subj'])
2321843Szelenkov@nginx.com        self.check_cert('www.example.com', bundles['example.com']['subj'])
2331843Szelenkov@nginx.com        self.check_cert('alt.example.com', bundles['example.com']['subj'])
2341843Szelenkov@nginx.com        self.check_cert('www.alt.example.com', bundles['example.com']['subj'])
2351843Szelenkov@nginx.com        self.check_cert('www.alt.example.ru', bundles['localhost.com']['subj'])
2361843Szelenkov@nginx.com
2371843Szelenkov@nginx.com    def test_tls_sni_duplicated_bundle(self):
2381843Szelenkov@nginx.com        bundles = {
2391843Szelenkov@nginx.com            "localhost.com": {
2401843Szelenkov@nginx.com                "subj": "localhost.com",
2411843Szelenkov@nginx.com                "alt_names": ["localhost.com", "alt2.localhost.com"],
2421843Szelenkov@nginx.com            }
2431843Szelenkov@nginx.com        }
2441843Szelenkov@nginx.com        self.config_bundles(bundles)
2451843Szelenkov@nginx.com        self.add_tls(["localhost.com", "localhost.com"])
2461843Szelenkov@nginx.com
2471843Szelenkov@nginx.com        self.check_cert('localhost.com', bundles['localhost.com']['subj'])
2481843Szelenkov@nginx.com        self.check_cert('alt2.localhost.com', bundles['localhost.com']['subj'])
2491843Szelenkov@nginx.com
2501843Szelenkov@nginx.com    def test_tls_sni_same_alt(self):
2511843Szelenkov@nginx.com        bundles = {
2521843Szelenkov@nginx.com            "localhost": {"subj": "subj1", "alt_names": "same.altname.com"},
2531843Szelenkov@nginx.com            "example": {"subj": "subj2", "alt_names": "same.altname.com"},
2541843Szelenkov@nginx.com        }
2551843Szelenkov@nginx.com        self.config_bundles(bundles)
2561843Szelenkov@nginx.com        self.add_tls(["localhost", "example"])
2571843Szelenkov@nginx.com
2581843Szelenkov@nginx.com        self.check_cert('localhost', bundles['localhost']['subj'])
2591843Szelenkov@nginx.com        self.check_cert('example', bundles['localhost']['subj'])
2601843Szelenkov@nginx.com
2611843Szelenkov@nginx.com    def test_tls_sni_empty_cn(self):
2621848Szelenkov@nginx.com        bundles = {"localhost": {"alt_names": ["alt.localhost.com"]}}
2631843Szelenkov@nginx.com        self.config_bundles(bundles)
2641843Szelenkov@nginx.com        self.add_tls(["localhost"])
2651843Szelenkov@nginx.com
2661843Szelenkov@nginx.com        resp, sock = self.get_ssl(
2671843Szelenkov@nginx.com            headers={
2681843Szelenkov@nginx.com                'Host': 'domain.com',
2691843Szelenkov@nginx.com                'Content-Length': '0',
2701843Szelenkov@nginx.com                'Connection': 'close',
2711843Szelenkov@nginx.com            },
2721843Szelenkov@nginx.com            start=True,
2731843Szelenkov@nginx.com        )
2741843Szelenkov@nginx.com
2751843Szelenkov@nginx.com        assert resp['status'] == 200
276*2073Szelenkov@nginx.com        assert sock.getpeercert()['subjectAltName'][0][1] == 'alt.localhost.com'
2771843Szelenkov@nginx.com
2781843Szelenkov@nginx.com    def test_tls_sni_invalid(self):
2791843Szelenkov@nginx.com        self.config_bundles({"localhost": {"subj": "subj1", "alt_names": ''}})
2801843Szelenkov@nginx.com        self.add_tls(["localhost"])
2811843Szelenkov@nginx.com
2821843Szelenkov@nginx.com        def check_certificate(cert):
2831843Szelenkov@nginx.com            assert 'error' in self.conf(
2841843Szelenkov@nginx.com                {"pass": "routes", "tls": {"certificate": cert}},
2851843Szelenkov@nginx.com                'listeners/*:7080',
2861843Szelenkov@nginx.com            )
2871843Szelenkov@nginx.com
2881843Szelenkov@nginx.com        check_certificate('')
2891843Szelenkov@nginx.com        check_certificate('blah')
2901843Szelenkov@nginx.com        check_certificate([])
2911843Szelenkov@nginx.com        check_certificate(['blah'])
2921843Szelenkov@nginx.com        check_certificate(['localhost', 'blah'])
2931843Szelenkov@nginx.com        check_certificate(['localhost', []])
294