--- /dev/null
+import logging
+import os
+import pytest
+import sys
+
+from .env import SSLTestEnv
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
+
+
+def pytest_report_header(config, start_path):
+ env = SSLTestEnv()
+ return f"mod_ssl [apache: {env.get_httpd_version()}, mpm: {env.mpm_module}, {env.prefix}]"
+
+
+@pytest.fixture(scope="package")
+def env(pytestconfig) -> SSLTestEnv:
+ level = logging.INFO
+ console = logging.StreamHandler()
+ console.setLevel(level)
+ console.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
+ logging.getLogger('').addHandler(console)
+ logging.getLogger('').setLevel(level=level)
+ env = SSLTestEnv(pytestconfig=pytestconfig)
+ env.setup_httpd()
+ env.apache_access_log_clear()
+ env.httpd_error_log.clear_log()
+ return env
+
+
+@pytest.fixture(autouse=True, scope="package")
+def require_openssl(env):
+ if not env.has_tool("openssl"):
+ pytest.skip("openssl not installed")
+
+
+@pytest.fixture(autouse=True, scope="package")
+def _stop_package_scope(env):
+ yield
+ assert env.apache_stop() == 0
--- /dev/null
+import inspect
+import logging
+import os
+
+from pyhttpd.env import HttpdTestEnv, HttpdTestSetup
+
+log = logging.getLogger(__name__)
+
+
+class SSLTestSetup(HttpdTestSetup):
+
+ def __init__(self, env: 'HttpdTestEnv'):
+ super().__init__(env=env)
+ self.add_source_dir(os.path.dirname(inspect.getfile(SSLTestSetup)))
+ self.add_modules(["ssl"])
+
+
+class SSLTestEnv(HttpdTestEnv):
+
+ def __init__(self, pytestconfig=None):
+ super().__init__(pytestconfig=pytestconfig)
+ self.add_httpd_log_modules(["http", "ssl", "core"])
+
+ def setup_httpd(self, setup: HttpdTestSetup = None):
+ super().setup_httpd(setup=SSLTestSetup(env=self))
\ No newline at end of file
--- /dev/null
+import os
+import pytest
+
+from pyhttpd.conf import HttpdConf
+
+
+class TestSNI:
+ LOG_FILE = "test_sni.log"
+
+ @pytest.fixture(autouse=True, scope="class")
+ def _class_scope(self, env):
+ conf = HttpdConf(env, extras={
+ "base":
+ f'CustomLog logs/{self.LOG_FILE} "%{{SSL_TLS_SNI}}x"'
+ })
+ conf.add_vhost_test1()
+ conf.install()
+ assert env.apache_restart() == 0
+
+ # check sni logging characters
+ def test_ssl_001_01(self, env):
+ log_path = os.path.join(env.server_logs_dir, self.LOG_FILE)
+
+ open(log_path, 'w').close()
+ sni = "httpd\x01\n2024\".org"
+
+ r = env.run(args=[
+ 'openssl', 's_client',
+ '-connect', f"localhost:{env.https_port}",
+ '-servername', sni
+ ], intext="GET / HTTP/1.1\n\n")
+ assert r.exit_code == 0
+
+ with open(log_path, 'rb') as f:
+ log_content = f.read()
+
+ assert sni.encode() not in log_content, \
+ f"found unescaped characters in {self.LOG_FILE}.log"
# In fact it should go in the corresponding VirtualHost... Not sure how to do that.
l = "SSLEngine On"
else:
- if line != "":
+ # conflict with the SSL_TLS_SNI
+ if line.lstrip().startswith("TLS"):
l = line.replace("TLS", "SSL")
else:
l = line
if domains[0] in self._extras:
self.add(self._extras[domains[0]])
return self
-
+
def end_vhost(self):
self._indents -= 1
self.add("</VirtualHost>")
f"ProxyPassReverse /h2proxy/ https://{host}.{self.env.http_tld}:self.env.https_port/",
])
return self
-
+
def add_vhost_test1(self, proxy_self=False, h2proxy_self=False):
domain = f"test1.{self.env.http_tld}"
self.start_vhost(domains=[domain, f"www1.{self.env.http_tld}"],
LIBEXEC_DIR = None
+ @staticmethod
+ def has_tool(name: str) -> bool:
+ return bool(shutil.which(name))
+
@classmethod
def has_python_package(cls, name: str) -> bool:
if name in sys.modules: