teleo-infrastructure/tests/test_gcp_leoclean_nosend_preflight.py

459 lines
17 KiB
Python

"""Contracts for the read-only leoclean GCP staging preflight."""
from __future__ import annotations
import copy
import hashlib
import json
import subprocess
import sys
from collections.abc import Callable
from pathlib import Path
import pytest
from ops import check_gcp_leoclean_nosend_preflight as preflight
REVISION = "a" * 40
PRIVATE_IP = "10.61.0.3"
def live_fixtures() -> dict[str, object]:
return {
"project": {"lifecycleState": "ACTIVE", "projectId": "teleo-501523"},
"instance": {
"name": "teleo-staging-1",
"status": "RUNNING",
"zone": "https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a",
"machineType": (
"https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a/"
"machineTypes/e2-standard-4"
),
"cpuPlatform": "Intel Broadwell",
"serviceAccounts": [
{"email": "sa-teleo-staging-vm@teleo-501523.iam.gserviceaccount.com"}
],
"disks": [
{
"boot": True,
"source": (
"https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a/"
"disks/teleo-staging-1"
),
}
],
"networkInterfaces": [
{
"network": (
"https://www.googleapis.com/compute/v1/projects/teleo-501523/global/networks/teleo-staging-net"
),
"subnetwork": (
"https://www.googleapis.com/compute/v1/projects/teleo-501523/regions/europe-west6/"
"subnetworks/teleo-staging-europe-west6"
),
"networkIP": "10.60.0.2",
"accessConfigs": [],
"ipv6AccessConfigs": [],
"stackType": "IPV4_ONLY",
}
],
"tags": {"items": ["teleo-staging-ssh"]},
},
"disk": {
"name": "teleo-staging-1",
"status": "READY",
"sizeGb": "80",
"zone": "https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a",
"architecture": "X86_64",
"users": [
"https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a/"
"instances/teleo-staging-1"
],
},
"subnet": {
"name": "teleo-staging-europe-west6",
"region": "https://www.googleapis.com/compute/v1/projects/teleo-501523/regions/europe-west6",
"network": "https://www.googleapis.com/compute/v1/projects/teleo-501523/global/networks/teleo-staging-net",
"ipCidrRange": "10.60.0.0/20",
"privateIpGoogleAccess": True,
},
"artifact": {
"name": "projects/teleo-501523/locations/europe-west6/repositories/teleo",
"format": "DOCKER",
"mode": "STANDARD_REPOSITORY",
"dockerConfig": {"immutableTags": True},
},
"cloudsql": {
"name": "teleo-pgvector-standby",
"state": "RUNNABLE",
"region": "europe-west6",
"databaseVersion": "POSTGRES_16",
"ipAddresses": [{"type": "PRIVATE", "ipAddress": PRIVATE_IP}],
"settings": {
"ipConfiguration": {
"ipv4Enabled": False,
"privateNetwork": "projects/teleo-501523/global/networks/teleo-staging-net",
"sslMode": "ENCRYPTED_ONLY",
}
},
},
"host": {
"architecture": "x86_64",
"project": "teleo-501523",
"instance": "teleo-staging-1",
"service_account": "sa-teleo-staging-vm@teleo-501523.iam.gserviceaccount.com",
"docker_server_version": "29.4.0",
"docker_server_os": "linux",
"docker_server_arch": "amd64",
"docker_root_free_bytes": str(20 * 1024**3),
"unit_load_state": "not-found",
"container_collision_count": "0",
"cloudsql_tcp": "reachable",
},
}
def fixture_runner(
values: dict[str, object],
calls: list[list[str]] | None = None,
) -> Callable[[list[str]], subprocess.CompletedProcess[str]]:
def run(argv: list[str]) -> subprocess.CompletedProcess[str]:
if calls is not None:
calls.append(argv)
if argv[:3] == ["gcloud", "projects", "describe"]:
value: object = values["project"]
elif argv[:4] == ["gcloud", "compute", "instances", "describe"]:
value = values["instance"]
elif argv[:4] == ["gcloud", "compute", "disks", "describe"]:
value = values["disk"]
elif argv[:5] == ["gcloud", "compute", "networks", "subnets", "describe"]:
value = values["subnet"]
elif argv[:4] == ["gcloud", "artifacts", "repositories", "describe"]:
value = values["artifact"]
elif argv[:4] == ["gcloud", "sql", "instances", "describe"]:
value = values["cloudsql"]
elif argv[:3] == ["gcloud", "compute", "ssh"]:
host = values["host"]
assert isinstance(host, dict)
output = "".join(f"{key}={item}\n" for key, item in host.items())
return subprocess.CompletedProcess(argv, 0, stdout=output, stderr="")
else: # pragma: no cover - the assertion shows unexpected command drift
raise AssertionError(argv)
return subprocess.CompletedProcess(argv, 0, stdout=json.dumps(value), stderr="")
return run
def test_exact_read_only_preflight_passes_without_exposing_private_ip() -> None:
calls: list[list[str]] = []
payload = preflight.build_preflight(runner=fixture_runner(live_fixtures(), calls), source_revision=REVISION)
assert payload["status"] == "pass"
assert payload["source_revision"] == REVISION
assert payload["target"] == {
"project": "teleo-501523",
"zone": "europe-west6-a",
"instance": "teleo-staging-1",
"service": "leoclean-gcp-nosend.service",
"container": "livingip-leoclean-nosend",
"platform": "linux/amd64",
}
assert payload["authority"] == {
"mode": "bounded_observation_preflight",
"secret_values_read": False,
"database_credentials_used": False,
"database_queries_run": False,
"control_plane_observations_before_ssh_are_describe_only": True,
"remote_host_runtime_mutations": False,
"ssh_access_may_register_time_bounded_key": True,
"ssh_key_maximum_lifetime": "5m",
}
assert payload["host"]["cloudsql_private_tcp_5432"] == "reachable"
assert payload["control_plane"]["cloudsql"]["private_ip_sha256"] == hashlib.sha256(
PRIVATE_IP.encode("ascii")
).hexdigest()
assert PRIVATE_IP not in json.dumps(payload)
assert len(calls) == 7
assert calls[:-1] == [
[
"gcloud",
"sql",
"instances",
"describe",
"teleo-pgvector-standby",
"--project",
"teleo-501523",
"--format=json(name,state,region,databaseVersion,ipAddresses,settings.ipConfiguration)",
],
[
"gcloud",
"projects",
"describe",
"teleo-501523",
"--format=json(projectId,lifecycleState)",
],
[
"gcloud",
"compute",
"instances",
"describe",
"teleo-staging-1",
"--project",
"teleo-501523",
"--zone",
"europe-west6-a",
"--format=json(name,status,zone,machineType,cpuPlatform,serviceAccounts,disks,networkInterfaces,tags.items)",
],
[
"gcloud",
"compute",
"disks",
"describe",
"teleo-staging-1",
"--project",
"teleo-501523",
"--zone",
"europe-west6-a",
"--format=json(name,status,sizeGb,zone,architecture,users)",
],
[
"gcloud",
"compute",
"networks",
"subnets",
"describe",
"teleo-staging-europe-west6",
"--project",
"teleo-501523",
"--region",
"europe-west6",
"--format=json(name,region,network,ipCidrRange,privateIpGoogleAccess)",
],
[
"gcloud",
"artifacts",
"repositories",
"describe",
"teleo",
"--project",
"teleo-501523",
"--location",
"europe-west6",
"--format=json(name,format,mode,dockerConfig.immutableTags)",
],
]
ssh = calls[-1]
assert ssh == preflight._ssh_argv(PRIVATE_IP)
assert "--tunnel-through-iap" in ssh
assert "--ssh-key-expire-after=5m" in ssh
remote = next(item.removeprefix("--command=") for item in ssh if item.startswith("--command="))
assert remote.startswith("set -euo pipefail\n")
for forbidden in (
"docker run",
"docker pull",
"docker rm",
"secretmanager",
"secrets versions access",
"systemctl start",
"systemctl restart",
"systemctl enable",
):
assert forbidden not in remote.casefold()
@pytest.mark.parametrize(
("path", "field", "unsafe", "message"),
[
(("instance", "serviceAccounts"), None, [{"email": "postgres-admin@example.com"}], "service account"),
(("disk",), "sizeGb", "40", "boot disk"),
(("disk",), "architecture", "ARM64", "architecture"),
(("disk",), "users", [], "attachment"),
(("instance", "networkInterfaces"), None, [], "network interface"),
(("instance", "networkInterfaces", 0), "accessConfigs", [{"natIP": "203.0.113.10"}], "public"),
(("instance", "networkInterfaces", 0), "ipv6AccessConfigs", [{"externalIpv6": "2001:db8::1"}], "IPv6"),
(("instance", "networkInterfaces", 0), "externalIpv6", "2001:db8::1", "IPv6"),
(("instance", "networkInterfaces", 0), "networkIP", "10.99.0.2", "reviewed subnet"),
(("subnet",), "privateIpGoogleAccess", False, "Private Google Access"),
(("subnet",), "ipCidrRange", "10.99.0.0/24", "CIDR drifted"),
(("artifact", "dockerConfig"), "immutableTags", False, "not immutable"),
(("cloudsql", "settings", "ipConfiguration"), "ipv4Enabled", True, "public IPv4"),
(("cloudsql",), "databaseVersion", "POSTGRES_160", "exactly PostgreSQL 16"),
(("host",), "container_collision_count", "1", "container name"),
(("host",), "unit_load_state", "loaded", "unit name"),
(("host",), "docker_root_free_bytes", "1024", "free space"),
(("host",), "cloudsql_tcp", "unreachable", "TCP/5432"),
],
)
def test_preflight_fails_closed_on_prerequisite_drift(
path: tuple[str | int, ...],
field: str | None,
unsafe: object,
message: str,
) -> None:
values = copy.deepcopy(live_fixtures())
target: object = values
for part in path:
if isinstance(part, int):
assert isinstance(target, list)
target = target[part]
else:
assert isinstance(target, dict)
target = target[part]
if field is None:
assert isinstance(target, list)
parent: object = values
for part in path[:-1]:
if isinstance(part, int):
assert isinstance(parent, list)
parent = parent[part]
else:
assert isinstance(parent, dict)
parent = parent[part]
assert isinstance(parent, dict)
last = path[-1]
assert isinstance(last, str)
parent[last] = unsafe
else:
assert isinstance(target, dict)
target[field] = unsafe
with pytest.raises(preflight.PreflightError, match=message):
preflight.build_preflight(runner=fixture_runner(values), source_revision=REVISION)
def test_authentication_failure_is_classified_without_echoing_error() -> None:
sentinel = "sensitive-browser-session-sentinel"
def fail(argv: list[str]) -> subprocess.CompletedProcess[str]:
return subprocess.CompletedProcess(
argv,
1,
stdout="",
stderr=f"Reauthentication failed. {sentinel}. Run gcloud auth login.",
)
with pytest.raises(preflight.PreflightError) as caught:
preflight.build_preflight(runner=fail, source_revision=REVISION)
assert caught.value.category == "authentication_required"
assert sentinel not in str(caught.value)
@pytest.mark.parametrize(
("mutation", "message"),
[
({"ipAddresses": [{"type": "PRIVATE", "ipAddress": "10.99.0.2"}]}, "address drifted"),
(
{
"ipAddresses": [
{"type": "PRIVATE", "ipAddress": PRIVATE_IP},
{"type": "PRIMARY", "ipAddress": "203.0.113.10"},
]
},
"private-only",
),
(
{"settings": {"ipConfiguration": {"sslMode": "TRUSTED_CLIENT_CERTIFICATE_REQUIRED"}}},
"TLS mode",
),
],
)
def test_cloudsql_endpoint_and_tls_are_exact(mutation: dict[str, object], message: str) -> None:
values = copy.deepcopy(live_fixtures())
cloudsql = values["cloudsql"]
assert isinstance(cloudsql, dict)
if "settings" in mutation:
settings = cloudsql["settings"]
assert isinstance(settings, dict)
ip_config = settings["ipConfiguration"]
assert isinstance(ip_config, dict)
changed = mutation["settings"]
assert isinstance(changed, dict)
changed_ip_config = changed["ipConfiguration"]
assert isinstance(changed_ip_config, dict)
ip_config.update(changed_ip_config)
else:
cloudsql.update(mutation)
with pytest.raises(preflight.PreflightError, match=message):
preflight.build_preflight(runner=fixture_runner(values), source_revision=REVISION)
def test_host_output_rejects_extra_duplicate_or_unbounded_fields() -> None:
valid = live_fixtures()["host"]
assert isinstance(valid, dict)
encoded = "".join(f"{key}={item}\n" for key, item in valid.items())
for unsafe in (
encoded + "unexpected=value\n",
encoded + "project=duplicate\n",
encoded.replace("docker_server_os=linux", "docker_server_os=linux value with spaces"),
):
with pytest.raises(preflight.PreflightError, match="host preflight"):
preflight._parse_host_output(unsafe)
def test_command_gate_rejects_mutating_or_unreviewed_gcloud_calls() -> None:
for command in (
["gcloud", "compute", "instances", "start", "teleo-staging-1"],
["gcloud", "secrets", "versions", "access", "latest"],
["gcloud", "compute", "scp", "file", "teleo-staging-1:/tmp/file"],
["gcloud", "compute", "ssh", "teleo-staging-1", "--command=sudo systemctl restart x"],
["gcloud", "compute", "ssh", "teleo-staging-1", "--command=touch /tmp/preflight-was-here"],
["bash", "-c", "true"],
):
with pytest.raises(preflight.PreflightError):
preflight._assert_read_only_command(command)
def test_source_revision_requires_clean_tracked_commit(tmp_path: Path) -> None:
subprocess.run(["git", "init", "--quiet", str(tmp_path)], check=True)
tracked = tmp_path / "tracked.txt"
tracked.write_text("committed\n", encoding="utf-8")
subprocess.run(["git", "-C", str(tmp_path), "add", "tracked.txt"], check=True)
subprocess.run(
[
"git",
"-C",
str(tmp_path),
"-c",
"user.name=Preflight Test",
"-c",
"user.email=preflight@example.invalid",
"commit",
"--quiet",
"-m",
"fixture",
],
check=True,
)
revision = subprocess.run(
["git", "-C", str(tmp_path), "rev-parse", "HEAD"],
text=True,
capture_output=True,
check=True,
).stdout.strip()
assert preflight._source_revision(tmp_path, critical_paths=("tracked.txt",)) == revision
(tmp_path / "untracked.txt").write_text("drift\n", encoding="utf-8")
with pytest.raises(preflight.PreflightError, match="clean tracked"):
preflight._source_revision(tmp_path, critical_paths=("tracked.txt",))
def test_preflight_rejects_an_unrelated_clean_repository(tmp_path: Path) -> None:
with pytest.raises(preflight.PreflightError, match="containing this script"):
preflight._validated_repo_root(tmp_path)
def test_direct_cli_imports_then_rejects_an_unrelated_repository(tmp_path: Path) -> None:
completed = subprocess.run(
[sys.executable, str(preflight.SOURCE_ROOT / "ops" / "check_gcp_leoclean_nosend_preflight.py"), "--repo-root", str(tmp_path)],
text=True,
capture_output=True,
check=False,
)
assert completed.returncode == 1
assert json.loads(completed.stdout) == {
"category": "invalid_source_root",
"schema": "livingip.leocleanNoSendGcpPreflight.v1",
"status": "blocked",
}