From e94b6fc8efdfcb29e4ed6b99f0115659dceaa9d4 Mon Sep 17 00:00:00 2001 From: kronecker <2026692687@qq.com> Date: Tue, 13 Oct 2026 03:15:17 +0800 Subject: [PATCH] feat(delivery): initialize PostgreSQL in a bounded ordinary unit --- backend/tests/test_delivery.py | 38 +++ scripts/release/README.md | 20 ++ scripts/release/dependencies.py | 7 +- scripts/release/dependency_initialize.py | 363 +++++++++++++++++++++++ 4 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 scripts/release/dependency_initialize.py diff --git a/backend/tests/test_delivery.py b/backend/tests/test_delivery.py index b20f408..19fdd60 100644 --- a/backend/tests/test_delivery.py +++ b/backend/tests/test_delivery.py @@ -22,6 +22,7 @@ service_install = importlib.import_module("scripts.release.service_install") factory_service = importlib.import_module("scripts.release.factory_service") dependencies = importlib.import_module("scripts.release.dependencies") dependency_install = importlib.import_module("scripts.release.dependency_install") +dependency_initialize = importlib.import_module("scripts.release.dependency_initialize") def test_delivery_authorities_and_layout(): @@ -687,3 +688,40 @@ def test_dependency_receipt_excludes_foreign_files_and_health_claims(): def test_dependency_pointer_does_not_adopt_unknown_units_or_paths(target): with pytest.raises(policy.DeliveryError, match="unknown dependency unit"): dependency_install.previous_batch(target, "kf-postgres.service") + + +@pytest.mark.parametrize( + "contents", + [ + b"", + b"123\n", + b"b" * 64, + b"b" * 64 + b"\nextra\n", + b"b" * 32 + b" " + b"b" * 31 + b"\n", + ], +) +def test_database_initializer_refuses_empty_multiline_or_noncanonical_password(contents): + with pytest.raises(policy.DeliveryError, match="bootstrap password"): + dependency_initialize.validate_password(contents) + + +def test_database_initialization_receipt_does_not_prove_migration_or_authentication(): + receipt = { + "schema_version": 1, + "source_sha": "b" * 40, + "configuration_id": "cfg-" + "e" * 24, + "cluster_identity": {"device": 5, "inode": 900, "major_version": 18, "uid": 1203}, + "bootstrap_sha256": "f" * 64, + "initialized": True, + "migrated": False, + "services_started": False, + "execution_verified": False, + } + dependency_initialize.validate_receipt(receipt) + receipt["migrated"] = True + with pytest.raises(policy.DeliveryError, match="binding"): + dependency_initialize.validate_receipt(receipt) + receipt["migrated"] = False + receipt["cluster_identity"]["uid"] = 0 + with pytest.raises(policy.DeliveryError, match="cluster identity"): + dependency_initialize.validate_receipt(receipt) diff --git a/scripts/release/README.md b/scripts/release/README.md index 0ea726d..e24609c 100644 --- a/scripts/release/README.md +++ b/scripts/release/README.md @@ -223,3 +223,23 @@ System unit pointers may replace only recorded root-owned dependency snapshots. The command retains old batches, verifies loaded/inactive metadata, starts no service and initializes no database. Cluster initialization, actual effective limits, broker authorization and startup/readiness still require VM acceptance. + +`python3.13 -m scripts.release.dependency_initialize --password-file PRIVATE_FILE +--password-sha256 SHA` is the separate explicit PostgreSQL cluster initialization +step. It requires the selected dependency snapshot, stopped services, the dedicated +storage mount and an operator-supplied hexadecimal bootstrap password file. Only +a fresh unrecorded target is initialized. The fixed ordinary PostgreSQL identity +runs `initdb` in a transient network-isolated unit with node/service/process/disk +budgets checked before execution. systemd passes the password as a private file +credential; its bytes are neither unit arguments nor environment values. Both +local and host authentication use SCRAM; incomplete data is retained for explicit +recovery, not silently deleted or reused. The initializer never starts a database +server or migrates business tables; Alembic and role provisioning remain separate. + +A root-owned initialization receipt binds the physical cluster identity and initial +credential fingerprint. Repeat initialization requires that recorded identity and +does not reset a password or claim successful DB authentication. The operator +reads the transient unit result and confirms stop; foreign/residual units and +unrecorded/partial targets are refused. These are implementation interfaces, not +actual VM acceptance. See [PostgreSQL initdb](https://www.postgresql.org/docs/18/app-initdb.html) +and [systemd credentials](https://systemd.io/CREDENTIALS/) for the underlying interfaces. diff --git a/scripts/release/dependencies.py b/scripts/release/dependencies.py index 4d566fd..977186b 100644 --- a/scripts/release/dependencies.py +++ b/scripts/release/dependencies.py @@ -75,7 +75,9 @@ def validate_storage(name: str, observation: dict) -> None: raise DeliveryError("dedicated hard dependency storage budget differs") -def check_runtime(name: str) -> dict: +def check_runtime(name: str, *, initializing: bool = False) -> dict: + if initializing and name != "postgres": + raise DeliveryError("unexpected dependency initializer") uid = {"postgres": 1203, "rabbitmq": 1204}[name] if ( os.getuid() != uid @@ -92,7 +94,8 @@ def check_runtime(name: str) -> dict: ): raise DeliveryError("dependency privilege boundary differs") group = preflight.current_cgroup() - if str(group) != "/sys/fs/cgroup/kfonlinejudge.slice/kf-" + name + ".service": + unit = name + "-initialize" if initializing else name + if str(group) != "/sys/fs/cgroup/kfonlinejudge.slice/kf-" + unit + ".service": raise DeliveryError("dependency cgroup identity differs") limits = { key: (group / key).read_text().strip() diff --git a/scripts/release/dependency_initialize.py b/scripts/release/dependency_initialize.py new file mode 100644 index 0000000..ff028c3 --- /dev/null +++ b/scripts/release/dependency_initialize.py @@ -0,0 +1,363 @@ +"""Explicit ordinary PostgreSQL initialization; no schema migration or server start.""" + +import argparse +import json +import os +import re +import stat +import subprocess +import time +from pathlib import Path + +from . import configuration, dependencies, dependency_install, install, preflight, service_install +from .policy import DeliveryError, canonical, digest, fields, read_json, sha256 +from .services import python_environment + +UNIT = "kf-postgres-initialize.service" +DATA = Path(dependencies.STORAGE["postgres"]["path"]) / "data" +RECEIPT = dependency_install.ROOT / "postgres.initialization.json" +CREDENTIAL = Path("/run/credentials") / UNIT / "bootstrap" + + +def validate_password(contents: bytes) -> None: + if re.fullmatch(rb"[0-9a-f]{64}\n", contents) is None: + raise DeliveryError("explicit 256-bit hexadecimal bootstrap password required") + + +def cluster_identity() -> dict: + root = DATA.lstat() + version = DATA / "PG_VERSION" + info = version.lstat() + if ( + not stat.S_ISDIR(root.st_mode) + or (root.st_uid, root.st_gid, stat.S_IMODE(root.st_mode)) != (1203, 1203, 0o700) + or not stat.S_ISREG(info.st_mode) + or info.st_nlink != 1 + or (info.st_uid, info.st_gid, stat.S_IMODE(info.st_mode)) != (1203, 1203, 0o600) + or info.st_size != 3 + or version.read_bytes() != b"18\n" + ): + raise DeliveryError("initialized PostgreSQL identity/version differs") + return {"device": root.st_dev, "inode": root.st_ino, "major_version": 18, "uid": 1203} + + +def validate_receipt(value: dict) -> dict: + fields( + value, + { + "schema_version", + "source_sha", + "configuration_id", + "cluster_identity", + "bootstrap_sha256", + "initialized", + "migrated", + "services_started", + "execution_verified", + }, + "database initialization 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 not isinstance(value["configuration_id"], str) + or not re.fullmatch(r"cfg-[0-9a-f]{24}", value["configuration_id"]) + or value["initialized"] is not True + or any( + value[key] is not False + for key in ("migrated", "services_started", "execution_verified") + ) + ): + raise DeliveryError("initialization receipt binding differs") + sha256(value["bootstrap_sha256"]) + identity = fields( + value["cluster_identity"], {"device", "inode", "major_version", "uid"}, "cluster identity" + ) + if ( + any(type(identity[key]) is not int or identity[key] < 1 for key in identity) + or identity["major_version"] != 18 + or identity["uid"] != 1203 + ): + raise DeliveryError("initialization receipt cluster identity differs") + return value + + +def run_in_unit() -> dict: + observed = dependencies.check_runtime("postgres", initializing=True) + interfaces = Path("/proc/net/dev").read_text().splitlines()[2:] + if {line.split(":", 1)[0].strip() for line in interfaces} != {"lo"}: + raise DeliveryError("network-isolated database initializer required") + credential = CREDENTIAL.lstat() + if ( + not stat.S_ISREG(credential.st_mode) + or credential.st_nlink != 1 + or credential.st_uid not in {0, 1203} + or stat.S_IMODE(credential.st_mode) not in {0o400, 0o600} + or credential.st_size != 65 + ): + raise DeliveryError("private database initialization credential required") + validate_password(CREDENTIAL.read_bytes()) + if DATA.exists() or DATA.is_symlink(): + raise DeliveryError("database initializer refuses any existing target") + preflight.protected(Path("/usr/bin/initdb")) + env = {"PATH": "/usr/bin:/bin", "LC_ALL": "C.UTF-8", "HOME": "/nonexistent"} + version = subprocess.run( + ["/usr/bin/initdb", "--version"], capture_output=True, timeout=5, env=env + ) + if version.returncode or version.stdout.strip() != b"initdb (PostgreSQL) 18.6": + raise DeliveryError("database initializer tool version differs") + result = subprocess.run( + [ + "/usr/bin/initdb", + "--pgdata=" + str(DATA), + "--username=postgres", + "--pwfile=" + str(CREDENTIAL), + "--auth-host=scram-sha-256", + "--auth-local=scram-sha-256", + "--encoding=UTF8", + "--locale=C.UTF-8", + "--data-checksums", + "--no-clean", + "--no-instructions", + "--set=shared_buffers=64MB", + "--set=max_connections=32", + "--set=work_mem=1MB", + "--set=maintenance_work_mem=32MB", + "--set=max_wal_senders=0", + "--set=listen_addresses=", + ], + env=env, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + timeout=165, + ) + if result.returncode: + raise DeliveryError("ordinary database initialization failed; partial data retained") + return { + **observed, + "initialized": True, + "cluster_identity": cluster_identity(), + "services_started": False, + } + + +def status() -> dict: + return dict( + line.split("=", 1) + for line in service_install.systemctl( + "show", + UNIT, + "--property=LoadState,ActiveState,SubState,MainPID,Result,Description,Transient", + ).splitlines() + if "=" in line + ) + + +def initialize(password_file: Path, password_sha: str) -> dict: + if os.getuid() != 0 or os.geteuid() != 0: + raise DeliveryError("explicit database initialization requires root operator") + preflight.protected(password_file) + configuration.private(password_file) + if ( + not re.fullmatch(r"/[A-Za-z0-9_./-]+", str(password_file)) + or password_file.stat().st_size != 65 + or digest(password_file) != sha256(password_sha) + ): + raise DeliveryError("private database bootstrap fingerprint/path differs") + validate_password(password_file.read_bytes()) + selected, original = service_install.current_receipt() + payload = install.RELEASES / selected["release_id"] / "payload" + for name in ("dependencies.py", "dependency_install.py", "dependency_initialize.py"): + shipped = payload / "delivery/scripts/release" / name + preflight.protected(shipped) + if digest(shipped) != digest(Path(__file__).parent / name): + raise DeliveryError("database initializer differs from selected delivery") + dependency_install.require_stopped() + dependency_install.check_accounts() + dependencies.storage_observation("postgres") + if dependency_install.known_link( + service_install.SYSTEM_UNITS / "kf-postgres.service" + ) != dependency_install.ROOT / (selected["release_id"] + "." + selected["configuration_id"]): + raise DeliveryError("database dependency snapshot is not the selected binding") + current = status() + if ( + current.get("LoadState") != "not-found" + or current.get("ActiveState") != "inactive" + or current.get("MainPID") != "0" + ): + raise DeliveryError("unknown or residual database initialization unit") + if RECEIPT.exists() or RECEIPT.is_symlink(): + preflight.protected(RECEIPT) + configuration.private(RECEIPT) + recorded = validate_receipt(read_json(RECEIPT, 8192)) + if recorded["bootstrap_sha256"] != password_sha or canonical( + recorded["cluster_identity"] + ) != canonical(cluster_identity()): + raise DeliveryError( + "database initialization identity/credential changed; explicit recovery required" + ) + return { + "initialized": True, + "reused": True, + "credentials_authenticated": False, + "migrated": False, + "services_started": False, + "execution_verified": False, + } + if DATA.exists() or DATA.is_symlink(): + raise DeliveryError("unrecorded or partial database target; explicit recovery required") + description = ( + "KF PostgreSQL initialization " + + original["source_sha"] + + "." + + selected["configuration_id"] + ) + command = [ + "/usr/bin/systemd-run", + "--quiet", + "--unit=" + UNIT, + "--description=" + description, + "--slice=kfonlinejudge.slice", + ] + properties = { + "Type": "exec", + "RemainAfterExit": "yes", + "User": "postgres", + "Group": "postgres", + "UMask": "0077", + "MemoryMax": "512M", + "MemorySwapMax": "0", + "TasksMax": "64", + "CPUQuota": "100%", + "LimitCORE": "0", + "LimitFSIZE": "256M", + "LimitNOFILE": "256", + "LimitSTACK": "16M", + "RuntimeMaxSec": "180", + "TimeoutStartSec": "10", + "TimeoutStopSec": "15", + "Restart": "no", + "KillMode": "control-group", + "NoNewPrivileges": "yes", + "CapabilityBoundingSet": "", + "AmbientCapabilities": "", + "ProtectSystem": "strict", + "ProtectHome": "yes", + "ProtectControlGroups": "yes", + "PrivateDevices": "yes", + "PrivateTmp": "yes", + "PrivateNetwork": "yes", + "RestrictAddressFamilies": "AF_UNIX", + "ReadWritePaths": dependencies.STORAGE["postgres"]["path"], + "RequiresMountsFor": dependencies.STORAGE["postgres"]["path"], + "LoadCredential": "bootstrap:" + str(password_file), + } + for key, value in properties.items(): + command += ["--property=" + key + "=" + value] + command += [ + "/usr/bin/env", + "-i", + "PATH=/usr/bin:/bin", + "LC_ALL=C.UTF-8", + *python_environment(payload).split(), + str(payload / "python/bin/python3.13"), + "-s", + "-P", + "-m", + "scripts.release.dependency_initialize", + "--run-in-unit", + ] + dispatched = False + try: + dispatched = True + result = subprocess.run( + command, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + timeout=15, + env={"PATH": "/usr/bin:/bin", "LC_ALL": "C"}, + ) + if result.returncode: + raise DeliveryError("database initialization dispatch failed") + deadline = time.monotonic() + 185 + while True: + actual = status() + if actual.get("Description") != description or actual.get("Transient") != "yes": + raise DeliveryError("database initialization unit ownership differs") + if actual.get("SubState") == "exited" and actual.get("ActiveState") == "active": + break + if ( + actual.get("ActiveState") not in {"active", "activating"} + or time.monotonic() >= deadline + ): + raise DeliveryError( + "database initialization failed/expired; partial state retained" + ) + time.sleep(0.5) + if actual.get("Result") != "success" or actual.get("MainPID") != "0": + raise DeliveryError("database initialization unit failed; partial state retained") + recorded = validate_receipt( + { + "schema_version": 1, + "source_sha": original["source_sha"], + "configuration_id": selected["configuration_id"], + "cluster_identity": cluster_identity(), + "bootstrap_sha256": password_sha, + "initialized": True, + "migrated": False, + "services_started": False, + "execution_verified": False, + } + ) + if install.selection() != selected: + raise DeliveryError("selected binding changed during database initialization") + install.exclusive_file(RECEIPT, canonical(recorded), 0o600) + finally: + if dispatched: + actual = status() + if actual.get("LoadState") != "not-found": + if actual.get("Description") != description or actual.get("Transient") != "yes": + raise DeliveryError("foreign initialization unit; refused cleanup") + service_install.systemctl("stop", UNIT) + actual = status() + if actual.get("ActiveState") != "inactive" or actual.get("MainPID") != "0": + raise DeliveryError("database initialization cleanup unconfirmed") + return { + "initialized": True, + "reused": False, + "credentials_authenticated": False, + "migrated": False, + "services_started": False, + "execution_verified": False, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Explicit bounded PostgreSQL initialization") + parser.add_argument("--password-file", type=Path) + parser.add_argument("--password-sha256") + parser.add_argument("--run-in-unit", action="store_true") + args = parser.parse_args() + try: + if args.run_in_unit: + if args.password_file is not None or args.password_sha256 is not None: + raise DeliveryError("unexpected initialization unit override") + result = run_in_unit() + else: + if args.password_file is None or args.password_sha256 is None: + raise DeliveryError("explicit private bootstrap inputs required") + result = initialize(args.password_file, args.password_sha256) + print(json.dumps(result, sort_keys=True)) + except (OSError, ValueError, subprocess.SubprocessError) as error: + parser.exit( + 1, + "Database initialization refused: " + + (str(error) if isinstance(error, DeliveryError) else type(error).__name__) + + "\n", + ) + + +if __name__ == "__main__": + main()