Arrange pytest to run with mod_ssl, still skipping some tests.

this closes #433


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1917039 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jean-Frederic Clere
2024-04-16 15:02:29 +00:00
parent 906fd9598e
commit d958349e72
11 changed files with 181 additions and 18 deletions

View File

@ -39,7 +39,9 @@ def env(pytestconfig) -> MDTestEnv:
@pytest.fixture(autouse=True, scope="package") @pytest.fixture(autouse=True, scope="package")
def _md_package_scope(env): def _md_package_scope(env):
env.httpd_error_log.add_ignored_lognos([ env.httpd_error_log.add_ignored_lognos([
"AH10085" # There are no SSL certificates configured and no other module contributed any "AH10085", # There are no SSL certificates configured and no other module contributed any
"AH10045", # No VirtualHost matches Managed Domain
"AH10105", # MDomain does not match any VirtualHost with 'SSLEngine on'
]) ])

View File

@ -13,7 +13,10 @@ class TlsTestConf(HttpdConf):
def start_tls_vhost(self, domains: List[str], port=None, ssl_module=None): def start_tls_vhost(self, domains: List[str], port=None, ssl_module=None):
if ssl_module is None: if ssl_module is None:
ssl_module = 'mod_tls' if not self.env.has_shared_module("tls"):
ssl_module = "mod_ssl"
else:
ssl_module = 'mod_tls'
super().start_vhost(domains=domains, port=port, doc_root=f"htdocs/{domains[0]}", ssl_module=ssl_module) super().start_vhost(domains=domains, port=port, doc_root=f"htdocs/{domains[0]}", ssl_module=ssl_module)
def end_tls_vhost(self): def end_tls_vhost(self):
@ -39,8 +42,12 @@ class TlsTestConf(HttpdConf):
f" MDCertificateKeyFile {pkey_file}", f" MDCertificateKeyFile {pkey_file}",
]) ])
self.add("</MDomain>") self.add("</MDomain>")
if self.env.has_shared_module("tls"):
ssl_module= "mod_tls"
else:
ssl_module= "mod_ssl"
super().add_vhost(domains=[domain], port=port, doc_root=f"htdocs/{domain}", super().add_vhost(domains=[domain], port=port, doc_root=f"htdocs/{domain}",
with_ssl=True, with_certificates=False, ssl_module='mod_tls') with_ssl=True, with_certificates=False, ssl_module=ssl_module)
def add_md_base(self, domain: str): def add_md_base(self, domain: str):
self.add([ self.add([

View File

@ -129,7 +129,10 @@ class TlsTestEnv(HttpdTestEnv):
]), ]),
CertificateSpec(name="user1", client=True, single_file=True), CertificateSpec(name="user1", client=True, single_file=True),
]) ])
self.add_httpd_log_modules(['tls']) if not HttpdTestEnv.has_shared_module("tls"):
self.add_httpd_log_modules(['ssl'])
else:
self.add_httpd_log_modules(['tls'])
def setup_httpd(self, setup: TlsTestSetup = None): def setup_httpd(self, setup: TlsTestSetup = None):

View File

@ -64,9 +64,15 @@ class TestConf:
]) ])
def test_tls_02_conf_cert_listen_valid(self, env, listen: str): def test_tls_02_conf_cert_listen_valid(self, env, listen: str):
conf = TlsTestConf(env=env) conf = TlsTestConf(env=env)
conf.add("TLSEngine {listen}".format(listen=listen)) if not env.has_shared_module("tls"):
conf.install() # Without cert/key openssl will complain
assert env.apache_restart() == 0 conf.add("SSLEngine on");
conf.install()
assert env.apache_restart() == 1
else:
conf.add("TLSEngine {listen}".format(listen=listen))
conf.install()
assert env.apache_restart() == 0
def test_tls_02_conf_cert_listen_cert(self, env): def test_tls_02_conf_cert_listen_cert(self, env):
domain = env.domain_a domain = env.domain_a

View File

@ -181,7 +181,10 @@ class TestCiphers:
}) })
conf.add_tls_vhosts(domains=[env.domain_a, env.domain_b]) conf.add_tls_vhosts(domains=[env.domain_a, env.domain_b])
conf.install() conf.install()
assert env.apache_restart() == 0 if not conf.env.has_shared_module("tls"):
assert env.apache_restart() != 0
else:
assert env.apache_restart() == 0
# #
env.httpd_error_log.ignore_recent( env.httpd_error_log.ignore_recent(
lognos = [ lognos = [
@ -204,4 +207,6 @@ class TestCiphers:
}) })
conf.add_tls_vhosts(domains=[env.domain_a, env.domain_b]) conf.add_tls_vhosts(domains=[env.domain_a, env.domain_b])
conf.install() conf.install()
if not conf.env.has_shared_module("tls"):
return
assert env.apache_restart() == 0 assert env.apache_restart() == 0

