Files
ansible-playbooks/.project-wiki/alerts/ALERT-20260909-001.md
T

84 lines
3.4 KiB
Markdown

---
id: ALERT-20260909-001
type: alert
status: resolved
severity: high
title: Plaintext admin password hash committed outside vault
created: 2026-09-09
updated: 2026-09-09
resolved: 2026-09-09
tags: [alert, security, secrets]
related: [NFR-002, OQ-006, TECH-SECURITY]
source_paths:
- roles/bootstrap/defaults/main.yml
- inventory/group_vars/external.yml
- README.md
confidence: confirmed
---
# ALERT-20260909-001 - Plaintext admin password hash committed outside vault
## Summary
The SHA-512 hash of the admin password (`admin_password`) is committed in
plaintext in `roles/bootstrap/defaults/main.yml` and
`inventory/group_vars/external.yml`, while the root README states that
`admin_password` lives encrypted in `vault.yml`. The documented secret
policy (AGENTS.md, README "Beitragen") forbids plaintext secrets in
`defaults/` or `group_vars/`. The same hash is reused across both locations.
## Why It Matters
- A committed password hash allows offline guessing of the shared admin
password; reusing one hash across hosts amplifies the impact.
- It contradicts the project's own documented vault policy and misleads
contributors (README claims the value is vaulted).
## Evidence
- `roles/bootstrap/defaults/main.yml` lines 12-13 (`admin_password` hash)
- `inventory/group_vars/external.yml` lines 10-11 (same hash)
- `README.md` "Secrets & Vault" section claims `admin_password` is in
`vault.yml`
- AGENTS.md "Secrets": no plaintext secrets in `defaults/`/`group_vars/`
## Suggested Resolution
Move `admin_password` to `vault.yml` in both locations; keep no plaintext
fallback in role defaults. If a default is truly needed for bootstrap,
document it explicitly as an accepted risk and set a unique hash per site.
Verify whether `vault.yml` already overrides it (OQ-006).
## Resolution
Resolved 2026-09-09. OQ-006 was verified first: decrypting `vault.yml`
confirmed it did **not** define `admin_password` (no override existed), so the
committed hash was a policy violation, not an intended fallback. Fix applied:
- `admin_password` (SHA-512 hash) moved into encrypted `vault.yml`.
- Plaintext hash removed from `roles/bootstrap/defaults/main.yml` and
`inventory/group_vars/external.yml` (comment now points to `vault.yml`).
- `bootstrap.yml` loads `vault.yml` via `vars_files` (same pattern as
`playbooks/monitoring.yml`), so the bootstrap role still resolves the
variable for every inventory.
- Documentation updated: root `README.md` "Secrets & Vault" and
`roles/bootstrap/README.md` (variable now documented as vault-sourced).
- NFR-002 acceptance notes and evidence updated accordingly.
Remaining recommendation (hardening, not blocking): the same hash is still
shared across sites; rotating to a unique hash per site would remove the
single shared credential.
## Resolution Evidence
- `ansible-vault view vault.yml` (decrypted during resolution) now lists
`admin_password` among `PROXMOX_USER_NAME`, `PROXMOX_USER_PASS`,
`monitoring_zabbix_api_password`.
- Repository-wide search for the former hash string (salt fragment) returns
no matches - the hash exists only inside the encrypted vault file.
- `ansible-playbook --syntax-check bootstrap.yml` passes (with
`--vault-password-file .vault_pass`).
- Resolved within coherent change-set commit `30f3e9a` (2026-09-09).
- `rg -n "admin_password"` shows the variable now only in `vault.yml`
(encrypted) and consumed at `roles/bootstrap/tasks/create-admin-user.yml:9`.