# Security: secrets in kickstarts A kickstart is fetched over the network by an unauthenticated installer and can embed real secrets: the root password hash, SSH keys, bootstrap tokens, repo credentials. bootapi's rule is: **NetBox holds identity and topology, never secrets. Secrets are injected at render time from Vault/env.** ## What goes where | Value | Where it lives | How it reaches the template | |-------|----------------|-----------------------------| | hostname, domain, IPs, MACs, VLANs, gateway, platform, role | NetBox | `internal/netbox` → `model.Host` | | template selection knobs (`provision_template`, `nameservers`, `gateway`) | NetBox custom fields | `.Custom` / typed fields | | **root password hash** | Vault → `BOOTAPI_ROOT_PASSWORD_HASH[_FILE]` | `.RootPasswordHash` | | **SSH authorized keys** | Vault → `BOOTAPI_SSH_AUTHORIZED_KEYS` | `.SSHAuthorizedKeys` | | puppet CA/server names | env (not secret) | `.PuppetServer` / `.PuppetCAServer` | `BOOTAPI_ROOT_PASSWORD_HASH_FILE` and `BOOTAPI_NETBOX_TOKEN_FILE` let the values arrive as Vault-mounted files rather than env, which is the k8s norm (see [deployment.md](deployment.md)). If no root hash is configured, the default templates emit `rootpw --lock` rather than a blank/guessable password. This matches the pre-bootapi setup, where the root hash was Cobbler's eyaml `default_password_crypted` injected into `settings.yaml` — an operator-managed secret, never in the NetBox/inventory layer. ## Exposure notes - Kickstarts are served over **HTTP** to the installer, so treat any embedded secret as visible to anything on the provisioning VLAN. Keep bootapi's kickstart endpoint on the trusted PXE network, exactly as Cobbler's was. - The root password hash *is* in the rendered kickstart by necessity (Anaconda needs it). Prefer SSH-key login + a locked or strong-random root password, and rotate the hash in Vault as normal. - The **puppet bootstrap uses no long-lived token**: the host generates a CSR and the puppetmaster autosigns it based on source subnet + `*.main.unkin.net` (unchanged from Cobbler). So the kickstart carries no puppet secret. ## Follow-up: per-template Vault lookups Today all render-time secrets are process-wide env/files (one root hash, one key set for the fleet), which covers the current estate. If per-host or per-role secrets are ever needed (e.g. a distinct bootstrap token per role), the seam is `internal/render.dataFor`: add a Vault kv fetch keyed by host/role there, behind an interface, the same way encapi's `internal/distro` resolver injects per-host params behind an interface. Tracked as a follow-up, not implemented, to avoid giving bootapi broad Vault read scope before it's needed.