View File

@ -23,7 +23,10 @@ class TestVars:
def test_tls_08_vars_root(self, env): def test_tls_08_vars_root(self, env):
# in domain_b root, the StdEnvVars is switch on # in domain_b root, the StdEnvVars is switch on
exp_proto = "TLSv1.2" exp_proto = "TLSv1.2"
exp_cipher = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" if env.has_shared_module("tls"):
exp_cipher = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
else:
exp_cipher = "ECDHE-ECDSA-AES256-GCM-SHA384"
options = [ '--tls-max', '1.2'] options = [ '--tls-max', '1.2']
r = env.tls_get(env.domain_b, "/vars.py", options=options) r = env.tls_get(env.domain_b, "/vars.py", options=options)
assert r.exit_code == 0, r.stderr assert r.exit_code == 0, r.stderr
@ -47,7 +50,12 @@ class TestVars:
def test_tls_08_vars_const(self, env, name: str, value: str): def test_tls_08_vars_const(self, env, name: str, value: str):
r = env.tls_get(env.domain_b, f"/vars.py?name={name}") r = env.tls_get(env.domain_b, f"/vars.py?name={name}")
assert r.exit_code == 0, r.stderr assert r.exit_code == 0, r.stderr
assert r.json == {name: value}, r.stdout if env.has_shared_module("tls"):
assert r.json == {name: value}, r.stdout
else:
if name == "SSL_SECURE_RENEG":
value = "true"
assert r.json == {name: value}, r.stdout
@pytest.mark.parametrize("name, pattern", [ @pytest.mark.parametrize("name, pattern", [
("SSL_VERSION_INTERFACE", r'mod_tls/\d+\.\d+\.\d+'), ("SSL_VERSION_INTERFACE", r'mod_tls/\d+\.\d+\.\d+'),
@ -57,4 +65,11 @@ class TestVars:
r = env.tls_get(env.domain_b, f"/vars.py?name={name}") r = env.tls_get(env.domain_b, f"/vars.py?name={name}")
assert r.exit_code == 0, r.stderr assert r.exit_code == 0, r.stderr
assert name in r.json assert name in r.json
assert re.match(pattern, r.json[name]), r.json if env.has_shared_module("tls"):
assert re.match(pattern, r.json[name]), r.json
else:
if name == "SSL_VERSION_INTERFACE":
pattern = r'mod_ssl/\d+\.\d+\.\d+'
else:
pattern = r'OpenSSL/\d+\.\d+\.\d+'
assert re.match(pattern, r.json[name]), r.json

View File

@ -2,6 +2,7 @@ import re
import pytest import pytest
from .conf import TlsTestConf from .conf import TlsTestConf
from pyhttpd.env import HttpdTestEnv
class TestProxySSL: class TestProxySSL:
@ -9,6 +10,12 @@ class TestProxySSL:
@pytest.fixture(autouse=True, scope='class') @pytest.fixture(autouse=True, scope='class')
def _class_scope(self, env): def _class_scope(self, env):
# add vhosts a+b and a ssl proxy from a to b # add vhosts a+b and a ssl proxy from a to b
if not HttpdTestEnv.has_shared_module("tls"):
myoptions="SSLOptions +StdEnvVars"
myssl="mod_ssl"
else:
myoptions="TLSOptions +StdEnvVars"
myssl="mod_tls"
conf = TlsTestConf(env=env, extras={ conf = TlsTestConf(env=env, extras={
'base': [ 'base': [
"LogLevel proxy:trace1 proxy_http:trace1 ssl:trace1 proxy_http2:trace1", "LogLevel proxy:trace1 proxy_http:trace1 ssl:trace1 proxy_http2:trace1",
@ -33,10 +40,10 @@ class TestProxySSL:
f'ProxyPass /proxy-ssl/ https://127.0.0.1:{env.https_port}/', f'ProxyPass /proxy-ssl/ https://127.0.0.1:{env.https_port}/',
f'ProxyPass /proxy-local/ https://localhost:{env.https_port}/', f'ProxyPass /proxy-local/ https://localhost:{env.https_port}/',
f'ProxyPass /proxy-h2-ssl/ h2://127.0.0.1:{env.https_port}/', f'ProxyPass /proxy-h2-ssl/ h2://127.0.0.1:{env.https_port}/',
"TLSOptions +StdEnvVars", myoptions,
], ],
}) })
conf.add_tls_vhosts(domains=[env.domain_a, env.domain_b]) conf.add_tls_vhosts(domains=[env.domain_a, env.domain_b], ssl_module=myssl)
conf.install() conf.install()
assert env.apache_restart() == 0 assert env.apache_restart() == 0
@ -69,7 +76,24 @@ class TestProxySSL:
("SSL_CIPHER_EXPORT", "false"), ("SSL_CIPHER_EXPORT", "false"),
("SSL_CLIENT_VERIFY", "NONE"), ("SSL_CLIENT_VERIFY", "NONE"),
]) ])
def test_tls_14_proxy_tsl_vars_const(self, env, name: str, value: str):
if not HttpdTestEnv.has_shared_module("tls"):
return
r = env.tls_get(env.domain_b, f"/proxy-ssl/vars.py?name={name}")
assert r.exit_code == 0, r.stderr
assert r.json == {name: value}, r.stdout
@pytest.mark.parametrize("name, value", [
("SERVER_NAME", "b.mod-tls.test"),
("SSL_SESSION_RESUMED", "Initial"),
("SSL_SECURE_RENEG", "true"),
("SSL_COMPRESS_METHOD", "NULL"),
("SSL_CIPHER_EXPORT", "false"),
("SSL_CLIENT_VERIFY", "NONE"),
])
def test_tls_14_proxy_ssl_vars_const(self, env, name: str, value: str): def test_tls_14_proxy_ssl_vars_const(self, env, name: str, value: str):
if HttpdTestEnv.has_shared_module("tls"):
return
r = env.tls_get(env.domain_b, f"/proxy-ssl/vars.py?name={name}") r = env.tls_get(env.domain_b, f"/proxy-ssl/vars.py?name={name}")
assert r.exit_code == 0, r.stderr assert r.exit_code == 0, r.stderr
assert r.json == {name: value}, r.stdout assert r.json == {name: value}, r.stdout
@ -78,7 +102,21 @@ class TestProxySSL:
("SSL_VERSION_INTERFACE", r'mod_tls/\d+\.\d+\.\d+'), ("SSL_VERSION_INTERFACE", r'mod_tls/\d+\.\d+\.\d+'),
("SSL_VERSION_LIBRARY", r'rustls-ffi/\d+\.\d+\.\d+/rustls/\d+\.\d+\.\d+'), ("SSL_VERSION_LIBRARY", r'rustls-ffi/\d+\.\d+\.\d+/rustls/\d+\.\d+\.\d+'),
]) ])
def test_tls_14_proxy_ssl_vars_match(self, env, name: str, pattern: str): def test_tls_14_proxy_tsl_vars_match(self, env, name: str, pattern: str):
if not HttpdTestEnv.has_shared_module("tls"):
return
r = env.tls_get(env.domain_b, f"/proxy-ssl/vars.py?name={name}")
assert r.exit_code == 0, r.stderr
assert name in r.json
assert re.match(pattern, r.json[name]), r.json
@pytest.mark.parametrize("name, pattern", [
("SSL_VERSION_INTERFACE", r'mod_ssl/\d+\.\d+\.\d+'),
("SSL_VERSION_LIBRARY", r'OpenSSL/\d+\.\d+\.\d+'),
])
def test_tls_14_proxy_ssl_vars_match(self, env, name: str, pattern: str):
if HttpdTestEnv.has_shared_module("tls"):
return
r = env.tls_get(env.domain_b, f"/proxy-ssl/vars.py?name={name}") r = env.tls_get(env.domain_b, f"/proxy-ssl/vars.py?name={name}")
assert r.exit_code == 0, r.stderr assert r.exit_code == 0, r.stderr
assert name in r.json assert name in r.json

