Merge pull request #241 from living-ip/fix/iap-missing-receipt
Some checks are pending
CI / lint-and-test (push) Waiting to run

Preserve no-send IAP failure receipts
This commit is contained in:
Fawaz 2026-07-23 15:07:13 -07:00 committed by GitHub
commit 6696f3adc0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 1136 additions and 92 deletions

View file

@ -344,6 +344,33 @@ contact GCP or Cloud SQL, change Telegram traffic, touch the VPS, approve a
proposal, or promote production. Those remain later, separately authorized proposal, or promote production. Those remain later, separately authorized
boundaries. boundaries.
## IAP result and failure receipts
The fixed-target IAP runner treats the remote `result.json`, the remote process
exit status/stdout, the IAP transport, and the private local result receipt as
one bounded result path. A successful operation is accepted only when the
structured result matches the exact request, revision, artifact, image, config
digest, service, database proof, and no-send authority contract.
Post-install failures are returned with allowlisted `failure_stage` and
`failure_reason` values. If explicit rollback passes, its exact restored
posture is recorded. If rollback cannot be proven, the remote helper stops the
staging service only after exact-binding the currently loaded unit, `ExecStop`,
supervising process, and container to the reviewed release. It then proves
`inactive/dead`, `MainPID=0`, and absence of the named candidate container
before reporting a fail-closed posture. Drift blocks the stop; a failure to
prove that posture is reported as unknown and is never converted into success.
If IAP returns no output, malformed output, a contract mismatch, or a transport
interruption, the local runner writes a private self-hashed failure receipt
with the bounded stage/reason, transfer-cleanup result, and `remote_state:
unknown`. Command stdout, stderr, tokens, passwords, and secret values are not
copied into a receipt or exception. That local-only receipt records requested
authority, not unproven remote outcomes. Operators must not infer a successful
or inert remote service from a local transport-failure receipt. Remote
operation failures and local transport failures use distinct outer receipt
schemas; the successful result-receipt v1 contract is unchanged.
## Offline evidence and live gate ## Offline evidence and live gate
Offline tests prove strict receipt/descriptor/inspection schemas, exact Offline tests prove strict receipt/descriptor/inspection schemas, exact

View file

