Files
NODEDC_MISSION_CORE/plugins/xgrids-k1/packaging/credential_install.py
T

128 lines
4.4 KiB
Python

"""Root-only preparation of the private application material shipped with K1.
An autonomous private installer supplies one fixed, root-readable file. The
worker receives only a systemd credential encrypted on this board. Reinstalling
the same material is harmless; changing it requires explicit rotation.
"""
import hmac
import os
import stat
import subprocess
import tempfile
from pathlib import Path
PROFILE_ID = "lixelgo.application.k1-fw-3.0.2.v1"
STORE = Path("/etc/credstore.encrypted")
BUNDLE = Path("/usr/share/mission-core-node/k1/private-application-key")
class CredentialInstallError(ValueError):
"""Secret-free failure; input and subprocess diagnostics never escape."""
def validate(secret):
if len(secret) != 36 or any(v < 33 or v > 126 for v in secret):
raise CredentialInstallError("Invalid material for the reviewed K1 profile")
def read_bundle(path=BUNDLE):
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
with os.fdopen(fd, "rb") as stream:
info = os.fstat(stream.fileno())
if not stat.S_ISREG(info.st_mode) or info.st_uid != 0 or info.st_mode & 0o077:
raise CredentialInstallError(
"Private material must be a root-owned private regular file"
)
secret = bytearray(stream.read(1025))
try:
validate(secret)
return secret
except CredentialInstallError:
secret[:] = b"\0" * len(secret)
raise
def install(secret, *, root=STORE, runner=subprocess.run):
if os.geteuid() != 0:
raise CredentialInstallError("Administrator authentication required")
validate(secret)
root.mkdir(mode=0o700, exist_ok=True)
if root.is_symlink() or root.stat().st_uid != 0 or root.stat().st_mode & 0o022:
raise CredentialInstallError("Unsafe credential store")
path = root / "k1-application"
if path.is_symlink():
raise CredentialInstallError("Unsafe credential target")
if path.exists():
info = path.stat()
if not stat.S_ISREG(info.st_mode) or info.st_uid != 0 or info.st_mode & 0o077:
raise CredentialInstallError("Unsafe installed credential")
result = runner(
["/usr/bin/systemd-creds", "decrypt", "--name=k1-application", str(path), "-"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
timeout=30,
check=False,
)
current = bytearray(result.stdout)
try:
if result.returncode or not hmac.compare_digest(current, secret):
raise CredentialInstallError(
"Existing material differs; explicit rotation required"
)
return "unchanged"
finally:
current[:] = b"\0" * len(current)
with tempfile.TemporaryDirectory(prefix=".k1-", dir=root) as directory:
staged = Path(directory) / "encrypted"
result = runner(
[
"/usr/bin/systemd-creds",
"encrypt",
"--name=k1-application",
"--with-key=host",
"-",
str(staged),
],
input=secret,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=30,
check=False,
)
if result.returncode:
raise CredentialInstallError("Application material preparation failed")
staged.chmod(0o600)
with staged.open("rb") as stream:
os.fsync(stream.fileno())
os.link(staged, path)
directory_fd = os.open(root, os.O_RDONLY | os.O_DIRECTORY)
try:
os.fsync(directory_fd)
finally:
os.close(directory_fd)
return "installed"
def main(*, bundled=False):
import sys
secret = bytearray()
try:
if os.geteuid() != 0 or len(sys.argv) != 1:
raise CredentialInstallError("Administrator-only preparation required")
if bundled:
if not BUNDLE.exists() and not BUNDLE.is_symlink():
return
secret = read_bundle()
else:
secret = bytearray(sys.stdin.buffer.read(1025).strip())
install(secret)
except (OSError, ValueError, subprocess.SubprocessError):
raise SystemExit(
"K1 application profile was not installed; "
"check the private release or existing profile"
) from None
finally:
secret[:] = b"\0" * len(secret)