Security notes¶
- No secrets in JSON. Passwords are read from environment variables loaded from
.env..envand real client configs are gitignored. - autounattend.xml contains a plain-text password. This is unavoidable for unattended OOBE. Mitigations: use a per-batch temporary local-admin password, store the answer file only on removable media, and wipe/rotate after deployment. Never commit
autounattend.xml. - Auto-logon during setup writes
DefaultPasswordto the Winlogon registry key in plain text so the run can resume across reboots. The orchestrator clears it (Clear-TotlAutoLogon) at finalize. If a run is interrupted, runInvoke-Provision.ps1 -Finalizeto clean up. - URL installers support an optional
sha256field — set it to verify downloads before executing. - Least privilege. The toolkit must run elevated; it does not create additional privileged accounts beyond the configured local admin.
- Third-party scripts. The optional winutil path executes a live remote script. It is off by default; prefer the native baked-in tweaks for production.
Zero-knowledge secret escrow (backend)¶
Admin passwords and BitLocker recovery keys are escrowed to the Cloudflare backend, but the backend never holds a decryption key and can never read them.
- Encrypt-only engine. Each machine fetches the tenant public key (
GET /v1/tenant/pubkey, a JWK) and encrypts the secret with RSA-OAEP-SHA256 (Totl.Crypto\Protect-TotlSecret). Only base64 ciphertext is uploaded (POST /v1/secret). - Browser-only decryption. The tenant private key is generated in the engineer's browser
(
portal/crypto.js), wrapped with an org passphrase (PBKDF2-SHA256 → AES-GCM), and stored only as that wrapped blob. Reveal returns ciphertext; the browser unwraps the key and decrypts locally. - D1 stores ciphertext only for
secrets.ciphertextandtenants.wrapped_private_key. Platform at-rest encryption is assumed but treated as insufficient on its own. - API tokens are hashed (
sha-256(salt + token)), never stored in plaintext — a DB leak cannot replay them. - Every ingest and reveal is audit-logged (append-only
audittable: actor email from the verified Access JWT, action, machine, time, IP).
Data classification (D1)¶
- App-layer encrypted, retrievable: BitLocker keys, local-admin passwords, BIOS passwords, domain/Entra-join creds, RMM keys, Wi-Fi PSKs.
- Hashed, never retrievable: machine API tokens.
- Field-level encrypt (PII): usernames / emails.
- Tenant-isolated + access-controlled only: run status, timings, asset inventory.
- Append-only / tamper-evident: audit log.
Key recovery / break-glass (REQUIRED)¶
Because the backend cannot decrypt, a lost tenant passphrase/private key means all that tenant's escrowed secrets are unrecoverable. Operate a recovery scheme before relying on escrow in production: Shamir secret-sharing (M-of-N) of the tenant passphrase (or the private key), with shares held by separate trusted custodians and/or sealed offline. The portal warns at onboarding that the passphrase cannot be recovered from the server.
Per-machine credentials (planned, Phase 2)¶
- Each machine gets a unique randomized local-admin password (
Totl.Crypto\New-TotlPassword), escrowed per machine — never a shared password across the fleet. - Autologon
DefaultPasswordmoves from plaintext registry to LSA-encrypted storage, with a failsafe cleanup task that clears credentials even if a run crashes before finalize.