warpgate/tests/conftest.py

323 lines
8.6 KiB
Python
Raw Normal View History

2022-08-14 18:36:49 +08:00
import logging
import os
import psutil
import pytest
import requests
import shutil
import signal
import subprocess
import tempfile
import urllib3
import uuid
from dataclasses import dataclass
from pathlib import Path
from textwrap import dedent
from typing import List
2022-11-12 00:00:12 +08:00
from .util import alloc_port, wait_port
from .test_http_common import echo_server_port # noqa
2022-08-14 18:36:49 +08:00
cargo_root = Path(os.getcwd()).parent
@dataclass
class Context:
tmpdir: Path
@dataclass
class Child:
process: subprocess.Popen
stop_signal: signal.Signals
stop_timeout: float
2022-11-12 00:00:12 +08:00
@dataclass
class WarpgateProcess:
config_path: Path
process: subprocess.Popen
http_port: int
ssh_port: int
mysql_port: int
2022-08-14 18:36:49 +08:00
class ProcessManager:
children: List[Child]
def __init__(self, ctx: Context) -> None:
self.children = []
self.ctx = ctx
def stop(self):
for child in self.children:
try:
p = psutil.Process(child.process.pid)
except psutil.NoSuchProcess:
continue
p.send_signal(child.stop_signal)
for sp in p.children(recursive=True):
try:
sp.terminate()
except psutil.NoSuchProcess:
pass
try:
p.wait(timeout=child.stop_timeout)
except psutil.TimeoutExpired:
for sp in p.children(recursive=True):
try:
sp.kill()
except psutil.NoSuchProcess:
pass
p.kill()
def start_ssh_server(self, trusted_keys=[]):
port = alloc_port()
2022-11-12 00:00:12 +08:00
data_dir = self.ctx.tmpdir / f"sshd-{uuid.uuid4()}"
2022-08-14 18:36:49 +08:00
data_dir.mkdir(parents=True)
2022-11-12 00:00:12 +08:00
authorized_keys_path = data_dir / "authorized_keys"
authorized_keys_path.write_text("\n".join(trusted_keys))
config_path = data_dir / "sshd_config"
2022-08-14 18:36:49 +08:00
config_path.write_text(
dedent(
2022-11-12 00:00:12 +08:00
f"""\
2022-08-14 18:36:49 +08:00
Port 22
AuthorizedKeysFile {authorized_keys_path}
AllowAgentForwarding yes
AllowTcpForwarding yes
GatewayPorts yes
X11Forwarding yes
UseDNS no
PermitTunnel yes
StrictModes no
PermitRootLogin yes
HostKey /ssh-keys/id_ed25519
Subsystem sftp /usr/lib/ssh/sftp-server
2022-11-12 00:00:12 +08:00
"""
2022-08-14 18:36:49 +08:00
)
)
data_dir.chmod(0o700)
authorized_keys_path.chmod(0o600)
config_path.chmod(0o600)
self.start(
[
2022-11-12 00:00:12 +08:00
"docker",
"run",
"--rm",
"-p",
f"{port}:22",
"-v",
f"{data_dir}:{data_dir}",
"-v",
f"{os.getcwd()}/ssh-keys:/ssh-keys",
"warpgate-e2e-ssh-server",
"-f",
2022-08-14 18:36:49 +08:00
str(config_path),
]
)
return port
def start_mysql_server(self):
port = alloc_port()
self.start(
2022-11-12 00:00:12 +08:00
["docker", "run", "--rm", "-p", f"{port}:3306", "warpgate-e2e-mysql-server"]
2022-08-14 18:36:49 +08:00
)
return port
2022-11-12 00:00:12 +08:00
def start_wg(
self,
config="",
args=None,
share_with: WarpgateProcess = None,
stderr=None,
stdout=None,
) -> WarpgateProcess:
args = args or ["run"]
if share_with:
config_path = share_with.config_path
ssh_port = share_with.ssh_port
mysql_port = share_with.mysql_port
http_port = share_with.http_port
else:
ssh_port = alloc_port()
http_port = alloc_port()
mysql_port = alloc_port()
data_dir = self.ctx.tmpdir / f"wg-data-{uuid.uuid4()}"
data_dir.mkdir(parents=True)
keys_dir = data_dir / "ssh-keys"
keys_dir.mkdir(parents=True)
for k in [
Path("ssh-keys/wg/client-ed25519"),
Path("ssh-keys/wg/client-rsa"),
Path("ssh-keys/wg/host-ed25519"),
Path("ssh-keys/wg/host-rsa"),
]:
shutil.copy(k, keys_dir / k.name)
for k in [
Path("certs/tls.certificate.pem"),
Path("certs/tls.key.pem"),
]:
shutil.copy(k, data_dir / k.name)
config_path = data_dir / "warpgate.yaml"
def run(args, env={}):
return self.start(
[
f"{cargo_root}/target/llvm-cov-target/debug/warpgate",
"--config",
str(config_path),
*args,
],
cwd=cargo_root,
env={
**os.environ,
"LLVM_PROFILE_FILE": f"{cargo_root}/target/llvm-cov-target/warpgate-%m.profraw",
**env,
},
stop_signal=signal.SIGINT,
stop_timeout=5,
stderr=stderr,
stdout=stdout,
)
if not share_with:
p = run(
[
"unattended-setup",
"--ssh-port",
str(ssh_port),
"--http-port",
str(http_port),
"--mysql-port",
str(mysql_port),
"--data-path",
data_dir,
],
env={"WARPGATE_ADMIN_PASSWORD": "123"},
)
p.communicate()
assert p.returncode == 0
import yaml
config = yaml.safe_load(config_path.open())
config["ssh"]["host_key_verification"] = "auto_accept"
with config_path.open("w") as f:
yaml.safe_dump(config, f)
p = run(args)
return WarpgateProcess(
process=p,
config_path=config_path,
ssh_port=ssh_port,
http_port=http_port,
mysql_port=mysql_port,
2022-08-14 18:36:49 +08:00
)
def start_ssh_client(self, *args, password=None, **kwargs):
preargs = []
if password:
2022-11-12 00:00:12 +08:00
preargs = ["sshpass", "-p", password]
2022-08-14 18:36:49 +08:00
p = self.start(
[
*preargs,
2022-11-12 00:00:12 +08:00
"ssh",
2022-08-14 18:36:49 +08:00
# '-v',
2022-11-12 00:00:12 +08:00
"-o",
"IdentitiesOnly=yes",
"-o",
"StrictHostKeychecking=no",
"-o",
"UserKnownHostsFile=/dev/null",
2022-08-14 18:36:49 +08:00
*args,
],
stdout=subprocess.PIPE,
**kwargs,
)
return p
def start(self, args, stop_timeout=3, stop_signal=signal.SIGTERM, **kwargs):
p = subprocess.Popen(args, **kwargs)
2022-11-12 00:00:12 +08:00
self.children.append(
Child(process=p, stop_signal=stop_signal, stop_timeout=stop_timeout)
)
2022-08-14 18:36:49 +08:00
return p
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
2022-09-02 20:00:08 +08:00
def timeout():
2022-11-12 00:00:12 +08:00
t = os.getenv("TIMEOUT", "10")
2022-09-02 20:00:08 +08:00
return int(t)
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
2022-08-14 18:36:49 +08:00
def ctx():
with tempfile.TemporaryDirectory() as tmpdir:
ctx = Context(tmpdir=Path(tmpdir))
yield ctx
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
2022-08-14 18:36:49 +08:00
def processes(ctx, report_generation):
mgr = ProcessManager(ctx)
try:
yield mgr
finally:
mgr.stop()
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session", autouse=True)
2022-08-14 18:36:49 +08:00
def report_generation():
# subprocess.call(['cargo', 'llvm-cov', 'clean', '--workspace'])
2022-11-12 00:00:12 +08:00
subprocess.check_call(
["cargo", "llvm-cov", "run", "--all-features", "--no-report", "--", "--version"], cwd=cargo_root
2022-11-12 00:00:12 +08:00
)
2022-08-14 18:36:49 +08:00
yield
# subprocess.check_call(['cargo', 'llvm-cov', '--no-run', '--hide-instantiations', '--html'], cwd=cargo_root)
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
def shared_wg(processes: ProcessManager):
wg = processes.start_wg()
wait_port(wg.http_port, for_process=wg.process, recv=False)
wait_port(wg.ssh_port, for_process=wg.process)
yield wg
2022-08-14 18:36:49 +08:00
# ----
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
2022-08-14 18:36:49 +08:00
def wg_c_ed25519_pubkey():
2022-11-12 00:00:12 +08:00
return Path(os.getcwd()) / "ssh-keys/wg/client-ed25519.pub"
2022-08-14 18:36:49 +08:00
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
2022-08-14 18:36:49 +08:00
def otp_key_base64():
2022-11-12 00:00:12 +08:00
return "Isj0ekwF1YsKW8VUUQiU4awp/9dMnyMcTPH9rlr1OsE="
2022-08-14 18:36:49 +08:00
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
2022-08-14 18:36:49 +08:00
def otp_key_base32():
2022-11-12 00:00:12 +08:00
return "ELEPI6SMAXKYWCS3YVKFCCEU4GWCT76XJSPSGHCM6H624WXVHLAQ"
2022-08-14 18:36:49 +08:00
2022-11-12 00:00:12 +08:00
@pytest.fixture(scope="session")
2022-08-14 18:36:49 +08:00
def password_123_hash():
2022-11-12 00:00:12 +08:00
return "$argon2id$v=19$m=4096,t=3,p=1$cxT6YKZS7r3uBT4nPJXEJQ$GhjTXyGi5vD2H/0X8D3VgJCZSXM4I8GiXRzl4k5ytk0"
2022-08-14 18:36:49 +08:00
logging.basicConfig(level=logging.DEBUG)
requests.packages.urllib3.disable_warnings()
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
2022-11-12 00:00:12 +08:00
subprocess.call("chmod 600 ssh-keys/id*", shell=True)