refactor: keep root install as the sole installer source
This commit is contained in:
1 parent
6491a766f4
commit
a46bd6e132
14 files changed
+95
-385
No files matched your search
@@ -0,0 +1,32 @@
|
||||
"""Read the real installer for focused tests; no installation logic is copied."""
|
||||
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
ENTRY = ROOT / "install"
|
||||
|
||||
|
||||
def entry_parts():
|
||||
entry = ENTRY.read_text()
|
||||
opening = "<<'DEVER_INSTALL_PYTHON'\n"
|
||||
closing = "\nDEVER_INSTALL_PYTHON\n"
|
||||
if entry.count(opening) != 1 or entry.count(closing) != 1:
|
||||
raise ValueError("root install must contain exactly one Python heredoc")
|
||||
before, remainder = entry.split(opening)
|
||||
source, after = remainder.split(closing)
|
||||
return before + opening, source, closing + after
|
||||
|
||||
|
||||
def load_installer():
|
||||
_, source, _ = entry_parts()
|
||||
module = ModuleType("dever_installer")
|
||||
exec(compile(source, str(ENTRY) + "::python", "exec"), module.__dict__)
|
||||
return module
|
||||
|
||||
|
||||
def entry_with_python(probe):
|
||||
"""Keep the actual shell wrapper while a harmless probe observes its contract."""
|
||||
before, _, after = entry_parts()
|
||||
return before + probe.rstrip("\n") + after
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Standalone Bash contract; subprocesses never install or change services."""
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -8,11 +7,10 @@ import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from install_support import entry_with_python, load_installer
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
spec = importlib.util.spec_from_file_location("dever_install_entry", ROOT / "sdk/install-entry.py")
|
||||
generator = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(generator)
|
||||
|
||||
|
||||
class InstallEntryTests(unittest.TestCase):
|
||||
@@ -20,10 +18,10 @@ class InstallEntryTests(unittest.TestCase):
|
||||
return subprocess.run(["/bin/bash", "-s", "--", *arguments], input=script,
|
||||
env={}, text=True, capture_output=True, timeout=10, **kwargs)
|
||||
|
||||
def test_generated_entry_matches_source_and_independent_key(self):
|
||||
expected = generator.render((ROOT / "skills/scripts/install.py").read_text(),
|
||||
(ROOT / "skills/assets/release.pub").read_text())
|
||||
self.assertEqual((ROOT / "install").read_text(), expected)
|
||||
def test_root_entry_pins_the_independent_key_and_has_valid_shell_syntax(self):
|
||||
installer = load_installer()
|
||||
self.assertEqual(installer.DEFAULT_PUBLIC_KEY,
|
||||
(ROOT / "skills/assets/release.pub").read_text().strip())
|
||||
syntax = subprocess.run(["/bin/bash", "-n", str(ROOT / "install")], env={},
|
||||
text=True, capture_output=True, timeout=10)
|
||||
self.assertEqual(syntax.returncode, 0, syntax.stderr)
|
||||
@@ -45,19 +43,17 @@ class InstallEntryTests(unittest.TestCase):
|
||||
def test_arguments_python_isolation_and_exit_code_are_preserved(self):
|
||||
source = '''import json
|
||||
import sys
|
||||
def main(default_key):
|
||||
print(json.dumps([sys.argv[1:], default_key, sys.flags.isolated]))
|
||||
raise SystemExit(37)
|
||||
print(json.dumps([sys.argv[1:], sys.flags.isolated]))
|
||||
raise SystemExit(37)
|
||||
'''
|
||||
key = "ab" * 32
|
||||
script = generator.render(source, key)
|
||||
script = entry_with_python(source)
|
||||
arguments = ["--release", "/a path/with 'quotes'", "", "$(not executed)", "*", "--no-start"]
|
||||
result = self.run_bash(script, *arguments)
|
||||
self.assertEqual(result.returncode, 37, result.stderr)
|
||||
self.assertEqual(json.loads(result.stdout), [arguments, key, 1])
|
||||
self.assertEqual(json.loads(result.stdout), [arguments, 1])
|
||||
|
||||
def test_truncated_body_does_not_execute_python_or_sudo(self):
|
||||
script = generator.render('def main(default_key):\n print("EXECUTED")\n', "ab" * 32)
|
||||
script = entry_with_python('print("EXECUTED")\n')
|
||||
for end in (script.index("import sys"), script.index("DEVER_INSTALL_PYTHON\n}"),
|
||||
script.index('\ndever_install "$@"')):
|
||||
result = self.run_bash(script[:end])
|
||||
|
||||
@@ -13,6 +13,8 @@ import tempfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from install_support import load_installer
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
@@ -24,7 +26,7 @@ def load_module(name, path):
|
||||
return module
|
||||
|
||||
|
||||
installer = load_module("dever_installer", ROOT / "skills/scripts/install.py")
|
||||
installer = load_installer()
|
||||
assets = load_module("dever_release_assets", ROOT / "sdk/release-assets.py")
|
||||
|
||||
|
||||
@@ -343,10 +345,9 @@ class InstallerTests(unittest.TestCase):
|
||||
installer.prerequisites("linux-x86_64")
|
||||
|
||||
@unittest.skipUnless(installer.os.geteuid() == 0, "owned image installation requires root")
|
||||
def test_standalone_bash_uses_embedded_key_and_cleans_after_signature_failure(self):
|
||||
def test_standalone_bash_requires_trusted_key_and_cleans_after_signature_failure(self):
|
||||
# Execute an independently signed fixture to observe the entire entry
|
||||
# and bootstrap call chain, without installing a machine or service.
|
||||
generator = load_module("dever_install_entry", ROOT / "sdk/install-entry.py")
|
||||
launcher = self.release / "bootstrap/dever"
|
||||
launcher.write_text('''#!/usr/bin/python3
|
||||
import json
|
||||
@@ -364,10 +365,16 @@ assert setting["activate_service"] is False
|
||||
artifact.update(bytes=len(content), sha256=hashlib.sha256(content).hexdigest())
|
||||
(self.release / "manifest.json").write_text(json.dumps(manifest))
|
||||
self.sign_manifest()
|
||||
entry = generator.render((ROOT / "skills/scripts/install.py").read_text(), self.public.read_text())
|
||||
entry = (ROOT / "install").read_text()
|
||||
image = self.directory("image")
|
||||
arguments = ["/bin/bash", "-s", "--", "--release", str(self.release),
|
||||
"--system-root", str(image), "--version", "1.2.3", "--no-start"]
|
||||
untrusted = subprocess.run(arguments, input=entry, env={}, text=True,
|
||||
capture_output=True, timeout=20)
|
||||
self.assertNotEqual(untrusted.returncode, 0)
|
||||
self.assertIn("签名校验失败", untrusted.stderr)
|
||||
self.assertEqual(list(image.iterdir()), [])
|
||||
arguments.extend(["--trusted-key", str(self.public)])
|
||||
completed = subprocess.run(arguments, input=entry, env={}, text=True,
|
||||
capture_output=True, timeout=20)
|
||||
self.assertEqual(completed.returncode, 0, completed.stderr)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3
|
||||
"""Opt-in real Bash installer acceptance in an owned image, without host services."""
|
||||
"""Opt-in root install acceptance in an owned image, without host services."""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
@@ -16,7 +16,7 @@ from verify_prepared_release import WORKSPACE, execute
|
||||
def verify(args):
|
||||
result = {
|
||||
"status": "running", "checks": [],
|
||||
"transport": "local signed assets; actual generated Bash entry and Rust bootstrap",
|
||||
"transport": "local signed assets; actual root Bash entry and Rust bootstrap",
|
||||
"online_install_and_update": "not_run",
|
||||
"entry_sha256": hashlib.sha256(args.entry.read_bytes()).hexdigest(),
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -13,6 +12,9 @@ import signal
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from install_support import load_installer
|
||||
|
||||
|
||||
WORKSPACE = Path(__file__).resolve().parents[2]
|
||||
OS_LIBRARIES = (
|
||||
"libc.so.6", "libdl.so.2", "libm.so.6", "libpthread.so.0", "librt.so.1",
|
||||
@@ -195,10 +197,7 @@ def ordinary_uid_target_reuse(image):
|
||||
def install_assets(assets, image, manifest):
|
||||
# Only transport is local: signature verification, full tar extraction and
|
||||
# the final bootstrap executable are the real first-install implementation.
|
||||
script = WORKSPACE / "skills/scripts/install.py"
|
||||
specification = importlib.util.spec_from_file_location("dever_release_installer", script)
|
||||
installer = importlib.util.module_from_spec(specification)
|
||||
specification.loader.exec_module(installer)
|
||||
installer = load_installer()
|
||||
stem = f"dever-{manifest['platform']}"
|
||||
base = installer.RELEASES
|
||||
routes = {
|
||||
@@ -239,7 +238,7 @@ def accept(release, assets, image, report, init, extension_helper=None):
|
||||
install_assets(assets, image, manifest)
|
||||
else:
|
||||
execute([
|
||||
"/usr/bin/python3", WORKSPACE / "skills/scripts/install.py",
|
||||
"/bin/bash", WORKSPACE / "install",
|
||||
"--release", release, "--version", manifest["version"],
|
||||
"--system-root", image,
|
||||
])
|
||||
|
||||
Reference in new issue
Block a user