Publish curated Unraid-AppOps-Gateway source from 451646fee806a47e3256731766980a05a262dcdd

This commit is contained in:
Public Source Publisher committed 2026-10-04 05:23:27 +00:00
1 parent 532f3d9f26
commit 0a82153d95
43 files changed
+17722 -93

No files matched your search

@@ -28,6 +28,7 @@ REVISION = "a" * 40
TREE = "b" * 40
CALLER_ID = "c" * 64
MODERN_HELPER_ID = "f" * 64
RETIRED_HELPER_ID = bootstrap.RETIRED_FAILED_MODERN_HELPER_ID
HOST_HOSTNAME = "Tower"
@@ -99,7 +100,7 @@ bash "$work/upgrade.sh" \\
'''
def _legacy_helper_spec() -> dict[str, Any]:
def _legacy_helper_spec(revision: str = REVISION) -> dict[str, Any]:
tree = ast.parse(LEGACY_HELPER_SOURCE_FIXTURE)
function = next(
node
@@ -121,7 +122,7 @@ def _legacy_helper_spec() -> dict[str, Any]:
compile(ast.Module(body=[function], type_ignores=[]), "legacy-promoter", "exec"),
namespace,
)
return namespace["helper_container_spec"](revision=REVISION, image_id=CONTROLLER_IMAGE_ID)
return namespace["helper_container_spec"](revision=revision, image_id=CONTROLLER_IMAGE_ID)
def _image(labels: dict[str, str]) -> dict[str, Any]:
@@ -409,14 +410,43 @@ class FakeDocker:
self.existing_child = existing_child
self.created: list[dict[str, Any]] = []
self.started: list[str] = []
self.removed: list[str] = []
self.inspected: list[str] = []
self.host_name = HOST_HOSTNAME
self.log_bytes = b"controller_v2_ready revision=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
self.named_inspect_override: dict[str, Any] | None = None
self.named_inspect_override_at = 0
self.retired_id_inspect_override: dict[str, Any] | None = None
self.appops_inspect_count = 0
self.appops_inspect_override: dict[str, Any] | None = None
self.appops_inspect_override_at = 0
self._inspect_counts: dict[str, int] = {}
def inspect_container(self, reference: str) -> dict[str, Any] | None:
self.inspected.append(reference)
self._inspect_counts[reference] = self._inspect_counts.get(reference, 0) + 1
if reference == "itworx-controller-artifact-promoter-modern":
if (
self.named_inspect_override is not None
and self._inspect_counts[reference] == self.named_inspect_override_at
):
return self.named_inspect_override
return self.existing_child
if (
reference == RETIRED_HELPER_ID
and self.retired_id_inspect_override is not None
):
return self.retired_id_inspect_override
if (
reference == RETIRED_HELPER_ID
and self.existing_child is not None
and self.existing_child.get("Id") == RETIRED_HELPER_ID
):
return self.existing_child
if reference == APPOPS_CONTAINER_ID:
self.appops_inspect_count += 1
if self.appops_inspect_count == self.appops_inspect_override_at:
return self.appops_inspect_override
return self.containers.get(reference)
def daemon_name(self) -> str:
@@ -444,8 +474,15 @@ class FakeDocker:
source_revision = self.created[-1]["Labels"]["itworx.source.revision"]
self.containers["gitea-deploy-control"] = _modern_controller_inspect(source_revision)
def remove_retired_failed_container(self, identifier: str) -> None:
assert identifier == RETIRED_HELPER_ID
assert self.existing_child is not None
assert self.existing_child["Id"] == RETIRED_HELPER_ID
self.removed.append(identifier)
self.existing_child = None
def container_logs(self, identifier: str, *, tail: int, max_bytes: int) -> bytes:
assert identifier == MODERN_HELPER_ID
assert identifier in {MODERN_HELPER_ID, RETIRED_HELPER_ID}
assert tail <= 40
assert max_bytes <= 8192
return self.log_bytes[:max_bytes]
@@ -458,9 +495,13 @@ class FakeProtectedGit:
self.helper_source = helper_source
self.blobs_read: list[tuple[str, str]] = []
self.main_checks = 0
self.fail_current_main_at: int | None = None
self.retired_tree_valid = True
def verify_current_main(self, revision: str, tree: str) -> None:
self.main_checks += 1
if self.fail_current_main_at == self.main_checks:
raise bootstrap.BootstrapError("controller_promoter_source_not_current_main")
if (revision, tree) != (self.revision, self.tree):
raise bootstrap.BootstrapError("controller_promoter_source_not_current_main")
@@ -468,9 +509,18 @@ class FakeProtectedGit:
if (revision, tree) != (LEGACY_REVISION, LEGACY_TREE):
raise bootstrap.BootstrapError("controller_promoter_legacy_provenance_invalid")
def verify_retired_modern_revision(self, revision: str, tree: str) -> None:
if (revision, tree) != (
bootstrap.RETIRED_FAILED_MODERN_REVISION,
bootstrap.RETIRED_FAILED_MODERN_TREE,
) or not self.retired_tree_valid:
raise bootstrap.BootstrapError(
"controller_promoter_retired_helper_provenance_invalid"
)
def read_blob(self, revision: str, path: str) -> bytes:
self.blobs_read.append((revision, path))
if revision != self.revision or path != "backend/app/services/controller_artifact_helper_spec.py":
if revision not in {self.revision, bootstrap.RETIRED_FAILED_MODERN_REVISION} or path != "backend/app/services/controller_artifact_helper_spec.py":
raise bootstrap.BootstrapError("controller_promoter_source_blob_invalid")
return self.helper_source.encode("utf-8")
@@ -518,7 +568,10 @@ def _run_bootstrap(
*,
health: dict[str, Any] | None = None,
initial_health: dict[str, Any] | None = None,
health_sequence: list[dict[str, Any]] | None = None,
hostname: str = HOST_HOSTNAME,
source_revision: str = REVISION,
source_tree: str = TREE,
):
source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text(
encoding="utf-8"
@@ -530,15 +583,15 @@ def _run_bootstrap(
version="2.1.12",
artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256,
)
health_reads = [initial, health or _health()]
health_reads = health_sequence or [initial, health or _health()]
def read_health() -> dict[str, Any]:
return health_reads.pop(0) if health_reads else health or _health()
return bootstrap.run_bootstrap(
"jens/unraid-appops-gateway",
REVISION,
TREE,
source_revision,
source_tree,
docker=docker,
protected_git=git,
hostname=hostname,
@@ -549,6 +602,54 @@ def _run_bootstrap(
)
def _retired_failed_child(helper_source: str) -> dict[str, Any]:
spec = bootstrap.render_modern_helper_spec(
helper_source,
revision=bootstrap.RETIRED_FAILED_MODERN_REVISION,
image_id=CONTROLLER_IMAGE_ID,
current_controller_id=CONTROLLER_CONTAINER_ID,
current_controller_revision=CONTROLLER_REVISION,
current_controller_tree=CONTROLLER_TREE,
current_controller_version="2.1.12",
)
child = _inspect_from_spec(
"itworx-controller-artifact-promoter-modern",
RETIRED_HELPER_ID,
CONTROLLER_IMAGE_ID,
spec,
state={"Running": False, "ExitCode": 1, "Status": "exited"},
)
child["Config"]["Volumes"] = None
child["Mounts"] = [
{"Type": "bind", "Source": "/mnt/cache", "Destination": "/mnt/cache", "RW": True, "Propagation": "rprivate"},
{"Type": "bind", "Source": "/mnt/user", "Destination": "/mnt/user", "RW": True, "Propagation": "rprivate"},
{"Type": "bind", "Source": "/var/lib/docker", "Destination": "/var/lib/docker", "RW": False, "Propagation": "rslave"},
{"Type": "bind", "Source": "/srv/example/appdata/gitea/git/repositories", "Destination": "/gitea-repositories", "RW": False, "Propagation": "rprivate"},
{"Type": "bind", "Source": "/var/run/docker.sock", "Destination": "/var/run/docker.sock", "RW": True, "Propagation": "rprivate"},
]
child["HostConfig"].update(
{
"Mounts": None,
"CapAdd": None,
"CapDrop": None,
"Devices": None,
"DeviceRequests": None,
"PortBindings": None,
"VolumesFrom": None,
"PidMode": "",
"UTSMode": "",
"UsernsMode": "",
"CgroupParent": "",
"IpcMode": "private",
"CgroupnsMode": "private",
"Runtime": "runc",
"AutoRemove": False,
"PublishAllPorts": False,
}
)
return child
def test_initial_controller_runtime_accepts_exact_current_compose_container():
docker = FakeDocker(_legacy_helper_spec())
base_image_labels = bootstrap._inspect_controller_base_image(docker)
@@ -876,6 +977,187 @@ def test_running_modern_child_returns_unknown_without_duplicate_or_stop():
assert docker.started == []
def test_exact_failed_historical_child_is_removed_before_current_helper_launch():
helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text(
encoding="utf-8"
)
legacy_health = _health(
revision=CONTROLLER_REVISION,
tree=CONTROLLER_TREE,
version="2.1.12",
artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256,
)
failed_child = _retired_failed_child(helper_source)
docker = FakeDocker(_legacy_helper_spec(), existing_child=failed_child)
git = FakeProtectedGit(helper_source)
result = _run_bootstrap(
docker,
git,
health_sequence=[legacy_health, legacy_health, legacy_health, _health()],
)
assert result["status"] == "promoted"
assert docker.removed == [RETIRED_HELPER_ID]
assert docker.created and docker.started == [MODERN_HELPER_ID]
assert RETIRED_HELPER_ID in docker.inspected
assert docker.appops_inspect_count == 2
assert git.blobs_read.count(
(bootstrap.RETIRED_FAILED_MODERN_REVISION, bootstrap.MODERN_HELPER_SOURCE_PATH)
) == 1
@pytest.mark.parametrize(
"mutation",
[
"running",
"successful-exit",
"boolean-exit",
"other-exit",
"wrong-id",
"config-volumes",
"extra-host-mount",
"cap-add",
"device-request",
"docker-root-writable",
"socket-bind-changed",
"legacy-controller-stopped",
"current-main-changed",
"second-name-inspect-changed",
"second-id-inspect-changed",
"appops-stopped-before-delete",
"historical-tree-proof-failed",
],
)
def test_retired_helper_cleanup_fails_closed_on_any_identity_or_state_drift(mutation: str):
helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text(
encoding="utf-8"
)
failed_child = _retired_failed_child(helper_source)
docker = FakeDocker(_legacy_helper_spec(), existing_child=failed_child)
git = FakeProtectedGit(helper_source)
if mutation == "running":
failed_child["State"].update({"Running": True, "Status": "running"})
elif mutation == "successful-exit":
failed_child["State"]["ExitCode"] = 0
elif mutation == "boolean-exit":
failed_child["State"]["ExitCode"] = True
elif mutation == "other-exit":
failed_child["State"]["ExitCode"] = 2
elif mutation == "wrong-id":
failed_child["Id"] = "9" * 64
elif mutation == "config-volumes":
failed_child["Config"]["Volumes"] = {"/extra": {}}
elif mutation == "extra-host-mount":
failed_child["HostConfig"]["Mounts"] = [{"Type": "volume"}]
elif mutation == "cap-add":
failed_child["HostConfig"]["CapAdd"] = ["SYS_ADMIN"]
elif mutation == "device-request":
failed_child["HostConfig"]["DeviceRequests"] = [{"Capabilities": [["gpu"]]}]
elif mutation == "docker-root-writable":
failed_child["HostConfig"]["Binds"][2] = "/var/lib/docker:/var/lib/docker:rw"
failed_child["Mounts"][2]["RW"] = True
elif mutation == "socket-bind-changed":
failed_child["HostConfig"]["Binds"][4] = "/tmp/docker.sock:/var/run/docker.sock"
failed_child["Mounts"][4]["Source"] = "/tmp/docker.sock"
elif mutation == "legacy-controller-stopped":
docker.containers["gitea-deploy-control"]["State"]["Running"] = False
elif mutation == "current-main-changed":
git.fail_current_main_at = 2
elif mutation == "second-name-inspect-changed":
docker.named_inspect_override = json.loads(json.dumps(failed_child))
docker.named_inspect_override["Config"]["Cmd"].append("unexpected")
docker.named_inspect_override_at = 2
elif mutation == "second-id-inspect-changed":
docker.retired_id_inspect_override = json.loads(json.dumps(failed_child))
docker.retired_id_inspect_override["State"]["ExitCode"] = 0
elif mutation == "appops-stopped-before-delete":
docker.appops_inspect_override = json.loads(
json.dumps(docker.containers[APPOPS_CONTAINER_ID])
)
docker.appops_inspect_override["State"]["Running"] = False
docker.appops_inspect_override_at = 2
elif mutation == "historical-tree-proof-failed":
git.retired_tree_valid = False
legacy_health = _health(
revision=CONTROLLER_REVISION,
tree=CONTROLLER_TREE,
version="2.1.12",
artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256,
)
with pytest.raises(bootstrap.BootstrapError):
_run_bootstrap(
docker,
git,
health_sequence=[legacy_health, legacy_health, legacy_health],
)
assert docker.removed == []
assert docker.created == []
assert docker.started == []
def test_retired_helper_remove_uses_non_force_non_volume_docker_delete():
client = bootstrap.DockerSocketClient()
calls: list[tuple[str, str]] = []
def request(method: str, path: str, **_kwargs: Any) -> tuple[int, bytes]:
calls.append((method, path))
return 204, b""
client._request = request # type: ignore[method-assign]
client.remove_retired_failed_container(RETIRED_HELPER_ID)
assert calls == [
("DELETE", f"/containers/{RETIRED_HELPER_ID}?force=false&v=false")
]
@pytest.mark.parametrize("status", [404, 409])
def test_retired_helper_delete_refuses_unexpected_docker_status(status: int):
client = bootstrap.DockerSocketClient()
def request(_method: str, _path: str, **_kwargs: Any) -> tuple[int, bytes]:
return status, b""
client._request = request # type: ignore[method-assign]
with pytest.raises(bootstrap.BootstrapError):
client.remove_retired_failed_container(RETIRED_HELPER_ID)
def test_same_failed_source_revision_is_never_retried_or_removed():
helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text(
encoding="utf-8"
)
docker = FakeDocker(
_legacy_helper_spec(bootstrap.RETIRED_FAILED_MODERN_REVISION),
existing_child=_retired_failed_child(helper_source),
)
git = FakeProtectedGit(helper_source)
git.revision = bootstrap.RETIRED_FAILED_MODERN_REVISION
git.tree = bootstrap.RETIRED_FAILED_MODERN_TREE
legacy_health = _health(
revision=CONTROLLER_REVISION,
tree=CONTROLLER_TREE,
version="2.1.12",
artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256,
)
result = _run_bootstrap(
docker,
git,
health_sequence=[legacy_health],
source_revision=git.revision,
source_tree=git.tree,
)
assert result["status"] == "failed"
assert result["exitCode"] == 1
assert docker.removed == []
assert docker.created == []
def test_completed_modern_child_with_wrong_artifact_is_not_relaunched():
helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text(
encoding="utf-8"
@@ -23,6 +23,18 @@ spec.loader.exec_module(module)
REVISION = "a" * 40
IMAGE_ID = "sha256:" + "b" * 64
ORIGINAL_COMPOSE = b"name: gitea-deploy-runners\nservices:\n deploy-control:\n image: immutable-controller\n"
HISTORICAL_CONFIG_ROOT_RELEASE_FILES = (
"controller_paths.py", "mcp_hub_release_store.py", "hub_permission_preparation.py",
"deploy.py", "managed_appdata_paths.py", "prepare_managed_appdata_root.py", "runner_fleet.py",
"runner_capacity_controller.py", "runner_capacity_policy.py", "runner_fleet_upgrade_v2.py",
"runner_profiles.py", "runner_admission_policy.py", "runner_admission_controller.py",
"runner_resource_envelope.py", "runner_fleet_specialize.py", "runner_statistics_binding.py",
"status_server.py", "repopulse_adoption_store.py", "repopulse_registry_restore.py",
"repopulse_adoption_executor.py", "repopulse_adoption_effects.py",
"repopulse_adoption_storage_effects.py", "repopulse_target_attestation.py",
"stage_repopulse_target_attestation.py", "repopulse_adoption_cli.py",
"stage_repopulse_maintenance_environment.py", "run_repopulse_adoption.py",
)
def volume():
@@ -58,6 +70,214 @@ def test_only_the_exact_local_volume_is_accepted():
module.validate_volume(volume())
def backup_volume():
return {
"Name": "itworx-forge-master-key-backup",
"Driver": "local",
"Scope": "local",
"Options": None,
"Labels": {
"itworx.managed-by": "deployment-controller",
"itworx.purpose": "forge-master-key-backup",
},
"Mountpoint": "/var/lib/docker/volumes/itworx-forge-master-key-backup/_data",
}
def test_backup_volume_identity_is_exact_and_never_repairs_existing_metadata():
module.validate_backup_volume(backup_volume())
bad = backup_volume()
bad["Labels"]["itworx.purpose"] = "foreign"
with pytest.raises(module.VolumeTransitionError):
module.validate_backup_volume(bad)
def test_promotion_preparer_creates_only_the_fixed_backup_volume_if_absent(monkeypatch, tmp_path):
commands = []
present = False
def run(arguments, **_kwargs):
nonlocal present
commands.append(arguments)
if arguments == ["docker", "info", "--format", "{{json .DockerRootDir}}"]:
return b'"/var/lib/docker"'
if arguments == ["docker", "volume", "ls", "--format", "{{.Name}}"]:
return b"" if not present else b"itworx-forge-master-key-backup\n"
if arguments[:3] == ["docker", "volume", "create"]:
present = True
return b"itworx-forge-master-key-backup\n"
if arguments == ["docker", "volume", "inspect", "itworx-forge-master-key-backup"]:
return json.dumps([backup_volume()]).encode()
raise AssertionError(arguments)
directory_calls = []
monkeypatch.setattr(module, "_run", run)
monkeypatch.setattr(module.os, "close", lambda _descriptor: None)
def directory(path, anchor):
directory_calls.append((path, anchor))
return 23
monkeypatch.setattr(module, "_directory", directory)
module.ensure_backup_volume(create=True)
create = [arguments for arguments in commands if arguments[:3] == ["docker", "volume", "create"]]
assert create == [[
"docker", "volume", "create", "--driver", "local",
"--label", "itworx.managed-by=deployment-controller",
"--label", "itworx.purpose=forge-master-key-backup",
"itworx-forge-master-key-backup",
]]
assert directory_calls == [
(module.DOCKER_ROOT / "volumes", module.DOCKER_ROOT),
(module.BACKUP_MOUNTPOINT, module.DOCKER_ROOT),
]
def test_existing_backup_volume_is_validated_without_relabel_or_recreation(monkeypatch, tmp_path):
commands = []
def run(arguments, **_kwargs):
commands.append(arguments)
if arguments == ["docker", "info", "--format", "{{json .DockerRootDir}}"]:
return b'"/var/lib/docker"'
if arguments == ["docker", "volume", "ls", "--format", "{{.Name}}"]:
return b"itworx-forge-master-key-backup\n"
if arguments == ["docker", "volume", "inspect", "itworx-forge-master-key-backup"]:
item = backup_volume()
item["CreatedAt"] = "2000-01-01T00:00:00Z"
return json.dumps([item]).encode()
raise AssertionError(arguments)
monkeypatch.setattr(module, "_run", run)
monkeypatch.setattr(module.os, "close", lambda _descriptor: None)
monkeypatch.setattr(
module, "_directory",
lambda _path, _anchor: 23,
)
module.ensure_backup_volume(create=False)
assert not any(arguments[:3] == ["docker", "volume", "create"] for arguments in commands)
def test_missing_backup_volume_is_not_created_outside_promotion(monkeypatch, tmp_path):
commands = []
def run(arguments, **_kwargs):
commands.append(arguments)
if arguments == ["docker", "info", "--format", "{{json .DockerRootDir}}"]:
return b'"/var/lib/docker"'
if arguments == ["docker", "volume", "ls", "--format", "{{.Name}}"]:
return b""
raise AssertionError(arguments)
monkeypatch.setattr(module, "_run", run)
monkeypatch.setattr(module.os, "close", lambda _descriptor: None)
monkeypatch.setattr(
module, "_directory",
lambda _path, _anchor: 23,
)
with pytest.raises(module.VolumeTransitionError, match="backup_volume_missing"):
module.ensure_backup_volume(create=False)
assert not any(arguments[:3] == ["docker", "volume", "create"] for arguments in commands)
def _prepare_promotion_doubles(monkeypatch, tmp_path, *, config_valid=True):
events = []
controller_id = "c" * 64
current = {
"Id": controller_id,
"Mounts": [{
"Destination": module.EXECUTABLE_TARGET,
"Source": module.CONTROLLER_DIR.as_posix(),
}],
}
previous_identity = {
"sourceRevision": "b" * 40,
"sourceTree": "c" * 40,
"controllerVersion": "2.1.0",
}
config_tree = ({}, {"": 0o755})
monkeypatch.setattr(module, "_validate_private_stage", lambda _stage: events.append("stage_validated"))
monkeypatch.setattr(module, "ensure_volume", lambda **_kwargs: None)
monkeypatch.setattr(
module, "_controller_image_inspect",
lambda _image_id: (events.append("controller_inspected") or current),
)
monkeypatch.setattr(
module, "_snapshot_attested_controller",
lambda *_args, **_kwargs: ({}, {"": 0o755}, previous_identity),
)
monkeypatch.setattr(module, "_validate_previous_private_tls", lambda _stage: None)
monkeypatch.setattr(module, "_release_tree", lambda *_args, **_kwargs: ({}, {"": 0o755}))
monkeypatch.setattr(module, "_config_tree", lambda *_args, **_kwargs: config_tree)
monkeypatch.setattr(module, "_read_config_leaf", lambda: config_tree)
monkeypatch.setattr(module, "_prepare_merged_tree", lambda *_args, **_kwargs: ({}, {"": 0o755}))
def validate_staged(*_args, **_kwargs):
events.append("config_validated")
if not config_valid:
raise module.VolumeTransitionError("staged_config_invalid")
monkeypatch.setattr(module, "_validate_staged_configuration", validate_staged)
monkeypatch.setattr(module, "_install_release_leaf", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, "_read_root_compose", lambda: (ORIGINAL_COMPOSE, 0o644))
monkeypatch.setattr(module, "_mirror_snapshot_tar", lambda: b"mirror")
monkeypatch.setattr(
module, "rendered_model",
lambda *_args, **_kwargs: (events.append("compose_rendered") or b"rendered-compose"),
)
monkeypatch.setattr(
module, "_verify_compose_image",
lambda *_args, **_kwargs: events.append("compose_verified"),
)
monkeypatch.setattr(
module, "ensure_backup_volume",
lambda **_kwargs: events.append("backup_volume_prepared"),
raising=False,
)
monkeypatch.setattr(module, "_release_tar_bytes", lambda *_args, **_kwargs: b"tar")
backup = tmp_path / "promotion-backup"
backup.mkdir(mode=0o700)
backup_fd = 23
monkeypatch.setattr(module, "_create_backup", lambda: (backup, backup_fd))
monkeypatch.setattr(module, "_write_backup", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module.os, "close", lambda _descriptor: None)
return events
def test_promotion_prepares_backup_volume_after_source_stage_controller_and_config_checks(
monkeypatch, tmp_path
):
events = _prepare_promotion_doubles(monkeypatch, tmp_path)
module.prepare_promotion(
tmp_path / "stage",
source_revision="d" * 40,
source_tree="e" * 40,
controller_version="2.2.0",
image_id=IMAGE_ID,
)
assert events.index("stage_validated") < events.index("config_validated")
assert events.index("config_validated") < events.index("backup_volume_prepared")
assert events.index("compose_rendered") < events.index("backup_volume_prepared")
assert events.count("compose_verified") == 2
assert events.index("compose_verified") < events.index("backup_volume_prepared")
def test_promotion_does_not_prepare_backup_volume_when_staged_configuration_fails(
monkeypatch, tmp_path
):
events = _prepare_promotion_doubles(monkeypatch, tmp_path, config_valid=False)
with pytest.raises(module.VolumeTransitionError, match="staged_config_invalid"):
module.prepare_promotion(
tmp_path / "stage",
source_revision="d" * 40,
source_tree="e" * 40,
controller_version="2.2.0",
image_id=IMAGE_ID,
)
assert "backup_volume_prepared" not in events
def writer_inventory(inherited_labels):
identifier = "c" * 64
spec = module._writer_spec(IMAGE_ID, REVISION, kind="config")
@@ -230,18 +450,28 @@ def test_compose_uses_protected_code_and_two_config_views(compose):
"target": module.TARGET,
"volume": {"nocopy": True},
} in volumes
assert {
"type": "volume",
"source": "itworx-forge-master-key-backup",
"target": "/forge-master-key-backup",
"volume": {"nocopy": True},
} in volumes
assert {
"type": "bind",
"source": module._expected_config_path().as_posix(),
"target": module.CONFIG_TARGET,
"read_only": True,
} in volumes
assert len(volumes) == 4
assert len(volumes) == 5
volumes.remove(next(item for item in volumes if item["target"] == module.EXECUTABLE_TARGET))
volumes.remove(next(item for item in volumes if item["target"] == module.TARGET))
volumes.remove(next(item for item in volumes if item["target"] == "/forge-master-key-backup"))
volumes.remove(next(item for item in volumes if item["target"] == module.CONFIG_TARGET))
assert rendered["volumes"] == {
module.VOLUME: {"name": module.VOLUME, "external": True}
module.VOLUME: {"name": module.VOLUME, "external": True},
"itworx-forge-master-key-backup": {
"name": "itworx-forge-master-key-backup", "external": True
},
}
assert rendered["services"]["runner"] == baseline["services"]["runner"]
assert service["environment"] == baseline["services"]["deploy-control"]["environment"]
@@ -257,6 +487,46 @@ def test_compose_uses_protected_code_and_two_config_views(compose):
assert calls[0][1] == ORIGINAL_COMPOSE
def test_compose_allows_only_the_fixed_managed_appdata_volume_mount(compose):
baseline, _calls = compose
mount = {
"type": "volume",
"source": module.MANAGED_APPDATA_VOLUME,
"target": module.MANAGED_APPDATA_MOUNTPOINT.as_posix(),
}
baseline["services"][module.SERVICE]["volumes"].append(mount)
baseline["volumes"] = {
module.MANAGED_APPDATA_VOLUME: {
"name": module.MANAGED_APPDATA_VOLUME,
"external": True,
}
}
rendered = json.loads(module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID))
assert mount in rendered["services"][module.SERVICE]["volumes"]
assert rendered["volumes"][module.MANAGED_APPDATA_VOLUME] == {
"name": module.MANAGED_APPDATA_VOLUME,
"external": True,
}
def test_compose_rejects_a_managed_appdata_target_alias(compose):
baseline, _calls = compose
baseline["services"][module.SERVICE]["volumes"].append({
"type": "volume",
"source": module.MANAGED_APPDATA_VOLUME,
"target": "/var/lib/docker/volumes/foreign/_data",
})
baseline["volumes"] = {
module.MANAGED_APPDATA_VOLUME: {
"name": module.MANAGED_APPDATA_VOLUME,
"external": True,
}
}
with pytest.raises(module.VolumeTransitionError, match="managed_appdata"):
module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID)
def test_source_relative_controller_bind_uses_original_compose_directory(monkeypatch):
def run(arguments, *, data=None, **kwargs):
if arguments[:3] == ["docker", "image", "inspect"]:
@@ -274,7 +544,14 @@ def test_source_relative_controller_bind_uses_original_compose_directory(monkeyp
assert next(m["source"] for m in mounts if m["target"] == module.EXECUTABLE_TARGET) == module._mount_source_for_revision(REVISION)
@pytest.mark.parametrize("damage", ["target_collision", "foreign_volume", "graph_rw", "relative_bind", "env_file"])
@pytest.mark.parametrize(
"damage",
[
"target_collision", "foreign_volume", "graph_rw", "relative_bind", "env_file",
"backup_target_collision", "backup_volume_alias", "backup_target_child_alias",
"backup_definition_collision",
],
)
def test_compose_rejects_unsafe_or_unpinned_activation_inputs(compose, damage):
baseline, _ = compose
if damage == "target_collision":
@@ -289,12 +566,65 @@ def test_compose_rejects_unsafe_or_unpinned_activation_inputs(compose, damage):
baseline["services"]["deploy-control"]["volumes"][0]["read_only"] = False
elif damage == "relative_bind":
baseline["services"]["runner"]["volumes"][0]["source"] = "./unsafe"
else:
elif damage == "env_file":
baseline["services"]["deploy-control"]["env_file"] = [".env"]
elif damage == "backup_target_collision":
baseline["services"]["deploy-control"]["volumes"].append(
{"type": "bind", "source": "/foreign", "target": "/forge-master-key-backup"}
)
elif damage == "backup_volume_alias":
baseline["services"]["runner"]["volumes"].append(
{
"type": "volume", "source": "itworx-forge-master-key-backup",
"target": "/foreign-backup",
}
)
elif damage == "backup_target_child_alias":
baseline["services"]["runner"]["volumes"].append(
{"type": "bind", "source": "/foreign", "target": "/forge-master-key-backup/state"}
)
else:
baseline["volumes"] = {
"itworx-forge-master-key-backup": {"name": "foreign", "external": True}
}
with pytest.raises(module.VolumeTransitionError):
module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID)
def test_pre_promotion_accepts_the_healthy_controller_without_the_new_mount(compose):
baseline, _calls = compose
reference = "registry.example/controller:stable"
baseline["services"][module.SERVICE]["image"] = reference
current = {
"Image": IMAGE_ID,
"Config": {
"Image": reference,
"Labels": {
"com.docker.compose.project": "gitea-deploy-runners",
"com.docker.compose.service": module.SERVICE,
},
},
"State": {"Health": {"Status": "healthy"}},
"Mounts": [
{
"Type": "volume", "Name": module.VOLUME,
"Source": module.MOUNTPOINT.as_posix(), "Destination": module.TARGET,
"RW": True,
}
],
}
original = f"name: gitea-deploy-runners\nservices:\n deploy-control:\n image: {reference}\n".encode()
rendered = json.loads(
module.rendered_model(original, REVISION, IMAGE_ID, current_container=current)
)
assert not any(mount.get("Destination") == "/forge-master-key-backup" for mount in current["Mounts"])
assert any(
mount.get("target") == "/forge-master-key-backup"
and mount.get("source") == "itworx-forge-master-key-backup"
for mount in rendered["services"][module.SERVICE]["volumes"]
)
def test_compose_tag_replacement_cannot_change_controller_startup_image(compose, monkeypatch):
_baseline, calls = compose
original_run = module._run
@@ -385,6 +715,184 @@ def test_config_snapshot_has_exact_allowlist_and_broker_token():
assert config_dirs == directories
def _forge_secret_entries():
return {
name: (f'fixture:{index}'.encode(), module.FORGE_SECRET_MODE)
for index, name in enumerate(sorted(module.FORGE_SECRET_OWNERS))
}
def test_complete_forge_secret_bundle_has_exact_export_modes_and_owners():
entries = {
'projects.json': (b'{}', 0o600),
'broker-token': (b'fixture-token', 0o600),
'gitea-validation.json': (b'{}', 0o600),
'gitea-validation-token': (b'fixture-validation-token', 0o600),
} | _forge_secret_entries()
directories = {'': 0o755, module.FORGE_SECRET_DIRECTORY: 0o700}
config, config_dirs = module._config_tree(entries, directories)
archive_bytes = module._release_tar_bytes(REVISION, (config, config_dirs))
restored, restored_dirs = module._read_tar_tree(archive_bytes)
assert restored == config
assert restored_dirs == config_dirs
with tarfile.open(fileobj=io.BytesIO(archive_bytes), mode='r:') as archive:
for name, owner in module.FORGE_SECRET_OWNERS.items():
member = archive.getmember(name)
assert (member.uid, member.gid) == owner
assert member.mode == module.FORGE_SECRET_MODE
assert archive.getmember('broker-token').uid == 0
assert archive.getmember('broker-token').gid == 0
@pytest.mark.parametrize('damage', ['partial', 'unknown', 'wrong_mode', 'nested'])
def test_forge_secret_snapshot_requires_the_complete_exact_private_tree(damage):
entries = {
'projects.json': (b'{}', 0o600),
'broker-token': (b'fixture-token', 0o600),
'gitea-validation.json': (b'{}', 0o600),
'gitea-validation-token': (b'fixture-validation-token', 0o600),
} | _forge_secret_entries()
directories = {'': 0o755, module.FORGE_SECRET_DIRECTORY: 0o700}
if damage == 'partial':
entries.pop(min(module.FORGE_SECRET_OWNERS))
elif damage == 'unknown':
entries[f'{module.FORGE_SECRET_DIRECTORY}/unexpected.secret'] = (b'x', 0o400)
elif damage == 'wrong_mode':
name = min(module.FORGE_SECRET_OWNERS)
entries[name] = (entries[name][0], 0o600)
else:
directories[f'{module.FORGE_SECRET_DIRECTORY}/nested'] = 0o700
with pytest.raises(module.VolumeTransitionError):
module._config_tree(entries, directories)
def test_forge_secret_tar_owner_drift_is_rejected():
output = io.BytesIO()
with tarfile.open(fileobj=output, mode='w', format=tarfile.USTAR_FORMAT) as archive:
member = tarfile.TarInfo(
f'{module.FORGE_SECRET_DIRECTORY}/runtime_database_url.secret'
)
member.size = 1
member.mode = module.FORGE_SECRET_MODE
member.uid = member.gid = 0
archive.addfile(member, io.BytesIO(b'x'))
with pytest.raises(module.VolumeTransitionError, match='entry_unsafe'):
module._read_tar_tree(output.getvalue())
def test_config_writer_uses_archive_mode_to_preserve_forge_leaf_owners(monkeypatch):
tree = (
{
'projects.json': (b'{}', 0o600),
'broker-token': (b'fixture-token', 0o600),
'gitea-validation.json': (b'{}', 0o600),
'gitea-validation-token': (b'fixture-validation-token', 0o600),
} | _forge_secret_entries(),
{'': 0o755, module.FORGE_SECRET_DIRECTORY: 0o700},
)
writer_id = 'f' * 64
commands = []
copy_payloads = []
def run(arguments, *, data=None, **_kwargs):
commands.append(arguments)
if arguments == ['docker', 'create']:
return writer_id.encode()
if arguments[:2] == ['docker', 'wait']:
return b'0'
if arguments[:2] == ['docker', 'cp']:
copy_payloads.append(data)
return b''
monkeypatch.setattr(module, '_read_partial_config_leaf', lambda: None)
monkeypatch.setattr(module, 'ensure_volume', lambda **_kwargs: None)
monkeypatch.setattr(module, '_release_tar_bytes', lambda *_args, **_kwargs: b'archive')
monkeypatch.setattr(module, '_remove_stale_writer', lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, '_writer_spec', lambda *_args, **_kwargs: ['docker', 'create'])
monkeypatch.setattr(module, '_inspect_writer', lambda *_args, **_kwargs: {})
monkeypatch.setattr(module, '_read_config_leaf', lambda: tree)
monkeypatch.setattr(module, '_run', run)
module._install_config_tree(tree, IMAGE_ID)
assert [command for command in commands if command[:2] == ['docker', 'cp']] == [[
'docker', 'cp', '-a', '-', f'{writer_id}:{module.TARGET}/{module.CONFIG_DIR}',
]]
assert copy_payloads == [b'archive']
def test_partial_forge_bundle_fails_during_promotion_preparation_before_stop(
monkeypatch, tmp_path
):
valid_config_tree = module._config_tree
events = _prepare_promotion_doubles(monkeypatch, tmp_path)
monkeypatch.setattr(module, '_config_tree', valid_config_tree)
partial = {
'projects.json': (b'{}', 0o600),
'broker-token': (b'fixture-token', 0o600),
'gitea-validation.json': (b'{}', 0o600),
'gitea-validation-token': (b'fixture-validation-token', 0o600),
min(module.FORGE_SECRET_OWNERS): (b'fixture', module.FORGE_SECRET_MODE),
}
directories = {'': 0o755, module.FORGE_SECRET_DIRECTORY: 0o700}
identity = {
'sourceRevision': 'b' * 40,
'sourceTree': 'c' * 40,
'controllerVersion': '2.1.0',
}
monkeypatch.setattr(
module, '_snapshot_attested_controller',
lambda *_args, **_kwargs: (partial, directories, identity),
)
with pytest.raises(module.VolumeTransitionError, match='forge_secret_bundle_incomplete'):
module.prepare_promotion(
tmp_path / 'stage',
source_revision='d' * 40,
source_tree='e' * 40,
controller_version='2.2.0',
image_id=IMAGE_ID,
)
assert 'backup_volume_prepared' not in events
@pytest.mark.skipif(os.name == 'nt' or not hasattr(os, 'geteuid') or os.geteuid() != 0,
reason='fixed Forge ownership roundtrip requires Linux root')
def test_forge_secret_filesystem_capture_and_private_stage_keep_exact_owner(tmp_path):
config_root = tmp_path / 'config'
config_root.mkdir(mode=0o700)
secret_root = config_root / module.FORGE_SECRET_DIRECTORY
secret_root.mkdir(mode=0o700)
for index, (relative, owner) in enumerate(sorted(module.FORGE_SECRET_OWNERS.items())):
path = config_root / relative
path.write_bytes(f'fixture:{index}'.encode())
os.chown(path, *owner)
path.chmod(module.FORGE_SECRET_MODE)
descriptor = os.open(config_root, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
try:
entries, directories = module._read_tree_fd(descriptor, allow_forge_secrets=True)
finally:
os.close(descriptor)
config, config_dirs = module._config_tree(entries, directories, allow_partial=True)
payload = module._release_tar_bytes(REVISION, (config, config_dirs))
roundtrip, roundtrip_dirs = module._read_tar_tree(payload)
assert roundtrip == config
assert roundtrip_dirs == config_dirs
staged = tmp_path / 'private-stage'
module._write_private_tree(staged, config, config_dirs)
for relative, owner in module.FORGE_SECRET_OWNERS.items():
info = (staged / relative).stat(follow_symlinks=False)
assert (info.st_uid, info.st_gid) == owner
assert stat.S_IMODE(info.st_mode) == module.FORGE_SECRET_MODE
def test_unknown_config_file_is_rejected():
entries = {
"projects.json": (b"{}", 0o600),
@@ -655,6 +1163,154 @@ def test_installer_release_order_matches_renderer_contract():
assert module.RELEASE_FILES[:3] == (
"controller_paths.py", "mcp_hub_release_store.py", "hub_permission_preparation.py"
)
assert module.RELEASE_FILES[module.RELEASE_FILES.index("forge_first_install.py") + 1] == (
"forge_database_credentials.py"
)
helper_index = module.RELEASE_FILES.index("forge_database_credentials.py") + 1
assert module.RELEASE_FILES[helper_index : helper_index + 2] == (
"forge_backup_paths.py", "forge_secret_bundle.py"
)
assert module.LEGACY_RELEASE_FILES_WITH_ROLE_HELPER == tuple(
name for name in module.RELEASE_FILES
if name not in {"forge_backup_paths.py", "forge_secret_bundle.py"}
)
assert module.LEGACY_RELEASE_FILES_WITHOUT_ROLE_HELPER == tuple(
name for name in module.LEGACY_RELEASE_FILES_WITH_ROLE_HELPER
if name != "forge_database_credentials.py"
)
installer = (ROOT / "scripts/upgrade_deployment_controller.sh").read_text(encoding="utf-8")
module_list = installer.split("module_files=(", 1)[1].split(")", 1)[0].split()
module_helper_index = module_list.index("forge_database_credentials.py") + 1
assert module_list[module_helper_index : module_helper_index + 2] == [
"forge_backup_paths.py", "forge_secret_bundle.py"
]
source_list = installer.split("source_paths=(", 1)[1].split(")", 1)[0].split()
source_helper_index = source_list.index("ops/gitea-deploy-broker/forge_database_credentials.py") + 1
assert source_list[source_helper_index : source_helper_index + 2] == [
"ops/gitea-deploy-broker/forge_backup_paths.py",
"ops/gitea-deploy-broker/forge_secret_bundle.py",
]
def test_current_release_digest_binds_the_role_password_helper():
entries = {name: (f"source:{name}".encode(), 0o644) for name in module.RELEASE_FILES}
expected = module._artifact_digest(entries)
assert module._matching_release_files(entries, expected) == module.RELEASE_FILES
entries["forge_database_credentials.py"] = (b"tampered helper", 0o644)
with pytest.raises(module.VolumeTransitionError, match="release_identity_invalid"):
module._matching_release_files(entries, expected)
def test_previous_role_helper_release_digest_remains_recognized_and_bound():
files = module.LEGACY_RELEASE_FILES_WITH_ROLE_HELPER
entries = {name: (f"source:{name}".encode(), 0o644) for name in files}
expected = module._artifact_digest(entries, files)
assert module._matching_release_files(entries, expected) == files
entries["forge_database_credentials.py"] = (b"tampered role helper", 0o644)
with pytest.raises(module.VolumeTransitionError, match="release_identity_invalid"):
module._matching_release_files(entries, expected)
def test_historical_config_root_manifest_order_and_digest_remain_exact():
files = HISTORICAL_CONFIG_ROOT_RELEASE_FILES
assert module.LEGACY_RELEASE_FILES_WITH_CONFIG_ROOT == files
entries = {name: (f"source:{name}".encode(), 0o644) for name in files}
expected = module._artifact_digest(entries, files)
assert module._matching_release_files(entries, expected) == files
entries["status_server.py"] = (b"tampered historical file", 0o644)
with pytest.raises(module.VolumeTransitionError, match="release_identity_invalid"):
module._matching_release_files(entries, expected)
def test_activated_controller_must_have_the_exact_backup_mount(monkeypatch):
mounts = [
{
"Type": "bind", "Source": module._expected_revision_path(REVISION).as_posix(),
"Destination": module.EXECUTABLE_TARGET, "RW": False,
},
{
"Type": "volume", "Name": module.VOLUME,
"Source": module.MOUNTPOINT.as_posix(), "Destination": module.TARGET, "RW": True,
},
{
"Type": "bind", "Source": module._expected_config_path().as_posix(),
"Destination": module.CONFIG_TARGET, "RW": False,
},
{
"Type": "bind", "Source": module.DOCKER_ROOT.as_posix(),
"Destination": module.DOCKER_ROOT.as_posix(), "RW": False,
},
{
"Type": "volume", "Name": "itworx-forge-master-key-backup",
"Source": "/var/lib/docker/volumes/itworx-forge-master-key-backup/_data",
"Destination": "/forge-master-key-backup", "RW": True,
},
]
item = {"Mounts": mounts}
monkeypatch.setattr(module, "_controller_image_inspect", lambda _image_id: item)
monkeypatch.setattr(module, "_read_release_leaf", lambda _revision: ({}, {"": 0o755}))
monkeypatch.setattr(module, "_release_identity", lambda _entries: {"sourceRevision": REVISION})
monkeypatch.setattr(module, "_read_config_leaf", lambda: ({}, {"": 0o755}))
module._verify_active_mount(REVISION, IMAGE_ID)
mounts.pop()
with pytest.raises(module.VolumeTransitionError, match="backup_mount"):
module._verify_active_mount(REVISION, IMAGE_ID)
@pytest.mark.parametrize(
"damage",
["backup_readonly", "backup_name", "backup_source", "backup_alias", "docker_root_rw"],
)
def test_activated_controller_rejects_backup_or_docker_root_mount_drift(
monkeypatch, damage
):
mounts = [
{
"Type": "bind", "Source": module._expected_revision_path(REVISION).as_posix(),
"Destination": module.EXECUTABLE_TARGET, "RW": False,
},
{
"Type": "volume", "Name": module.VOLUME,
"Source": module.MOUNTPOINT.as_posix(), "Destination": module.TARGET, "RW": True,
},
{
"Type": "bind", "Source": module._expected_config_path().as_posix(),
"Destination": module.CONFIG_TARGET, "RW": False,
},
{
"Type": "bind", "Source": module.DOCKER_ROOT.as_posix(),
"Destination": module.DOCKER_ROOT.as_posix(), "RW": False,
},
{
"Type": "volume", "Name": module.BACKUP_VOLUME,
"Source": module.BACKUP_MOUNTPOINT.as_posix(),
"Destination": module.BACKUP_TARGET, "RW": True,
},
]
if damage == "backup_readonly":
mounts[-1]["RW"] = False
elif damage == "backup_name":
mounts[-1]["Name"] = "foreign-volume"
elif damage == "backup_source":
mounts[-1]["Source"] = "/var/lib/docker/volumes/foreign/_data"
elif damage == "backup_alias":
mounts.append({
"Type": "bind", "Source": "/foreign", "Destination": f"{module.BACKUP_TARGET}/child",
"RW": True,
})
else:
mounts[-2]["RW"] = True
monkeypatch.setattr(module, "_controller_image_inspect", lambda _image_id: {"Mounts": mounts})
monkeypatch.setattr(module, "_read_release_leaf", lambda _revision: ({}, {"": 0o755}))
monkeypatch.setattr(module, "_release_identity", lambda _entries: {"sourceRevision": REVISION})
monkeypatch.setattr(module, "_read_config_leaf", lambda: ({}, {"": 0o755}))
with pytest.raises(module.VolumeTransitionError):
module._verify_active_mount(REVISION, IMAGE_ID)
@pytest.mark.skipif(os.name == "nt", reason="dirfd swap test requires POSIX openat")
@@ -943,6 +1599,10 @@ def test_capture_program_is_fixed_descriptor_based_and_compiles():
@pytest.mark.parametrize("legacy_config", [True, False])
def test_capture_archive_roundtrips_through_a_nonseekable_pipe(legacy_config):
source = module._controller_capture_source(config_root_in_code=legacy_config)
assert '__FORGE_' not in source
assert 'runtime_database_url.secret' in source
assert '/forge-master-key-backup' not in source
compile(source, 'controller-capture', 'exec')
statements = ast.parse(source).body
start = next(i for i, node in enumerate(statements)
if isinstance(node, ast.Assign)
@@ -958,6 +1618,7 @@ def test_capture_archive_roundtrips_through_a_nonseekable_pipe(legacy_config):
"import io,sys,tarfile\n"
"records={'deploy.py':(b'fixture source',0o644),'secrets/key':(b'fixture secret',0o600)}\n"
"dirs={'secrets':0o700}\n"
"forge_secret_owners={}\n"
)
result = subprocess.run([sys.executable, "-c", fixture + archive_program],
capture_output=True, timeout=10, check=False)
@@ -971,8 +1632,13 @@ def test_capture_archive_roundtrips_through_a_nonseekable_pipe(legacy_config):
assert all(member.uid == member.gid == 0 for member in archive.getmembers())
def test_prior_release_capture_must_match_protected_git_tree(monkeypatch):
files = module.LEGACY_RELEASE_FILES_2_1_5
@pytest.mark.parametrize("files", [
module.LEGACY_RELEASE_FILES_WITHOUT_ROLE_HELPER,
module.LEGACY_RELEASE_FILES_2_1_5,
module.LEGACY_RELEASE_FILES_WITH_CONFIG_ROOT,
module.LEGACY_RELEASE_FILES_BEFORE_FORGE,
])
def test_prior_release_capture_must_match_protected_git_tree(monkeypatch, files):
entries = {name: (f"source:{name}".encode(), 0o644) for name in files}
source_revision = "32a6eece0e3e1276946abdc04196c819daf02e2a"
source_tree = "e0d7e80b79504b964d968e51e0d21c288cac8f0d"
@@ -31,6 +31,10 @@ def test_windows_wrapper_archives_every_file_required_by_root_installer():
)
assert vendor_sources == required_vendor_sources
assert "scripts/prepare_controller_hub_release_volume.py" in archived.group(1)
assert "ops/gitea-deploy-broker/forge_first_install.py" in required_paths
assert "ops/gitea-deploy-broker/forge_database_credentials.py" in required_paths
assert "ops/gitea-deploy-broker/forge_backup_paths.py" in required_paths
assert "ops/gitea-deploy-broker/forge_secret_bundle.py" in required_paths
assert "scripts/upgrade_deployment_controller.sh" in archived.group(1)
assert "scripts/vendor" in archived.group(1)
assert "scripts/vendor/pyyaml-LICENSE" in required.group(1)
@@ -39,6 +43,28 @@ def test_windows_wrapper_archives_every_file_required_by_root_installer():
assert '"${work}/vendor/yaml/${vendor_file}"' in installer
assert ('[[ -d "${script_dir}/vendor" && ! -L "${script_dir}/vendor" && '
'-d "${script_dir}/vendor/yaml" && ! -L "${script_dir}/vendor/yaml" ]]') in installer
assert "forge_first_install.py" in installer.split("module_files=(", 1)[1].split(")", 1)[0]
assert "forge_database_credentials.py" in installer.split("module_files=(", 1)[1].split(")", 1)[0]
assert "forge_backup_paths.py" in installer.split("module_files=(", 1)[1].split(")", 1)[0]
assert "forge_secret_bundle.py" in installer.split("module_files=(", 1)[1].split(")", 1)[0]
def test_forge_first_install_module_is_bound_into_controller_release_and_config():
renderer = (ROOT / "scripts/prepare_controller_artifact.py").read_text(encoding="utf-8")
preparer = (ROOT / "scripts/prepare_controller_hub_release_volume.py").read_text(encoding="utf-8")
assert '"forge_first_install.py"' in renderer.split("_ARTIFACT_FILES = (", 1)[1].split(")", 1)[0]
release_files = preparer.split("RELEASE_FILES = (", 1)[1].split(")", 1)[0]
legacy_role_release_files = preparer.split(
"LEGACY_RELEASE_FILES_WITH_ROLE_HELPER = (", 1
)[1].split(")", 1)[0]
assert "'forge_first_install.py'" in legacy_role_release_files
assert "'forge_database_credentials.py'" in legacy_role_release_files
assert "*LEGACY_RELEASE_FILES_WITH_ROLE_HELPER" in release_files
assert "'forge_backup_paths.py'" in release_files
assert "'forge_secret_bundle.py'" in release_files
assert "'forge-first-install.json'" in preparer.split("CONFIG_FILES = {", 1)[1].split("}", 1)[0]
assert "'forge-bootstrap-secrets'" in preparer.split("CONFIG_SECRET_DIRS = {", 1)[1].split("}", 1)[0]
def test_windows_wrapper_supplies_private_archive_and_exact_image_identity():
@@ -192,6 +192,8 @@ def test_upgrade_prepares_before_stop_and_reconciles_between_stop_and_activation
activate = 'python3 "${work}/${PREPARER_NAME}" activate --backup "$backup"'
assert '/srv/example/appdata/gitea-deploy-runners/deploy.lock' not in script
assert 'lock_path="${ROOT}/promotion.lock"' in script
assert 'lock_path="${VOLUME_ROOT}/promotion.lock"' not in script
assert script.index(prepare) < script.index(stop)
assert script.index(stop) < script.index(reconcile)
assert script.index(reconcile) < script.index(activate)
+6
View File
@@ -7001,6 +7001,12 @@ def test_controller_release_identity_is_content_bound(status_module, tmp_path, m
renderer = importlib.util.module_from_spec(renderer_spec)
renderer_spec.loader.exec_module(renderer)
assert renderer._ARTIFACT_FILES == status_module._CONTROLLER_ARTIFACT_FILES
forge_first_install_index = status_module._CONTROLLER_ARTIFACT_FILES.index(
"forge_first_install.py"
)
assert status_module._CONTROLLER_ARTIFACT_FILES[forge_first_install_index + 1] == (
"forge_database_credentials.py"
)
artifact_root = tmp_path / "controller"
artifact_root.mkdir()
for filename in status_module._CONTROLLER_ARTIFACT_FILES:
+339
View File
@@ -0,0 +1,339 @@
from __future__ import annotations
import importlib.util
import os
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
MODULE_PATH = ROOT / "ops/gitea-deploy-broker/forge_backup_paths.py"
if MODULE_PATH.is_file():
spec = importlib.util.spec_from_file_location("forge_backup_paths_test", MODULE_PATH)
assert spec is not None and spec.loader is not None
paths = importlib.util.module_from_spec(spec)
spec.loader.exec_module(paths)
else:
paths = None
def _paths():
if paths is None:
pytest.fail("fixed Forge backup path verifier has not been implemented", pytrace=False)
return paths
def volume_metadata():
return {
"Name": "itworx-forge-master-key-backup",
"Driver": "local",
"Scope": "local",
"Options": None,
"Labels": {
"itworx.managed-by": "deployment-controller",
"itworx.purpose": "forge-master-key-backup",
},
"Mountpoint": "/var/lib/docker/volumes/itworx-forge-master-key-backup/_data",
}
def controller_mounts():
return [
{
"Type": "volume",
"Name": "itworx-forge-master-key-backup",
"Driver": "local",
"Source": "/var/lib/docker/volumes/itworx-forge-master-key-backup/_data",
"Destination": "/forge-master-key-backup",
"RW": True,
},
{
"Type": "bind",
"Name": "",
"Source": "/var/lib/docker",
"Destination": "/var/lib/docker",
"RW": False,
},
{
"Type": "volume",
"Name": "itworx-managed-appdata",
"Driver": "local",
"Source": "/var/lib/docker/volumes/itworx-managed-appdata/_data",
"Destination": "/var/lib/docker/volumes/itworx-managed-appdata/_data",
"RW": True,
},
{
"Type": "volume",
"Name": "itworx-controller-hub-releases",
"Driver": "local",
"Source": "/var/lib/docker/volumes/itworx-controller-hub-releases/_data",
"Destination": "/controller-hub-releases",
"RW": True,
},
{
"Type": "bind",
"Name": "",
"Source": "/var/lib/docker/volumes/itworx-controller-hub-releases/_data/controller-config",
"Destination": "/controller-config",
"RW": False,
},
]
def mountinfo(*, backup_options="rw,nosuid,nodev", docker_options="ro,relatime"):
base = [
"35 25 8:1 / / rw,relatime - ext4 /dev/sda1 rw,relatime",
f"41 35 8:2 / /var/lib/docker {docker_options} - ext4 /dev/sda2 ro,relatime",
f"42 35 8:3 / /forge-master-key-backup {backup_options} - ext4 /dev/sda3 rw,relatime",
]
base.extend(
f"{50 + index} 41 8:{index + 4} / /var/lib/docker/containers/c{index}/mount{index} "
f"{'ro' if index % 2 else 'rw'},relatime - ext4 /dev/sdb{index} rw,relatime"
for index in range(35)
)
return "\n".join(base)
def test_exact_local_backup_volume_metadata_is_accepted():
module = _paths()
assert module.validate_volume_metadata(volume_metadata(), "/var/lib/docker") is None
@pytest.mark.parametrize(
("field", "value"),
[
("Name", "foreign"),
("Driver", "nfs"),
("Scope", "global"),
("Options", {"device": "/mnt/share"}),
("Options", []),
("Labels", {"itworx.managed-by": "deployment-controller"}),
("Labels", {
"itworx.managed-by": "deployment-controller",
"itworx.purpose": "managed-appdata",
}),
("Mountpoint", "/mnt/foreign"),
],
)
def test_backup_volume_metadata_drift_is_rejected(field, value):
module = _paths()
item = volume_metadata()
item[field] = value
with pytest.raises(module.UnsafeForgeBackupVolume):
module.validate_volume_metadata(item, "/var/lib/docker")
def test_backup_volume_metadata_requires_the_fixed_docker_root():
module = _paths()
with pytest.raises(module.UnsafeForgeBackupVolume):
module.validate_volume_metadata(volume_metadata(), "/mnt/docker")
def test_mount_contract_allows_only_the_fixed_rw_backup_and_ro_docker_root():
module = _paths()
assert module.validate_controller_mounts(
controller_mounts(),
controller_id_before="a" * 64,
controller_id_after="a" * 64,
) is None
def test_mount_contract_allows_managed_appdata_to_be_absent():
module = _paths()
mounts = controller_mounts()
mounts.pop(2)
assert module.validate_controller_mounts(
mounts,
controller_id_before="a" * 64,
controller_id_after="a" * 64,
) is None
def test_mount_contract_allows_only_the_exact_managed_appdata_nested_volume():
module = _paths()
mounts = controller_mounts()
module.validate_controller_mounts(
mounts,
controller_id_before="a" * 64,
controller_id_after="a" * 64,
)
mounts[-3]["Destination"] = "/var/lib/docker/volumes/foreign/_data"
with pytest.raises(module.UnsafeForgeBackupVolume):
module.validate_controller_mounts(
mounts,
controller_id_before="a" * 64,
controller_id_after="a" * 64,
)
@pytest.mark.parametrize(
"damage",
[
"controller_changed",
"backup_rw_disabled",
"backup_name_changed",
"backup_source_changed",
"backup_destination_changed",
"backup_driver_changed",
"duplicate_backup_mount",
"backup_source_alias",
"backup_target_child_alias",
"backup_target_parent_alias",
"docker_root_rw",
"docker_root_child_mount",
"managed_appdata_duplicate",
"managed_appdata_source_changed",
"managed_appdata_driver_changed",
"managed_appdata_target_alias",
"managed_appdata_name_alias",
"root_exposure",
],
)
def test_mount_contract_rejects_aliases_and_mount_drift(damage):
module = _paths()
mounts = controller_mounts()
before = after = "a" * 64
if damage == "controller_changed":
after = "b" * 64
elif damage == "backup_rw_disabled":
mounts[0]["RW"] = False
elif damage == "backup_name_changed":
mounts[0]["Name"] = "foreign"
elif damage == "backup_source_changed":
mounts[0]["Source"] = "/mnt/foreign"
elif damage == "backup_destination_changed":
mounts[0]["Destination"] = "/backup-copy"
elif damage == "backup_driver_changed":
mounts[0]["Driver"] = "nfs"
elif damage == "duplicate_backup_mount":
mounts.append(dict(mounts[0]))
elif damage == "backup_source_alias":
mounts.append({
"Type": "bind", "Source": volume_metadata()["Mountpoint"],
"Destination": "/backup-copy", "RW": False,
})
elif damage == "backup_target_child_alias":
mounts.append({
"Type": "bind", "Source": "/foreign", "Destination": "/forge-master-key-backup/state",
"RW": True,
})
elif damage == "backup_target_parent_alias":
mounts.append({
"Type": "bind", "Source": "/foreign", "Destination": "/",
"RW": True,
})
elif damage == "docker_root_rw":
mounts[1]["RW"] = True
elif damage == "docker_root_child_mount":
mounts.append({
"Type": "bind", "Source": "/var/lib/docker/volumes/other",
"Destination": "/var/lib/docker/volumes/other", "RW": True,
})
elif damage == "managed_appdata_duplicate":
mounts.append(dict(mounts[2]))
elif damage == "managed_appdata_source_changed":
mounts[2]["Source"] = "/var/lib/docker/volumes/foreign/_data"
elif damage == "managed_appdata_driver_changed":
mounts[2]["Driver"] = "nfs"
elif damage == "managed_appdata_target_alias":
mounts[2]["Destination"] = "/appdata-copy"
elif damage == "managed_appdata_name_alias":
mounts[2].update({
"Source": "/var/lib/docker/volumes/foreign/_data",
"Destination": "/appdata-copy",
})
else:
mounts.append({
"Type": "bind", "Source": "/", "Destination": "/", "RW": True,
})
with pytest.raises(module.UnsafeForgeBackupVolume):
module.validate_controller_mounts(
mounts, controller_id_before=before, controller_id_after=after
)
def test_proc_mountinfo_requires_the_exact_rw_backup_and_ro_docker_mounts():
module = _paths()
assert module._validate_mountinfo_contract(mountinfo()) is None
for unsafe in (
mountinfo(backup_options="ro,nosuid,nodev"),
mountinfo(docker_options="rw,relatime"),
mountinfo().replace("/forge-master-key-backup", "/backup-copy"),
mountinfo() + "\n43 35 8:4 / /forge-master-key-backup rw - ext4 /dev/sda4 rw",
):
with pytest.raises(module.UnsafeForgeBackupVolume):
module._validate_mountinfo_contract(unsafe)
@pytest.mark.skipif(os.name == "nt", reason="directory inode checks require POSIX descriptors")
def test_directory_identity_binds_backup_mount_to_the_fixed_docker_volume_source(tmp_path):
module = _paths()
source = tmp_path / "source"
mounted = tmp_path / "mounted"
source.mkdir()
mounted.mkdir()
source_fd = os.open(source, os.O_RDONLY)
mounted_fd = os.open(mounted, os.O_RDONLY)
same_fd = os.open(source, os.O_RDONLY)
try:
module._require_same_directory(source_fd, same_fd)
with pytest.raises(module.UnsafeForgeBackupVolume, match="mount_source_mismatch"):
module._require_same_directory(source_fd, mounted_fd)
finally:
os.close(source_fd)
os.close(mounted_fd)
os.close(same_fd)
@pytest.mark.skipif(
os.name == "nt" or not hasattr(os, "geteuid") or os.geteuid() != 0,
reason="directory ownership checks require root on POSIX",
)
def test_fixed_directory_walk_rejects_symlinks_and_unsafe_modes(tmp_path):
module = _paths()
volume_root = tmp_path / "volume"
volume_root.mkdir(mode=0o755)
volume_root.chmod(0o755)
fd = module._open_directory_no_symlinks(volume_root, anchor=tmp_path)
os.close(fd)
linked = tmp_path / "linked"
linked.symlink_to(volume_root, target_is_directory=True)
with pytest.raises(module.UnsafeForgeBackupVolume):
module._open_directory_no_symlinks(linked, anchor=tmp_path)
volume_root.chmod(0o777)
with pytest.raises(module.UnsafeForgeBackupVolume):
module._open_directory_no_symlinks(volume_root, anchor=tmp_path)
@pytest.mark.skipif(
os.name == "nt" or not hasattr(os, "geteuid") or os.geteuid() != 0,
reason="directory ACL checks require root on POSIX",
)
def test_fixed_directory_walk_rejects_access_acl(tmp_path, monkeypatch):
module = _paths()
volume_root = tmp_path / "volume"
volume_root.mkdir(mode=0o755)
volume_root.chmod(0o755)
monkeypatch.setattr(module.os, "listxattr", lambda _fd: ["system.posix_acl_access"])
with pytest.raises(module.UnsafeForgeBackupVolume):
module._open_directory_no_symlinks(volume_root, anchor=tmp_path)
def test_read_only_identity_check_fails_closed_without_creating_or_repairing_paths(tmp_path, monkeypatch):
module = _paths()
sentinel = tmp_path / "unchanged"
sentinel.write_bytes(b"keep")
bad = volume_metadata()
bad["Labels"]["itworx.purpose"] = "foreign-purpose"
monkeypatch.setattr(module, "_read_volume_snapshot", lambda: (bad, "/var/lib/docker"))
monkeypatch.setattr(
module,
"_open_fixed_backup_root",
lambda: pytest.fail("unsafe volume metadata must fail before opening the data root"),
)
with pytest.raises(module.UnsafeForgeBackupVolume):
module.validate_forge_backup_volume()
assert sentinel.read_bytes() == b"keep"
assert sorted(path.name for path in tmp_path.iterdir()) == ["unchanged"]
File diff suppressed because it is too large. Load diff
@@ -177,6 +177,11 @@ def test_forge_host_preflight_compares_exact_main_and_reports_fixed_runtime_chec
monkeypatch.setattr(Path, "lstat", lstat_with_fixed_service_owners)
monkeypatch.setattr(status_module, "_forge_gitea_main_revision", lambda: revision)
monkeypatch.setattr(status_module, "_forge_push_validation", lambda _revision: "unknown")
monkeypatch.setattr(
status_module,
"_load_forge_bootstrap_config",
lambda _path: {"private_package_owner": "itworx-forge-private"},
)
monkeypatch.setattr(
status_module, "FORGE_COMPOSE_SHA256", hashlib.sha256(manifest).hexdigest()
)
@@ -217,13 +222,14 @@ def test_forge_host_preflight_compares_exact_main_and_reports_fixed_runtime_chec
"frontdoor_image_digest": "sha256:" + "c" * 64,
}
result = status_module._forge_bootstrap_runtime_checks(profile)
assert result["status"] == "preflight_unknown"
assert result["status"] == "preflight_blocked"
assert result["checks"]["exact_main"] == {"status": "passed"}
assert result["checks"]["source_manifest"] == {"status": "passed"}
assert result["checks"]["managed_appdata"] == {"status": "passed"}
assert result["checks"]["proxy_network"] == {"status": "passed"}
assert result["checks"]["resource_collisions"] == {"status": "passed"}
assert result["checks"]["push_validation"] == {"status": "unknown"}
assert result["checks"]["secret_delivery"] == {"status": "blocked"}
assert result["evidence"]["mainRevision"] == revision
assert result["evidence"]["sourceManifestSha256"] == hashlib.sha256(manifest).hexdigest()
assert result["evidence"]["workspaceImagePresent"] is True
@@ -2,6 +2,7 @@ from __future__ import annotations
from sqlmodel import Session, SQLModel, create_engine
from backend.app.core.config import settings
from backend.app.db.models import DeploymentJob
from backend.app.schemas.enrollment import EnrollmentTrustConfigV1
from backend.app.schemas.forge_bootstrap import ForgeBootstrapPreflightV1
@@ -10,6 +11,9 @@ from backend.app.services.forge_bootstrap_preflight import (
ForgeBootstrapPreflightService,
)
_PRIVATE_OWNER = "itworx-forge-private"
_SOURCE_URL = "https://gitea.example.com/Jens/itworx-forge"
def _profile(**overrides):
value = {
@@ -58,6 +62,67 @@ def test_forge_profile_is_strict_and_cannot_select_runtime_inputs():
raise AssertionError(f"invalid {field} must be rejected")
def test_preflight_registry_attestation_binds_protected_owner_and_both_image_labels(monkeypatch):
from backend.app.services.forge_bootstrap_preflight import (
_checked_controller_result,
)
profile = _profile()
checks = {
name: {"status": "unknown"}
for name in (
"exact_main", "source_manifest", "managed_appdata", "proxy_network",
"resource_collisions", "push_validation", "image_registry", "oidc",
"secret_delivery",
)
}
checks["image_registry"] = {"status": "passed"}
registry = {
"owner": _PRIVATE_OWNER,
"ownerVisibility": "private",
"workspaceDigest": profile.workspace_image_digest,
"frontdoorDigest": profile.frontdoor_image_digest,
"workspacePullable": True,
"frontdoorPullable": True,
"workspaceSource": _SOURCE_URL,
"workspaceRevision": profile.revision,
"frontdoorSource": _SOURCE_URL,
"frontdoorRevision": profile.revision,
}
result = {
"apiVersion": "itworx.forge-bootstrap-preflight/v1",
"status": "preflight_unknown",
"revision": profile.revision,
"workspaceImageDigest": profile.workspace_image_digest,
"frontdoorImageDigest": profile.frontdoor_image_digest,
"checks": checks,
"evidence": {"attestations": {"image_registry": registry}},
}
monkeypatch.setitem(settings.apis, "FORGE_PRIVATE_PACKAGE_OWNER", _PRIVATE_OWNER)
accepted = _checked_controller_result(result, profile)
assert accepted is not None
assert accepted[1]["attestations"]["image_registry"] == registry
for protected_owner in (None, "Jens", "owner.with.dot", "a" * 40):
monkeypatch.setitem(settings.apis, "FORGE_PRIVATE_PACKAGE_OWNER", protected_owner)
assert _checked_controller_result(result, profile) is None
monkeypatch.setitem(settings.apis, "FORGE_PRIVATE_PACKAGE_OWNER", _PRIVATE_OWNER)
for field, invalid in (
("owner", "another-owner"),
("workspaceSource", "https://attacker.invalid/repo"),
("frontdoorRevision", "d" * 40),
("workspaceDigest", profile.frontdoor_image_digest),
("workspacePullable", 1),
("frontdoorPullable", 1),
("workspacePullable", "true"),
("frontdoorPullable", None),
):
bad = {**registry, field: invalid}
result["evidence"]["attestations"]["image_registry"] = bad
assert _checked_controller_result(result, profile) is None
class _Broker:
def __init__(self, result):
self.result = result
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,487 @@
from __future__ import annotations
import importlib.util
import json
import os
import sys
import threading
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
BROKER = ROOT / "ops" / "gitea-deploy-broker"
if str(BROKER) not in sys.path:
sys.path.insert(0, str(BROKER))
@pytest.fixture
def credentials_module():
spec = importlib.util.spec_from_file_location(
"forge_database_credentials_test",
BROKER / "forge_database_credentials.py",
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _secrets(module):
return {
module.RUNTIME_ROLE: "runtime-secret-SENTINEL-0123456789abcdef",
module.MIGRATOR_ROLE: "migrator-secret-SENTINEL-fedcba9876543210",
}
def _uri(role: str, password: str) -> bytes:
return f"postgresql://{role}:{password}@postgres:5432/forge".encode("ascii")
def _install_fake_docker(
module,
tmp_path: Path,
secrets: dict[str, str],
*,
behavior: str = "success",
):
marker = tmp_path / "fake-docker-state.txt"
script = tmp_path / "fake-docker"
secret_literals = json.dumps(secrets)
marker_literal = json.dumps(str(marker))
script.write_text(
f"#!{sys.executable}\n"
"import json, os, sys, termios, time\n"
f"expected = {secret_literals}\n"
f"marker = {marker_literal}\n"
f"behavior = {behavior!r}\n"
"args = sys.argv[1:]\n"
"try:\n"
" role_arg = args[args.index('-c') + 1]\n"
" role = role_arg.split(' ', 1)[1]\n"
"except Exception:\n"
" open(marker, 'a').write('bad-command\\n')\n"
" raise SystemExit(90)\n"
f"expected_args = ['exec', '-i', '-t', '--user', '70:70', '--env', 'LANG=C', '--env', 'LC_ALL=C', '--env', 'PGPASSFILE=/dev/null', '--env', 'PGSERVICEFILE=/dev/null', '--env', 'PGPASSWORD=', '--env', 'PGOPTIONS=', 'itworx-forge-postgres-1', '/bin/busybox', 'timeout', '-s', 'TERM', '-k', '{module.REMOTE_KILL_AFTER_SECONDS}', '{module.REMOTE_TIMEOUT_SECONDS}', 'psql', '-X', '-w', '-v', 'ON_ERROR_STOP=1', '-h', '/var/run/postgresql', '-p', '5432', '-U', 'forge_bootstrap', '-d', 'forge', '-c', role_arg]\n"
"if args != expected_args or role not in expected:\n"
" open(marker, 'a').write('bad-command\\n')\n"
" raise SystemExit(91)\n"
"secret = expected[role]\n"
"if any(secret in value for value in sys.argv + list(os.environ.values())):\n"
" open(marker, 'a').write('secret-in-command-or-env\\n')\n"
" raise SystemExit(92)\n"
"if termios.tcgetattr(0)[3] & (termios.ECHO | termios.ECHONL):\n"
" open(marker, 'a').write('echo-enabled\\n')\n"
" raise SystemExit(93)\n"
"if behavior == 'exit-before-prompt':\n"
" raise SystemExit(7)\n"
"if behavior == 'timeout-before-prompt':\n"
" open(marker + '.pid', 'w').write(str(os.getpid()))\n"
" time.sleep(30)\n"
" raise SystemExit(0)\n"
"if behavior == 'output-flood':\n"
" os.write(2, b'x' * 65536)\n"
" time.sleep(30)\n"
" raise SystemExit(0)\n"
"if behavior == 'prompts-then-exit':\n"
" os.write(2, f'Enter new password for user \\\"{role}\\\": Enter it again: '.encode())\n"
" raise SystemExit(0)\n"
"os.write(2, f'Enter new password for user \\\"{role}\\\": '.encode())\n"
"first = sys.stdin.readline().rstrip('\\r\\n')\n"
"if first != secret:\n"
" open(marker, 'a').write('first-mismatch\\n')\n"
" raise SystemExit(94)\n"
"if behavior == 'echo-received':\n"
" os.write(2, first.encode() + b'\\n')\n"
"if behavior == 'exit-after-first':\n"
" raise SystemExit(8)\n"
"os.write(2, b'Enter it again: ')\n"
"second = sys.stdin.readline().rstrip('\\r\\n')\n"
"if second != secret:\n"
" open(marker, 'a').write('second-mismatch\\n')\n"
" raise SystemExit(95)\n"
"open(marker, 'a').write(role + '=matched-noecho\\n')\n"
"raise SystemExit(0)\n",
encoding="utf-8",
)
script.chmod(0o700)
module._DOCKER_CLI = str(script)
module._CHILD_PATH = str(tmp_path)
return marker
def _install_uri_reader(monkeypatch, module, secrets):
uri_by_name = {
"runtime_database_url": _uri(module.RUNTIME_ROLE, secrets[module.RUNTIME_ROLE]),
"migrator_database_url": _uri(module.MIGRATOR_ROLE, secrets[module.MIGRATOR_ROLE]),
}
reads = []
def read(_root, name, role):
reads.append((name, role))
return uri_by_name.get(name)
monkeypatch.setattr(
module,
"read_database_uri",
read,
)
return reads
def _journal(initial):
states = dict(initial)
calls: list[tuple[str, str]] = []
def role_state(role):
return states[role]
def transition(name, allowed, destination):
def apply(role):
if states[role] not in allowed:
raise RuntimeError("compare-and-swap rejected")
states[role] = destination
calls.append((name, role))
return True
return apply
return states, calls, {
"role_state": role_state,
"persist_role_intent": transition("intent", {"pending", "retryable"}, "intent"),
"persist_role_retryable": transition("retryable", {"intent"}, "retryable"),
"persist_role_unknown": transition("unknown", {"intent"}, "unknown"),
"persist_role_complete": transition("complete", {"intent"}, "completed"),
}
def _run(module, secret_root, journal):
return module.set_forge_database_role_passwords(secret_root, **journal)
def test_fixed_roles_are_set_through_hidden_prompts_without_secret_output(
credentials_module, monkeypatch, tmp_path
):
module = credentials_module
secrets = _secrets(module)
reads = _install_uri_reader(monkeypatch, module, secrets)
marker = _install_fake_docker(module, tmp_path, secrets, behavior="echo-received")
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
)
result = _run(module, tmp_path, journal)
assert result == {
"status": "completed",
"completedRoles": [module.RUNTIME_ROLE, module.MIGRATOR_ROLE],
"activeRole": None,
}
assert states == {
module.RUNTIME_ROLE: "completed",
module.MIGRATOR_ROLE: "completed",
}
assert calls == [
("intent", module.RUNTIME_ROLE),
("complete", module.RUNTIME_ROLE),
("intent", module.MIGRATOR_ROLE),
("complete", module.MIGRATOR_ROLE),
]
assert reads == [
("runtime_database_url", module.RUNTIME_ROLE),
("migrator_database_url", module.MIGRATOR_ROLE),
]
assert marker.read_text(encoding="utf-8").splitlines() == [
f"{module.RUNTIME_ROLE}=matched-noecho",
f"{module.MIGRATOR_ROLE}=matched-noecho",
]
serialized = json.dumps(result)
assert all(secret not in serialized for secret in secrets.values())
def test_completed_role_is_skipped_and_uncertain_role_is_never_replayed(
credentials_module, monkeypatch, tmp_path
):
module = credentials_module
secrets = _secrets(module)
_install_uri_reader(monkeypatch, module, secrets)
marker = _install_fake_docker(module, tmp_path, secrets)
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "completed", module.MIGRATOR_ROLE: "intent"}
)
result = _run(module, tmp_path, journal)
assert result["status"] == "outcome_unknown"
assert result["completedRoles"] == [module.RUNTIME_ROLE]
assert result["activeRole"] == module.MIGRATOR_ROLE
assert calls == []
assert not marker.exists()
def test_migrator_cannot_be_journaled_complete_before_runtime_role(
credentials_module, tmp_path
):
module = credentials_module
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "completed"}
)
result = _run(module, tmp_path, journal)
assert result["status"] == "blocked"
assert result["reason"] == "role_journal_order_invalid"
assert result["completedRoles"] == []
assert calls == []
@pytest.mark.parametrize("behavior", ["exit-before-prompt", "timeout-before-prompt", "output-flood"])
def test_no_input_failures_are_bounded_reaped_and_retryable(
credentials_module, monkeypatch, tmp_path, behavior
):
module = credentials_module
secrets = _secrets(module)
_install_uri_reader(monkeypatch, module, secrets)
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
)
monkeypatch.setattr(module, "PROMPT_TIMEOUT_SECONDS", 0.15)
monkeypatch.setattr(module, "ROLE_TIMEOUT_SECONDS", 0.6)
monkeypatch.setattr(module, "REMOTE_TIMEOUT_SECONDS", 1)
monkeypatch.setattr(module, "REMOTE_KILL_AFTER_SECONDS", 1)
monkeypatch.setattr(module, "REMOTE_TERMINATION_MARGIN_SECONDS", 0.1)
monkeypatch.setattr(module, "MAX_TRANSCRIPT_BYTES", 128)
marker = _install_fake_docker(module, tmp_path, secrets, behavior=behavior)
started = __import__("time").monotonic()
result = _run(module, tmp_path, journal)
elapsed = __import__("time").monotonic() - started
assert result["status"] == "failed"
assert result["reason"] == "psql_failed_before_password_input"
assert result["activeRole"] == module.RUNTIME_ROLE
assert states[module.RUNTIME_ROLE] == "retryable"
assert states[module.MIGRATOR_ROLE] == "pending"
assert calls == [
("intent", module.RUNTIME_ROLE),
("retryable", module.RUNTIME_ROLE),
]
assert elapsed < 3
if behavior == "timeout-before-prompt":
assert elapsed >= 1.8
child_pid = int(Path(str(marker) + ".pid").read_text(encoding="utf-8"))
with pytest.raises(ProcessLookupError):
os.kill(child_pid, 0)
assert not marker.exists() or not any(
value in marker.read_text(encoding="utf-8")
for value in secrets.values()
)
def test_any_password_input_without_success_becomes_unknown(
credentials_module, monkeypatch, tmp_path
):
module = credentials_module
secrets = _secrets(module)
_install_uri_reader(monkeypatch, module, secrets)
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
)
monkeypatch.setattr(module, "PROMPT_TIMEOUT_SECONDS", 0.15)
monkeypatch.setattr(module, "REMOTE_TIMEOUT_SECONDS", 1)
monkeypatch.setattr(module, "REMOTE_KILL_AFTER_SECONDS", 1)
monkeypatch.setattr(module, "REMOTE_TERMINATION_MARGIN_SECONDS", 0.1)
marker = _install_fake_docker(module, tmp_path, secrets, behavior="exit-after-first")
result = _run(module, tmp_path, journal)
assert result["status"] == "outcome_unknown"
assert result["reason"] == "psql_result_uncertain"
assert states[module.RUNTIME_ROLE] == "unknown"
assert states[module.MIGRATOR_ROLE] == "pending"
assert calls == [
("intent", module.RUNTIME_ROLE),
("unknown", module.RUNTIME_ROLE),
]
assert not marker.exists() or not any(
value in marker.read_text(encoding="utf-8")
for value in secrets.values()
)
def test_invalid_fixed_uri_blocks_before_any_intent_or_process(
credentials_module, monkeypatch, tmp_path
):
module = credentials_module
monkeypatch.setattr(module, "read_database_uri", lambda *_args: None)
secrets = _secrets(module)
marker = _install_fake_docker(module, tmp_path, secrets)
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
)
result = _run(module, tmp_path, journal)
assert result["status"] == "blocked"
assert result["reason"] == "database_uri_unavailable"
assert states == {module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
assert calls == []
assert not marker.exists()
def test_concurrent_call_stops_on_durable_intent_without_starting_a_second_child(
credentials_module, monkeypatch, tmp_path
):
module = credentials_module
secrets = _secrets(module)
_install_uri_reader(monkeypatch, module, secrets)
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
)
entered = threading.Event()
release = threading.Event()
child_calls = []
def hold_child(role, _password):
child_calls.append(role)
entered.set()
assert release.wait(timeout=2)
return "failed", False
monkeypatch.setattr(module, "_run_password_command", hold_child)
first_result = []
thread = threading.Thread(
target=lambda: first_result.append(_run(module, tmp_path, journal)),
daemon=True,
)
thread.start()
assert entered.wait(timeout=1)
second_result = _run(module, tmp_path, journal)
release.set()
thread.join(timeout=2)
assert not thread.is_alive()
assert second_result["status"] == "outcome_unknown"
assert second_result["reason"] == "role_requires_login_reconciliation"
assert first_result[0]["status"] == "failed"
assert child_calls == [module.RUNTIME_ROLE]
assert states[module.RUNTIME_ROLE] == "retryable"
assert calls == [
("intent", module.RUNTIME_ROLE),
("retryable", module.RUNTIME_ROLE),
]
@pytest.mark.parametrize("claim_result", [False, None])
def test_failed_durable_intent_claim_never_starts_child(
credentials_module, monkeypatch, tmp_path, claim_result
):
module = credentials_module
secrets = _secrets(module)
_install_uri_reader(monkeypatch, module, secrets)
marker = _install_fake_docker(module, tmp_path, secrets)
states, calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
)
journal["persist_role_intent"] = lambda _role: claim_result
result = _run(module, tmp_path, journal)
assert result["status"] == "outcome_unknown"
assert result["reason"] == "role_intent_persistence_failed"
assert states[module.RUNTIME_ROLE] == "pending"
assert calls == []
assert not marker.exists()
def test_exited_child_prompt_output_is_drained_without_sending_password(
credentials_module, monkeypatch, tmp_path
):
module = credentials_module
secrets = _secrets(module)
_install_uri_reader(monkeypatch, module, secrets)
_install_fake_docker(module, tmp_path, secrets, behavior="prompts-then-exit")
states, _calls, journal = _journal(
{module.RUNTIME_ROLE: "pending", module.MIGRATOR_ROLE: "pending"}
)
child = []
writes = []
original_popen = module.subprocess.Popen
original_select = module.select.select
original_write_password = module._write_password_line
def capture_popen(*args, **kwargs):
process = original_popen(*args, **kwargs)
child.append(process)
return process
def select_after_exit(*args, **kwargs):
child[0].wait(timeout=2)
return original_select(*args, **kwargs)
def record_write(*args, **kwargs):
writes.append(True)
return original_write_password(*args, **kwargs)
monkeypatch.setattr(module.subprocess, "Popen", capture_popen)
monkeypatch.setattr(module.select, "select", select_after_exit)
monkeypatch.setattr(module, "_write_password_line", record_write)
result = _run(module, tmp_path, journal)
assert result["status"] != "completed"
assert writes == []
assert states[module.RUNTIME_ROLE] != "completed"
assert states[module.RUNTIME_ROLE] == "retryable"
def test_zero_length_pty_write_fails_closed(credentials_module, monkeypatch):
module = credentials_module
monkeypatch.setattr(module.os, "write", lambda *_args: 0)
monkeypatch.setattr(
module.select,
"select",
lambda _readers, writers, _errors, _timeout: ([], writers, []),
)
with pytest.raises(OSError):
module._write_password_line(123, bytearray(b"bounded-secret"))
def test_pty_write_backpressure_expires_within_fixed_budget(
credentials_module, monkeypatch
):
module = credentials_module
ticks = iter(range(0, 10))
monkeypatch.setattr(module.time, "monotonic", lambda: float(next(ticks)))
monkeypatch.setattr(
module.select,
"select",
lambda _readers, _writers, _errors, _timeout: ([], [], []),
)
monkeypatch.setattr(module, "PTY_WRITE_TIMEOUT_SECONDS", 2.0)
with pytest.raises(TimeoutError):
module._write_password_line(123, bytearray(b"bounded-secret"))
def test_uri_password_decoder_rejects_malformed_or_unbounded_values(credentials_module):
module = credentials_module
role = module.RUNTIME_ROLE
assert module._password_from_uri(_uri(role, "x" * 31), role) is None
assert module._password_from_uri(_uri(role, "x" * 257), role) is None
assert module._password_from_uri(
f"postgresql://{role}:bad%Q0value-with-more-than-32-characters@postgres:5432/forge".encode(),
role,
) is None
decoded = module._password_from_uri(
f"postgresql://{role}:encoded%2Dpassword%2D0123456789abcdef@postgres:5432/forge".encode(),
role,
)
assert decoded == bytearray(b"encoded-password-0123456789abcdef")
decoded[:] = b"\0" * len(decoded)
File diff suppressed because it is too large. Load diff
+300
View File
@@ -0,0 +1,300 @@
from __future__ import annotations
import os
from contextlib import contextmanager
import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, SQLModel, create_engine, select
from backend.app.services import user_access
_REAL_CURRENT_USER = user_access.current_user
_PROVISION_PATH = "/api/apps/authentik/forge/oidc/provision"
@pytest.fixture
def forge_identity_client(
tmp_path,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.core.config import settings
from backend.app.db import session as db_session
from backend.app.db.init_db import init_db
from backend.app.main import app
from backend.app.services import forge_oidc_provisioning
if os.name == "nt":
# The shared Windows runner temp ACL is deliberately not the production
# data-directory ACL; isolate route tests from that host configuration.
monkeypatch.setattr(
forge_oidc_provisioning,
"_windows_private_path",
lambda *_args, **_kwargs: None,
)
else:
monkeypatch.setattr(
forge_oidc_provisioning,
"_FORGE_OIDC_LOCK_ROOT",
tmp_path / "forge-oidc-runtime-lock",
)
engine = create_engine(
f"sqlite:///{tmp_path / 'forge-oidc-auth.db'}",
connect_args={"check_same_thread": False},
)
monkeypatch.setattr(db_session, "engine", engine)
monkeypatch.setenv("APPOPS_REQUIRE_OPERATOR_AUTH", "false")
monkeypatch.setattr(settings, "operator_proxy_auth_enabled", False)
monkeypatch.setattr(settings, "auth_admin_subjects", ["authentik:owner"], raising=False)
monkeypatch.setattr(settings, "auth_admin_emails", [], raising=False)
monkeypatch.setattr(settings, "data_dir", str(tmp_path))
monkeypatch.setattr(user_access, "current_user", _REAL_CURRENT_USER)
SQLModel.metadata.create_all(engine)
init_db()
client = TestClient(app, client=("198.18.10.20", 42000))
yield client
engine.dispose()
def _sign_in(client: TestClient, subject: str) -> None:
from backend.app.db import session as db_session
from backend.app.services.user_access import issue_session, provision_user
with Session(db_session.engine) as session:
user = provision_user(
session,
"authentik",
"https://auth.example/application/o/appops/",
{"sub": subject, "email": f"{subject}@example.com"},
)
cookie = issue_session(session, user)
client.cookies.set("appops_user_session", cookie)
def test_forge_oidc_route_requires_authenticated_operator_and_csrf(
forge_identity_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.services import setup_service
original_loader = setup_service.apply_stored_connector_settings
loader_calls = []
def tracked_loader(session, connector_ids=None):
loader_calls.append(connector_ids)
return original_loader(session, connector_ids)
monkeypatch.setattr(setup_service, "apply_stored_connector_settings", tracked_loader)
unauthenticated = forge_identity_client.post(_PROVISION_PATH, json={})
assert unauthenticated.status_code == 401
_sign_in(forge_identity_client, "owner")
missing_csrf = forge_identity_client.post(_PROVISION_PATH, json={})
assert missing_csrf.status_code == 403
assert loader_calls == []
def test_forge_oidc_route_rejects_read_only_user_and_nonempty_body(
forge_identity_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.services import setup_service
original_loader = setup_service.apply_stored_connector_settings
loader_calls = []
def tracked_loader(session, connector_ids=None):
loader_calls.append(connector_ids)
return original_loader(session, connector_ids)
monkeypatch.setattr(setup_service, "apply_stored_connector_settings", tracked_loader)
_sign_in(forge_identity_client, "reader")
denied = forge_identity_client.post(
_PROVISION_PATH,
json={},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert denied.status_code == 403
_sign_in(forge_identity_client, "owner")
invalid = forge_identity_client.post(
_PROVISION_PATH,
json={"client_id": "caller-selected"},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert invalid.status_code == 422
missing_body = forge_identity_client.post(
_PROVISION_PATH,
headers={"X-AppOps-Session-CSRF": "1"},
)
assert missing_body.status_code == 422
assert loader_calls == []
def test_forge_oidc_route_loads_stored_connector_settings_and_passes_actor(
forge_identity_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.core.config import settings
from backend.app.db import session as db_session
from backend.app.db.models import AppSetting
from backend.app.services import forge_oidc_provisioning, setup_service
from backend.app.services.secret_store import set_secret
_sign_in(forge_identity_client, "owner")
loader_calls = []
constructed = []
outcome_identities = []
events = []
protected_admin_token = "protected-authentik-admin-token-fixture"
monkeypatch.setattr(settings, "environment", "production")
monkeypatch.setattr(settings, "secret_key", "forge-oidc-api-secret-key-material-0123456789")
monkeypatch.setattr(settings, "secret_key_source", "env")
with Session(db_session.engine) as session:
session.add(
AppSetting(
key="connector:authentik:config",
value_json={"url": "https://auth.example", "verifyTls": True},
is_secret=False,
)
)
set_secret(session, "AUTHENTIK_TOKEN", protected_admin_token)
session.commit()
original_loader = setup_service.apply_stored_connector_settings
original_lock = forge_oidc_provisioning._forge_oidc_process_lock
original_store_status = forge_oidc_provisioning.secret_storage_status
@contextmanager
def tracked_lock():
with original_lock():
events.append("lock-enter")
yield
events.append("lock-exit")
def tracked_store_status():
events.append("store-check")
return original_store_status()
def tracked_loader(session, connector_ids=None):
events.append("connector-loader")
loader_calls.append(connector_ids)
assert connector_ids == {"authentik"}
return original_loader(session, connector_ids)
class FakeAuthentikConnector:
def __init__(self):
constructed.append(
(
settings.apis.get("AUTHENTIK_URL"),
settings.apis.get("AUTHENTIK_VERIFY_TLS"),
settings.apis.get("AUTHENTIK_TOKEN"),
)
)
def ensure_forge_oidc_prerequisites(self, *, expected_client_secret):
assert expected_client_secret is None
return {"success": False, "status": "failed"}
original_outcome = forge_oidc_provisioning._connector_outcome
def tracked_outcome(session, *, identity, outcome, secret_created):
outcome_identities.append(identity)
return original_outcome(
session,
identity=identity,
outcome=outcome,
secret_created=secret_created,
)
monkeypatch.setattr(setup_service, "apply_stored_connector_settings", tracked_loader)
monkeypatch.setattr(forge_oidc_provisioning, "_forge_oidc_process_lock", tracked_lock)
monkeypatch.setattr(forge_oidc_provisioning, "secret_storage_status", tracked_store_status)
monkeypatch.setattr(forge_oidc_provisioning, "AuthentikConnector", FakeAuthentikConnector)
monkeypatch.setattr(forge_oidc_provisioning, "_connector_outcome", tracked_outcome)
response = forge_identity_client.post(
_PROVISION_PATH,
json={},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert response.status_code == 200
assert response.json()["status"] == "failed"
assert response.json()["message"] == "forge_oidc_provisioning_failed"
assert constructed == [("https://auth.example", True, protected_admin_token)]
assert loader_calls == [{"authentik"}]
assert events == ["lock-enter", "store-check", "connector-loader", "lock-exit"]
assert outcome_identities == ["authentik:owner"]
assert protected_admin_token not in response.text
assert "client_secret" not in response.json()
def test_forge_oidc_production_store_not_ready_does_not_load_connector(
forge_identity_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.core.config import settings
from backend.app.services import setup_service
_sign_in(forge_identity_client, "owner")
monkeypatch.setattr(settings, "environment", "production")
monkeypatch.setattr(settings, "secret_key", "")
monkeypatch.setattr(settings, "secret_key_source", "missing")
loader_calls = []
original_loader = setup_service.apply_stored_connector_settings
def tracked_loader(session, connector_ids=None):
loader_calls.append(connector_ids)
return original_loader(session, connector_ids)
monkeypatch.setattr(setup_service, "apply_stored_connector_settings", tracked_loader)
response = forge_identity_client.post(
_PROVISION_PATH,
json={},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert response.status_code == 200
assert response.json()["status"] == "blocked"
assert response.json()["message"] == "forge_oidc_secret_store_not_production_ready"
assert loader_calls == []
def test_forge_oidc_route_returns_blocked_when_exact_policy_is_disabled(
forge_identity_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.db import session as db_session
from backend.app.db.models import Policy
from backend.app.services import setup_service
loader_calls = []
original_loader = setup_service.apply_stored_connector_settings
def tracked_loader(session, connector_ids=None):
loader_calls.append(connector_ids)
return original_loader(session, connector_ids)
monkeypatch.setattr(setup_service, "apply_stored_connector_settings", tracked_loader)
_sign_in(forge_identity_client, "owner")
with Session(db_session.engine) as session:
policy = session.exec(
select(Policy).where(
(Policy.action == "forge.oidc.provision")
& (Policy.target == "itworx-forge")
)
).one()
policy.manual = "blocked"
session.add(policy)
session.commit()
response = forge_identity_client.post(
_PROVISION_PATH,
json={},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert response.status_code == 200
assert response.json()["status"] == "blocked"
assert response.json()["message"] == "forge_oidc_exact_target_policy_required"
assert loader_calls == []
File diff suppressed because it is too large. Load diff
+188
View File
@@ -0,0 +1,188 @@
from __future__ import annotations
import os
import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, SQLModel, create_engine
from backend.app.services import user_access
_REAL_CURRENT_USER = user_access.current_user
_PROVISION_PATH = "/api/apps/authentik/forge/controller-reader/provision"
@pytest.fixture
def forge_reader_operator_client(tmp_path, monkeypatch: pytest.MonkeyPatch):
from backend.app.core.config import settings
from backend.app.db import session as db_session
from backend.app.db.init_db import init_db
from backend.app.main import app
if os.name == "nt":
from backend.app.services import forge_oidc_provisioning
monkeypatch.setattr(
forge_oidc_provisioning,
"_windows_private_path",
lambda *_args, **_kwargs: None,
)
else:
from backend.app.services import forge_oidc_provisioning
monkeypatch.setattr(
forge_oidc_provisioning,
"_FORGE_OIDC_LOCK_ROOT",
tmp_path / "forge-reader-api-lock",
)
engine = create_engine(
f"sqlite:///{tmp_path / 'forge-reader-operator.db'}",
connect_args={"check_same_thread": False},
)
monkeypatch.setattr(db_session, "engine", engine)
monkeypatch.setenv("APPOPS_REQUIRE_OPERATOR_AUTH", "false")
monkeypatch.setattr(settings, "operator_proxy_auth_enabled", False)
monkeypatch.setattr(settings, "auth_admin_subjects", ["authentik:owner"], raising=False)
monkeypatch.setattr(settings, "auth_admin_emails", [], raising=False)
monkeypatch.setattr(settings, "data_dir", str(tmp_path))
monkeypatch.setattr(user_access, "current_user", _REAL_CURRENT_USER)
SQLModel.metadata.create_all(engine)
init_db()
client = TestClient(app, client=("198.18.10.20", 42000))
yield client
engine.dispose()
def _spy_actual_connector_loader(monkeypatch: pytest.MonkeyPatch):
from backend.app.services import setup_service
original = setup_service.apply_stored_connector_settings
calls = []
def tracked(session, connector_ids=None):
calls.append(connector_ids)
return original(session, connector_ids)
monkeypatch.setattr(setup_service, "apply_stored_connector_settings", tracked)
return calls
def _sign_in(client: TestClient, subject: str) -> None:
from backend.app.db import session as db_session
from backend.app.services.user_access import issue_session, provision_user
with Session(db_session.engine) as session:
user = provision_user(
session,
"authentik",
"https://auth.example/application/o/appops/",
{"sub": subject, "email": f"{subject}@example.com"},
)
cookie = issue_session(session, user)
client.cookies.set("appops_user_session", cookie)
def test_forge_reader_route_requires_authenticated_operator_and_csrf(
forge_reader_operator_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
loader_calls = _spy_actual_connector_loader(monkeypatch)
unauthenticated = forge_reader_operator_client.post(_PROVISION_PATH, json={})
assert unauthenticated.status_code == 401
_sign_in(forge_reader_operator_client, "owner")
missing_csrf = forge_reader_operator_client.post(_PROVISION_PATH, json={})
assert missing_csrf.status_code == 403
assert loader_calls == []
def test_forge_reader_route_rejects_read_only_user_and_caller_selectors(
forge_reader_operator_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
loader_calls = _spy_actual_connector_loader(monkeypatch)
_sign_in(forge_reader_operator_client, "reader")
denied = forge_reader_operator_client.post(
_PROVISION_PATH,
json={},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert denied.status_code == 403
_sign_in(forge_reader_operator_client, "owner")
invalid = forge_reader_operator_client.post(
_PROVISION_PATH,
json={"token": "caller-selected"},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert invalid.status_code == 422
assert loader_calls == []
def test_forge_reader_default_denied_policy_does_not_load_connector_or_mutate(
forge_reader_operator_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.db import session as db_session
from backend.app.db.models import Policy
loader_calls = _spy_actual_connector_loader(monkeypatch)
_sign_in(forge_reader_operator_client, "owner")
with Session(db_session.engine) as session:
policy = session.get(Policy, "policy-forge-oidc-provision")
policy.manual = "blocked"
session.add(policy)
session.commit()
response = forge_reader_operator_client.post(
_PROVISION_PATH,
json={},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert response.status_code == 200
assert response.json()["status"] == "blocked"
assert response.json()["message"] == "forge_reader_exact_target_policy_required"
assert loader_calls == []
def test_forge_reader_route_passes_operator_and_projects_only_safe_result(
forge_reader_operator_client: TestClient,
monkeypatch: pytest.MonkeyPatch,
):
from backend.app.api import routes_authentik
_sign_in(forge_reader_operator_client, "owner")
loader_calls = _spy_actual_connector_loader(monkeypatch)
calls = []
held_token = "reader-token-must-never-be-returned"
def provision(session, *, actor_identity):
calls.append((session, actor_identity))
return {
"success": True,
"status": "ready",
"message": "forge_reader_ready",
"ready": True,
"secretRef": "ITWORX_FORGE_AUTHENTIK_READ_TOKEN",
}
monkeypatch.setattr(routes_authentik, "provision_forge_controller_reader", provision)
response = forge_reader_operator_client.post(
_PROVISION_PATH,
json={},
headers={"X-AppOps-Session-CSRF": "1"},
)
assert response.status_code == 200
assert response.json() == {
"success": True,
"status": "ready",
"message": "forge_reader_ready",
"ready": True,
"secretRef": "ITWORX_FORGE_AUTHENTIK_READ_TOKEN",
}
assert calls and calls[0][1] == "authentik:owner"
assert loader_calls == []
assert held_token not in response.text
+747
View File
@@ -0,0 +1,747 @@
from __future__ import annotations
import importlib.util
import json
import os
import stat
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
BROKER = ROOT / "ops" / "gitea-deploy-broker"
if str(BROKER) not in sys.path:
sys.path.insert(0, str(BROKER))
@pytest.fixture
def bundle_module():
spec = importlib.util.spec_from_file_location(
"forge_secret_bundle_test",
BROKER / "forge_secret_bundle.py",
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _config(**overrides: str) -> bytes:
value = {
"schema": "itworx.forge-first-install-config/v4",
"issuer": "https://auth.example.test/application/o/forge/",
}
value.update(overrides)
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode("utf-8")
def _initialize_test_roots(roots) -> None:
for path in dict.fromkeys(
(roots.config_root, roots.state_parent, roots.backup_root)
):
path.mkdir(mode=0o700)
path.chmod(0o700)
def test_validator_must_return_literal_true_before_any_filesystem_effect(
bundle_module, tmp_path
):
oidc = b"oidc-secret-sentinel-123456789"
authentik = b"authentik-token-sentinel-123456789"
config = _config()
roots = bundle_module._Roots.for_test(tmp_path)
for result in (False, None, 1):
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda _raw, result=result: result,
roots=roots,
)
assert list(tmp_path.iterdir()) == []
def raises_with_secret(_raw: bytes) -> bool:
raise RuntimeError("validation failed: " + oidc.decode("ascii"))
with pytest.raises(bundle_module.ForgeSecretBundleError) as raised:
bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=raises_with_secret,
roots=roots,
)
assert oidc.decode("ascii") not in str(raised.value)
assert list(tmp_path.iterdir()) == []
@pytest.mark.parametrize(
"config",
[
b'{"schema":"v4","schema":"v4"}',
b'{ "schema":"v4"}',
b'{"schema":NaN}',
b"[]",
b"\xff",
],
)
def test_rejects_duplicate_invalid_noncanonical_or_nonobject_config_without_effects(
bundle_module, tmp_path, config
):
roots = bundle_module._Roots.for_test(tmp_path)
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
config,
validate_config=lambda _raw: True,
roots=roots,
)
assert list(tmp_path.iterdir()) == []
def test_rejects_supplied_secret_embedded_in_config_before_validator_or_writes(
bundle_module, tmp_path
):
oidc = b"oidc-secret-sentinel-123456789"
roots = bundle_module._Roots.for_test(tmp_path)
called = False
def validate(_raw: bytes) -> bool:
nonlocal called
called = True
return True
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
oidc,
b"authentik-token-sentinel-123456789",
_config(note=oidc.decode("ascii")),
validate_config=validate,
roots=roots,
)
assert called is False
assert list(tmp_path.iterdir()) == []
def test_rejects_escaped_secret_value_without_calling_validator(
bundle_module, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
secret = b'oidc-"quoted"-secret-123456789'
encoded = _config(note=secret.decode("ascii"))
called = False
def validate(_raw: bytes) -> bool:
nonlocal called
called = True
return True
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
secret,
b"authentik-token-sentinel-123456789",
encoded,
validate_config=validate,
roots=roots,
)
assert called is False
assert list(tmp_path.iterdir()) == []
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_creates_fixed_files_and_backup_and_retry_only_verifies(
bundle_module, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
oidc = b"oidc-secret-sentinel-123456789"
authentik = b"authentik-token-sentinel-123456789"
config = _config()
def validate(raw):
return raw == config
result = bundle_module._prepare_with_roots(
oidc, authentik, config, validate_config=validate, roots=roots
)
assert result == {
"status": "bundle_ready",
"secretFilesReady": 7,
"masterKeyBackupVerified": True,
"applyPreflight": "not_evaluated",
}
assert oidc.decode("ascii") not in repr(result)
assert authentik.decode("ascii") not in repr(result)
assert sorted(path.name for path in roots.secrets_root.iterdir()) == sorted(
f"{name}.secret"
for name in (
"postgres_bootstrap_password",
"runtime_database_url",
"migrator_database_url",
"oidc_client_secret",
"session_secret",
"master_key",
"authentik_read_token",
)
)
assert (roots.secrets_root / "oidc_client_secret.secret").read_bytes() == oidc
assert (
roots.secrets_root / "authentik_read_token.secret"
).read_bytes() == authentik
master_key = (roots.secrets_root / "master_key.secret").read_bytes()
assert len(master_key) == 32
assert (roots.backup_root / "master_key.backup").read_bytes() == master_key
import urllib.parse
generated = {
(roots.secrets_root / "postgres_bootstrap_password.secret").read_bytes(),
urllib.parse.urlsplit(
(roots.secrets_root / "runtime_database_url.secret").read_text(
encoding="ascii"
)
).password.encode("ascii"),
urllib.parse.urlsplit(
(roots.secrets_root / "migrator_database_url.secret").read_text(
encoding="ascii"
)
).password.encode("ascii"),
(roots.secrets_root / "session_secret.secret").read_bytes(),
}
assert len(generated) == 4
assert all(len(value) >= 32 for value in generated)
from forge_first_install import FORGE_SECRETS
for name, (_logical, uid, gid, minimum, maximum) in FORGE_SECRETS.items():
path = roots.secrets_root / f"{name}.secret"
info = path.stat()
assert (info.st_uid, info.st_gid) == (uid, gid)
assert info.st_mode & 0o777 == 0o400
assert minimum <= info.st_size <= maximum
assert (roots.secrets_root.stat().st_mode & 0o777) == 0o700
assert (roots.config_path.stat().st_mode & 0o777) == 0o600
assert (roots.backup_root / "master_key.backup").stat().st_mode & 0o777 == 0o400
journal = roots.journal_path.read_text(encoding="utf-8")
assert "oidc-secret-sentinel-123456789" not in journal
assert "authentik-token-sentinel-123456789" not in journal
assert "sha256" not in journal.lower()
before = {
path.name: (path.stat().st_ino, path.read_bytes())
for path in roots.secrets_root.iterdir()
}
backup_identity = (roots.backup_root / "master_key.backup").stat().st_ino
config_identity = roots.config_path.stat().st_ino
original_write = bundle_module._write_all
def reject_write(_fd: int, _payload: bytes) -> None:
raise AssertionError("a completed retry must not write")
bundle_module._write_all = reject_write
retry = bundle_module._prepare_with_roots(
oidc, authentik, config, validate_config=validate, roots=roots
)
bundle_module._write_all = original_write
assert retry == result
assert before == {
path.name: (path.stat().st_ino, path.read_bytes())
for path in roots.secrets_root.iterdir()
}
assert (roots.backup_root / "master_key.backup").stat().st_ino == backup_identity
assert roots.config_path.stat().st_ino == config_identity
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_partial_retry_reuses_already_committed_password_and_recovers_backup(
bundle_module, monkeypatch, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
oidc = b"oidc-secret-sentinel-123456789"
authentik = b"authentik-token-sentinel-123456789"
config = _config()
original_write = bundle_module._write_all
failed = False
def fail_runtime_write(fd: int, payload: bytes) -> None:
nonlocal failed
if payload.startswith(b"postgresql://") and not failed:
failed = True
raise OSError("injected write failure")
original_write(fd, payload)
monkeypatch.setattr(bundle_module, "_write_all", fail_runtime_write)
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
bootstrap = roots.secrets_root / "postgres_bootstrap_password.secret"
original_bootstrap = bootstrap.read_bytes()
original_inode = bootstrap.stat().st_ino
monkeypatch.setattr(bundle_module, "_write_all", original_write)
result = bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
assert result["status"] == "bundle_ready"
assert bootstrap.read_bytes() == original_bootstrap
assert bootstrap.stat().st_ino == original_inode
master = (roots.secrets_root / "master_key.secret").read_bytes()
assert (roots.backup_root / "master_key.backup").read_bytes() == master
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_link_failure_after_durable_stage_keeps_and_reuses_original_secret(
bundle_module, monkeypatch, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
oidc = b"oidc-secret-sentinel-123456789"
authentik = b"authentik-token-sentinel-123456789"
config = _config()
original_link = os.link
failed = False
def fail_runtime_link(source, destination, **kwargs):
nonlocal failed
if destination == "runtime_database_url.secret" and not failed:
failed = True
raise OSError("injected link failure")
return original_link(source, destination, **kwargs)
monkeypatch.setattr(os, "link", fail_runtime_link)
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
journal = json.loads(roots.journal_path.read_bytes())
stage = journal["staging"]
assert stage["kind"] == "secret"
assert stage["name"] == "runtime_database_url"
staged_file = roots.secrets_root / stage["temporary"]
assert staged_file.is_file()
original_payload = staged_file.read_bytes()
assert original_payload.startswith(b"postgresql://forge_runtime:")
monkeypatch.setattr(os, "link", original_link)
result = bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
assert result["status"] == "bundle_ready"
runtime_password = (roots.secrets_root / "runtime_database_url.secret").read_bytes()
assert runtime_password == original_payload
assert not staged_file.exists()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_journal_directory_fsync_uncertainty_preserves_referenced_stage(
bundle_module, monkeypatch, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
oidc = b"oidc-secret-sentinel-123456789"
authentik = b"authentik-token-sentinel-123456789"
config = _config()
original_fsync = os.fsync
failed = False
def fail_stage_directory_fsync(fd: int) -> None:
nonlocal failed
if not failed and stat.S_ISDIR(os.fstat(fd).st_mode):
try:
journal = json.loads(roots.journal_path.read_bytes())
except (OSError, ValueError):
journal = {}
stage = journal.get("staging")
if stage is not None and stage.get("name") == "runtime_database_url":
failed = True
raise OSError("injected directory fsync uncertainty")
original_fsync(fd)
monkeypatch.setattr(os, "fsync", fail_stage_directory_fsync)
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
journal = json.loads(roots.journal_path.read_bytes())
stage = journal["staging"]
assert stage["name"] == "runtime_database_url"
staged_file = roots.secrets_root / stage["temporary"]
assert staged_file.is_file()
original_payload = staged_file.read_bytes()
monkeypatch.setattr(os, "fsync", original_fsync)
result = bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
assert result["status"] == "bundle_ready"
assert (
roots.secrets_root / "runtime_database_url.secret"
).read_bytes() == original_payload
assert not staged_file.exists()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_backup_link_failure_reuses_same_master_key_and_completes_backup(
bundle_module, monkeypatch, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
oidc = b"oidc-secret-sentinel-123456789"
authentik = b"authentik-token-sentinel-123456789"
config = _config()
original_link = os.link
failed = False
def fail_backup_link(source, destination, **kwargs):
nonlocal failed
if destination == "master_key.backup" and not failed:
failed = True
raise OSError("injected backup link failure")
return original_link(source, destination, **kwargs)
monkeypatch.setattr(os, "link", fail_backup_link)
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
journal = json.loads(roots.journal_path.read_bytes())
stage = journal["staging"]
assert stage["kind"] == "backup"
assert stage["name"] == "master_key.backup"
staged_file = roots.backup_root / stage["temporary"]
original_payload = staged_file.read_bytes()
original_master = (roots.secrets_root / "master_key.secret").read_bytes()
original_master_inode = (roots.secrets_root / "master_key.secret").stat().st_ino
assert original_payload == original_master
assert len(original_payload) == 32
monkeypatch.setattr(os, "link", original_link)
result = bundle_module._prepare_with_roots(
oidc,
authentik,
config,
validate_config=lambda raw: raw == config,
roots=roots,
)
assert result["status"] == "bundle_ready"
assert (roots.secrets_root / "master_key.secret").read_bytes() == original_master
assert (
roots.secrets_root / "master_key.secret"
).stat().st_ino == original_master_inode
assert (roots.backup_root / "master_key.backup").read_bytes() == original_payload
assert not staged_file.exists()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_existing_secret_residue_is_never_overwritten_or_adopted(
bundle_module, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
roots.secrets_root.mkdir(mode=0o700)
residue = roots.secrets_root / "session_secret.secret"
residue.write_bytes(b"foreign-residue-must-survive")
residue.chmod(0o400)
before = residue.read_bytes()
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
_config(),
validate_config=lambda _raw: True,
roots=roots,
)
assert residue.read_bytes() == before
assert not roots.journal_path.exists()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_unsafe_ancestor_and_symlinked_secret_root_fail_without_chmod(
bundle_module, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
roots.config_root.chmod(0o777)
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
_config(),
validate_config=lambda _raw: True,
roots=roots,
)
assert roots.config_root.stat().st_mode & 0o777 == 0o777
roots.config_root.chmod(0o700)
target = tmp_path / "foreign-secret-dir"
target.mkdir(mode=0o700)
roots.secrets_root.symlink_to(target, target_is_directory=True)
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
_config(),
validate_config=lambda _raw: True,
roots=roots,
)
assert list(target.iterdir()) == []
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_stale_journal_generation_is_rejected_by_compare_and_swap(
bundle_module, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
roots.state_root.mkdir(mode=0o700)
descriptor = os.open(roots.state_root, os.O_RDONLY | os.O_DIRECTORY)
try:
record = bundle_module._empty_journal()
bundle_module._write_journal(descriptor, record, expected_generation=None)
stale = dict(record)
record["status"] = "progress"
bundle_module._persist(descriptor, record)
before = roots.journal_path.read_bytes()
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._write_journal(descriptor, stale, expected_generation=0)
assert roots.journal_path.read_bytes() == before
finally:
os.close(descriptor)
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_backup_root_rejects_untracked_files_before_creating_journal(
bundle_module, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
foreign = roots.backup_root / "unrelated-backup-residue"
foreign.write_bytes(b"leave this untouched")
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
_config(),
validate_config=lambda _raw: True,
roots=roots,
)
assert foreign.read_bytes() == b"leave this untouched"
assert not roots.state_root.exists()
assert not roots.secrets_root.exists()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
@pytest.mark.parametrize("target", ["config", "backup"])
def test_existing_config_or_backup_files_are_never_overwritten(
bundle_module, tmp_path, target
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
path = (
roots.config_path
if target == "config"
else roots.backup_root / "master_key.backup"
)
path.write_bytes(b"foreign-config-or-backup")
path.chmod(0o600)
original = path.read_bytes()
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
_config(),
validate_config=lambda _raw: True,
roots=roots,
)
assert path.read_bytes() == original
assert not roots.state_root.exists()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_hardlinked_existing_secret_fails_closed_without_rekeying(
bundle_module, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
args = (
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
_config(),
)
bundle_module._prepare_with_roots(
*args,
validate_config=lambda _raw: True,
roots=roots,
)
master = roots.secrets_root / "master_key.secret"
original = master.read_bytes()
foreign_link = tmp_path / "foreign-master-link"
os.link(master, foreign_link)
journal_before = roots.journal_path.read_bytes()
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
*args,
validate_config=lambda _raw: True,
roots=roots,
)
assert master.read_bytes() == original
assert roots.journal_path.read_bytes() == journal_before
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_pinned_file_reader_rejects_symlink_swap_between_stat_and_open(
bundle_module, monkeypatch, tmp_path
):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
roots.secrets_root.mkdir(mode=0o700)
target = roots.secrets_root / "trusted-content"
target.write_bytes(b"trusted")
target.chmod(0o400)
race = roots.secrets_root / "race-target"
race.write_bytes(b"before-race")
race.chmod(0o400)
directory_fd = os.open(roots.secrets_root, os.O_RDONLY | os.O_DIRECTORY)
original_stat_at = bundle_module._stat_at
swapped = False
def swap_after_stat(fd: int, name: str):
nonlocal swapped
info = original_stat_at(fd, name)
if name == "race-target" and info is not None and not swapped:
swapped = True
os.unlink(name, dir_fd=fd)
os.symlink("trusted-content", name, dir_fd=fd)
return info
monkeypatch.setattr(bundle_module, "_stat_at", swap_after_stat)
try:
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._read_file_at(
directory_fd,
"race-target",
uid=0,
gid=0,
min_size=1,
max_size=32,
allowed_modes=frozenset({0o400}),
)
finally:
os.close(directory_fd)
assert swapped is True
assert race.is_symlink()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the production helper requires POSIX root ownership",
)
def test_changed_master_key_backup_is_not_silently_rewritten(bundle_module, tmp_path):
roots = bundle_module._Roots.for_test(tmp_path)
_initialize_test_roots(roots)
args = (
b"oidc-secret-sentinel-123456789",
b"authentik-token-sentinel-123456789",
_config(),
)
bundle_module._prepare_with_roots(
*args,
validate_config=lambda _raw: True,
roots=roots,
)
backup = roots.backup_root / "master_key.backup"
original = backup.read_bytes()
backup.write_bytes(bytes(byte ^ 0xFF for byte in original))
journal_before = roots.journal_path.read_bytes()
with pytest.raises(bundle_module.ForgeSecretBundleError):
bundle_module._prepare_with_roots(
*args,
validate_config=lambda _raw: True,
roots=roots,
)
assert backup.read_bytes() == bytes(byte ^ 0xFF for byte in original)
assert (roots.secrets_root / "master_key.secret").read_bytes() == original
assert roots.journal_path.read_bytes() == journal_before
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,863 @@
from __future__ import annotations
import base64
import hashlib
import http.client
import importlib.util
import json
import os
import subprocess
import sys
import threading
import time
import types
import uuid
from http.server import ThreadingHTTPServer
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
BROKER = ROOT / "ops" / "gitea-deploy-broker"
KEY = b"forge-delivery-test-hmac-key-32bytes"
OIDC_SECRET = b"oidc-client-secret-sentinel-1234567890"
READ_TOKEN = b"authentik-read-token-sentinel-1234567890"
ARTIFACT = {
"controllerVersion": "1.2.3",
"sourceRepository": "jens/unraid-appops-gateway",
"sourceRevision": "1" * 40,
"sourceTree": "2" * 40,
"artifactSha256": "3" * 64,
"builtAt": "2026-10-03T10:00:00Z",
}
PROFILE = {
"api_version": "itworx.forge-bootstrap-preflight/v1",
"revision": "a" * 40,
"workspace_image_digest": "sha256:" + "b" * 64,
"frontdoor_image_digest": "sha256:" + "c" * 64,
}
def _load_status_module():
try:
import fcntl # noqa: F401 - use the real Linux flock implementation in tests.
except ImportError: # pragma: no cover - Windows-only import shim.
fcntl = types.ModuleType("fcntl")
fcntl.LOCK_EX = 2
fcntl.LOCK_UN = 8
fcntl.flock = lambda *_args: None
sys.modules["fcntl"] = fcntl
spec = importlib.util.spec_from_file_location(
"forge_secret_delivery_status_test", BROKER / "status_server.py"
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
@pytest.fixture
def status_module():
return _load_status_module()
@pytest.fixture
def endpoint(status_module):
server = ThreadingHTTPServer(("127.0.0.1", 0), status_module.Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
yield server.server_address
finally:
server.shutdown()
server.server_close()
thread.join(timeout=2)
def _config(**overrides: str) -> bytes:
value = {
"schema": "itworx.forge-first-install-config/v4",
"issuer": "https://auth.example-host.com/application/o/itworx-forge/",
"audience": "itworx-forge",
"client_id": "itworx-forge",
"authentik_api_url": "https://auth.example-host.com",
"launch_url": "https://forge.example.com",
"authorization_flow": "default-provider-authorization-implicit-consent",
"invalidation_flow": "default-provider-invalidation-flow",
"authentik_openid_mapping_id": "01234567-89ab-4cde-8fab-0123456789ab",
"authentik_email_mapping_id": "21234567-89ab-4cde-8fab-0123456789ab",
"authentik_profile_mapping_id": "31234567-89ab-4cde-8fab-0123456789ab",
"authentik_provider_id": "1234567890",
"authentik_signing_key_id": "11234567-89ab-4cde-8fab-0123456789ab",
"private_package_owner": "itworx-forge-private",
}
value.update(overrides)
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
def _payload(status_module, *, config_bytes: bytes | None = None, **overrides):
config_bytes = config_bytes if config_bytes is not None else _config()
value = {
"apiVersion": status_module.FORGE_SECRET_DELIVERY_API_VERSION,
"target": status_module.FORGE_SECRET_DELIVERY_TARGET,
"profile": dict(PROFILE),
"configBase64": base64.b64encode(config_bytes).decode("ascii"),
"oidcClientSecretBase64": base64.b64encode(OIDC_SECRET).decode("ascii"),
"authentikReadTokenBase64": base64.b64encode(READ_TOKEN).decode("ascii"),
}
value.update(overrides)
return value
def _signed_headers(status_module, body: bytes, *, nonce: str | None = None):
timestamp = str(int(time.time()))
nonce = nonce or uuid.uuid4().hex
signature = status_module._request_signature(
KEY,
method="POST",
path=status_module.FORGE_SECRET_DELIVERY_PATH,
timestamp=timestamp,
nonce=nonce,
raw_body=body,
)
return {
"Content-Type": "application/json",
"X-ITWorx-Deployment-Timestamp": timestamp,
"X-ITWorx-Deployment-Nonce": nonce,
"X-ITWorx-Deployment-Signature": signature,
}
def _post(address, path: str, body: bytes, headers: dict[str, str]):
connection = http.client.HTTPConnection(*address, timeout=3)
try:
connection.request("POST", path, body=body, headers=headers)
response = connection.getresponse()
return response.status, response.read()
finally:
connection.close()
def _install_hmac_test(monkeypatch, status_module):
consumed: set[str] = set()
monkeypatch.setattr(status_module, "_forge_first_install_secret", lambda: KEY)
def consume(_secret, nonce):
if nonce in consumed:
return False
consumed.add(nonce)
return True
monkeypatch.setattr(status_module, "_consume_appops_nonce", consume)
return consumed
def _install_successful_prerequisites(monkeypatch, status_module, helper=None):
counters = {"artifact": 0, "store": 0, "backup": 0, "runtime": 0}
def artifact():
counters["artifact"] += 1
return dict(ARTIFACT)
def store():
counters["store"] += 1
return ("/var/lib/docker", "itworx-controller-hub-releases", (1, 2), (1, 2))
def backup():
counters["backup"] += 1
return True
def runtime(profile, *, _held_candidate):
counters["runtime"] += 1
assert profile == PROFILE
assert isinstance(_held_candidate, dict)
return {"status": "bundle_prerequisites_passed"}
monkeypatch.setattr(status_module, "_forge_first_install_requests_are_absent", lambda: True)
monkeypatch.setattr(status_module, "_forge_controller_artifact_identity", artifact)
monkeypatch.setattr(status_module, "_forge_release_store_identity", store)
monkeypatch.setattr(status_module, "_forge_fixed_backup_volume_is_ready", backup)
monkeypatch.setattr(status_module, "_forge_bootstrap_runtime_checks", runtime)
if helper is not None:
monkeypatch.setattr(status_module, "_prepare_forge_secret_bundle", helper)
return counters
def test_candidate_parser_requires_canonical_config_and_secret_free_strings(status_module):
parsed = status_module._forge_secret_delivery_candidate(_payload(status_module))
assert parsed is not None
profile, candidate = parsed
assert profile == PROFILE
assert candidate["config_bytes"] == _config()
assert candidate["oidc_client_secret"] == OIDC_SECRET
assert candidate["authentik_read_token"] == READ_TOKEN
pretty = json.dumps(json.loads(_config()), indent=2).encode()
assert status_module._forge_secret_delivery_candidate(
_payload(status_module, config_bytes=pretty)
) is None
secret_bearing_config = _config(authorization_flow=OIDC_SECRET.decode())
assert status_module._forge_secret_delivery_candidate(
_payload(status_module, config_bytes=secret_bearing_config)
) is None
noncanonical = _payload(status_module)
noncanonical["oidcClientSecretBase64"] += "\n"
assert status_module._forge_secret_delivery_candidate(noncanonical) is None
def test_invalid_authentication_has_no_remote_or_bundle_side_effects(
status_module, endpoint, monkeypatch
):
counters = {"runtime": 0, "bundle": 0}
consumed = _install_hmac_test(monkeypatch, status_module)
monkeypatch.setattr(
status_module,
"_forge_controller_artifact_identity",
lambda: pytest.fail("unauthenticated input reached local gate"),
)
monkeypatch.setattr(
status_module,
"_forge_release_store_identity",
lambda: pytest.fail("unauthenticated input reached volume probe"),
)
monkeypatch.setattr(
status_module,
"_forge_fixed_backup_volume_is_ready",
lambda: pytest.fail("unauthenticated input reached backup probe"),
)
monkeypatch.setattr(
status_module,
"_forge_bootstrap_runtime_checks",
lambda *_args, **_kwargs: counters.__setitem__("runtime", counters["runtime"] + 1),
)
monkeypatch.setattr(
status_module,
"_prepare_forge_secret_bundle",
lambda *_args, **_kwargs: counters.__setitem__("bundle", counters["bundle"] + 1),
)
body = json.dumps(_payload(status_module), separators=(",", ":")).encode()
headers = _signed_headers(status_module, body)
headers["X-ITWorx-Deployment-Signature"] = "0" * 64
status, response = _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
headers,
)
assert status == 403
assert response == b'{"status":"authentication_rejected"}\n'
assert counters == {"runtime": 0, "bundle": 0}
assert consumed == set()
def test_duplicate_json_key_is_rejected_before_auth_or_remote_checks(
status_module, endpoint, monkeypatch
):
consumed = _install_hmac_test(monkeypatch, status_module)
called = []
monkeypatch.setattr(
status_module,
"_forge_prepare_secret_delivery_bundle",
lambda *_args, **_kwargs: called.append(True),
)
body = b'{"apiVersion":"x","apiVersion":"y"}'
status, response = _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
{
"Content-Type": "application/json",
"X-ITWorx-Deployment-Timestamp": str(int(time.time())),
"X-ITWorx-Deployment-Nonce": uuid.uuid4().hex,
"X-ITWorx-Deployment-Signature": "0" * 64,
},
)
assert status == 400
assert response == b'{"status":"invalid_json"}\n'
assert called == []
assert consumed == set()
@pytest.mark.parametrize(
("body", "content_type", "expected_status"),
[
(b"{}", "application/json; charset=utf-8", 400),
(b"x" * 16_385, "application/json", 413),
],
)
def test_body_limits_and_content_type_fail_before_auth(
status_module, endpoint, monkeypatch, body, content_type, expected_status
):
consumed = _install_hmac_test(monkeypatch, status_module)
monkeypatch.setattr(
status_module,
"_forge_controller_artifact_identity",
lambda: pytest.fail("invalid HTTP framing reached local gate"),
)
headers = {
"Content-Type": content_type,
"X-ITWorx-Deployment-Timestamp": str(int(time.time())),
"X-ITWorx-Deployment-Nonce": uuid.uuid4().hex,
"X-ITWorx-Deployment-Signature": "0" * 64,
}
status, _response = _post(
endpoint, status_module.FORGE_SECRET_DELIVERY_PATH, body, headers
)
assert status == expected_status
assert consumed == set()
@pytest.mark.parametrize("failed_gate", ["artifact", "store", "backup", "runtime"])
def test_every_fixed_prerequisite_gate_precedes_bundle_writer(
status_module, endpoint, monkeypatch, failed_gate
):
_install_hmac_test(monkeypatch, status_module)
calls = {name: 0 for name in ("artifact", "store", "backup", "runtime", "helper")}
def artifact():
calls["artifact"] += 1
return None if failed_gate == "artifact" else dict(ARTIFACT)
def store():
calls["store"] += 1
return None if failed_gate == "store" else ("fixed", "volume", (1, 2), (1, 2))
def backup():
calls["backup"] += 1
return failed_gate != "backup"
def runtime(profile, *, _held_candidate):
calls["runtime"] += 1
return {
"status": "bundle_prerequisites_blocked"
if failed_gate == "runtime"
else "bundle_prerequisites_passed"
}
def helper(*_args, **_kwargs):
calls["helper"] += 1
return {
"status": "bundle_ready",
"secretFilesReady": 7,
"masterKeyBackupVerified": True,
"applyPreflight": "not_evaluated",
}
monkeypatch.setattr(status_module, "_forge_first_install_requests_are_absent", lambda: True)
monkeypatch.setattr(status_module, "_forge_controller_artifact_identity", artifact)
monkeypatch.setattr(status_module, "_forge_release_store_identity", store)
monkeypatch.setattr(status_module, "_forge_fixed_backup_volume_is_ready", backup)
monkeypatch.setattr(status_module, "_forge_bootstrap_runtime_checks", runtime)
monkeypatch.setattr(status_module, "_prepare_forge_secret_bundle", helper)
body = json.dumps(_payload(status_module), separators=(",", ":")).encode()
status, _response = _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
_signed_headers(status_module, body),
)
assert status == 409
assert calls["helper"] == 0
if failed_gate in {"artifact", "store", "backup"}:
assert calls["runtime"] == 0
else:
assert calls["runtime"] == 1
def test_signed_route_returns_only_sanitized_bundle_readiness_and_replay_is_rejected(
status_module, endpoint, monkeypatch
):
consumed = _install_hmac_test(monkeypatch, status_module)
helper_calls = []
def helper(oidc_secret, read_token, config_bytes, *, validate_config):
helper_calls.append((oidc_secret, read_token, config_bytes))
assert oidc_secret == OIDC_SECRET
assert read_token == READ_TOKEN
assert validate_config(config_bytes) is True
return {
"status": "bundle_ready",
"secretFilesReady": 7,
"masterKeyBackupVerified": True,
"applyPreflight": "not_evaluated",
}
counters = _install_successful_prerequisites(monkeypatch, status_module, helper)
monkeypatch.setattr(
status_module._forge_first_install_controller,
"remember_preflight",
lambda *_args: pytest.fail("bundle readiness must not remember a preflight"),
)
monkeypatch.setattr(
status_module._forge_first_install_controller,
"apply",
lambda *_args: pytest.fail("bundle readiness must not start first install"),
)
body = json.dumps(_payload(status_module), separators=(",", ":")).encode()
headers = _signed_headers(status_module, body)
nonce = headers["X-ITWorx-Deployment-Nonce"]
status, response = _post(
endpoint, status_module.FORGE_SECRET_DELIVERY_PATH, body, headers
)
assert status == 200
decoded = json.loads(response)
assert decoded == {
"apiVersion": status_module.FORGE_SECRET_DELIVERY_API_VERSION,
"target": "itworx-forge",
"status": "bundle_ready",
"profile": PROFILE,
"controllerArtifact": {
key: ARTIFACT[key]
for key in (
"controllerVersion",
"sourceRepository",
"sourceRevision",
"sourceTree",
"artifactSha256",
)
},
"secretFilesReady": 7,
"masterKeyBackupVerified": True,
"applyPreflight": "not_evaluated",
}
assert "secretFilesReady" in decoded
assert OIDC_SECRET.decode() not in response.decode()
assert READ_TOKEN.decode() not in response.decode()
assert base64.b64encode(OIDC_SECRET).decode() not in response.decode()
assert base64.b64encode(READ_TOKEN).decode() not in response.decode()
assert len(consumed) == 1 and nonce in consumed
assert helper_calls == [(OIDC_SECRET, READ_TOKEN, _config())]
assert counters == {"artifact": 2, "store": 2, "backup": 2, "runtime": 1}
status, replay_response = _post(
endpoint, status_module.FORGE_SECRET_DELIVERY_PATH, body, headers
)
assert status == 403
assert replay_response == b'{"status":"authentication_rejected"}\n'
assert len(helper_calls) == 1
assert counters == {"artifact": 2, "store": 2, "backup": 2, "runtime": 1}
def test_new_nonce_retries_same_bundle_and_changed_input_is_rejected(
status_module, endpoint, monkeypatch
):
_install_hmac_test(monkeypatch, status_module)
from forge_secret_bundle import ForgeSecretBundleError
accepted_inputs = None
helper_calls = []
def helper(oidc_secret, read_token, config_bytes, *, validate_config):
nonlocal accepted_inputs
inputs = (oidc_secret, read_token, config_bytes)
helper_calls.append(inputs)
assert validate_config(config_bytes) is True
if accepted_inputs is None:
accepted_inputs = inputs
elif inputs != accepted_inputs:
raise ForgeSecretBundleError("input_changed")
return {
"status": "bundle_ready",
"secretFilesReady": 7,
"masterKeyBackupVerified": True,
"applyPreflight": "not_evaluated",
}
_install_successful_prerequisites(monkeypatch, status_module, helper)
def send(payload):
body = json.dumps(payload, separators=(",", ":")).encode()
return _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
_signed_headers(status_module, body),
)
assert send(_payload(status_module))[0] == 200
assert send(_payload(status_module))[0] == 200
changed = _payload(status_module)
changed["oidcClientSecretBase64"] = base64.b64encode(b"x" * 40).decode("ascii")
status, response = send(changed)
assert status == 409
assert json.loads(response) == {
"status": "secret_bundle_failed",
"code": "input_changed",
}
assert accepted_inputs == (OIDC_SECRET, READ_TOKEN, _config())
assert helper_calls == [
accepted_inputs,
accepted_inputs,
(b"x" * 40, READ_TOKEN, _config()),
]
assert OIDC_SECRET.decode() not in response.decode()
assert READ_TOKEN.decode() not in response.decode()
@pytest.mark.skipif(
os.name != "posix" or getattr(os, "geteuid", lambda: 1)() != 0,
reason="the real bundle helper requires POSIX root ownership",
)
def test_signed_http_delivery_uses_real_bundle_helper_and_never_rotates(
status_module, endpoint, monkeypatch, tmp_path
):
_install_hmac_test(monkeypatch, status_module)
import importlib
bundle_module = importlib.import_module("forge_secret_bundle")
assert Path(bundle_module.__file__).resolve() == (
BROKER / "forge_secret_bundle.py"
).resolve()
root = tmp_path / "real-bundle-root"
root.mkdir(mode=0o700)
root.chmod(0o700)
roots = bundle_module._Roots.for_test(root)
for path in (roots.config_root, roots.state_parent, roots.backup_root):
path.mkdir(mode=0o700, exist_ok=True)
path.chmod(0o700)
monkeypatch.setattr(bundle_module, "_PRODUCTION_ROOTS", roots)
counters = _install_successful_prerequisites(
monkeypatch, status_module
)
monkeypatch.setattr(
status_module._forge_first_install_controller,
"remember_preflight",
lambda *_args: pytest.fail("bundle readiness must not persist a preflight"),
)
monkeypatch.setattr(
status_module._forge_first_install_controller,
"apply",
lambda *_args: pytest.fail("bundle readiness must not start first install"),
)
def send(payload):
body = json.dumps(payload, separators=(",", ":")).encode()
return _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
_signed_headers(status_module, body),
)
status, response = send(_payload(status_module))
assert status == 200
decoded = json.loads(response)
assert decoded["status"] == "bundle_ready"
assert decoded["secretFilesReady"] == 7
assert decoded["masterKeyBackupVerified"] is True
assert decoded["applyPreflight"] == "not_evaluated"
assert OIDC_SECRET.decode() not in response.decode()
assert READ_TOKEN.decode() not in response.decode()
secret_paths = sorted(roots.secrets_root.iterdir())
assert {path.name for path in secret_paths} == {
"postgres_bootstrap_password.secret",
"runtime_database_url.secret",
"migrator_database_url.secret",
"oidc_client_secret.secret",
"session_secret.secret",
"master_key.secret",
"authentik_read_token.secret",
}
assert (roots.secrets_root / "oidc_client_secret.secret").read_bytes() == OIDC_SECRET
assert (roots.secrets_root / "authentik_read_token.secret").read_bytes() == READ_TOKEN
assert roots.config_path.read_bytes() == _config()
master_key = (roots.secrets_root / "master_key.secret").read_bytes()
assert len(master_key) == 32
backup_path = roots.backup_root / "master_key.backup"
assert backup_path.read_bytes() == master_key
protected_paths = [*secret_paths, roots.config_path, backup_path]
def identities():
return {
path: (path.stat().st_ino, path.read_bytes())
for path in protected_paths
}
original = identities()
status, retry_response = send(_payload(status_module))
assert status == 200
assert json.loads(retry_response)["status"] == "bundle_ready"
assert identities() == original
changed = _payload(status_module)
changed["authentikReadTokenBase64"] = base64.b64encode(
b"different-authentik-read-token-value-12345"
).decode("ascii")
status, changed_response = send(changed)
assert status == 409
assert json.loads(changed_response) == {
"status": "secret_bundle_failed",
"code": "input_changed",
}
assert identities() == original
assert OIDC_SECRET.decode() not in changed_response.decode()
assert READ_TOKEN.decode() not in changed_response.decode()
assert counters == {"artifact": 6, "store": 6, "backup": 6, "runtime": 3}
@pytest.mark.parametrize(
"bad_result",
[
{
"status": "bundle_ready",
"secretFilesReady": True,
"masterKeyBackupVerified": True,
"applyPreflight": "not_evaluated",
},
{
"status": "bundle_ready",
"secretFilesReady": 7,
"masterKeyBackupVerified": 1,
"applyPreflight": "not_evaluated",
},
{
"status": "bundle_ready",
"secretFilesReady": 7,
"masterKeyBackupVerified": True,
"applyPreflight": "not_evaluated",
"unexpected": "must be rejected",
},
],
)
def test_malformed_core_helper_results_are_not_accepted(
status_module, endpoint, monkeypatch, bad_result
):
_install_hmac_test(monkeypatch, status_module)
_install_successful_prerequisites(
monkeypatch, status_module, lambda *_args, **_kwargs: bad_result
)
body = json.dumps(_payload(status_module), separators=(",", ":")).encode()
status, response = _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
_signed_headers(status_module, body),
)
assert status == 409
assert json.loads(response) == {
"status": "secret_bundle_failed",
"code": "bundle_result_unverified",
}
def test_unrecognized_core_error_code_is_replaced_with_fixed_code(
status_module, endpoint, monkeypatch
):
_install_hmac_test(monkeypatch, status_module)
from forge_secret_bundle import ForgeSecretBundleError
private_error = "unapproved-" + OIDC_SECRET.decode()
def helper(*_args, **_kwargs):
raise ForgeSecretBundleError(private_error)
_install_successful_prerequisites(monkeypatch, status_module, helper)
body = json.dumps(_payload(status_module), separators=(",", ":")).encode()
status, response = _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
_signed_headers(status_module, body),
)
assert status == 409
assert json.loads(response) == {
"status": "secret_bundle_failed",
"code": "bundle_preparation_failed",
}
assert private_error.encode() not in response
assert OIDC_SECRET.decode().encode() not in response
def test_helper_error_text_is_never_returned(status_module, endpoint, monkeypatch):
_install_hmac_test(monkeypatch, status_module)
def helper(*_args, **_kwargs):
raise RuntimeError("disk failed: " + OIDC_SECRET.decode())
_install_successful_prerequisites(monkeypatch, status_module, helper)
body = json.dumps(_payload(status_module), separators=(",", ":")).encode()
status, response = _post(
endpoint,
status_module.FORGE_SECRET_DELIVERY_PATH,
body,
_signed_headers(status_module, body),
)
assert status == 409
assert OIDC_SECRET.decode() not in response.decode()
assert READ_TOKEN.decode() not in response.decode()
assert b"disk failed" not in response
def test_held_candidate_runtime_skips_destination_secrets_and_preflight_receipt(
status_module, monkeypatch, tmp_path
):
parsed = status_module._forge_secret_delivery_candidate(_payload(status_module))
assert parsed is not None
profile, candidate = parsed
app_root = tmp_path / "managed-appdata"
postgres = app_root / "itworx-forge" / "postgres"
postgres.mkdir(parents=True)
postgres.chmod(0o700)
os.chown(postgres, 70, 70)
app_root.chmod(0o700)
monkeypatch.setattr(status_module, "MANAGED_APPDATA_ROOT", app_root)
monkeypatch.setattr(status_module, "_refresh_appdata_root_selection", lambda: app_root)
monkeypatch.setattr(status_module, "appdata_root_is_safe", lambda _root: True)
monkeypatch.setattr(status_module, "_forge_app_dir_metadata_is_safe", lambda _info: True)
monkeypatch.setattr(status_module, "_forge_gitea_main_revision", lambda: profile["revision"])
manifest = b"fixed compose bytes used by this unit test"
monkeypatch.setattr(
status_module,
"FORGE_COMPOSE_SHA256",
hashlib.sha256(manifest).hexdigest(),
)
calls = {"registry": 0, "oidc": 0, "secret_metadata": 0}
def registry(_profile, *, candidate_config):
calls["registry"] += 1
assert candidate_config is candidate["config"]
return "passed", {"ownerVisibility": "private"}
def oidc(*, held_candidate):
calls["oidc"] += 1
assert held_candidate is candidate
return "passed", {"clientSecretMatched": True}
def secret_metadata(*_args, **_kwargs):
calls["secret_metadata"] += 1
pytest.fail("held candidate must not read destination secret files")
monkeypatch.setattr(status_module, "_forge_image_registry_attestation", registry)
monkeypatch.setattr(status_module, "_forge_oidc_attestation", oidc)
monkeypatch.setattr(status_module, "_forge_secret_metadata", secret_metadata)
monkeypatch.setattr(status_module, "_load_forge_bootstrap_config", lambda *_args: pytest.fail("held config fell back to disk"))
monkeypatch.setattr(status_module, "_forge_push_validation_attestation", lambda _revision: ("passed", {"context": "fixed"}))
monkeypatch.setattr(status_module, "_forge_local_image_present", lambda _image: None)
monkeypatch.setattr(
status_module._forge_first_install_controller,
"remember_preflight",
lambda *_args: pytest.fail("secret bundle readiness must not persist a preflight receipt"),
)
def fake_run(arguments, **_kwargs):
if arguments[:2] == ["git", "--git-dir"]:
return subprocess.CompletedProcess(arguments, 0, stdout=manifest, stderr=b"")
if arguments == ["docker", "network", "ls", "--format", "{{.Name}}"]:
return subprocess.CompletedProcess(arguments, 0, stdout="itworx-mcp-hub-npm\n", stderr="")
if arguments[:3] == ["docker", "network", "inspect"]:
return subprocess.CompletedProcess(
arguments,
0,
stdout=json.dumps({"Name": "itworx-mcp-hub-npm", "Driver": "bridge", "Scope": "local"}),
stderr="",
)
if arguments == ["docker", "ps", "-a", "--format", "{{.Names}}"]:
return subprocess.CompletedProcess(arguments, 0, stdout="", stderr="")
pytest.fail(f"unexpected runtime command: {arguments}")
monkeypatch.setattr(status_module.subprocess, "run", fake_run)
result = status_module._forge_bootstrap_runtime_checks(
profile, _held_candidate=candidate
)
assert result["status"] == "bundle_prerequisites_passed"
assert "secret_delivery" not in result["checks"]
assert calls == {"registry": 1, "oidc": 1, "secret_metadata": 0}
def test_release_store_proof_checks_fixed_volume_alias_and_pinned_identity(
status_module, monkeypatch
):
release_store = status_module._mcp_hub_release_store
document = {
"Name": release_store.VOLUME_NAME,
"Driver": "local",
"Scope": "local",
"Mountpoint": release_store.VOLUME_HOST_ROOT.as_posix(),
"Options": None,
"Labels": dict(release_store.EXPECTED_LABELS),
}
commands = []
def run_fixed(arguments, *, maximum_stdout, **_kwargs):
commands.append((arguments, maximum_stdout))
if arguments[:2] == ["docker", "info"]:
return b"/var/lib/docker\n", b""
if arguments[:3] == ["docker", "volume", "inspect"]:
return json.dumps(document).encode(), b""
pytest.fail(f"unexpected command {arguments}")
class _PinnedDirectory:
def __init__(self, identity):
self.value = identity
def identity(self):
return self.value
class _Store:
host = _PinnedDirectory((4, 5))
logical = _PinnedDirectory((4, 5))
def __enter__(self):
return self
def __exit__(self, *_args):
return None
def assert_current(self):
return None
opened = []
def open_fixed(metadata, docker_root_dir):
opened.append((metadata, docker_root_dir))
return _Store()
monkeypatch.setattr(status_module, "_forge_bounded_readonly_command", run_fixed)
monkeypatch.setattr(status_module, "_repopulse_adoption_alias_mounts_ready", lambda: True)
monkeypatch.setattr(
status_module._repopulse_adoption_store,
"capability_report",
lambda: {"controllerAliasesReady": True},
)
monkeypatch.setattr(release_store, "open_fixed_release_store", open_fixed)
identity = status_module._forge_release_store_identity()
assert identity == (
"/var/lib/docker",
release_store.VOLUME_NAME,
(4, 5),
(4, 5),
)
assert opened == [(document, "/var/lib/docker")]
assert [item[0] for item in commands] == [
["docker", "info", "--format", "{{.DockerRootDir}}"],
[
"docker",
"volume",
"inspect",
"--format",
"{{json .}}",
release_store.VOLUME_NAME,
],
]
opened.clear()
document["Labels"] = {"itworx.purpose": "different-volume"}
assert status_module._forge_release_store_identity() is None
assert opened == []
def test_backup_volume_verifier_requires_its_read_only_none_contract(
status_module, monkeypatch
):
backup = types.ModuleType("forge_backup_paths")
backup.validate_forge_backup_volume = lambda: None
monkeypatch.setitem(sys.modules, "forge_backup_paths", backup)
assert status_module._forge_fixed_backup_volume_is_ready() is True
backup.validate_forge_backup_volume = lambda: True
assert status_module._forge_fixed_backup_volume_is_ready() is False
@@ -267,6 +267,36 @@ def test_forge_bootstrap_preflight_is_read_only_and_rejects_generic_apply(integr
json={"operation": "apply", "plan": profile},
)
assert generic_apply.status_code == 422
generic_rollback = client.post(
"/api/deployments",
headers={**human_headers(client), "Idempotency-Key": "forge-generic-rollback-0001"},
json={"operation": "rollback", "plan": profile},
)
assert generic_rollback.status_code == 422
apply_profile = {
"api_version": "itworx.forge-first-install/v1",
"revision": profile["revision"],
"workspace_image_digest": profile["workspace_image_digest"],
"frontdoor_image_digest": profile["frontdoor_image_digest"],
"preflight_job_id": result["id"],
"preflight_sha256": result["preflightSha256"],
}
dedicated_apply = client.post(
"/api/deployments/forge-bootstrap/first-install",
headers={**human_headers(client), "Idempotency-Key": "forge-api-install-0001"},
json=apply_profile,
)
assert dedicated_apply.status_code == 409
assert "passed Forge preflight" in dedicated_apply.json()["detail"]
assert client.get(
"/api/deployments/forge-bootstrap/first-install/forge-install-" + "0" * 64,
headers=human_headers(client),
).status_code == 404
assert client.get(
"/api/deployments/forge-install-" + "0" * 64,
headers=human_headers(client),
).status_code == 404
async def exercise_mcp():
async with Client(mcp_server.appops_mcp) as mcp_client:
@@ -280,6 +310,23 @@ def test_forge_bootstrap_preflight_is_read_only_and_rejects_generic_apply(integr
{"plan": profile, "idempotencyKey": "forge-mcp-apply-0001"},
)
assert apply.structured_content["status"] == "invalid"
forge_apply = await mcp_client.call_tool(
"forge.bootstrap.apply",
{"profile": apply_profile, "idempotencyKey": "forge-mcp-install-0001"},
)
assert forge_apply.structured_content["status"] == "invalid"
forge_status = await mcp_client.call_tool(
"forge.bootstrap.status", {"jobId": "forge-install-" + "0" * 64}
)
assert forge_status.structured_content["status"] == "not_found"
generic_forge_status = await mcp_client.call_tool(
"deployment.status", {"jobId": "forge-install-" + "0" * 64}
)
assert generic_forge_status.structured_content["status"] == "invalid"
forge_resume = await mcp_client.call_tool(
"forge.bootstrap.resume", {"jobId": "forge-install-" + "0" * 64}
)
assert forge_resume.structured_content["status"] == "not_found"
asyncio.run(exercise_mcp())
@@ -2,13 +2,13 @@ from __future__ import annotations
import importlib.util
import io
import json
import sys
import types
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
CONTROLLER_ROOT = ROOT / "ops" / "gitea-deploy-broker"
CONTROLLER_PATH = CONTROLLER_ROOT / "runner_capacity_controller.py"
@@ -123,8 +123,18 @@ def test_host_snapshot_handles_absent_load_tuple(capacity_module, monkeypatch):
assert result["host"] is None
@pytest.mark.parametrize("raw", ["", "pswpin 1\n", "pswpin -1\npswpout 2\n",
"pswpin 1\npswpin 2\npswpout 3\n", "pswpin invalid\npswpout 2\n", "x" * 65537])
@pytest.mark.parametrize(
"raw",
[
"",
"pswpin 1\n",
"pswpin -1\npswpout 2\n",
"pswpin 1\npswpin 2\npswpout 3\n",
"pswpin invalid\npswpout 2\n",
"x" * 65537,
],
ids=["empty", "missing-counter", "negative", "duplicate", "invalid", "oversized"],
)
def test_strict_swap_reader_rejects_missing_or_invalid_counters(capacity_module, monkeypatch, raw):
monkeypatch.setattr(capacity_module, "Path", lambda _path: types.SimpleNamespace(
open=lambda **_kwargs: io.StringIO(raw)
@@ -161,6 +171,96 @@ def test_host_snapshot_rejects_invalid_global_metrics(capacity_module, monkeypat
assert result["host"] is None
def test_snapshot_and_cycle_treat_empty_optional_burst_inspect_as_not_configured(
capacity_module, monkeypatch
):
service = capacity_module.RunnerCapacityController()
commands = []
state_reads = []
def inspect_only(arguments, **kwargs):
commands.append((arguments, kwargs))
assert arguments == ["docker", "inspect", capacity_module.BURST_RUNNER_NAME]
assert kwargs == {"check": False}
return "[]"
def unexpected(*_args, **_kwargs):
pytest.fail("an absent optional burst runner must not reach registration, state, or mutation")
monkeypatch.setattr(service, "_run", inspect_only)
monkeypatch.setattr(service, "_read_state", lambda: state_reads.append("read") or {})
for name in ("_runner_registration", "_gitea_request", "_write_state", "_record", "_set_disabled"):
monkeypatch.setattr(service, name, unexpected)
snapshot = service.snapshot()
result = service.cycle()
assert snapshot["status"] == "not_configured"
assert snapshot["runner"] == capacity_module.BURST_RUNNER_NAME
assert result["status"] == "not_configured"
assert result["runner"] == capacity_module.BURST_RUNNER_NAME
assert state_reads == ["read"]
assert commands == [
(["docker", "inspect", capacity_module.BURST_RUNNER_NAME], {"check": False}),
(["docker", "inspect", capacity_module.BURST_RUNNER_NAME], {"check": False}),
]
@pytest.mark.parametrize(
("raw", "reason"),
[
("not-json", "runner_capacity_container_invalid"),
("{}", "runner_capacity_container_invalid"),
(
json.dumps([
{"Config": {"Labels": {
"com.docker.compose.project": "gitea-deploy-runners",
"tech.itworx.role": "gitea-actions-burst-runner",
}}, "State": {}},
{"Config": {"Labels": {
"com.docker.compose.project": "gitea-deploy-runners",
"tech.itworx.role": "gitea-actions-burst-runner",
}}, "State": {}},
]),
"runner_capacity_container_invalid",
),
(
json.dumps([{
"Config": {"Labels": {
"com.docker.compose.project": "foreign-project",
"tech.itworx.role": "gitea-actions-burst-runner",
}},
"State": {},
}]),
"runner_capacity_container_identity_invalid",
),
],
)
def test_cycle_still_refuses_malformed_foreign_or_multiple_burst_inspect_results(
capacity_module, monkeypatch, raw, reason
):
service = capacity_module.RunnerCapacityController()
commands = []
def inspect_only(arguments, **kwargs):
commands.append((arguments, kwargs))
return raw
def unexpected(*_args, **_kwargs):
pytest.fail("invalid burst inventory must stop before registration or mutation")
monkeypatch.setattr(service, "_run", inspect_only)
for name in ("_runner_registration", "_gitea_request", "_write_state", "_record", "_set_disabled"):
monkeypatch.setattr(service, name, unexpected)
with pytest.raises(capacity_module.RunnerCapacityError, match=reason):
service.cycle()
assert commands == [
(["docker", "inspect", capacity_module.BURST_RUNNER_NAME], {"check": False})
]
def _wire_common(service, monkeypatch, *, running=False, busy=False, disabled=True):
monkeypatch.setattr(
service,
@@ -183,7 +283,7 @@ def _wire_common(service, monkeypatch, *, running=False, busy=False, disabled=Tr
"labels": ["linux-validation", "ubuntu-latest"],
},
)
monkeypatch.setattr(service, "_read_state", lambda: {})
monkeypatch.setattr(service, "_read_state", dict)
monkeypatch.setattr(service, "_write_state", lambda _state: None)
monkeypatch.setattr(service, "_record", lambda _event: None)
monkeypatch.setattr(service, "_host_guard_state", lambda: ("clear", ""))
@@ -421,6 +421,119 @@ def test_successful_main_run_accepts_actual_gitea_run_without_workflow_id(
assert calls[1].endswith("/actions/runs?branch=main&limit=100&page=1")
def test_source_image_fetch_accepts_actual_gitea_commit_tree_shape(
specialize, monkeypatch, tmp_path
):
revision = "a" * 40
tree = "b" * 40
calls = []
workflow_calls = []
def api(path):
calls.append(path)
if path.endswith("/branches/main"):
return {"commit": {"id": revision}}
if path.endswith(f"/git/commits/{revision}"):
return {"sha": revision, "commit": {"tree": {"sha": tree}}}
if "/actions/runs/" in path and "/artifacts?" in path:
return {
"artifacts": [
{
"name": specialize.IMAGE_ARTIFACT_PREFIX + revision,
"expired": False,
"id": 44,
"size_in_bytes": 1,
}
]
}
raise AssertionError(f"unexpected fixed Gitea API path: {path}")
def successful_run(workflow_path, requested_revision):
assert requested_revision == revision
workflow_calls.append(workflow_path)
return {"id": 41 if workflow_path == specialize.IMAGE_WORKFLOW else 42}
class ReachedArtifactDownload(Exception):
pass
def download(_artifact_id, _destination):
raise ReachedArtifactDownload
monkeypatch.setattr(specialize, "_api_json", api)
monkeypatch.setattr(specialize, "_successful_main_run", successful_run)
monkeypatch.setattr(specialize, "_download_artifact_zip", download)
with pytest.raises(ReachedArtifactDownload):
specialize._fetch_source_image_artifact(revision, tree, tmp_path)
assert workflow_calls == [specialize.IMAGE_WORKFLOW, specialize.FULL_WORKFLOW]
assert calls == [
f"/repos/{specialize.SOURCE_REPOSITORY_API}/branches/main",
f"/repos/{specialize.SOURCE_REPOSITORY_API}/git/commits/{revision}",
f"/repos/{specialize.SOURCE_REPOSITORY_API}/actions/runs/41/artifacts?name={specialize.IMAGE_ARTIFACT_PREFIX}{revision}",
]
@pytest.mark.parametrize(
("commit_value", "error"),
[
({"sha": "a" * 40}, "source_tree_mismatch"),
({"sha": "a" * 40, "commit": {}}, "source_tree_mismatch"),
(
{"sha": "a" * 40, "commit": {"tree": {"sha": "c" * 40}}},
"source_tree_mismatch",
),
(
{"sha": "a" * 40, "tree": {"sha": "b" * 40}},
"source_tree_mismatch",
),
(
{"sha": "c" * 40, "commit": {"tree": {"sha": "b" * 40}}},
"source_revision_mismatch",
),
(
{"commit": {"tree": {"sha": "b" * 40}}},
"source_revision_mismatch",
),
],
)
def test_source_image_fetch_rejects_noncanonical_gitea_commit_payload_before_artifact_lookup(
specialize, monkeypatch, tmp_path, commit_value, error
):
revision = "a" * 40
calls = []
workflow_calls = []
def api(path):
calls.append(path)
if path.endswith("/branches/main"):
return {"commit": {"id": revision}}
if path.endswith(f"/git/commits/{revision}"):
return commit_value
raise AssertionError(f"unexpected fixed Gitea API path: {path}")
def unexpected_workflow(*_args):
workflow_calls.append(True)
raise AssertionError("workflow lookup must not follow an untrusted commit response")
monkeypatch.setattr(specialize, "_api_json", api)
monkeypatch.setattr(specialize, "_successful_main_run", unexpected_workflow)
monkeypatch.setattr(
specialize,
"_download_artifact_zip",
lambda *_args: pytest.fail("artifact download must not follow an untrusted commit response"),
)
with pytest.raises(specialize.RunnerFleetSpecializeError, match=error):
specialize._fetch_source_image_artifact(revision, "b" * 40, tmp_path)
assert calls == [
f"/repos/{specialize.SOURCE_REPOSITORY_API}/branches/main",
f"/repos/{specialize.SOURCE_REPOSITORY_API}/git/commits/{revision}",
]
assert workflow_calls == []
@pytest.mark.parametrize(
"changes",
[
+48 -1
View File
@@ -93,12 +93,59 @@ def test_portainer_config_save_never_returns_secret(api_client):
"verifyTls": False,
}
assert secret not in str(payload)
all_settings = api_client.get("/api/settings/connectors").json()
assert secret not in str(all_settings)
assert next(item for item in all_settings if item["connectorId"] == "portainer")["hasSecret"] is True
def test_generic_authentik_setup_cannot_set_or_overwrite_forge_oidc_binding(api_client):
import json
from sqlmodel import Session
from backend.app.db import session as db_session
from backend.app.schemas.forge_oidc import FORGE_OIDC_BINDING_SECRET_REF
from backend.app.services.secret_store import get_secret, set_secret
from backend.app.services.setup_service import _apply_forge_oidc_binding_to_runtime
from backend.app.core.config import settings
binding = json.dumps(
{
"version": 1,
"providerId": "31",
"scopeMappingIds": {
"openid": "75a34fd6-cc81-40b8-a8c3-2745af9567ab",
"email": "312810bb-11da-47d8-97d4-f2155348eaa2",
"profile": "7aa8c5f8-5e8e-4a1b-9c11-3a4f0a731cff",
},
}
)
with Session(db_session.engine) as session:
set_secret(session, FORGE_OIDC_BINDING_SECRET_REF, binding)
rejected = api_client.patch(
"/api/settings/connectors/authentik",
json={"forgeOidcBinding": binding, "providerId": "999"},
)
assert rejected.status_code == 422
saved = api_client.patch(
"/api/settings/connectors/authentik",
json={"url": "https://auth.example-host.com", "secret": "GENERIC_AUTHENTIK_TOKEN"},
)
assert saved.status_code == 200
assert binding not in saved.text
assert "GENERIC_AUTHENTIK_TOKEN" not in saved.text
with Session(db_session.engine) as session:
assert get_secret(session, FORGE_OIDC_BINDING_SECRET_REF, touch=False) == binding
assert _apply_forge_oidc_binding_to_runtime(session) is True
assert settings.apis["AUTHENTIK_FORGE_OIDC_PROVIDER_ID"] == "31"
all_settings = api_client.get("/api/settings/connectors").json()
assert binding not in str(all_settings)
assert "GENERIC_AUTHENTIK_TOKEN" not in str(all_settings)
def test_portainer_config_save_trims_safe_fields(api_client):
payload = api_client.patch(
"/api/settings/connectors/portainer",