View File

@ -3,7 +3,9 @@ from datetime import timedelta
import pytest import pytest
from .conf import TlsTestConf from .conf import TlsTestConf
from pyhttpd.env import HttpdTestEnv
@pytest.mark.skipif(condition=not HttpdTestEnv.has_shared_module("tls"), reason="no mod_tls available")
class TestProxyTLS: class TestProxyTLS:

View File

@ -3,6 +3,9 @@ import time
import pytest import pytest
from .conf import TlsTestConf from .conf import TlsTestConf
from pyhttpd.env import HttpdTestEnv
@pytest.mark.skipif(condition=not HttpdTestEnv.has_shared_module("tls"), reason="no mod_tls available")
class TestProxyMixed: class TestProxyMixed:

View File

@ -3,8 +3,9 @@ import os
import pytest import pytest
from .conf import TlsTestConf from .conf import TlsTestConf
from pyhttpd.env import HttpdTestEnv
@pytest.mark.skipif(condition=not HttpdTestEnv.has_shared_module("tls"), reason="no mod_tls available")
class TestProxyMachineCert: class TestProxyMachineCert:
@pytest.fixture(autouse=True, scope='class') @pytest.fixture(autouse=True, scope='class')

View File

@ -26,15 +26,96 @@ class HttpdConf(object):
def install(self): def install(self):
self.env.install_test_conf(self._lines) self.env.install_test_conf(self._lines)
def replacetlsstr(self, line):
l = line.replace("TLS_", "")
l = l.replace("\n", " ")
l = l.replace("\\", " ")
l = " ".join(l.split())
l = l.replace(" ", ":")
l = l.replace("_", "-")
l = l.replace("-WITH", "")
l = l.replace("AES-", "AES")
l = l.replace("POLY1305-SHA256", "POLY1305")
return l
def replaceinstr(self, line):
if line.startswith("TLSCiphersPrefer"):
# the "TLS_" are changed into "".
l = self.replacetlsstr(line)
l = l.replace("TLSCiphersPrefer:", "SSLCipherSuite ")
elif line.startswith("TLSCiphersSuppress"):
# like SSLCipherSuite but with :!
l = self.replacetlsstr(line)
l = l.replace("TLSCiphersSuppress:", "SSLCipherSuite !")
l = l.replace(":", ":!")
elif line.startswith("TLSCertificate"):
l = line.replace("TLSCertificate", "SSLCertificateFile")
elif line.startswith("TLSProtocol"):
# mod_ssl is different (+ no supported and 0x code have to be translated)
l = line.replace("TLSProtocol", "SSLProtocol")
l = l.replace("+", "")
l = l.replace("default", "all")
l = l.replace("0x0303", "1.2") # need to check 1.3 and 1.1
elif line.startswith("SSLProtocol"):
l = line # we have that in test/modules/tls/test_05_proto.py
elif line.startswith("TLSHonorClientOrder"):
# mod_ssl has SSLHonorCipherOrder on = use server off = use client.
l = line.lower()
if "on" in l:
l = "SSLHonorCipherOrder off"
else:
l = "SSLHonorCipherOrder on"
elif line.startswith("TLSEngine"):
# In fact it should go in the corresponding VirtualHost... Not sure how to do that.
l = "SSLEngine On"
else:
if line != "":
l = line.replace("TLS", "SSL")
else:
l = line
return l
def add(self, line: Any): def add(self, line: Any):
# make we transform the TLS to SSL if we are using mod_ssl
if isinstance(line, str): if isinstance(line, str):
if not HttpdTestEnv.has_shared_module("tls"):
line = self.replaceinstr(line)
if self._indents > 0: if self._indents > 0:
line = f"{' ' * self._indents}{line}" line = f"{' ' * self._indents}{line}"
self._lines.append(line) self._lines.append(line)
else: else:
if self._indents > 0: if not HttpdTestEnv.has_shared_module("tls"):
line = [f"{' ' * self._indents}{l}" for l in line] new = []
self._lines.extend(line) previous = ""
for l in line:
if previous.startswith("SSLCipherSuite"):
if l.startswith("TLSCiphersPrefer") or l.startswith("TLSCiphersSuppress"):
# we need to merge it
l = self.replaceinstr(l)
l = l.replace("SSLCipherSuite ", ":")
previous = previous + l
continue
else:
if self._indents > 0:
previous = f"{' ' * self._indents}{previous}"
new.append(previous)
previous = ""
l = self.replaceinstr(l)
if l.startswith("SSLCipherSuite"):
previous = l
continue
if self._indents > 0:
l = f"{' ' * self._indents}{l}"
new.append(l)
if previous != "":
if self._indents > 0:
previous = f"{' ' * self._indents}{previous}"
new.append(previous)
self._lines.extend(new)
else:
if self._indents > 0:
line = [f"{' ' * self._indents}{l}" for l in line]
self._lines.extend(line)
return self return self
def add_certificate(self, cert_file, key_file, ssl_module=None): def add_certificate(self, cert_file, key_file, ssl_module=None):