Publish curated Unraid-AppOps-Gateway source from ba66029223865607deddc234e283c11127324eb8
This commit is contained in:
1 parent
820244dbb3
commit
8dd4b40252
52 files changed
+11814
-288
No files matched your search
@@ -81,7 +81,21 @@ class _Broker:
|
||||
|
||||
def enrollment_capabilities(self, *, secret):
|
||||
assert secret == "b" * 64
|
||||
return {"status": "ready", "trustedAppdataRoot": self.appdata_root}
|
||||
return {
|
||||
"status": "ready",
|
||||
"trustedAppdataRoot": self.appdata_root,
|
||||
"features": {
|
||||
"deploymentApply": True,
|
||||
"deploymentVerify": True,
|
||||
"rollback": True,
|
||||
"exactTargetPolicy": True,
|
||||
"hmacV2": True,
|
||||
"replayProtection": True,
|
||||
"enrollment": True,
|
||||
"targetRegistration": True,
|
||||
"runtimeSecretSealing": True,
|
||||
},
|
||||
}
|
||||
|
||||
def enrollment_preflight(self, enrollment, *, secret):
|
||||
assert secret == "b" * 64
|
||||
@@ -209,6 +223,30 @@ def _plane(portainer=None, broker=None, npm=None, authentik=None):
|
||||
)
|
||||
|
||||
|
||||
class _CompensatingBroker(_Broker):
|
||||
def unenroll_target(self, _target, *, secret):
|
||||
assert secret == "b" * 64
|
||||
return {"status": "rolled_back"}
|
||||
|
||||
|
||||
class _CompensatingNpm(_Npm):
|
||||
def remove_managed_proxy_host(self, *, host_id, ownership_id):
|
||||
return {"success": True, "status": "removed"}
|
||||
|
||||
|
||||
class _CompensatingAuthentik(_Authentik):
|
||||
def remove_deployment_oidc_application(self, **_kwargs):
|
||||
return {"success": True, "status": "removed"}
|
||||
|
||||
|
||||
def _compensating_plane(broker=None):
|
||||
return _plane(
|
||||
broker=broker or _CompensatingBroker(),
|
||||
npm=_CompensatingNpm(),
|
||||
authentik=_CompensatingAuthentik(),
|
||||
)
|
||||
|
||||
|
||||
def _configure(session):
|
||||
set_secret(session, "ENROLLMENT_BOOTSTRAP", "b" * 64)
|
||||
EnrollmentPlane.set_trust_config(session, _trust())
|
||||
@@ -405,8 +443,9 @@ def test_capabilities_block_when_controller_root_differs_from_operator_trust(tmp
|
||||
result = _plane(broker=AlternateRootBroker()).capabilities(session)
|
||||
|
||||
assert result["targetEnrollment"]["status"] == "unavailable"
|
||||
assert result["controller"]["status"] == "unavailable"
|
||||
assert result["controller"]["reason"] == "trusted_appdata_root_mismatch"
|
||||
assert result["controller"]["status"] == "ready"
|
||||
assert result["controller"]["reason"] == ""
|
||||
assert result["enrollmentReadiness"]["reason"] == "trusted_appdata_root_unavailable"
|
||||
|
||||
|
||||
def test_capabilities_treat_roots_as_independent_availability_not_global_selection(tmp_path):
|
||||
@@ -424,6 +463,17 @@ def test_capabilities_treat_roots_as_independent_availability_not_global_selecti
|
||||
{"root": managed_root, "status": "ready", "reason": ""},
|
||||
],
|
||||
"managedStorage": {"status": "ready", "initializationAvailable": False},
|
||||
"features": {
|
||||
"deploymentApply": True,
|
||||
"deploymentVerify": True,
|
||||
"rollback": True,
|
||||
"exactTargetPolicy": True,
|
||||
"hmacV2": True,
|
||||
"replayProtection": True,
|
||||
"enrollment": True,
|
||||
"targetRegistration": True,
|
||||
"runtimeSecretSealing": True,
|
||||
},
|
||||
}
|
||||
|
||||
with _session(tmp_path) as session:
|
||||
@@ -454,6 +504,123 @@ def test_capabilities_treat_roots_as_independent_availability_not_global_selecti
|
||||
]
|
||||
|
||||
|
||||
def test_runtime_capabilities_remain_available_when_selected_enrollment_root_is_unavailable(tmp_path):
|
||||
legacy_root = "/srv/example/appdata"
|
||||
managed_root = "/var/lib/docker/volumes/itworx-managed-appdata/_data"
|
||||
|
||||
class CoreReadyBroker(_Broker):
|
||||
def enrollment_capabilities(self, *, secret):
|
||||
assert secret == "b" * 64
|
||||
return {
|
||||
"status": "ready",
|
||||
"reason": "",
|
||||
"availableTrustedAppdataRoots": [
|
||||
{"root": legacy_root, "status": "unavailable", "reason": "legacy_appdata_root_unavailable"},
|
||||
{"root": managed_root, "status": "unavailable", "reason": "managed_appdata_volume_missing"},
|
||||
],
|
||||
"managedStorage": {
|
||||
"status": "unavailable",
|
||||
"reason": "managed_appdata_volume_missing",
|
||||
"initializationAvailable": False,
|
||||
},
|
||||
"features": {
|
||||
"deploymentApply": True,
|
||||
"deploymentVerify": True,
|
||||
"rollback": True,
|
||||
"exactTargetPolicy": True,
|
||||
"hmacV2": True,
|
||||
"replayProtection": True,
|
||||
"enrollment": False,
|
||||
"targetRegistration": False,
|
||||
"runtimeSecretSealing": False,
|
||||
},
|
||||
"enrollmentReadiness": {
|
||||
"status": "unavailable",
|
||||
"reason": "legacy_appdata_root_unavailable",
|
||||
},
|
||||
"registry": {"status": "ready", "registeredProjectCount": 48},
|
||||
}
|
||||
|
||||
with _session(tmp_path) as session:
|
||||
_configure(session)
|
||||
result = _plane(broker=CoreReadyBroker()).capabilities(session)
|
||||
|
||||
assert result["controller"] == {"status": "ready", "reason": ""}
|
||||
assert result["targetEnrollment"]["status"] == "unavailable"
|
||||
assert result["runtimeDeployment"]["status"] == "ready"
|
||||
assert result["rollback"]["status"] == "ready"
|
||||
assert result["verification"]["status"] == "ready"
|
||||
assert result["enrollmentReadiness"] == {
|
||||
"status": "unavailable",
|
||||
"reason": "legacy_appdata_root_unavailable",
|
||||
}
|
||||
assert result["availableTrustedAppdataRoots"] == [
|
||||
{"root": legacy_root, "status": "unavailable", "reason": "legacy_appdata_root_unavailable"},
|
||||
{"root": managed_root, "status": "unavailable", "reason": "managed_appdata_volume_missing"},
|
||||
]
|
||||
|
||||
|
||||
def test_runtime_capabilities_fail_closed_when_controller_feature_attestation_is_missing(tmp_path):
|
||||
class MissingFeaturesBroker(_Broker):
|
||||
def enrollment_capabilities(self, *, secret):
|
||||
return {"status": "ready", "trustedAppdataRoot": self.appdata_root}
|
||||
|
||||
with _session(tmp_path) as session:
|
||||
_configure(session)
|
||||
result = _plane(broker=MissingFeaturesBroker()).capabilities(session)
|
||||
|
||||
assert result["controller"]["status"] == "ready"
|
||||
assert result["runtimeDeployment"]["status"] == "unavailable"
|
||||
assert result["rollback"]["status"] == "unavailable"
|
||||
assert result["verification"]["status"] == "unavailable"
|
||||
assert result["targetEnrollment"]["status"] == "unavailable"
|
||||
|
||||
|
||||
def test_managed_storage_capability_preserves_only_bounded_controller_reason(tmp_path):
|
||||
managed_root = "/var/lib/docker/volumes/itworx-managed-appdata/_data"
|
||||
|
||||
class ManagedStorageUnavailableBroker(_Broker):
|
||||
def enrollment_capabilities(self, *, secret):
|
||||
return {
|
||||
"status": "ready",
|
||||
"availableTrustedAppdataRoots": [
|
||||
{"root": "/srv/example/appdata", "status": "ready"},
|
||||
{"root": managed_root, "status": "unavailable", "reason": "managed_appdata_volume_missing"},
|
||||
],
|
||||
"managedStorage": {
|
||||
"status": "unavailable",
|
||||
"reason": "managed_appdata_volume_missing",
|
||||
"initializationAvailable": False,
|
||||
},
|
||||
"features": {
|
||||
"deploymentApply": True,
|
||||
"deploymentVerify": True,
|
||||
"rollback": True,
|
||||
"exactTargetPolicy": True,
|
||||
"hmacV2": True,
|
||||
"replayProtection": True,
|
||||
"enrollment": False,
|
||||
"targetRegistration": False,
|
||||
"runtimeSecretSealing": False,
|
||||
},
|
||||
}
|
||||
|
||||
with _session(tmp_path) as session:
|
||||
_configure(session)
|
||||
EnrollmentPlane.set_trust_config(
|
||||
session,
|
||||
_trust().model_copy(update={
|
||||
"trusted_appdata_root": managed_root,
|
||||
"managed_appdata_backup_acknowledged": True,
|
||||
}),
|
||||
)
|
||||
result = _plane(broker=ManagedStorageUnavailableBroker()).capabilities(session)
|
||||
|
||||
assert result["managedStorage"]["status"] == "unavailable"
|
||||
assert result["managedStorage"]["reason"] == "managed_appdata_volume_missing"
|
||||
assert result["enrollmentReadiness"]["reason"] == "managed_appdata_volume_missing"
|
||||
|
||||
|
||||
def test_capabilities_fail_closed_and_hide_unrecognized_controller_roots(tmp_path):
|
||||
class MalformedRootsBroker(_Broker):
|
||||
def enrollment_capabilities(self, *, secret):
|
||||
@@ -543,6 +710,186 @@ def test_enrollment_creates_write_only_credentials_exact_policies_and_is_idempot
|
||||
assert len(session.exec(select(EnrollmentJob)).all()) == 1
|
||||
|
||||
|
||||
def test_enrollment_policy_id_collision_blocks_before_mutation_and_leaves_no_partial_policy(tmp_path):
|
||||
class RecordingBroker(_CompensatingBroker):
|
||||
enroll_calls = 0
|
||||
|
||||
def enroll_target(self, *args, **kwargs):
|
||||
self.enroll_calls += 1
|
||||
return super().enroll_target(*args, **kwargs)
|
||||
|
||||
with _session(tmp_path) as session:
|
||||
_configure(session)
|
||||
collision = Policy(
|
||||
id="enrollment-wsbc-portal-deployment-rollback",
|
||||
action="deployment.rollback",
|
||||
target="wsbc-portal",
|
||||
voice="blocked",
|
||||
manual="allowed",
|
||||
risk="high",
|
||||
reason="pre-existing exact-target policy",
|
||||
provenance_json={"owner": "pre-existing"},
|
||||
)
|
||||
session.add(collision)
|
||||
session.commit()
|
||||
|
||||
broker = RecordingBroker()
|
||||
result = _compensating_plane(broker).enroll(
|
||||
session, _plan(), idempotency_key="enroll-policy-collision-0001"
|
||||
)
|
||||
|
||||
# A pre-existing rollback policy must never leave behind the apply half
|
||||
# of the enrollment-generated pair, even after failure compensation.
|
||||
assert session.get(Policy, "enrollment-wsbc-portal-deployment-apply") is None
|
||||
assert session.get(Policy, collision.id).provenance_json == {"owner": "pre-existing"}
|
||||
assert result["enrollment"]["status"] == "preflight_failed"
|
||||
assert result["job"]["status"] == "preflight_failed"
|
||||
assert "exact_policy_id_collision" in result["job"]["preflight"]["blockers"]
|
||||
assert broker.enroll_calls == 0
|
||||
assert session.exec(select(Policy).where(Policy.target == "wsbc-portal")).all() == [collision]
|
||||
|
||||
|
||||
def test_late_policy_id_collision_rolls_back_the_whole_pair(tmp_path, monkeypatch):
|
||||
class FailedControllerCompensationBroker(_CompensatingBroker):
|
||||
def unenroll_target(self, _target, *, secret):
|
||||
assert secret == "b" * 64
|
||||
return {"status": "rollback_failed"}
|
||||
|
||||
collision = Policy(
|
||||
id="enrollment-wsbc-portal-deployment-rollback",
|
||||
action="deployment.rollback",
|
||||
target="wsbc-portal",
|
||||
voice="blocked",
|
||||
manual="allowed",
|
||||
risk="high",
|
||||
reason="concurrent exact-target policy",
|
||||
provenance_json={"owner": "concurrent"},
|
||||
)
|
||||
checkpoint = EnrollmentPlane._checkpoint
|
||||
|
||||
def add_collision_after_preflight(session, enrollment, job, *, stage, evidence):
|
||||
checkpoint(session, enrollment, job, stage=stage, evidence=evidence)
|
||||
if stage == "installing_policy":
|
||||
session.add(collision)
|
||||
session.commit()
|
||||
|
||||
monkeypatch.setattr(
|
||||
EnrollmentPlane, "_checkpoint", staticmethod(add_collision_after_preflight)
|
||||
)
|
||||
with _session(tmp_path) as session:
|
||||
_configure(session)
|
||||
result = _compensating_plane(FailedControllerCompensationBroker()).enroll(
|
||||
session, _plan(), idempotency_key="fixture-" + "x" * 32
|
||||
)
|
||||
|
||||
assert result["job"]["preflight"]["status"] == "ready"
|
||||
assert result["enrollment"]["status"] == "compensation_required"
|
||||
assert result["job"]["status"] == "compensation_required"
|
||||
assert result["job"]["evidence"]["compensation"] == {
|
||||
"completed": False,
|
||||
"outcomes": {
|
||||
"oidc": "removed",
|
||||
"proxy": "removed",
|
||||
"controller": "rollback_failed",
|
||||
},
|
||||
}
|
||||
assert session.get(Policy, "enrollment-wsbc-portal-deployment-apply") is None
|
||||
assert session.get(Policy, collision.id).provenance_json == {"owner": "concurrent"}
|
||||
assert session.exec(select(Policy).where(Policy.target == "wsbc-portal")).all() == [collision]
|
||||
|
||||
|
||||
def test_final_ready_commit_failure_rolls_back_the_policy_pair(tmp_path):
|
||||
engine = create_engine(f"sqlite:///{tmp_path / 'enrollment.db'}")
|
||||
SQLModel.metadata.create_all(engine)
|
||||
with Session(engine) as session:
|
||||
_configure(session)
|
||||
plane = _plane()
|
||||
commit = session.commit
|
||||
|
||||
def fail_after_policy_pair_is_staged():
|
||||
ready = session.exec(
|
||||
select(Enrollment).where(Enrollment.status == "ready")
|
||||
).first()
|
||||
policies = session.exec(
|
||||
select(Policy).where(Policy.target == "wsbc-portal")
|
||||
).all()
|
||||
if ready is not None and {policy.action for policy in policies} == {
|
||||
"deployment.apply", "deployment.rollback",
|
||||
}:
|
||||
raise RuntimeError("simulated final enrollment commit failure")
|
||||
commit()
|
||||
|
||||
session.commit = fail_after_policy_pair_is_staged
|
||||
with pytest.raises(RuntimeError, match="final enrollment commit failure"):
|
||||
plane.enroll(session, _plan(), idempotency_key="enroll-final-commit-failure-0001")
|
||||
|
||||
# A request-scoped MCP session is closed when the operation raises; prove
|
||||
# the pending pair was not committed by reading through a fresh session.
|
||||
with Session(engine) as verification:
|
||||
enrollment = verification.exec(select(Enrollment)).one()
|
||||
job = verification.exec(select(EnrollmentJob)).one()
|
||||
assert enrollment.status == "provisioning"
|
||||
assert job.stage == "installing_policy"
|
||||
assert verification.exec(
|
||||
select(Policy).where(Policy.target == "wsbc-portal")
|
||||
).all() == []
|
||||
|
||||
|
||||
def test_concurrent_policy_id_collision_during_flush_rolls_back_pending_pair(tmp_path):
|
||||
collision = Policy(
|
||||
id="enrollment-wsbc-portal-deployment-rollback",
|
||||
action="deployment.rollback",
|
||||
target="wsbc-portal",
|
||||
voice="blocked",
|
||||
manual="allowed",
|
||||
risk="high",
|
||||
reason="concurrent exact-target policy",
|
||||
provenance_json={"owner": "concurrent"},
|
||||
)
|
||||
with _session(tmp_path) as session:
|
||||
_configure(session)
|
||||
flush = session.flush
|
||||
inserted = False
|
||||
|
||||
def flush_with_concurrent_collision(*args, **kwargs):
|
||||
nonlocal inserted
|
||||
staged_ids = {
|
||||
policy.id for policy in session.new if isinstance(policy, Policy)
|
||||
}
|
||||
if not inserted and staged_ids == {
|
||||
"enrollment-wsbc-portal-deployment-apply",
|
||||
"enrollment-wsbc-portal-deployment-rollback",
|
||||
}:
|
||||
inserted = True
|
||||
with Session(session.get_bind()) as concurrent_session:
|
||||
concurrent_session.add(collision)
|
||||
concurrent_session.commit()
|
||||
return flush(*args, **kwargs)
|
||||
|
||||
session.flush = flush_with_concurrent_collision
|
||||
result = _compensating_plane().enroll(
|
||||
session, _plan(), idempotency_key="enroll-policy-concurrent-0001"
|
||||
)
|
||||
|
||||
assert inserted is True
|
||||
assert result["job"]["preflight"]["status"] == "ready"
|
||||
assert result["enrollment"]["status"] == "failed"
|
||||
assert session.get(Policy, "enrollment-wsbc-portal-deployment-apply") is None
|
||||
concurrent_policy = session.get(
|
||||
Policy, "enrollment-wsbc-portal-deployment-rollback"
|
||||
)
|
||||
assert concurrent_policy is not None
|
||||
assert concurrent_policy.provenance_json == {"owner": "concurrent"}
|
||||
policies = session.exec(select(Policy).where(Policy.target == "wsbc-portal")).all()
|
||||
assert [(policy.id, policy.action, policy.provenance_json) for policy in policies] == [
|
||||
(
|
||||
"enrollment-wsbc-portal-deployment-rollback",
|
||||
"deployment.rollback",
|
||||
{"owner": "concurrent"},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def test_interrupted_external_enrollment_requires_explicit_recovery(tmp_path):
|
||||
class InterruptedBroker(_Broker):
|
||||
def enroll_target(self, _enrollment, *, secret, idempotency_key):
|
||||
|
||||
Reference in new issue
Block a user