Share accounts: per-site Samba passwords from the webapp

Tools > Share accounts sets the pxe-upload, blancco and clonezilla Samba
passwords. Saving one runs smbpasswd and writes it everywhere it is used:
startnet.cmd in boot.wim (pxe-upload), the served Blancco preferences and
the live config.img (blancco), the iPXE menu's Clonezilla entries
(clonezilla). Refused while a PC is imaging unless forced. Stored 0600 in
UPDATES_DIR/shares/accounts.json, never shown back.

- The stored values are the authority: the playbook only creates the
  Samba accounts, seeds the store from site-vars (pxe_upload_password,
  blancco_smb_password, clonezilla_smb_password) for accounts it does not
  hold, and runs "share_accounts apply" at the end of every run.
- Every startnet save re-pins SHAREPASS, so a restored snapshot cannot
  bring an old password back; new menu blocks use %SHAREUSER%/%SHAREPASS%.
- Shop floor unattend Order 4 reads the login from fetch-source.txt;
  Invoke-PostPpkg deletes that file once the PPKG is applied.
- Upload-Image.ps1 asks for the password unless -Pass is given.
- pxe_admin_password: the Linux pxe login. Required while the box still
  has the shipped "pxe"; set with chpasswd when it differs.
- Blancco site settings now also reach the live config.img (BIOS path).
This commit is contained in:
cproudlock
2026-10-17 12:29:06 -04:00
parent 5f7954ac88
commit fa79f76951
20 changed files with 1526 additions and 671 deletions

View File

@@ -4,6 +4,8 @@ Three values differ per site and are filled into our preferences.xml:
business_location printed on every erasure report (the site)
wifi_ssid the Wi-Fi Blancco joins to license erasures in the GE
wifi_password Blancco cloud (the imaging LAN has no internet)
A fourth, the password of the blancco Samba account in <network_share>,
comes from services/share_accounts.py (Tools > Share accounts).
They live in UPDATES_DIR/blancco/site-settings.json (0600, root). The repo's
playbook/blancco-preferences.xml carries none of them; the playbook installs
@@ -38,6 +40,10 @@ _LOCATION = re.compile(rb"(<business_location>)[^<]*(</business_location>)")
_WIRELESS = re.compile(
rb'(<wireless>\s*<enabled>[^<]*</enabled>\s*<ssid encrypted="false">)[^<]*'
rb'(</ssid>\s*<password encrypted="false">)[^<]*(</password>)')
# The report share login: the blancco Samba account (Tools > Share accounts).
_SHARE = re.compile(
rb'(<network_share>\s*<username encrypted="false">blancco</username>\s*'
rb'<password encrypted="false">)[^<]*(</password>)')
def settings_path():
@@ -99,6 +105,8 @@ def apply(xml, settings=None):
XML-escaped; the replacement is a function, so nothing in them is read
as a regex group reference."""
s = settings or load()
from services import share_accounts
share_pw = escape(share_accounts.load()["blancco"]).encode("utf-8")
loc = escape(s["business_location"] or "Unnamed site").encode("utf-8")
ssid = escape(s["wifi_ssid"]).encode("utf-8")
pw = escape(s["wifi_password"]).encode("utf-8")
@@ -108,6 +116,9 @@ def apply(xml, settings=None):
out, n = _WIRELESS.subn(lambda m: m.group(1) + ssid + m.group(2) + pw + m.group(3), out)
if n != 1:
raise ValueError("preferences have no single <wireless> ssid/password block")
out, n = _SHARE.subn(lambda m: m.group(1) + share_pw + m.group(2), out)
if n != 1:
raise ValueError("preferences have no single <network_share> login for blancco")
return out