@ -16,6 +16,7 @@ import subprocess
import sys import sys
import tarfile import tarfile
import tempfile import tempfile
import time
import zipfile import zipfile
from collections.abc import Callable, Mapping from collections.abc import Callable, Mapping
from pathlib import Path from pathlib import Path
@ -24,6 +25,7 @@ from typing import Any
SCHEMA = "livingip.leocleanNoSendIapRequest.v1" SCHEMA = "livingip.leocleanNoSendIapRequest.v1"
RESULT_SCHEMA = "livingip.leocleanNoSendIapRemoteResult.v1" RESULT_SCHEMA = "livingip.leocleanNoSendIapRemoteResult.v1"
FAILURE_RESULT_SCHEMA = "livingip.leocleanNoSendIapFailure.v1" FAILURE_RESULT_SCHEMA = "livingip.leocleanNoSendIapFailure.v1"
OPERATION_FAILURE_RESULT_SCHEMA = "livingip.leocleanNoSendIapOperationalFailure.v1"
PROJECT = "teleo-501523" PROJECT = "teleo-501523"
ZONE = "europe-west6-a" ZONE = "europe-west6-a"
INSTANCE = "teleo-staging-1" INSTANCE = "teleo-staging-1"
@ -48,6 +50,17 @@ MAX_MEMBER_BYTES = 2 * 1024 * 1024
MAX_MANIFEST_BYTES = 128 * 1024 MAX_MANIFEST_BYTES = 128 * 1024
MAX_COMMAND_OUTPUT_BYTES = 1024 * 1024 MAX_COMMAND_OUTPUT_BYTES = 1024 * 1024
MAX_TOKEN_BYTES = 64 * 1024 MAX_TOKEN_BYTES = 64 * 1024
POST_INSTALL_FAILURES = frozenset(
{
("install_receipt", "install_receipt_invalid"),
("service_before_restart", "service_verification_failed"),
("database_before_restart", "database_verification_failed"),
("service_restart", "service_restart_failed"),
("service_after_restart", "service_verification_failed"),
("restart_identity", "restart_identity_mismatch"),
("database_after_restart", "database_verification_failed"),
}
)
EXPECTED_MEMBERS = { EXPECTED_MEMBERS = {
"request-manifest.json", "request-manifest.json",
"release-artifact.zip", "release-artifact.zip",
@ -852,6 +865,236 @@ def _is_verification_failure(installer_module: Any, failure: Exception) -> bool:
return isinstance(failure_type, type) and isinstance(failure, failure_type) return isinstance(failure_type, type) and isinstance(failure, failure_type)
def _post_install_failure_coordinates(
installer_module: Any,
failure: Exception,
*,
fallback_stage: str,
fallback_reason: str,
) -> tuple[str, str]:
if _is_verification_failure(installer_module, failure):
failure_stage = getattr(failure, "failure_stage", None)
failure_reason = getattr(failure, "failure_reason", None)
_require(
isinstance(failure_stage, str)
and isinstance(failure_reason, str)
and (failure_stage, failure_reason) in installer_module.VERIFICATION_FAILURES,
"verification failure was not allowlisted",
)
return failure_stage, failure_reason
_require(
(fallback_stage, fallback_reason) in POST_INSTALL_FAILURES,
"post-install failure was not allowlisted",
)
return fallback_stage, fallback_reason
def _rollback_proven_service(posture: str) -> dict[str, Any]:
_require(posture in {"active", "inactive", "absent"}, "rollback posture was invalid")
return {
"status": "not_required",
"service_posture": posture,
"active_state": "not_observed",
"sub_state": "not_observed",
"main_pid": None,
"n_restarts": None,
"candidate_container": "not_observed",
}
def _fail_closed_inert_service(
*,
installer_module: Any,
release: Mapping[str, Any],
paths: Any,
runner: Runner,
) -> dict[str, Any]:
def contract_runner(argv: list[str]) -> subprocess.CompletedProcess[bytes]:
return _run(
argv,
runner=runner,
label="fail-closed service command",
require_success=False,
)
try:
systemctl = getattr(installer_module, "_systemctl", None)
stable_service_posture = getattr(installer_module, "_stable_service_posture", None)
verify_running_release = getattr(installer_module, "verify_running_release", None)
require_candidate_absent = getattr(installer_module, "_require_candidate_absent", None)
_require(
callable(systemctl)
and callable(stable_service_posture)
and callable(verify_running_release)
and callable(require_candidate_absent),
"reviewed installer lacked fail-closed service controls",
)
before = stable_service_posture(
release,
runner=contract_runner,
paths=paths,
sleeper=time.sleep,
)
_require(before.kind in {"active", "inactive"}, "managed service posture was not trusted")
if before.kind == "active":
verification = verify_running_release(
release,
runner=contract_runner,
paths=paths,
sleeper=time.sleep,
)
_require(
verification.get("main_pid") == before.state.get("MainPID")
and verification.get("invocation_id") == before.state.get("InvocationID")
and verification.get("n_restarts") == before.state.get("NRestarts"),
"running service identity changed before fail-closed stop",
)
systemctl("stop", contract_runner)
after = stable_service_posture(
release,
runner=contract_runner,
paths=paths,
sleeper=time.sleep,
)
_require(
after.kind == "inactive"
and after.state.get("ActiveState") == "inactive"
and after.state.get("SubState") == "dead"
and after.state.get("MainPID") == 0
and isinstance(after.state.get("NRestarts"), int)
and not isinstance(after.state.get("NRestarts"), bool)
and after.state["NRestarts"] >= 0,
"service did not stop into a stable inert posture",
)
require_candidate_absent(
runner=contract_runner,
paths=paths,
sleeper=time.sleep,
)
return {
"status": "pass",
"service_posture": "inactive",
"active_state": "inactive",
"sub_state": "dead",
"main_pid": 0,
"n_restarts": after.state["NRestarts"],
"candidate_container": "absent",
}
except Exception:
return {
"status": "fail",
"service_posture": "unknown",
"active_state": "unknown",
"sub_state": "unknown",
"main_pid": None,
"n_restarts": None,
"candidate_container": "unknown",
}
def _operational_failure_result(
*,
manifest: Mapping[str, Any],
release: Mapping[str, Any],
failure_stage: str,
failure_reason: str,
rollback: Mapping[str, str],
fail_closed: Mapping[str, Any],
archive_sha256: str,
helper_sha256: str,
installer_module: Any,
) -> dict[str, Any]:
_require(
manifest.get("operation") == "install"
and set(rollback) == {"status", "mode", "restored_posture"}
and (
(failure_stage, failure_reason) in POST_INSTALL_FAILURES
or (failure_stage, failure_reason) in installer_module.VERIFICATION_FAILURES
),
"post-install failure was not allowlisted",
)
rollback_passed = (
rollback.get("status") == "pass"
and rollback.get("mode") == "explicit"
and rollback.get("restored_posture") in {"active", "inactive", "absent"}
)
rollback_failed = rollback == {
"status": "fail",
"mode": "explicit",
"restored_posture": "unknown",
}
verification_pair = (failure_stage, failure_reason) in installer_module.VERIFICATION_FAILURES
_require(
(rollback_passed and not verification_pair) or rollback_failed,
"post-install rollback summary was invalid",
)
if rollback_passed:
_require(
fail_closed == _rollback_proven_service(str(rollback["restored_posture"])),
"post-install restored posture summary was invalid",
)
else:
_require(
fail_closed
in (
{
"status": "fail",
"service_posture": "unknown",
"active_state": "unknown",
"sub_state": "unknown",
"main_pid": None,
"n_restarts": None,
"candidate_container": "unknown",
},
{
"status": "pass",
"service_posture": "inactive",
"active_state": "inactive",
"sub_state": "dead",
"main_pid": 0,
"n_restarts": fail_closed.get("n_restarts"),
"candidate_container": "absent",
},
)
and (
fail_closed.get("status") != "pass"
or (
isinstance(fail_closed.get("n_restarts"), int)
and not isinstance(fail_closed.get("n_restarts"), bool)
and fail_closed["n_restarts"] >= 0
)
),
"post-install fail-closed summary was invalid",
)
return {
"schema": OPERATION_FAILURE_RESULT_SCHEMA,
"status": "fail",
"operation": manifest["operation"],
"request_id": manifest["request_id"],
"runner_revision": manifest["runner_revision"],
"release_revision": manifest["release_revision"],
"release_sha256": release["release_sha256"],
"image_digest": release["image"]["digest"],
"image_config_digest": release["image"]["config_digest"],
"release_artifact_sha256": manifest["release_artifact"]["archive_sha256"],
"request_binding": {
"archive_sha256": archive_sha256,
"helper_sha256": helper_sha256,
"manifest_sha256": manifest["manifest_sha256"],
},
"failure_stage": failure_stage,
"failure_reason": failure_reason,
"rollback": dict(rollback),
"fail_closed": dict(fail_closed),
"authority": {
"sending_enabled": False,
"production_traffic_changed": False,
"vps_touched": False,
"canonical_proposal_approved": False,
},
}
def execute_request( def execute_request(
manifest: Mapping[str, Any], manifest: Mapping[str, Any],
payload_root: Path, payload_root: Path,
@ -919,7 +1162,6 @@ def execute_request(
installer_module=installer_module, installer_module=installer_module,
) )
) from exc ) from exc
_require(install_receipt.get("status") == "pass", "installation did not pass")
elif operation == "rollback": elif operation == "rollback":
rollback = _execute_explicit_rollback( rollback = _execute_explicit_rollback(
installer_module=installer_module, installer_module=installer_module,
@ -945,12 +1187,19 @@ def execute_request(
) )
if operation == "install": if operation == "install":
failure_stage = "install_receipt"
failure_reason = "install_receipt_invalid"
try: try:
_require(install_receipt.get("status") == "pass", "installation did not pass")
failure_stage = "service_before_restart"
failure_reason = "service_verification_failed"
before = installer_module.verify_running_release( before = installer_module.verify_running_release(
release, release,
runner=contract_runner, runner=contract_runner,
paths=paths, paths=paths,
) )
failure_stage = "database_before_restart"
failure_reason = "database_verification_failed"
before_database = _verify_database( before_database = _verify_database(
container_id=before["container_id"], container_id=before["container_id"],
run_id=f"{manifest['request_id']}-pre", run_id=f"{manifest['request_id']}-pre",
@ -959,16 +1208,22 @@ def execute_request(
package_module=package_module, package_module=package_module,
runner=runner, runner=runner,
) )
failure_stage = "service_restart"
failure_reason = "service_restart_failed"
_run( _run(
[installer_module.SYSTEMCTL, "restart", SERVICE], [installer_module.SYSTEMCTL, "restart", SERVICE],
runner=runner, runner=runner,
label="fixed service restart", label="fixed service restart",
) )
failure_stage = "service_after_restart"
failure_reason = "service_verification_failed"
after = installer_module.verify_running_release( after = installer_module.verify_running_release(
release, release,
runner=contract_runner, runner=contract_runner,
paths=paths, paths=paths,
) )
failure_stage = "restart_identity"
failure_reason = "restart_identity_mismatch"
_require( _require(
before["main_pid"] != after["main_pid"] before["main_pid"] != after["main_pid"]
and before["invocation_id"] != after["invocation_id"] and before["invocation_id"] != after["invocation_id"]
@ -976,6 +1231,8 @@ def execute_request(
and after["n_restarts"] >= before["n_restarts"], and after["n_restarts"] >= before["n_restarts"],
"post-install restart did not create a new exact service/container identity", "post-install restart did not create a new exact service/container identity",
) )
failure_stage = "database_after_restart"
failure_reason = "database_verification_failed"
after_database = _verify_database( after_database = _verify_database(
container_id=after["container_id"], container_id=after["container_id"],
run_id=f"{manifest['request_id']}-post", run_id=f"{manifest['request_id']}-post",
@ -985,6 +1242,12 @@ def execute_request(
runner=runner, runner=runner,
) )
except Exception as exc: except Exception as exc:
bounded_stage, bounded_reason = _post_install_failure_coordinates(
installer_module,
exc,
fallback_stage=failure_stage,
fallback_reason=failure_reason,
)
try: try:
recovery = _execute_explicit_rollback( recovery = _execute_explicit_rollback(
installer_module=installer_module, installer_module=installer_module,
@ -1000,8 +1263,30 @@ def execute_request(
archive_sha256=archive_sha256, archive_sha256=archive_sha256,
helper_sha256=helper_sha256, helper_sha256=helper_sha256,
) )
except Exception as rollback_exc: except Exception:
raise RemoteError("install_failed_rollback_incomplete") from rollback_exc fail_closed = _fail_closed_inert_service(
installer_module=installer_module,
release=release,
paths=paths,
runner=runner,
)
raise RemoteOperationFailure(
_operational_failure_result(
manifest=manifest,
release=release,
failure_stage=bounded_stage,
failure_reason=bounded_reason,
rollback={
"status": "fail",
"mode": "explicit",
"restored_posture": "unknown",
},
fail_closed=fail_closed,
archive_sha256=archive_sha256,
helper_sha256=helper_sha256,
installer_module=installer_module,
)
) from exc
if _is_verification_failure(installer_module, exc): if _is_verification_failure(installer_module, exc):
raise RemoteOperationFailure( raise RemoteOperationFailure(
_verification_failure_result( _verification_failure_result(
@ -1018,7 +1303,24 @@ def execute_request(
installer_module=installer_module, installer_module=installer_module,
) )
) from exc ) from exc
raise RemoteError("install_failed_rolled_back") from exc restored_posture = str(rollback_result["service"]["restored_posture"])
raise RemoteOperationFailure(
_operational_failure_result(
manifest=manifest,
release=release,
failure_stage=bounded_stage,
failure_reason=bounded_reason,
rollback={
"status": "pass",
"mode": "explicit",
"restored_posture": restored_posture,
},
fail_closed=_rollback_proven_service(restored_posture),
archive_sha256=archive_sha256,
helper_sha256=helper_sha256,
installer_module=installer_module,
)
) from exc
service = { service = {
"name": SERVICE, "name": SERVICE,
"before_restart": before, "before_restart": before,
@ -1089,6 +1391,35 @@ def execute_request(
} }
def _execute_and_persist_result(
manifest: Mapping[str, Any],
payload_root: Path,
request_dir: Path,
archive_sha256: str,
helper_sha256: str,
*,
runner: Runner,
) -> dict[str, Any]:
try:
result = execute_request(
manifest,
payload_root,
request_dir,
archive_sha256,
helper_sha256,
runner=runner,
)
except RemoteOperationFailure as exc:
result = exc.result
result_path = request_dir / "result.json"
_copy_into_request(
result_path,
(json.dumps(result, allow_nan=False, indent=2, sort_keys=True) + "\n").encode("utf-8"),
0o600,
)
return result
def receive_request( def receive_request(
operation: str, operation: str,
request_id: str, request_id: str,
@ -1154,8 +1485,7 @@ def receive_request(
release_revision=release_revision, release_revision=release_revision,
) )
verify_service_account(runner=runner) verify_service_account(runner=runner)
try: return _execute_and_persist_result(
result = execute_request(
manifest, manifest,
payload_root, payload_root,
request_dir, request_dir,
@ -1163,15 +1493,6 @@ def receive_request(
helper_sha256, helper_sha256,
runner=runner, runner=runner,
) )
except RemoteOperationFailure as exc:
result = exc.result
result_path = request_dir / "result.json"
_copy_into_request(
result_path,
(json.dumps(result, allow_nan=False, indent=2, sort_keys=True) + "\n").encode("utf-8"),
0o600,
)
return result
finally: finally:
os.close(lock_descriptor) os.close(lock_descriptor)
finally: finally:

View file

@ -33,7 +33,10 @@ SCHEMA = "livingip.leocleanNoSendIapRequest.v1"
PLAN_SCHEMA = "livingip.leocleanNoSendIapPlan.v1" PLAN_SCHEMA = "livingip.leocleanNoSendIapPlan.v1"
REMOTE_RESULT_SCHEMA = "livingip.leocleanNoSendIapRemoteResult.v1" REMOTE_RESULT_SCHEMA = "livingip.leocleanNoSendIapRemoteResult.v1"
REMOTE_FAILURE_SCHEMA = "livingip.leocleanNoSendIapFailure.v1" REMOTE_FAILURE_SCHEMA = "livingip.leocleanNoSendIapFailure.v1"
REMOTE_OPERATION_FAILURE_SCHEMA = "livingip.leocleanNoSendIapOperationalFailure.v1"
RESULT_RECEIPT_SCHEMA = "livingip.leocleanNoSendIapResultReceipt.v1" RESULT_RECEIPT_SCHEMA = "livingip.leocleanNoSendIapResultReceipt.v1"
FAILURE_RECEIPT_SCHEMA = "livingip.leocleanNoSendIapFailureReceipt.v1"
LOCAL_FAILURE_RECEIPT_SCHEMA = "livingip.leocleanNoSendIapLocalFailureReceipt.v1"
EXECUTION_SCHEMA = "livingip.leocleanNoSendIapExecution.v1" EXECUTION_SCHEMA = "livingip.leocleanNoSendIapExecution.v1"
PROJECT = package.PROJECT PROJECT = package.PROJECT
ZONE = package.ZONE ZONE = package.ZONE
@ -78,6 +81,26 @@ FORBIDDEN_OUTPUT_MARKERS = (
"77.42.65.182", "77.42.65.182",
"teleo-prod-1", "teleo-prod-1",
) )
POST_INSTALL_FAILURES = frozenset(
{
("install_receipt", "install_receipt_invalid"),
("service_before_restart", "service_verification_failed"),
("database_before_restart", "database_verification_failed"),
("service_restart", "service_restart_failed"),
("service_after_restart", "service_verification_failed"),
("restart_identity", "restart_identity_mismatch"),
("database_after_restart", "database_verification_failed"),
}
)
LOCAL_FAILURES = frozenset(
{
("iap_transport", "upload_failed"),
("iap_transport", "remote_command_interrupted"),
("remote_receipt", "absent"),
("remote_receipt", "malformed"),
("remote_receipt", "contract_mismatch"),
}
)
BOOTSTRAP_CODE = textwrap.dedent( BOOTSTRAP_CODE = textwrap.dedent(
r""" r"""
import fcntl, hashlib, io, json, os, pwd, re, stat, sys, tarfile import fcntl, hashlib, io, json, os, pwd, re, stat, sys, tarfile
@ -203,7 +226,7 @@ def _strict_json(raw: bytes, label: str) -> dict[str, Any]:
object_pairs_hook=reject_duplicates, object_pairs_hook=reject_duplicates,
parse_constant=lambda _value: (_ for _ in ()).throw(ValueError("constant")), parse_constant=lambda _value: (_ for _ in ()).throw(ValueError("constant")),
) )
except (UnicodeError, ValueError, json.JSONDecodeError) as exc: except (UnicodeError, ValueError, json.JSONDecodeError, RecursionError) as exc:
raise IapRequestError(f"{label} was invalid JSON") from exc raise IapRequestError(f"{label} was invalid JSON") from exc
_require(isinstance(value, dict), f"{label} was not a JSON object") _require(isinstance(value, dict), f"{label} was not a JSON object")
return value return value
@ -717,6 +740,17 @@ def _action_receipts(commands: tuple[list[str], list[str], list[str]]) -> list[d
] ]
def _expected_authority(operation: str) -> dict[str, bool]:
_require(operation in OPERATIONS, "operation was invalid")
return {
"gcp_mutation": operation in {"install", "rollback"},
"service_restart": operation in {"install", "rollback"},
"database_or_secret_provisioning": False,
"production_or_transport": False,
"vps": False,
}
def prepare_request( def prepare_request(
operation: str, operation: str,
request_id: str, request_id: str,
@ -833,13 +867,7 @@ def prepare_request(
"ssh_key": str(ssh_key), "ssh_key": str(ssh_key),
}, },
"actions": actions, "actions": actions,
"authority": { "authority": _expected_authority(operation),
"gcp_mutation": operation in {"install", "rollback"},
"service_restart": operation in {"install", "rollback"},
"database_or_secret_provisioning": False,
"production_or_transport": False,
"vps": False,
},
} }
@ -1028,9 +1056,10 @@ def validate_remote_result(plan: Mapping[str, Any], result: Mapping[str, Any]) -
def validate_remote_failure(plan: Mapping[str, Any], result: Mapping[str, Any]) -> None: def validate_remote_failure(plan: Mapping[str, Any], result: Mapping[str, Any]) -> None:
_require( request = plan["request"]
set(result) release = plan["release"]
== { rollback = result.get("rollback")
common_fields = {
"schema", "schema",
"status", "status",
"operation", "operation",
@ -1046,15 +1075,95 @@ def validate_remote_failure(plan: Mapping[str, Any], result: Mapping[str, Any])
"failure_reason", "failure_reason",
"rollback", "rollback",
"authority", "authority",
}, }
schema = result.get("schema")
if schema == REMOTE_FAILURE_SCHEMA:
_require(set(result) == common_fields, "remote failure fields were not exact")
failure_contract = (
(result.get("failure_stage"), result.get("failure_reason")) in installer.VERIFICATION_FAILURES
and rollback
in (
{"status": "pass", "mode": "installer_automatic", "restored_posture": "prior"},
{"status": "pass", "mode": "explicit", "restored_posture": "active"},
{"status": "pass", "mode": "explicit", "restored_posture": "inactive"},
{"status": "pass", "mode": "explicit", "restored_posture": "absent"},
{"status": "not_run", "mode": "not_applicable", "restored_posture": "unchanged"},
)
)
else:
_require(
schema == REMOTE_OPERATION_FAILURE_SCHEMA and set(result) == common_fields | {"fail_closed"},
"remote failure fields were not exact", "remote failure fields were not exact",
) )
request = plan["request"] failure_pair = (result.get("failure_stage"), result.get("failure_reason"))
release = plan["release"] rollback_passed = (
rollback = result.get("rollback") isinstance(rollback, dict)
and set(rollback) == {"status", "mode", "restored_posture"}
and rollback.get("status") == "pass"
and rollback.get("mode") == "explicit"
and rollback.get("restored_posture") in {"active", "inactive", "absent"}
)
rollback_failed = rollback == {
"status": "fail",
"mode": "explicit",
"restored_posture": "unknown",
}
fail_closed = result.get("fail_closed")
if rollback_passed:
fail_closed_contract = fail_closed == {
"status": "not_required",
"service_posture": rollback["restored_posture"],
"active_state": "not_observed",
"sub_state": "not_observed",
"main_pid": None,
"n_restarts": None,
"candidate_container": "not_observed",
}
else:
fail_closed_contract = (
fail_closed
== {
"status": "fail",
"service_posture": "unknown",
"active_state": "unknown",
"sub_state": "unknown",
"main_pid": None,
"n_restarts": None,
"candidate_container": "unknown",
}
or (
isinstance(fail_closed, dict)
and set(fail_closed)
== {
"status",
"service_posture",
"active_state",
"sub_state",
"main_pid",
"n_restarts",
"candidate_container",
}
and fail_closed.get("status") == "pass"
and fail_closed.get("service_posture") == "inactive"
and fail_closed.get("active_state") == "inactive"
and fail_closed.get("sub_state") == "dead"
and fail_closed.get("main_pid") == 0
and isinstance(fail_closed.get("n_restarts"), int)
and not isinstance(fail_closed.get("n_restarts"), bool)
and fail_closed["n_restarts"] >= 0
and fail_closed.get("candidate_container") == "absent"
)
)
failure_contract = (
plan["operation"] == "install"
and (
(failure_pair in POST_INSTALL_FAILURES and (rollback_passed or rollback_failed))
or (failure_pair in installer.VERIFICATION_FAILURES and rollback_failed)
)
and fail_closed_contract
)
_require( _require(
result.get("schema") == REMOTE_FAILURE_SCHEMA result.get("status") == "fail"
and result.get("status") == "fail"
and result.get("operation") == plan["operation"] and result.get("operation") == plan["operation"]
and result.get("request_id") == plan["request_id"] and result.get("request_id") == plan["request_id"]
and result.get("runner_revision") == plan["runner_revision"] and result.get("runner_revision") == plan["runner_revision"]
@ -1069,15 +1178,7 @@ def validate_remote_failure(plan: Mapping[str, Any], result: Mapping[str, Any])
"helper_sha256": request["helper_sha256"], "helper_sha256": request["helper_sha256"],
"manifest_sha256": request["manifest_sha256"], "manifest_sha256": request["manifest_sha256"],
} }
and (result.get("failure_stage"), result.get("failure_reason")) in installer.VERIFICATION_FAILURES and failure_contract
and rollback
in (
{"status": "pass", "mode": "installer_automatic", "restored_posture": "prior"},
{"status": "pass", "mode": "explicit", "restored_posture": "active"},
{"status": "pass", "mode": "explicit", "restored_posture": "inactive"},
{"status": "pass", "mode": "explicit", "restored_posture": "absent"},
{"status": "not_run", "mode": "not_applicable", "restored_posture": "unchanged"},
)
and result.get("authority") and result.get("authority")
== { == {
"sending_enabled": False, "sending_enabled": False,
@ -1089,33 +1190,8 @@ def validate_remote_failure(plan: Mapping[str, Any], result: Mapping[str, Any])
) )
def _write_result_receipt( def _publish_local_receipt(output: Path, stable: Mapping[str, Any]) -> dict[str, Any]:
plan: Mapping[str, Any],
result: Mapping[str, Any],
*,
status: str = "pass",
) -> tuple[Path, dict[str, Any]]:
_require(status in {"pass", "fail"} and result.get("status") == status, "local result status was invalid")
archive = Path(str(plan["request"]["archive"]))
output = archive.parent / f"livingip-leoclean-nosend-result-{plan['request_id']}.json"
_require(not output.exists() and not output.is_symlink(), "local result receipt already existed") _require(not output.exists() and not output.is_symlink(), "local result receipt already existed")
stable = {
"schema": RESULT_RECEIPT_SCHEMA,
"status": status,
"operation": plan["operation"],
"request_id": plan["request_id"],
"runner_revision": plan["runner_revision"],
"release_revision": plan["release_revision"],
"request": {
"archive_sha256": plan["request"]["archive_sha256"],
"helper_sha256": plan["request"]["helper_sha256"],
"manifest_sha256": plan["request"]["manifest_sha256"],
},
"release": dict(plan["release"]),
"release_artifact": dict(plan["release_artifact"]),
"expected": dict(plan["expected"]),
"remote_result": dict(result),
}
receipt = {**stable, "receipt_sha256": package.canonical_sha256(stable)} receipt = {**stable, "receipt_sha256": package.canonical_sha256(stable)}
content = _canonical_json(receipt) content = _canonical_json(receipt)
descriptor, temporary_name = tempfile.mkstemp(prefix=".iap-result-", dir=output.parent) descriptor, temporary_name = tempfile.mkstemp(prefix=".iap-result-", dir=output.parent)
@ -1142,6 +1218,74 @@ def _write_result_receipt(
temporary.unlink() temporary.unlink()
except FileNotFoundError: except FileNotFoundError:
pass pass
return receipt
def _write_result_receipt(
plan: Mapping[str, Any],
result: Mapping[str, Any],
*,
status: str = "pass",
) -> tuple[Path, dict[str, Any]]:
_require(status in {"pass", "fail"} and result.get("status") == status, "local result status was invalid")
archive = Path(str(plan["request"]["archive"]))
output = archive.parent / f"livingip-leoclean-nosend-result-{plan['request_id']}.json"
stable = {
"schema": RESULT_RECEIPT_SCHEMA if status == "pass" else FAILURE_RECEIPT_SCHEMA,
"status": status,
"operation": plan["operation"],
"request_id": plan["request_id"],
"runner_revision": plan["runner_revision"],
"release_revision": plan["release_revision"],
"request": {
"archive_sha256": plan["request"]["archive_sha256"],
"helper_sha256": plan["request"]["helper_sha256"],
"manifest_sha256": plan["request"]["manifest_sha256"],
},
"release": dict(plan["release"]),
"release_artifact": dict(plan["release_artifact"]),
"expected": dict(plan["expected"]),
"remote_result": dict(result),
}
receipt = _publish_local_receipt(output, stable)
return output, receipt
def _write_local_failure_receipt(
plan: Mapping[str, Any],
*,
failure_stage: str,
failure_reason: str,
cleanup_status: str,
) -> tuple[Path, dict[str, Any]]:
_require(
(failure_stage, failure_reason) in LOCAL_FAILURES and cleanup_status in {"pass", "fail"},
"local failure classification was invalid",
)
archive = Path(str(plan["request"]["archive"]))
output = archive.parent / f"livingip-leoclean-nosend-result-{plan['request_id']}.json"
stable = {
"schema": LOCAL_FAILURE_RECEIPT_SCHEMA,
"status": "fail",
"operation": plan["operation"],
"request_id": plan["request_id"],
"runner_revision": plan["runner_revision"],
"release_revision": plan["release_revision"],
"request": {
"archive_sha256": plan["request"]["archive_sha256"],
"helper_sha256": plan["request"]["helper_sha256"],
"manifest_sha256": plan["request"]["manifest_sha256"],
},
"release": dict(plan["release"]),
"release_artifact": dict(plan["release_artifact"]),
"expected": dict(plan["expected"]),
"failure_stage": failure_stage,
"failure_reason": failure_reason,
"cleanup": {"status": cleanup_status, "mode": "failed_transfer"},
"remote_state": "unknown",
"requested_authority": dict(plan["authority"]),
}
receipt = _publish_local_receipt(output, stable)
return output, receipt return output, receipt
@ -1177,6 +1321,7 @@ def execute_plan(
) )
operation = plan.get("operation") operation = plan.get("operation")
_require(operation in {"install", "verify", "rollback"}, "operation was not executable") _require(operation in {"install", "verify", "rollback"}, "operation was not executable")
_require(plan.get("authority") == _expected_authority(operation), "request authority was not exact")
request_id = plan.get("request_id") request_id = plan.get("request_id")
runner_revision = plan.get("runner_revision") runner_revision = plan.get("runner_revision")
release_revision = plan.get("release_revision") release_revision = plan.get("release_revision")
@ -1261,9 +1406,12 @@ def execute_plan(
scp, ssh, cleanup = expected_commands scp, ssh, cleanup = expected_commands
validate_remote_main(runner_revision, runner=git_runner) validate_remote_main(runner_revision, runner=git_runner)
environment = dict(gcloud_env or gcloud_environment()) environment = dict(gcloud_env or gcloud_environment())
failure_stage = "iap_transport"
failure_reason = "upload_failed"
try: try:
try: try:
_run_bounded(list(scp), runner=runner, label="IAP upload", environment=environment) _run_bounded(list(scp), runner=runner, label="IAP upload", environment=environment)
failure_reason = "remote_command_interrupted"
completed = _run_bounded( completed = _run_bounded(
list(ssh), list(ssh),
runner=runner, runner=runner,
@ -1271,8 +1419,17 @@ def execute_plan(
environment=environment, environment=environment,
accepted_returncodes=(0, 65), accepted_returncodes=(0, 65),
) )
failure_stage = "remote_receipt"
if not completed.stdout:
failure_reason = "absent"
raise IapRequestError("remote result was absent")
failure_reason = "malformed"
result = _strict_json(completed.stdout, "remote result") result = _strict_json(completed.stdout, "remote result")
try:
encoded = json.dumps(result, allow_nan=False, sort_keys=True).casefold() encoded = json.dumps(result, allow_nan=False, sort_keys=True).casefold()
except (TypeError, ValueError, RecursionError) as exc:
raise IapRequestError("remote result could not be normalized") from exc
failure_reason = "contract_mismatch"
_require( _require(
not any(marker in encoded for marker in FORBIDDEN_OUTPUT_MARKERS), not any(marker in encoded for marker in FORBIDDEN_OUTPUT_MARKERS),
"remote result was not sanitized", "remote result was not sanitized",
@ -1282,6 +1439,7 @@ def execute_plan(
else: else:
validate_remote_result(plan, result) validate_remote_result(plan, result)
except IapRequestError as exc: except IapRequestError as exc:
cleanup_status = "pass"
try: try:
_run_bounded( _run_bounded(
list(cleanup), list(cleanup),
@ -1289,9 +1447,19 @@ def execute_plan(
label="IAP failed-transfer cleanup", label="IAP failed-transfer cleanup",
environment=environment, environment=environment,
) )
except IapRequestError as cleanup_exc: except IapRequestError:
raise IapRequestError("IAP operation failed and remote upload cleanup was unproven") from cleanup_exc cleanup_status = "fail"
raise exc receipt_path, _receipt = _write_local_failure_receipt(
plan,
failure_stage=failure_stage,
failure_reason=failure_reason,
cleanup_status=cleanup_status,
)
raise IapRequestError(
"IAP operation failed closed "
f"at {failure_stage}/{failure_reason}; "
f"sanitized receipt: {receipt_path}"
) from exc
finally: finally:
_cleanup_local_ssh_key(ssh_key) _cleanup_local_ssh_key(ssh_key)
if completed.returncode == 65: if completed.returncode == 65:

View file

@ -159,6 +159,106 @@ def verifier_failure_result(
} }
def operational_failure_result(plan: dict[str, Any]) -> dict[str, Any]:
result = verifier_failure_result(
plan,
failure_stage="database_before_restart",
failure_reason="database_verification_failed",
)
result["schema"] = local.REMOTE_OPERATION_FAILURE_SCHEMA
result["rollback"] = {
"status": "fail",
"mode": "explicit",
"restored_posture": "unknown",
}
result["fail_closed"] = {
"status": "pass",
"service_posture": "inactive",
"active_state": "inactive",
"sub_state": "dead",
"main_pid": 0,
"n_restarts": 0,
"candidate_container": "absent",
}
return result
def successful_remote_result(plan: dict[str, Any]) -> dict[str, Any]:
def verification(main_pid: int, invocation: str, container: str) -> dict[str, Any]:
return {
"active_state": "active",
"sub_state": "running",
"main_pid": main_pid,
"n_restarts": 0,
"invocation_id": invocation * 32,
"supervising_process": {"status": "pass"},
"container_id": container * 64,
"container_pid": main_pid + 1,
"health": "healthy",
"validated_process_environment_fields": ["PATH"],
"dropins": "absent",
"environment_files": "absent",
}
def database(name: str, receipt_hash: str) -> dict[str, Any]:
return {
"schema": "livingip.leocleanNoSendContainerDatabaseVerification.v1",
"status": "pass",
"receipt_sha256": receipt_hash * 64,
"receipt_path": f"/var/lib/livingip/leoclean-nosend-iap/requests/{plan['request_id']}/{name}",
"proof": {
"proposal_staging": "function_only",
"proposal_transaction": "rolled_back",
"durable_proposal_created": False,
"canonical_direct_writes": "denied",
"stage_direct_writes": "denied",
"role_escalation": "denied",
},
}
before = verification(101, "2", "3")
if plan["operation"] == "install":
after = verification(103, "5", "6")
service_result = {
"name": local.SERVICE,
"before_restart": before,
"after_restart": after,
}
database_result = {
"status": "pass",
"before_restart": database("database-before-restart.json", "4"),
"after_restart": database("database-after-restart.json", "7"),
}
else:
service_result = {"name": local.SERVICE, "verification": before}
database_result = database("database-verify.json", "4")
return {
"schema": local.REMOTE_RESULT_SCHEMA,
"status": "pass",
"operation": plan["operation"],
"request_id": plan["request_id"],
"runner_revision": plan["runner_revision"],
"release_revision": plan["release_revision"],
"release_sha256": plan["release"]["release_sha256"],
"image_digest": plan["release"]["image_digest"],
"image_config_digest": plan["release"]["image_config_digest"],
"release_artifact_sha256": plan["release_artifact"]["archive_sha256"],
"request_binding": {
"archive_sha256": plan["request"]["archive_sha256"],
"helper_sha256": plan["request"]["helper_sha256"],
"manifest_sha256": plan["request"]["manifest_sha256"],
},
"service": service_result,
"database": database_result,
"authority": {
"sending_enabled": False,
"production_traffic_changed": False,
"vps_touched": False,
"canonical_proposal_approved": False,
},
}
def test_gcloud_commands_are_fixed_iap_only_and_expire_keys(tmp_path: Path) -> None: def test_gcloud_commands_are_fixed_iap_only_and_expire_keys(tmp_path: Path) -> None:
scp, ssh, cleanup = fixed_commands(tmp_path) scp, ssh, cleanup = fixed_commands(tmp_path)
@ -183,6 +283,10 @@ def test_embedded_root_bootstrap_compiles() -> None:
compile(local.BOOTSTRAP_CODE, "<leoclean-iap-bootstrap>", "exec") compile(local.BOOTSTRAP_CODE, "<leoclean-iap-bootstrap>", "exec")
def test_remote_and_local_post_install_failure_contracts_match() -> None:
assert remote.POST_INSTALL_FAILURES == local.POST_INSTALL_FAILURES
def test_live_origin_main_readback_is_exact() -> None: def test_live_origin_main_readback_is_exact() -> None:
revision = "a" * 40 revision = "a" * 40
responses = iter( responses = iter(
@ -499,6 +603,125 @@ def test_local_result_receipt_is_private_and_self_hashed(tmp_path: Path) -> None
assert json.loads(path.read_text(encoding="utf-8")) == receipt assert json.loads(path.read_text(encoding="utf-8")) == receipt
def test_execute_success_returns_private_receipt(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
plan = executable_plan(tmp_path, "install")
remote_result = successful_remote_result(plan)
monkeypatch.setattr(local, "validate_remote_main", lambda *_args, **_kwargs: None)
calls = 0
def runner(argv: list[str], **_kwargs: Any) -> subprocess.CompletedProcess[bytes]:
nonlocal calls
calls += 1
if calls == 1:
return completed(argv)
return completed(argv, stdout=json.dumps(remote_result).encode())
execution = local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"})
receipt_path = Path(execution["receipt_path"])
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
assert calls == 2
assert execution["status"] == "pass"
assert stat.S_IMODE(receipt_path.stat().st_mode) == 0o600
assert receipt["remote_result"] == remote_result
assert receipt["receipt_sha256"] == execution["receipt_sha256"]
@pytest.mark.parametrize("remote_returncode", [0, 65], ids=["success", "failure"])
def test_execute_remote_without_output_records_absent_receipt(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
remote_returncode: int,
) -> None:
plan = executable_plan(tmp_path, "verify")
monkeypatch.setattr(local, "validate_remote_main", lambda *_args, **_kwargs: None)
calls = 0
def runner(argv: list[str], **_kwargs: Any) -> subprocess.CompletedProcess[bytes]:
nonlocal calls
calls += 1
return completed(argv, returncode=remote_returncode if calls == 2 else 0)
with pytest.raises(local.IapRequestError, match="remote_receipt/absent"):
local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"})
receipt_path = tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json"
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
assert calls == 3
assert receipt["schema"] == local.LOCAL_FAILURE_RECEIPT_SCHEMA
assert (receipt["failure_stage"], receipt["failure_reason"]) == ("remote_receipt", "absent")
assert receipt["cleanup"] == {"status": "pass", "mode": "failed_transfer"}
assert receipt["remote_state"] == "unknown"
def test_execute_transport_interruption_records_bounded_failure(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
plan = executable_plan(tmp_path, "verify")
monkeypatch.setattr(local, "validate_remote_main", lambda *_args, **_kwargs: None)
calls = 0
def runner(argv: list[str], **_kwargs: Any) -> subprocess.CompletedProcess[bytes]:
nonlocal calls
calls += 1
if calls == 2:
raise subprocess.TimeoutExpired(argv, 300, output=b"access_token=hidden")
return completed(argv)
with pytest.raises(local.IapRequestError, match="iap_transport/remote_command_interrupted") as caught:
local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"})
receipt_path = tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json"
receipt_text = receipt_path.read_text(encoding="utf-8")
receipt = json.loads(receipt_text)
assert calls == 3
assert (receipt["failure_stage"], receipt["failure_reason"]) == (
"iap_transport",
"remote_command_interrupted",
)
assert "hidden" not in receipt_text
assert "access_token" not in receipt_text
assert "hidden" not in str(caught.value)
@pytest.mark.parametrize(
"payload",
[
b"{not-json",
(b"[" * 1100) + b"0" + (b"]" * 1100),
],
ids=["invalid-json", "recursive-json"],
)
def test_execute_malformed_remote_receipt_fails_closed(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
payload: bytes,
) -> None:
plan = executable_plan(tmp_path, "verify")
monkeypatch.setattr(local, "validate_remote_main", lambda *_args, **_kwargs: None)
calls = 0
def runner(argv: list[str], **_kwargs: Any) -> subprocess.CompletedProcess[bytes]:
nonlocal calls
calls += 1
if calls == 2:
return completed(argv, stdout=payload)
return completed(argv)
with pytest.raises(local.IapRequestError, match="remote_receipt/malformed"):
local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"})
receipt_path = tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json"
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
assert calls == 3
assert (receipt["failure_stage"], receipt["failure_reason"]) == ("remote_receipt", "malformed")
assert receipt["remote_state"] == "unknown"
def test_execute_records_sanitized_verifier_failure( def test_execute_records_sanitized_verifier_failure(
monkeypatch: pytest.MonkeyPatch, monkeypatch: pytest.MonkeyPatch,
tmp_path: Path, tmp_path: Path,
@ -532,6 +755,94 @@ def test_execute_records_sanitized_verifier_failure(
assert "remote_operation_failed" not in str(caught.value) assert "remote_operation_failed" not in str(caught.value)
def test_execute_records_operational_failure_and_inert_state(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
plan = executable_plan(tmp_path, "install")
failure = operational_failure_result(plan)
monkeypatch.setattr(local, "validate_remote_main", lambda *_args, **_kwargs: None)
calls = 0
def runner(argv: list[str], **_kwargs: Any) -> subprocess.CompletedProcess[bytes]:
nonlocal calls
calls += 1
if calls == 1:
return completed(argv)
return completed(argv, returncode=65, stdout=json.dumps(failure).encode())
with pytest.raises(
local.IapRequestError,
match="database_before_restart/database_verification_failed",
):
local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"})
receipt_path = tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json"
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
assert calls == 2
assert receipt["remote_result"] == failure
assert receipt["remote_result"]["fail_closed"]["service_posture"] == "inactive"
def test_operational_failure_persists_and_propagates_to_local_receipt(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
plan = executable_plan(tmp_path, "install")
failure = operational_failure_result(plan)
request_dir = private_directory(tmp_path / "remote-request")
payload_root = private_directory(tmp_path / "remote-payload")
def fail_execute(*_args: Any, **_kwargs: Any) -> dict[str, Any]:
raise remote.RemoteOperationFailure(failure)
monkeypatch.setattr(remote, "execute_request", fail_execute)
persisted = remote._execute_and_persist_result(
plan,
payload_root,
request_dir,
plan["request"]["archive_sha256"],
plan["request"]["helper_sha256"],
runner=lambda argv, **_kwargs: completed(argv),
)
remote_receipt_path = request_dir / "result.json"
assert persisted == failure
assert stat.S_IMODE(remote_receipt_path.stat().st_mode) == 0o600
assert json.loads(remote_receipt_path.read_text(encoding="utf-8")) == failure
monkeypatch.setattr(remote, "receive_request", lambda *_args: persisted)
previous_umask = os.umask(0o077)
try:
returncode = remote.main(["install", "request", "runner", "release", "archive", "helper"])
finally:
os.umask(previous_umask)
captured = capsys.readouterr()
assert returncode == 65
assert captured.err == ""
assert json.loads(captured.out) == failure
monkeypatch.setattr(local, "validate_remote_main", lambda *_args, **_kwargs: None)
calls = 0
def local_runner(argv: list[str], **_kwargs: Any) -> subprocess.CompletedProcess[bytes]:
nonlocal calls
calls += 1
if calls == 1:
return completed(argv)
return completed(argv, returncode=65, stdout=captured.out.encode())
with pytest.raises(local.IapRequestError, match="database_before_restart/database_verification_failed"):
local.execute_plan(plan, runner=local_runner, gcloud_env={"PATH": "/usr/bin"})
local_receipt_path = tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json"
local_receipt = json.loads(local_receipt_path.read_text(encoding="utf-8"))
stable = {key: value for key, value in local_receipt.items() if key != "receipt_sha256"}
assert local_receipt["schema"] == local.FAILURE_RECEIPT_SCHEMA
assert local_receipt["remote_result"] == failure
assert local_receipt["receipt_sha256"] == local.package.canonical_sha256(stable)
def test_remote_failure_rejects_unallowlisted_reason(tmp_path: Path) -> None: def test_remote_failure_rejects_unallowlisted_reason(tmp_path: Path) -> None:
plan = executable_plan(tmp_path, "install") plan = executable_plan(tmp_path, "install")
failure = verifier_failure_result(plan) failure = verifier_failure_result(plan)
@ -541,6 +852,15 @@ def test_remote_failure_rejects_unallowlisted_reason(tmp_path: Path) -> None:
local.validate_remote_failure(plan, failure) local.validate_remote_failure(plan, failure)
def test_operational_failure_rejects_extra_rollback_fields(tmp_path: Path) -> None:
plan = executable_plan(tmp_path, "install")
failure = operational_failure_result(plan)
failure["rollback"]["credential"] = "not-allowed"
with pytest.raises(local.IapRequestError, match="did not match"):
local.validate_remote_failure(plan, failure)
def test_remote_main_emits_structured_failure_on_stdout( def test_remote_main_emits_structured_failure_on_stdout(
monkeypatch: pytest.MonkeyPatch, monkeypatch: pytest.MonkeyPatch,
tmp_path: Path, tmp_path: Path,
@ -570,8 +890,16 @@ def test_execute_failure_does_not_forward_command_output(monkeypatch: pytest.Mon
with pytest.raises(local.IapRequestError) as caught: with pytest.raises(local.IapRequestError) as caught:
local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"}) local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"})
receipt_path = tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json"
receipt_text = receipt_path.read_text(encoding="utf-8")
receipt = json.loads(receipt_text)
assert "extremely-secret" not in str(caught.value) assert "extremely-secret" not in str(caught.value)
assert "password" not in str(caught.value).casefold() assert "password" not in str(caught.value).casefold()
assert "extremely-secret" not in receipt_text
assert "password" not in receipt_text.casefold()
assert receipt["cleanup"] == {"status": "fail", "mode": "failed_transfer"}
assert "authority" not in receipt
assert receipt["requested_authority"] == plan["authority"]
def test_execute_rejects_unsanitized_remote_result(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: def test_execute_rejects_unsanitized_remote_result(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
@ -595,7 +923,16 @@ def test_execute_rejects_unsanitized_remote_result(monkeypatch: pytest.MonkeyPat
with pytest.raises(local.IapRequestError) as caught: with pytest.raises(local.IapRequestError) as caught:
local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"}) local.execute_plan(plan, runner=runner, gcloud_env={"PATH": "/usr/bin"})
receipt_path = tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json"
receipt_text = receipt_path.read_text(encoding="utf-8")
receipt = json.loads(receipt_text)
assert "not-allowed" not in str(caught.value) assert "not-allowed" not in str(caught.value)
assert "not-allowed" not in receipt_text
assert (receipt["failure_stage"], receipt["failure_reason"]) == (
"remote_receipt",
"contract_mismatch",
)
assert receipt["cleanup"] == {"status": "pass", "mode": "failed_transfer"}
def test_execute_rederives_commands_and_rejects_plan_tampering(tmp_path: Path) -> None: def test_execute_rederives_commands_and_rejects_plan_tampering(tmp_path: Path) -> None:
@ -606,6 +943,22 @@ def test_execute_rederives_commands_and_rejects_plan_tampering(tmp_path: Path) -
local.execute_plan(plan, runner=lambda argv, **_kwargs: completed(argv)) local.execute_plan(plan, runner=lambda argv, **_kwargs: completed(argv))
def test_execute_rejects_authority_tampering_before_dispatch(tmp_path: Path) -> None:
plan = executable_plan(tmp_path, "verify")
plan["authority"]["credential"] = "not-allowed"
calls: list[list[str]] = []
with pytest.raises(local.IapRequestError, match="request authority was not exact") as caught:
local.execute_plan(
plan,
runner=lambda argv, **_kwargs: (calls.append(argv) or completed(argv)),
)
assert calls == []
assert "not-allowed" not in str(caught.value)
assert not (tmp_path / "livingip-leoclean-nosend-result-iap-abcdefghijkl.json").exists()
@pytest.mark.parametrize("fail_pull", [False, True]) @pytest.mark.parametrize("fail_pull", [False, True])
def test_ephemeral_registry_credentials_are_always_removed(tmp_path: Path, fail_pull: bool) -> None: def test_ephemeral_registry_credentials_are_always_removed(tmp_path: Path, fail_pull: bool) -> None:
calls: list[tuple[list[str], bytes | None]] = [] calls: list[tuple[list[str], bytes | None]] = []
@ -838,6 +1191,7 @@ def test_post_install_proof_failure_executes_explicit_rollback(
installer_module = SimpleNamespace( installer_module = SimpleNamespace(
SYSTEMCTL="/usr/bin/systemctl", SYSTEMCTL="/usr/bin/systemctl",
VERIFICATION_FAILURES=local.installer.VERIFICATION_FAILURES,
InstallPaths=Paths, InstallPaths=Paths,
validate_release_bundle=lambda _bundle: (release, b"unit"), validate_release_bundle=lambda _bundle: (release, b"unit"),
_validate_source_binding=lambda _binding: None, _validate_source_binding=lambda _binding: None,
@ -881,7 +1235,7 @@ def test_post_install_proof_failure_executes_explicit_rollback(
return completed(argv, returncode=1) return completed(argv, returncode=1)
return completed(argv) return completed(argv)
with pytest.raises(remote.RemoteError, match="install_failed_rolled_back"): with pytest.raises(remote.RemoteOperationFailure) as caught:
remote.execute_request( remote.execute_request(
manifest, manifest,
tmp_path, tmp_path,
@ -891,6 +1245,180 @@ def test_post_install_proof_failure_executes_explicit_rollback(
runner=runner, runner=runner,
) )
assert rollbacks == [release["release_sha256"]] assert rollbacks == [release["release_sha256"]]
expected_failure = {
"first_database": ("database_before_restart", "database_verification_failed"),
"restart": ("service_restart", "service_restart_failed"),
"second_database": ("database_after_restart", "database_verification_failed"),
}[failure_point]
result = caught.value.result
assert result["schema"] == remote.OPERATION_FAILURE_RESULT_SCHEMA
assert (result["failure_stage"], result["failure_reason"]) == expected_failure
assert result["rollback"] == {
"status": "pass",
"mode": "explicit",
"restored_posture": "absent",
}
assert result["fail_closed"] == {
"status": "not_required",
"service_posture": "absent",
"active_state": "not_observed",
"sub_state": "not_observed",
"main_pid": None,
"n_restarts": None,
"candidate_container": "not_observed",
}
def test_incomplete_rollback_stops_service_and_reports_inert_posture(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
release = {
"release_sha256": "a" * 64,
"image": {
"reference": f"{remote.IMAGE_REPOSITORY}@sha256:{'b' * 64}",
"digest": f"sha256:{'b' * 64}",
"config_digest": f"sha256:{'c' * 64}",
},
"image_input": {"runtime": {"teleo_git_head": "d" * 40}},
}
manifest = {
"operation": "install",
"request_id": "iap-abcdefghijkl",
"runner_revision": "e" * 40,
"release_revision": "d" * 40,
"manifest_sha256": "f" * 64,
"release": {
"release_sha256": release["release_sha256"],
"image_reference": release["image"]["reference"],
"image_digest": release["image"]["digest"],
"image_config_digest": release["image"]["config_digest"],
},
"release_artifact": {"archive_sha256": "1" * 64},
"installer_source": {"revision": "e" * 40},
}
stops: list[str] = []
absence_checks: list[bool] = []
class Paths:
@staticmethod
def under(_root: Path) -> object:
return object()
postures = iter(
[
SimpleNamespace(
kind="active",
state={
"ActiveState": "active",
"SubState": "running",
"MainPID": 10,
"NRestarts": 0,
"InvocationID": "2" * 32,
},
),
SimpleNamespace(
kind="inactive",
state={
"ActiveState": "inactive",
"SubState": "dead",
"MainPID": 0,
"NRestarts": 0,
"InvocationID": "",
},
),
]
)
installer_module = SimpleNamespace(
SYSTEMCTL="/usr/bin/systemctl",
InstallPaths=Paths,
validate_release_bundle=lambda _bundle: (release, b"unit"),
_validate_source_binding=lambda _binding: None,
install_release=lambda *_args, **_kwargs: {"status": "pass"},
verify_running_release=lambda *_args, **_kwargs: {
"main_pid": 10,
"invocation_id": "2" * 32,
"container_id": "3" * 64,
"n_restarts": 0,
},
rollback_release=lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("rollback failed")),
_systemctl=lambda action, _runner: stops.append(action),
_stable_service_posture=lambda *_args, **_kwargs: next(postures),
_require_candidate_absent=lambda **_kwargs: absence_checks.append(True),
VERIFICATION_FAILURES=frozenset(),
)
package_module = SimpleNamespace(
render_systemd_unit=lambda _release: "unit",
canonical_sha256=local.package.canonical_sha256,
)
monkeypatch.setattr(remote, "validate_original_release_artifact", lambda *_args, **_kwargs: None)
monkeypatch.setattr(remote, "_load_contract_modules", lambda _root: (package_module, installer_module))
monkeypatch.setattr(remote, "pull_immutable_image", lambda *_args, **_kwargs: None)
monkeypatch.setattr(
remote,
"_verify_database",
lambda **_kwargs: (_ for _ in ()).throw(remote.RemoteError("database failed")),
)
monkeypatch.setattr(remote.time, "sleep", lambda _seconds: None)
with pytest.raises(remote.RemoteOperationFailure) as caught:
remote.execute_request(
manifest,
tmp_path,
tmp_path,
"6" * 64,
"7" * 64,
runner=lambda argv, **_kwargs: completed(argv),
)
result = caught.value.result
assert stops == ["stop"]
assert absence_checks == [True]
assert result["rollback"] == {
"status": "fail",
"mode": "explicit",
"restored_posture": "unknown",
}
assert result["fail_closed"] == {
"status": "pass",
"service_posture": "inactive",
"active_state": "inactive",
"sub_state": "dead",
"main_pid": 0,
"n_restarts": 0,
"candidate_container": "absent",
}
assert result["authority"]["sending_enabled"] is False
def test_fail_closed_stop_refuses_untrusted_unit_or_dropin() -> None:
stop_calls: list[str] = []
installer_module = SimpleNamespace(
_systemctl=lambda action, _runner: stop_calls.append(action),
_stable_service_posture=lambda *_args, **_kwargs: (_ for _ in ()).throw(
local.installer.InstallError("unit or drop-in drift")
),
verify_running_release=lambda *_args, **_kwargs: {"status": "unexpected"},
_require_candidate_absent=lambda **_kwargs: None,
)
result = remote._fail_closed_inert_service(
installer_module=installer_module,
release={"release_sha256": "a" * 64},
paths=object(),
runner=lambda argv, **_kwargs: completed(argv),
)
assert stop_calls == []
assert result == {
"status": "fail",
"service_posture": "unknown",
"active_state": "unknown",
"sub_state": "unknown",
"main_pid": None,
"n_restarts": None,
"candidate_container": "unknown",
}
@pytest.mark.parametrize( @pytest.mark.parametrize(