"""Compose public contracts and private identities without application state writes.""" import hashlib import json import os import re import secrets import stat import uuid from pathlib import Path from urllib.parse import parse_qs, urlsplit from . import manifest, preflight from .policy import DeliveryError, canonical, digest, fields, read_json, sha256 PROFILES = { "api": "kf_platform", "coordinator": "kf_coordinator", "import": "kf_problem_import", "identity": "kf_identity_operator", "migration": "kf_migrator", } IMPORT_CGROUP = "/sys/fs/cgroup/kfonlinejudge.slice/kf-problem-import.service" CONFIG_FILES = { "api.env", "outbox.env", "import.env", "identity.env", "migration.env", "coordinator.json", "worker.json", "runtime.json", "admission.json", "measurement.json", } def private(path: Path) -> None: info = path.lstat() if ( not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or stat.S_IMODE(info.st_mode) != 0o600 or info.st_uid != os.geteuid() ): raise DeliveryError("private single-link operator input required") def profile(path: Path) -> dict: private(path) return validate_profile(read_json(path, 16384)) def validate_profile(value: dict) -> dict: value = fields( value, {"schema_version", "origin", "worker_id", "measurement_nonce"}, "node profile", ) if type(value["schema_version"]) is not int or value["schema_version"] != 1: raise DeliveryError("unsupported node profile") origin = value["origin"] if not isinstance(origin, str) or len(origin) > 253: raise DeliveryError("invalid browser origin") parts = urlsplit(origin) if ( parts.scheme != "https" or not parts.hostname or parts.path or parts.query or parts.fragment or parts.username or parts.password or not re.fullmatch(r"[a-z0-9][a-z0-9.-]*[a-z0-9]", parts.hostname) or parts.port not in (None, 443) ): raise DeliveryError("explicit HTTPS origin required") try: worker = uuid.UUID(value["worker_id"]) except (ValueError, TypeError, AttributeError) as error: raise DeliveryError("invalid worker identity") from error if str(worker) != value["worker_id"] or worker.int == 0: raise DeliveryError("canonical nonzero worker UUID required") if value["measurement_nonce"] is not None: sha256(value["measurement_nonce"]) return value def connections(path: Path) -> dict: private(path) return validate_connections(read_json(path, 16384)) def validate_connections(value: dict) -> dict: value = fields(value, {"schema_version", "database", "amqp"}, "service connections") if type(value["schema_version"]) is not int or value["schema_version"] != 1: raise DeliveryError("unsupported service connections") database = fields(value["database"], set(PROFILES), "database connections") passwords = set() for name, raw in database.items(): if not isinstance(raw, str) or len(raw) > 1024: raise DeliveryError("invalid database connection") parts = urlsplit(raw) unix = ( name == "import" and parts.hostname is None and parse_qs(parts.query, strict_parsing=True) == {"host": ["/run/postgresql"]} ) network = ( name != "import" and parts.hostname == "127.0.0.1" and parts.port == 5432 and not parts.query ) if ( parts.scheme != "postgresql" or parts.username != PROFILES[name] or not re.fullmatch(r"[A-Za-z0-9_-]{32,128}", parts.password or "") or parts.path != "/kf_onlinejudge" or parts.fragment or not (unix or network) ): raise DeliveryError("database authority or loopback/socket policy mismatch") if parts.password in passwords: raise DeliveryError("service credentials must be independent") passwords.add(parts.password) broker = fields(value["amqp"], {"outbox", "worker"}, "broker connections") for name, raw in broker.items(): if not isinstance(raw, str) or len(raw) > 1024: raise DeliveryError("invalid broker connection") parts = urlsplit(raw) if ( parts.scheme != "amqp" or parts.hostname != "127.0.0.1" or parts.port != 5672 or parts.username != "kf_" + name or not re.fullmatch(r"[A-Za-z0-9_-]{32,128}", parts.password or "") or parts.path != "/kf" or parts.query or parts.fragment ): raise DeliveryError("broker authority or loopback policy mismatch") if parts.password in passwords: raise DeliveryError("service credentials must be independent") passwords.add(parts.password) return value def environmental(values: dict) -> bytes: """JSON strings are valid systemd EnvironmentFile double-quoted values here. Input URLs/passwords cannot contain dollar/backtick/newline expansions; only trusted JSON admission data and known fixed paths appear otherwise. """ return "".join( key + "=" + json.dumps(value, separators=(",", ":")) + "\n" for key, value in sorted(values.items()) ).encode() def measured_hardware() -> dict: value = preflight.hardware() # Operator command cgroup does not define execution scheduling. The fixed # execution/supervisor budgets below are bound separately and read back by # Sandbox; no transient installer PID/unit name is part of the epoch. value.pop("parent_limits") # Bind stable node-wide scheduling and ancestor constraints too. Per-service # ceilings are in the policy; Sandbox separately verifies their actual # values on every execution. This is not a transient operator unit snapshot. value["node_scheduler"] = {} for label, group in ( ("root", Path("/sys/fs/cgroup")), ("node", Path("/sys/fs/cgroup/kfonlinejudge.slice")), ): value["node_scheduler"][label] = { name: (group / name).read_text().strip() if (group / name).exists() else None for name in ("cpu.max", "memory.max", "cpuset.cpus.effective") } return value def admission_runtime(payload: Path, data: dict) -> tuple[dict, dict]: from kf_oj.contracts.judging_v2 import AdmissionRegistry admission = read_json(payload / "deploy/judge.admission.example.json", 16384) root = read_json(payload / "deploy/sandbox.managed.runtime.example.json", 16384) for cap in admission["multi_runtime"]["capabilities"]: image = {"c17": "gnu", "cpp20": "gnu", "python313": "python313", "java21": "java21"}[ cap["language"] ] cap["toolchain"] = data["images"][image] admission = AdmissionRegistry.model_validate(admission).model_dump(mode="json") state = "/var/lib/kf-judge/images/" + data["release_id"] root["image_root"] = state + "/legacy-gnu" root["delegated_cgroup_root"] = "/sys/fs/cgroup/kfonlinejudge.slice/kf-judge-tasks" root["identities"] = { "worker_uid": preflight.ACCOUNTS["kf-worker"], "worker_gid": preflight.ACCOUNTS["kf-worker"], "first_task_uid": preflight.TASK_UID, "first_task_gid": preflight.TASK_GID, "count": 1, } for image, entry in zip(("gnu", "python313", "java21"), root["runtime_images"], strict=True): entry["image_root"] = state + "/" + image entry["runtime_identity"] = data["images"][image] if root["max_concurrent_runs"] != 1: raise DeliveryError("single-slot configuration required") return admission, root def compose( payload: Path, data: dict, node: dict, endpoints: dict, *, attest: bool, service_tokens: tuple[str, str] | None = None, ) -> dict: """Caller has verified the full delivery inventory before this composition.""" admission, root = admission_runtime(payload, data) measurement = { "schema_version": 1, "hardware": measured_hardware(), "source_sha": data["source_sha"], "images": data["images"], "admission": admission, "sandbox_budget": root["aggregate"], "measurement_nonce": node["measurement_nonce"], "worker_id": node["worker_id"], } epoch = None if attest: if node["measurement_nonce"] is None: raise DeliveryError("explicit fresh measurement nonce required for attestation") epoch = hashlib.sha256(canonical(measurement)).hexdigest() worker_token, platform_token = service_tokens or (secrets.token_hex(32), secrets.token_hex(32)) coordinator = { "listen": "127.0.0.1:8100", "database_url": endpoints["database"]["coordinator"], "workers": [{"id": node["worker_id"], "token": worker_token, "measurement_epoch": epoch}], "platform_token": platform_token, "admission": admission, "lease_seconds": 120, "recovery_seconds": 5, } worker = { "worker_id": node["worker_id"], "service_token": worker_token, "coordinator_url": "http://127.0.0.1:8100", "platform_url": "http://127.0.0.1:8000", "amqp_url": endpoints["amqp"]["worker"], "socket": "/var/lib/kf-judge/factory/control.sock", "staging": "/var/lib/kf-onlinejudge/worker", "admission": admission, "control_timeout_ms": 5000, } common = { "KF_OJ_ENVIRONMENT": "production", "KF_OJ_LOG_LEVEL": "INFO", "KF_OJ_BROWSER_ORIGINS": canonical([node["origin"]]).decode().strip(), "KF_OJ_ARTIFACT_ROOT": "/var/lib/kf-onlinejudge/artifacts", "KF_OJ_IMPORT_STAGING_ROOT": "/var/lib/kf-onlinejudge/imports", "KF_OJ_ARTIFACT_MAX_BYTES": "536870912", "KF_OJ_ARTIFACT_MAX_FILES": "4096", } def backend_url(name): return endpoints["database"][name].replace("postgresql:", "postgresql+asyncpg:", 1) api = { **common, "KF_OJ_DATABASE_URL": backend_url("api"), "KF_OJ_JUDGE_ADMISSION": canonical(admission).decode().strip(), "KF_OJ_COORDINATOR_URL": "http://127.0.0.1:8100", "KF_OJ_COORDINATOR_TOKEN": platform_token, } files = { "api.env": environmental(api), "outbox.env": environmental( { **common, "KF_OJ_DATABASE_URL": backend_url("api"), "KF_OJ_AMQP_URL": endpoints["amqp"]["outbox"], } ), "import.env": environmental( { **common, "KF_OJ_DATABASE_URL": backend_url("import"), "KF_OJ_IMPORT_WORKER_CGROUP": IMPORT_CGROUP, } ), "identity.env": environmental( { **common, "KF_OJ_DATABASE_URL": backend_url("api"), "KF_OJ_IDENTITY_OPERATOR_DATABASE_URL": backend_url("identity"), } ), "migration.env": environmental({**common, "KF_OJ_DATABASE_URL": backend_url("migration")}), "admission.json": canonical(admission), "runtime.json": canonical(root), "coordinator.json": canonical(coordinator), "worker.json": canonical(worker), "measurement.json": canonical( {"binding": measurement, "epoch": epoch, "operator_attested": attest} ), } return files def validate_receipt(value: dict, *, installed: bool) -> dict: value = fields( value, { "schema_version", "source_sha", "release_id", "configuration_id", "manifest_sha256", "configuration_files", "operator_attested_measurement", "installed", "execution_verified", }, "configuration receipt", ) if ( type(value["schema_version"]) is not int or value["schema_version"] != 1 or not isinstance(value["source_sha"], str) or not re.fullmatch(r"[0-9a-f]{40}", value["source_sha"]) or value["release_id"] != "sd-" + value["source_sha"][:12] or not isinstance(value["configuration_id"], str) or not re.fullmatch(r"cfg-[0-9a-f]{24}", value["configuration_id"]) or type(value["operator_attested_measurement"]) is not bool or value["installed"] is not installed or value["execution_verified"] is not False ): raise DeliveryError("invalid configuration binding") sha256(value["manifest_sha256"]) names = CONFIG_FILES if installed: names = { installed_path(value["release_id"], value["configuration_id"], name).as_posix() for name in CONFIG_FILES } fields(value["configuration_files"], names, "configuration inventory") for checksum in value["configuration_files"].values(): sha256(checksum) hashes = { name: value["configuration_files"][ installed_path(value["release_id"], value["configuration_id"], name).as_posix() if installed else name ] for name in CONFIG_FILES } if value["configuration_id"] != configuration_id( value["source_sha"], value["manifest_sha256"], hashes ): raise DeliveryError("configuration batch identity mismatch") return value def configuration_id(source_sha: str, manifest_sha: str, hashes: dict) -> str: return "cfg-" + hashlib.sha256(canonical([source_sha, manifest_sha, hashes])).hexdigest()[:24] def installed_receipt_path(release_id: str, batch: str) -> Path: return Path("/etc/kf-onlinejudge") / (release_id + "." + batch + ".configuration.json") def installed_path(release_id: str, batch: str, name: str) -> Path: base = "/etc/kf-judge" if name == "runtime.json" else "/etc/kf-onlinejudge" return Path(base) / (release_id + "." + batch + "." + name) def environment_read(contents: bytes) -> dict: if len(contents) > 32768: raise DeliveryError("environment file exceeds limit") values = {} for line in contents.decode().splitlines(): name, sep, raw = line.partition("=") if not sep or not re.fullmatch(r"KF_OJ_[A-Z_]+", name) or name in values: raise DeliveryError("invalid service environment") value = json.loads(raw) if not isinstance(value, str) or "\n" in value or "\r" in value or "\x00" in value: raise DeliveryError("invalid service environment value") values[name] = value return values def validate_contents(paths: dict[str, Path], payload: Path, data: dict, *, attest: bool) -> None: """Validate public policy, endpoint roles and epoch binding, without health claims.""" if set(paths) != CONFIG_FILES: raise DeliveryError("incomplete service configuration") raw = {} for name, path in paths.items(): private(path) if path.stat().st_size > 32768: raise DeliveryError("service configuration exceeds limit") raw[name] = path.read_bytes() envs = {name: environment_read(raw[name]) for name in CONFIG_FILES if name.endswith(".env")} measurement = fields( read_json(paths["measurement.json"], 32768), {"binding", "epoch", "operator_attested"}, "measurement record", ) binding = fields( measurement["binding"], { "schema_version", "hardware", "source_sha", "images", "admission", "sandbox_budget", "measurement_nonce", "worker_id", }, "measurement binding", ) origins = json.loads(envs["api.env"].get("KF_OJ_BROWSER_ORIGINS", "null")) if not isinstance(origins, list) or len(origins) != 1: raise DeliveryError("one explicit browser origin required") node = validate_profile( { "schema_version": 1, "origin": origins[0], "worker_id": binding["worker_id"], "measurement_nonce": binding["measurement_nonce"], } ) coordinator = fields( read_json(paths["coordinator.json"], 32768), { "listen", "database_url", "workers", "platform_token", "admission", "lease_seconds", "recovery_seconds", }, "coordinator configuration", ) worker = fields( read_json(paths["worker.json"], 32768), { "worker_id", "service_token", "coordinator_url", "platform_url", "amqp_url", "socket", "staging", "admission", "control_timeout_ms", }, "worker configuration", ) def database_url(name: str, key="KF_OJ_DATABASE_URL"): raw = envs[name].get(key) if not isinstance(raw, str) or not raw.startswith("postgresql+asyncpg:"): raise DeliveryError("backend database driver mismatch") return raw.replace("postgresql+asyncpg:", "postgresql:", 1) endpoints = validate_connections( { "schema_version": 1, "database": { "api": database_url("api.env"), "coordinator": coordinator.get("database_url"), "import": database_url("import.env"), "migration": database_url("migration.env"), "identity": database_url("identity.env", "KF_OJ_IDENTITY_OPERATOR_DATABASE_URL"), }, "amqp": { "outbox": envs["outbox.env"].get("KF_OJ_AMQP_URL"), "worker": worker.get("amqp_url"), }, } ) worker_token, platform_token = ( sha256(worker.get("service_token")), sha256(coordinator.get("platform_token")), ) if worker_token == platform_token: raise DeliveryError("service identities overlap") expected = compose( payload, data, node, endpoints, attest=attest, service_tokens=(worker_token, platform_token) ) for name in CONFIG_FILES: # JSON comparisons via canonical bytes distinguish true/1 and null/[]; # environmental files must have precisely the generated keys/encoding. if name.endswith(".json"): match = canonical(read_json(paths[name], 32768)) == expected[name] else: match = raw[name] == expected[name] if not match: raise DeliveryError("configuration/identity/measurement policy mismatch") def check_installed(receipt_path: Path) -> dict: from .install import RELEASES preflight.protected(receipt_path) private(receipt_path) receipt = validate_receipt(read_json(receipt_path, 16384), installed=True) release = RELEASES / receipt["release_id"] preflight.protected(release, directory=True) data = manifest.verify(release, receipt["manifest_sha256"]) if receipt["source_sha"] != data["source_sha"]: raise DeliveryError("configuration source differs from release") paths = {} for name in CONFIG_FILES: path = installed_path(receipt["release_id"], receipt["configuration_id"], name) preflight.protected(path) if digest(path) != receipt["configuration_files"][path.as_posix()]: raise DeliveryError("installed configuration fingerprint differs") paths[name] = path validate_contents( paths, release / "payload", data, attest=receipt["operator_attested_measurement"] ) return receipt def generate( bundle: Path, expected: str, node_path: Path, connections_path: Path, output: Path, *, attest: bool = False, ) -> dict: data = manifest.verify(bundle, expected) node, endpoints = profile(node_path), connections(connections_path) if output.exists() or output.is_symlink(): raise DeliveryError("configuration output must be new; secrets are never overwritten") files = compose(bundle / "payload", data, node, endpoints, attest=attest) output.mkdir(mode=0o700) hashes = {} for name, contents in files.items(): with (output / name).open("xb") as stream: os.chmod(output / name, 0o600) stream.write(contents) hashes[name] = digest(output / name) receipt = { "schema_version": 1, "source_sha": data["source_sha"], "release_id": data["release_id"], "configuration_id": configuration_id(data["source_sha"], expected, hashes), "manifest_sha256": expected, "configuration_files": hashes, "operator_attested_measurement": attest, "installed": False, "execution_verified": False, } (output / "receipt.json").write_bytes(canonical(receipt)) (output / "receipt.json").chmod(0o600) return { "configuration_generated": True, "source_sha": data["source_sha"], "configuration_id": receipt["configuration_id"], "receipt_sha256": digest(output / "receipt.json"), "operator_attested_measurement": attest, "installed": False, "execution_verified": False, } def install_configuration(source: Path, expected_receipt: str) -> dict: from .install import RELEASES, exclusive_file if os.geteuid() != 0: raise DeliveryError("configuration installation requires root") preflight.protected(source, directory=True) receipt_path = source / "receipt.json" preflight.protected(receipt_path) if digest(receipt_path) != sha256(expected_receipt): raise DeliveryError("configuration receipt fingerprint differs") receipt = validate_receipt(read_json(receipt_path, 16384), installed=False) data = manifest.verify(RELEASES / receipt["release_id"], receipt["manifest_sha256"]) if ( receipt["source_sha"] != data["source_sha"] or type(receipt["schema_version"]) is not int or receipt["schema_version"] != 1 ): raise DeliveryError("configuration source mismatch") if {p.name for p in source.iterdir()} != CONFIG_FILES | {"receipt.json"}: raise DeliveryError("unexpected configuration member") # Validate the entire group before publishing any configuration member. for name, checksum in receipt["configuration_files"].items(): preflight.protected(source / name) if digest(source / name) != checksum: raise DeliveryError("configuration bytes differ") validate_contents( {name: source / name for name in CONFIG_FILES}, RELEASES / data["release_id"] / "payload", data, attest=receipt["operator_attested_measurement"], ) installed = {} for name, checksum in receipt["configuration_files"].items(): member = source / name preflight.protected(member) private(member) if digest(member) != sha256(checksum): raise DeliveryError("configuration bytes differ") target = installed_path(data["release_id"], receipt["configuration_id"], name) exclusive_file(target, member.read_bytes(), 0o600) installed[str(target)] = checksum receipt = {**receipt, "configuration_files": installed, "installed": True} target = installed_receipt_path(data["release_id"], receipt["configuration_id"]) exclusive_file(target, canonical(receipt), 0o600) return { "configuration_installed": True, "source_sha": data["source_sha"], "configuration_id": receipt["configuration_id"], "receipt_sha256": digest(target), "execution_verified": False, }