初始化奇妙小屏幕控制器项目

This commit is contained in:
2026-09-08 22:56:52 +08:00
commit 8d368de3b5
491 changed files with 67678 additions and 0 deletions
@@ -0,0 +1,250 @@
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import uuid
try:
import pwd
except ImportError: # pragma: no cover - allows Windows unit tests to import this module
pwd = None # type: ignore[assignment]
from scripts.image_config import read_config_path
SOURCE_ROOT = Path(__file__).resolve().parents[1]
SSH_POLICY_SOURCE = SOURCE_ROOT / "systemd" / "10-walnutpi-screen-hardening.conf"
SSH_POLICY_TARGET = Path("/etc/ssh/sshd_config.d/10-walnutpi-screen-hardening.conf")
SSH_RUNTIME_DIRECTORY = Path("/run/sshd")
SUDOERS_TARGET = Path("/etc/sudoers.d/90-matrix-screen-controller-account")
SUDOERS_MAIN = Path("/etc/sudoers")
HOSTS_PATH = Path("/etc/hosts")
SSH_EXPECTED = {
"permitrootlogin": "no",
"passwordauthentication": "yes",
"kbdinteractiveauthentication": "no",
"permitemptypasswords": "no",
}
def run(args: list[str], *, input_text: str | None = None) -> None:
subprocess.run(args, input=input_text, text=True, check=True)
def _nm_escape(value: str) -> str:
return value.replace("\\", "\\\\").replace(";", "\\;")
def _rooted(root: Path, path: Path) -> Path:
return root / path.relative_to("/")
def _remove_temporary(path: Path) -> None:
if os.name == "nt" and path.exists():
path.chmod(0o600)
path.unlink(missing_ok=True)
def _atomic_write(path: Path, data: str, mode: int) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
try:
temporary.write_text(data, encoding="utf-8", newline="\n")
temporary.chmod(mode)
os.replace(temporary, path)
finally:
_remove_temporary(temporary)
def _sanitize_vendor_sudoers(data: str) -> str:
forbidden = (
re.compile(r"^\s*Defaults\s+rootpw\s*(?:#.*)?$"),
re.compile(r"^\s*pi\s+ALL\s*=\s*\(ALL\)\s+NOPASSWD\s*:\s*ALL\s*(?:#.*)?$"),
)
lines = [line for line in data.splitlines() if not any(pattern.fullmatch(line) for pattern in forbidden)]
return "\n".join(lines) + "\n"
def _install_sudoers(path: Path, main_path: Path, username: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
main_temporary = main_path.with_name(f".{main_path.name}.{uuid.uuid4().hex}.tmp")
try:
temporary.write_text(f"{username} ALL=(ALL:ALL) ALL\n", encoding="utf-8", newline="\n")
temporary.chmod(0o440)
main_temporary.write_text(
_sanitize_vendor_sudoers(main_path.read_text(encoding="utf-8")),
encoding="utf-8",
newline="\n",
)
main_temporary.chmod(0o440)
run(["visudo", "-cf", str(temporary)])
run(["visudo", "-cf", str(main_temporary)])
os.replace(main_temporary, main_path)
os.replace(temporary, path)
run(["visudo", "-c"])
finally:
_remove_temporary(temporary)
_remove_temporary(main_temporary)
def _configure_hostname_mapping(path: Path, hostname: str) -> None:
lines = path.read_text(encoding="utf-8").splitlines()
replacement = f"127.0.1.1\t{hostname}"
replaced = False
for index, line in enumerate(lines):
fields = line.split()
if fields and fields[0] == "127.0.1.1":
lines[index] = replacement
replaced = True
break
if not replaced:
lines.append(replacement)
_atomic_write(path, "\n".join(lines) + "\n", 0o644)
def configure_network(config: dict) -> None:
wifi = config["wifi"]
ipv4 = config["ipv4"]
connection_uuid = str(uuid.uuid4())
lines = [
"[connection]",
"id=matrix-screen",
f"uuid={connection_uuid}",
"type=wifi",
"interface-name=wlan0",
"autoconnect=true",
"",
"[wifi]",
"mode=infrastructure",
f"ssid={_nm_escape(wifi['ssid'])}",
"",
"[wifi-security]",
"key-mgmt=wpa-psk",
f"psk={_nm_escape(wifi['password'])}",
"",
"[ipv4]",
]
if ipv4["mode"] == "dhcp":
lines.extend(("method=auto", ""))
else:
dns = ";".join(ipv4["dns"]) + ";"
lines.extend(
(
f"address1={ipv4['address']}/{ipv4['prefix']},{ipv4['gateway']}",
f"dns={dns}",
"method=manual",
"",
)
)
lines.extend(("[ipv6]", "method=disabled", ""))
root = Path("/etc/NetworkManager/system-connections")
root.mkdir(parents=True, exist_ok=True)
for candidate in root.glob("*.nmconnection"):
candidate.unlink()
target = root / "matrix-screen.nmconnection"
target.write_text("\n".join(lines), encoding="utf-8", newline="\n")
target.chmod(0o600)
def configure_account(config: dict, *, root: Path = Path("/")) -> None:
if pwd is None:
raise RuntimeError("account provisioning requires the POSIX pwd module")
account = config["account"]
username = account["username"]
try:
pwd.getpwnam(username)
except KeyError:
pi = pwd.getpwnam("pi")
groups = subprocess.check_output(["id", "-nG", "pi"], text=True).split()
supplementary = ",".join(group for group in groups if group not in {"pi", username})
args = ["useradd", "--create-home", "--shell", "/bin/bash"]
if supplementary:
args.extend(("--groups", supplementary))
args.append(username)
run(args)
home = _rooted(root, Path(f"/home/{username}"))
if Path(pi.pw_dir).is_dir() and not any(home.iterdir()):
for source in Path(pi.pw_dir).iterdir():
if source.name not in {".ssh", ".bash_history"}:
destination = home / source.name
if source.is_dir():
shutil.copytree(source, destination, symlinks=True)
elif source.is_file():
shutil.copy2(source, destination)
run(["chown", "-R", f"{username}:{username}", str(home)])
run(["chpasswd"], input_text=f"{username}:{account['password']}\n")
run(["usermod", "--append", "--groups", "sudo", username])
sudoers = _rooted(root, SUDOERS_TARGET)
_install_sudoers(sudoers, _rooted(root, SUDOERS_MAIN), username)
if username != "pi":
run(["passwd", "--lock", "pi"])
def reset_identity(*, root: Path = Path("/")) -> None:
_configure_hostname_mapping(_rooted(root, HOSTS_PATH), "matrix-screen")
run(["hostnamectl", "set-hostname", "matrix-screen"])
run(["systemctl", "stop", "ssh.service"])
for key in _rooted(root, Path("/etc/ssh")).glob("ssh_host_*"):
if key.is_file():
key.unlink()
_rooted(root, Path("/etc/machine-id")).write_text("", encoding="ascii")
dbus = _rooted(root, Path("/var/lib/dbus/machine-id"))
if dbus.exists() or dbus.is_symlink():
dbus.unlink()
run(["systemd-machine-id-setup"])
run(["ssh-keygen", "-A"])
def configure_ssh(*, root: Path = Path("/")) -> None:
policy = SSH_POLICY_SOURCE.read_text(encoding="utf-8")
target = _rooted(root, SSH_POLICY_TARGET)
_atomic_write(target, policy, 0o644)
runtime = _rooted(root, SSH_RUNTIME_DIRECTORY)
runtime.mkdir(parents=True, exist_ok=True)
runtime.chmod(0o755)
run(["/usr/sbin/sshd", "-t"])
effective = subprocess.check_output(["/usr/sbin/sshd", "-T"], text=True)
observed = {}
for line in effective.splitlines():
key, separator, value = line.partition(" ")
if separator and key in SSH_EXPECTED:
observed[key] = value.strip()
if observed != SSH_EXPECTED:
raise RuntimeError(f"effective SSH policy is invalid: {observed!r}")
run(["systemctl", "unmask", "ssh.service"])
run(["systemctl", "enable", "ssh.service"])
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=("network", "account", "identity", "ssh", "print-public"))
parser.add_argument("--config", type=Path, required=True)
args = parser.parse_args()
config = read_config_path(args.config)
if args.command == "network":
configure_network(config)
elif args.command == "account":
configure_account(config)
elif args.command == "identity":
reset_identity()
elif args.command == "ssh":
configure_ssh()
else:
public = {
"software_version": config["software_version"],
"username": config["account"]["username"],
"ssid": config["wifi"]["ssid"],
"ipv4": config["ipv4"],
}
print(json.dumps(public, ensure_ascii=False))
return 0
if __name__ == "__main__":
raise SystemExit(main())