Publish curated Unraid-AppOps-Gateway source from 50b3c135619d2d32304d515aa76903c6cb062eab
This commit is contained in:
1 parent
af3c376330
commit
1188c86539
3 files changed
+161
-4
No files matched your search
@@ -1,3 +1,3 @@
|
||||
# Public source export
|
||||
|
||||
This source snapshot was generated from the private canonical repository at revision `166e10b7084c977816a75560d015da8f350c534c`. It contains no private Git history or operational evidence. Changes are published from the canonical repository.
|
||||
This source snapshot was generated from the private canonical repository at revision `50b3c135619d2d32304d515aa76903c6cb062eab`. It contains no private Git history or operational evidence. Changes are published from the canonical repository.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"sourceRevision": "166e10b7084c977816a75560d015da8f350c534c",
|
||||
"sourceRevision": "50b3c135619d2d32304d515aa76903c6cb062eab",
|
||||
"files": [
|
||||
{
|
||||
"path": ".dockerignore",
|
||||
@@ -1659,8 +1659,8 @@
|
||||
},
|
||||
{
|
||||
"path": "backend/tests/test_runner_fleet_specialize.py",
|
||||
"sha256": "e37f114322da2182d8448439025abe2ad772564332cff5955e93cc43d28cf45b",
|
||||
"bytes": 188449
|
||||
"sha256": "da0b0099ab3d8b5e651db0d69aa6470e67be911ec6f2814216c05889ad5f8065",
|
||||
"bytes": 195475
|
||||
},
|
||||
{
|
||||
"path": "backend/tests/test_runner_fleet_v2_execution_surface.py",
|
||||
|
||||
@@ -7,6 +7,7 @@ import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
@@ -145,6 +146,162 @@ def _fixture_rollback_maintenance():
|
||||
yield lambda: None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def task_archive_source(specialize, tmp_path, monkeypatch):
|
||||
state = tmp_path / "fleet-state"
|
||||
state.mkdir(mode=0o700)
|
||||
state.chmod(0o700)
|
||||
workdir = state / ".specialization-images-reviewed"
|
||||
workdir.mkdir(mode=0o700)
|
||||
workdir.chmod(0o700)
|
||||
archive = workdir / "task-images.tar"
|
||||
payload = b"verified archive bytes\x00" * 1024
|
||||
archive.write_bytes(payload)
|
||||
archive.chmod(0o600)
|
||||
manifest = _valid_task_image_manifest(specialize)
|
||||
manifest.update(dockerTarBytes=len(payload), dockerTarSha256=hashlib.sha256(payload).hexdigest())
|
||||
monkeypatch.setattr(specialize, "SPECIALIZED_STATE_ROOT", state)
|
||||
return archive, manifest, payload
|
||||
|
||||
|
||||
@pytest.mark.parametrize("outcome", ["success", "nonzero", "timeout", "verify_failed", "source_changed"])
|
||||
def test_image_load_streams_exact_fd_over_unix_without_temporary_container_file(
|
||||
specialize, task_archive_source, monkeypatch, outcome,
|
||||
):
|
||||
archive, manifest, payload = task_archive_source
|
||||
calls = []
|
||||
checks = []
|
||||
|
||||
# File ownership is separately qualified under real POSIX metadata below.
|
||||
@contextmanager
|
||||
def opened(path, value):
|
||||
assert path == archive and value is manifest
|
||||
with archive.open("rb") as stream:
|
||||
def current():
|
||||
checks.append(True)
|
||||
if outcome == "source_changed" and len(checks) > 1:
|
||||
raise specialize.RunnerFleetSpecializeError("runner_fleet_specialization_image_archive_source_untrusted")
|
||||
yield stream, current
|
||||
current()
|
||||
|
||||
def run(args, **kwargs):
|
||||
calls.append(args)
|
||||
assert args == ["docker", "exec", "-i", "-e",
|
||||
f"DOCKER_HOST={specialize._DIND_DOCKER_HOST}",
|
||||
specialize.DIND_NAME, "docker", "image", "load"]
|
||||
assert kwargs["stdin"].tell() == 0
|
||||
assert kwargs["stdin"].read() == payload
|
||||
assert "input" not in kwargs and "shell" not in kwargs
|
||||
assert kwargs["stdout"] == kwargs["stderr"] == subprocess.DEVNULL
|
||||
assert kwargs["timeout"] == 900 and kwargs["check"] is False
|
||||
if outcome == "timeout":
|
||||
raise subprocess.TimeoutExpired(args, 900, stderr="secret must stay redacted")
|
||||
return SimpleNamespace(returncode=1 if outcome == "nonzero" else 0)
|
||||
|
||||
verified = []
|
||||
monkeypatch.setattr(specialize, "_open_verified_task_archive", opened)
|
||||
monkeypatch.setattr(specialize.subprocess, "run", run)
|
||||
monkeypatch.setattr(specialize, "_run", lambda *_a, **_k: pytest.fail("no cp, temp or rm command allowed"))
|
||||
monkeypatch.setattr(specialize, "_verify_manifest_images_in_dind",
|
||||
lambda images: verified.append(images) or outcome != "verify_failed")
|
||||
if outcome == "success":
|
||||
specialize._load_images_into_dind(archive, manifest)
|
||||
assert verified == [manifest["images"]]
|
||||
else:
|
||||
with pytest.raises(specialize.RunnerFleetSpecializeError) as error:
|
||||
specialize._load_images_into_dind(archive, manifest)
|
||||
assert "secret" not in str(error.value)
|
||||
assert bool(verified) is (outcome == "verify_failed")
|
||||
assert len(calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("directory", [False, True])
|
||||
@pytest.mark.parametrize("fault", [None, "uid", "gid", "type", "mode", "special"])
|
||||
def test_task_archive_identity_requires_exact_private_root_metadata(specialize, directory, fault):
|
||||
values = dict(st_mode=(stat.S_IFDIR | 0o700) if directory else (stat.S_IFREG | 0o600),
|
||||
st_uid=0, st_gid=0, st_nlink=2 if directory else 1,
|
||||
st_dev=1, st_ino=2, st_size=123, st_mtime_ns=4, st_ctime_ns=5)
|
||||
if fault in {"uid", "gid"}:
|
||||
values[f"st_{fault}"] = 1000
|
||||
elif fault == "type":
|
||||
values["st_mode"] = stat.S_IFLNK | 0o700
|
||||
elif fault == "mode":
|
||||
values["st_mode"] |= 0o010
|
||||
elif fault == "special":
|
||||
values["st_mode"] |= stat.S_ISUID
|
||||
if fault is None:
|
||||
assert specialize._task_archive_identity(SimpleNamespace(**values), directory=directory)
|
||||
else:
|
||||
with pytest.raises(specialize.RunnerFleetSpecializeError, match="source_untrusted"):
|
||||
specialize._task_archive_identity(SimpleNamespace(**values), directory=directory)
|
||||
|
||||
|
||||
def test_task_archive_identity_rejects_multiple_links(specialize):
|
||||
info = SimpleNamespace(st_mode=stat.S_IFREG | 0o600, st_uid=0, st_gid=0, st_nlink=2)
|
||||
with pytest.raises(specialize.RunnerFleetSpecializeError, match="source_untrusted"):
|
||||
specialize._task_archive_identity(info)
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name != "posix" or os.geteuid() != 0, reason="requires POSIX root ownership")
|
||||
@pytest.mark.parametrize("fault", [None, "missing", "symlink", "directory", "hardlink", "file_mode",
|
||||
"parent_mode", "owner", "parent_owner", "size", "digest", "foreign_path", "replace", "changed_fd"])
|
||||
def test_source_image_archive_is_verified_and_same_fd_pinned_before_any_load(
|
||||
specialize, task_archive_source, monkeypatch, fault,
|
||||
):
|
||||
archive, manifest, payload = task_archive_source
|
||||
if fault == "missing":
|
||||
archive.unlink()
|
||||
elif fault == "symlink":
|
||||
original = archive.with_name("owned-original")
|
||||
archive.rename(original)
|
||||
archive.symlink_to(original)
|
||||
elif fault == "directory":
|
||||
archive.unlink()
|
||||
archive.mkdir(mode=0o600)
|
||||
elif fault == "hardlink":
|
||||
os.link(archive, archive.with_name("extra-link"))
|
||||
elif fault == "file_mode":
|
||||
archive.chmod(0o640)
|
||||
elif fault == "parent_mode":
|
||||
archive.parent.chmod(0o710)
|
||||
elif fault == "owner":
|
||||
os.chown(archive, 1000, 0)
|
||||
elif fault == "parent_owner":
|
||||
os.chown(archive.parent, 1000, 0)
|
||||
elif fault == "size":
|
||||
manifest["dockerTarBytes"] += 1
|
||||
elif fault == "digest":
|
||||
manifest["dockerTarSha256"] = "e" * 64
|
||||
elif fault == "foreign_path":
|
||||
archive = archive.with_name("foreign.tar")
|
||||
loaded = []
|
||||
|
||||
def run(args, **kwargs):
|
||||
loaded.append(args)
|
||||
assert kwargs["stdin"].read() == payload
|
||||
if fault == "replace":
|
||||
replacement = archive.with_name("replacement")
|
||||
replacement.write_bytes(payload)
|
||||
replacement.chmod(0o600)
|
||||
replacement.replace(archive)
|
||||
elif fault == "changed_fd":
|
||||
with archive.open("r+b") as writer:
|
||||
writer.write(b"X")
|
||||
return SimpleNamespace(returncode=0)
|
||||
|
||||
verified = []
|
||||
monkeypatch.setattr(specialize.subprocess, "run", run)
|
||||
monkeypatch.setattr(specialize, "_verify_manifest_images_in_dind", lambda images: verified.append(images) or True)
|
||||
if fault is None:
|
||||
specialize._load_images_into_dind(archive, manifest)
|
||||
assert len(loaded) == 1 and verified == [manifest["images"]]
|
||||
else:
|
||||
with pytest.raises(specialize.RunnerFleetSpecializeError):
|
||||
specialize._load_images_into_dind(archive, manifest)
|
||||
assert len(loaded) == (1 if fault in {"replace", "changed_fd"} else 0)
|
||||
assert verified == []
|
||||
|
||||
|
||||
def _valid_task_image_manifest(specialize, revision="a" * 40, tree="b" * 40):
|
||||
source = "https://gitea.example.com/Jens/Unraid-AppOps-Gateway"
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user