#!/usr/bin/env python3 """Owner-run provisioning/verification of Notification Service storage. No deletion.""" import json import os from pathlib import Path import stat import subprocess import sys BASE = Path("/mnt/shared/notification-service") UUID = "c2c31640-a247-4988-bc7b-9ee9789464d1" DIRECTORIES = (("postgres", 999, 999), ("rabbitmq", 100, 101)) def main(): if sys.platform != "linux" or os.geteuid() != 0: raise RuntimeError("run this script as root on mini-pc-2") for location in (Path("/mnt/files"), Path("/mnt/shared")): if not location.is_dir() or location.is_symlink(): raise RuntimeError("expected real storage mount directory is missing") result = subprocess.run(["findmnt", "--json", "--target", str(location), "--output", "TARGET,FSTYPE,UUID"], check=True, capture_output=True, timeout=20) filesystems = json.loads(result.stdout)["filesystems"] if len(filesystems) != 1 or filesystems[0]["target"] != str(location) or filesystems[0]["fstype"] != "xfs" or filesystems[0]["uuid"] != UUID: raise RuntimeError("storage mount/UUID/XFS verification failed; no directories created") if BASE.is_symlink(): raise RuntimeError("notification-service path must not be a symlink") if BASE.exists(): info = BASE.stat() if not BASE.is_dir() or (info.st_uid, info.st_gid, stat.S_IMODE(info.st_mode)) != (0, 0, 0o755): raise RuntimeError("existing parent path has unexpected type/ownership/permissions") if set(child.name for child in BASE.iterdir()) - {name for name, _, _ in DIRECTORIES}: raise RuntimeError("unexpected contents in notification-service parent") # Inspect both directories before creating anything. Never adopt arbitrary data. for name, uid, gid in DIRECTORIES: path = BASE / name if path.is_symlink(): raise RuntimeError("service data path must not be a symlink") if not path.exists(): continue info = path.stat() if not path.is_dir() or (info.st_uid, info.st_gid, stat.S_IMODE(info.st_mode)) != (uid, gid, 0o700): raise RuntimeError("existing service directory has unexpected ownership/permissions") marker = path / ".invemory-storage.json" if marker.is_symlink(): raise RuntimeError("marker must not be a symlink") if marker.exists(): marker_info = marker.stat() if (marker_info.st_uid, marker_info.st_gid, stat.S_IMODE(marker_info.st_mode)) != (0, 0, 0o644): raise RuntimeError("existing marker has unexpected ownership/permissions") if json.loads(marker.read_text(encoding="utf-8")) != {"uuid": UUID, "role": "shared-notification/" + name}: raise RuntimeError("existing marker mismatch; never overwritten") elif any(path.iterdir()): raise RuntimeError("unmarked service directory is not empty; never adopted") os.umask(0o077) if not BASE.exists(): BASE.mkdir(mode=0o755) os.chown(BASE, 0, 0) os.chmod(BASE, 0o755) for name, uid, gid in DIRECTORIES: path = BASE / name if not path.exists(): path.mkdir(mode=0o700) os.chown(path, uid, gid) marker = path / ".invemory-storage.json" if not marker.exists(): fd = os.open(marker, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) with os.fdopen(fd, "w", encoding="utf-8") as stream: json.dump({"uuid": UUID, "role": "shared-notification/" + name}, stream) stream.write("\n") os.chown(marker, 0, 0) os.chmod(marker, 0o644) info = path.stat() marker_info = marker.stat() if (info.st_uid, info.st_gid, stat.S_IMODE(info.st_mode)) != (uid, gid, 0o700): raise RuntimeError("directory ownership verification failed") if (marker_info.st_uid, marker_info.st_gid, stat.S_IMODE(marker_info.st_mode)) != (0, 0, 0o644): raise RuntimeError("marker ownership verification failed") print("Provisioned two Notification Service directories; XFS UUID and permissions verified.") if __name__ == "__main__": try: main() except (RuntimeError, OSError, ValueError, subprocess.SubprocessError) as error: print("STOP: " + str(error), file=sys.stderr) sys.exit(1)