diff --git a/docs/changelog-20260715-creds-consolidation.md b/docs/changelog-20260715-creds-consolidation.md new file mode 100644 index 0000000..8d1932b --- /dev/null +++ b/docs/changelog-20260715-creds-consolidation.md @@ -0,0 +1,52 @@ +# 2026-07-15 -- credential/env consolidation into ~/vr1-office1-creds/ (SEC-009) + +Operator-approved. Closes a credential SPRAWL + world-readable exposure on vcloud, and establishes +the standing per-site consolidation convention. + +## The finding + +Three env files sat loose in `~`, outside the consolidated `~/vr1-office1-creds/` folder: + + ~/.vr1-netbox.env 0600 upstream/v1-draft NetBox token (NETBOX_URL + NETBOX_TOKEN) + ~/vr1-office1.env 0600 OPNsense edge config (WAN/LAN/DHCP/root-hash/SSH-key vars) + ~/vr1-stage1.env 0664 OpenTofu TF_VAR_* -- INCLUDING TF_VAR_maas_api_key (a MAAS secret) + +`vr1-stage1.env` at **0664 (group/world-readable)** held a MAAS API key -- a real exposure, not just +untidiness. `~/vr1-office1-creds/tailscale-authkey.txt` was also 0664. None were created this +session (mtimes 2026-07-10..12); a prior session in this continuity scattered them. + +## What was done (files OUTSIDE the repo; not committed, recorded here) + +- Moved all three into `~/vr1-office1-creds/` (`vr1-netbox.env`, `vr1-office1.env`, `vr1-stage1.env`). +- `chmod 600` on every sensitive file in the folder -- fixed the two 0664 files. +- No file CONTENTS were read; key NAMES only were listed to identify each file's purpose. +- Fixed a D-119 follow-up the sweep could not reach (it is outside the repo): `vr1-stage1.env` had + `TF_VAR_dc1_pool_path` / `TF_VAR_dc2_pool_path` -> renamed to `TF_VAR_vr1_dc0_pool_path` / + `TF_VAR_vr1_dc1_pool_path` to match the renamed OpenTofu variables. + +## Repo changes (committed) + +- Active path references updated old `~/.vr1-netbox.env` -> `~/vr1-office1-creds/vr1-netbox.env`: + `netbox/prod-draft-dump.py` (docstring + usage + error message), `runbooks/dc-dc-phase1-office1- + standup.md` (Step 10b table), `docs/vr1-office1-as-built.md`, `docs/session-ledger.md`. + Dated changelog/incident records that reference the OLD paths are left as historical snapshots. +- `docs/vr1-office1-as-built.md` secrets table: added the three env files + the consolidation note. + Corrected a wrong assumption recorded mid-session -- the SANDBOX NetBox token is NOT on vcloud; it + is on office1-netbox (`/root/netbox-secrets/`). `vr1-netbox.env` is the UPSTREAM token only. +- `docs/security-ledger.md`: SEC-009 (this finding, REMEDIATED) + the STANDING CONVENTION. + +## Standing convention (SEC-009) + +Per-site: ALL sensitive + env/config files live in one `~/-creds/` folder (0700; files 0600; +public keys 0644). No loose env files in `~`. `~/vr1-dc0-creds/` and `~/vr1-dc1-creds/` follow the +same pattern FROM DAY ONE when DC deployment starts. This is a DATA-LOSS / continuity control first: +scattered creds get lost between sessions and after compaction. Real credential hardening (rotation, +revocation) stays deferred to post-teardown redeploy cycles -- this is a closed in-house test. + +Also seeded agent memory (was empty): the creds convention and the closed-test posture, so neither +is re-lost at the next compaction. + +## Revert + +Repo edits: `git revert `. The file moves are outside the repo -- to reverse, move the +three env files back to `~` (not recommended: it re-opens the sprawl and the 0664 exposure). diff --git a/docs/security-ledger.md b/docs/security-ledger.md index 7e96b5d..b715b1b 100644 --- a/docs/security-ledger.md +++ b/docs/security-ledger.md @@ -17,3 +17,14 @@ | SEC-006 | 2026-07-12 | NetBox API token PASTED INTO AGENT CHAT CONTEXT during D-111 IPv6 reconciliation -- the token is BURNED (it exists in a transcript that is not under our control) and must be revoked + reissued, not merely rotated on schedule | docs/changelog-20260712-office1-opnsense-edge-build.md; flagged again at 2026-07-12 session close | operator | OPEN -- **DEFERRED by operator ruling 2026-07-13: revoke at completion of this deployment.** Until then the token is live and exposed. Revoke: NetBox -> Admin -> API Tokens | | SEC-007 | 2026-07-12 | `~/vr1-office1-creds/` on the jumphost holds the Office1 edge root password + its bcrypt hash + the `office1_svc` SSH PRIVATE key + (added 2026-07-13) the OPNsense **API key/secret** (`opnsense-api.txt`, 0600), all in plaintext (dir 0700, files 0600) | 2026-07-12 edge build; custody detail deliberately not recorded here per D-069 | operator | OPEN -- required for edge management (D-112(c) makes SSH the only management path), so this is a ROTATION obligation, not a delete-me. Rotate at v1 close / if the jumphost is rebuilt or shared | | SEC-008 | 2026-07-13 | `~/vr1-office1-creds/tailscale-authkey.txt` (vcloud, 0600) -- the operator's 48-char Tailscale auth key for the SELF-HOSTED control plane `tailscale.baldurkeep.com`. Used to enrol `office1-tailscale`. Consumed BY PATH and never printed into a session; the copy shipped to the node was SHREDDED after `tailscale up` (tailscaled holds its own node key now). | 2026-07-13 Office1 Tailscale enrolment; docs/vr1-office1-as-built.md | operator | OPEN -- ROTATION obligation, not a delete-me: the key is still on vcloud for re-enrolment. Revoke/reissue on the control server at v1 close, or immediately if vcloud is rebuilt or shared. | +| SEC-009 | 2026-07-15 | Credential/env SPRAWL + world-readable exposure on vcloud: three env files sat loose in `~` outside the consolidated `~/vr1-office1-creds/` -- `.vr1-netbox.env` (upstream NetBox token), `vr1-office1.env` (edge root-password hash + SSH key path), and `vr1-stage1.env` which held `TF_VAR_maas_api_key` (a MAAS API secret) at mode **0664 (group/world-readable)**; `tailscale-authkey.txt` in the creds dir was also 0664. | docs/changelog-20260715-creds-consolidation.md | operator | **REMEDIATED 2026-07-15** -- all three moved into `~/vr1-office1-creds/`, every sensitive file `chmod 600`. STANDING CONVENTION established (below). Note: this is a CLOSED in-house test; the rotation obligations of SEC-005/006/007 still apply at v1 close, this row only closes the SPRAWL + PERMS exposure. | + +**STANDING CONVENTION (SEC-009, 2026-07-15): per-site credential/env consolidation.** ALL sensitive +files AND environment/config files for a site live in a single `~/-creds/` folder on vcloud, +mode 0700, files 0600 (public keys 0644). No loose env files in `~`. Sites: `~/vr1-office1-creds/` +(exists); when DC deployment starts, `~/vr1-dc0-creds/` and `~/vr1-dc1-creds/` follow the SAME +pattern from day one -- do not accumulate loose `vr1-dc0*.env` in `~` and consolidate later. This is +a DATA-LOSS / continuity control as much as a security one: scattered creds get lost between +sessions and after compaction. Rationale is looser-security-but-strict-hygiene: this is a closed +in-house test, so real credential hardening (rotation, revocation, non-shared tokens) is deferred to +post-teardown redeploy cycles (test2/3/4+), but consolidation happens NOW so nothing is lost. diff --git a/docs/session-ledger.md b/docs/session-ledger.md index 0117c38..3baca9d 100644 --- a/docs/session-ledger.md +++ b/docs/session-ledger.md @@ -411,7 +411,7 @@ is **jumbo, underlay_mtu=9000** (operator ruling; verified on all 10 nets). - The repo is temporarily **PUBLIC** (SEC-004) -- flip private at v1 close. - Secrets live jumphost-local and are NEVER committed: `~/vr1-office1-creds/` (edge SSH key, - root password, API key), `~/.vr1-netbox.env` (NetBox token), `~/tenant-devteam/`, `~/vault-init/`. + root password, API key), `~/vr1-office1-creds/vr1-netbox.env` (NetBox token), `~/tenant-devteam/`, `~/vault-init/`. ## Project-completion (execute after D-011 passes) diff --git a/docs/vr1-office1-as-built.md b/docs/vr1-office1-as-built.md index 9880ae5..fd42910 100644 --- a/docs/vr1-office1-as-built.md +++ b/docs/vr1-office1-as-built.md @@ -105,9 +105,16 @@ | OPNsense root password | `~/vr1-office1-creds/opnsense-root-password` (vcloud) | GUI login | | MAAS admin password + API key; DB password; LXD trust password | `/root/maas-secrets/` on **voffice1** (`0600`) | generated on the box | | NetBox SECRET_KEY, admin password, API token | `/root/netbox-secrets/` on **office1-netbox** (`0600`) | SECRET_KEY rotated off netbox-docker's PUBLIC default | -| NetBox (upstream, production) token | `~/.vr1-netbox.env` (vcloud) | SEC-006 -- burned, revoke at deployment completion | +| NetBox (upstream / v1-draft) token env | `~/vr1-office1-creds/vr1-netbox.env` (vcloud) | `NETBOX_URL`+`NETBOX_TOKEN` for `netbox.baldurkeep.com`. SEC-006 -- burned, revoke at completion. The SANDBOX token is NOT here -- it is on office1-netbox (`/root/netbox-secrets/`). | +| Office1 edge config env | `~/vr1-office1-creds/vr1-office1.env` (vcloud) | OPNsense WAN/LAN/DHCP/root-hash/SSH-key vars. Consolidated 2026-07-15 (SEC-009). | +| VR1 OpenTofu env | `~/vr1-office1-creds/vr1-stage1.env` (vcloud) | `TF_VAR_*` incl. `TF_VAR_maas_api_key`. Consolidated + chmod 600 2026-07-15 (was loose + 0664, SEC-009). TF_VAR pool-path names updated to D-119 (`vr1_dc0`/`vr1_dc1`). | | Tailscale auth key (self-hosted tailnet) | `~/vr1-office1-creds/tailscale-authkey.txt` (vcloud, `0600`) | Operator-supplied, 48 chars. Consumed by PATH, never printed. The copy on the node was SHREDDED after `tailscale up`. Rotate/revoke on the control server at v1 close. | +**Credential consolidation (SEC-009, standing convention):** ALL sensitive + env/config files for a +site live in one `~/-creds/` folder (0700; files 0600; public keys 0644) -- no loose env files +in `~`. When DC deployment starts, `~/vr1-dc0-creds/` and `~/vr1-dc1-creds/` follow the same pattern +from day one. See `docs/security-ledger.md` SEC-009. + **NetBox API token format (4.6):** the wire form is `nbt_.`. The API's `token` field alone is NOT usable -- present it raw and you get `403 Invalid v1 token`. See `docs/changelog-20260713-office1-netbox-deployed.md`. diff --git a/netbox/prod-draft-dump.py b/netbox/prod-draft-dump.py index 05267d9..dc2830f 100755 --- a/netbox/prod-draft-dump.py +++ b/netbox/prod-draft-dump.py @@ -13,7 +13,7 @@ WHY TWO PHASES instead of one source->dest script: 1. The upstream token NEVER travels. This script runs on vcloud (where - ~/.vr1-netbox.env lives); sandbox-seed.py runs on office1-netbox (where + ~/vr1-office1-creds/vr1-netbox.env lives); sandbox-seed.py runs on office1-netbox (where the sandbox token lives). Neither host holds the other's credential. 2. "The sim NEVER writes upstream" becomes STRUCTURAL, not a discipline: the script that writes has no upstream credentials and no upstream URL. @@ -29,7 +29,7 @@ not. We send an accepted UA. pynetbox (python-requests/...) is bitten too. Usage: - . ~/.vr1-netbox.env # NETBOX_URL + NETBOX_TOKEN + . ~/vr1-office1-creds/vr1-netbox.env # NETBOX_URL + NETBOX_TOKEN python3 netbox/prod-draft-dump.py --out netbox/draft/vr1-draft.json """ @@ -65,7 +65,7 @@ def require_env(name: str) -> str: v = os.environ.get(name) if not v: - die(f"{name} is not set. Source the upstream env first: . ~/.vr1-netbox.env") + die(f"{name} is not set. Source the upstream env first: . ~/vr1-office1-creds/vr1-netbox.env") return v diff --git a/runbooks/dc-dc-phase1-office1-standup.md b/runbooks/dc-dc-phase1-office1-standup.md index d073e0a..dccad26 100644 --- a/runbooks/dc-dc-phase1-office1-standup.md +++ b/runbooks/dc-dc-phase1-office1-standup.md @@ -702,7 +702,7 @@ | Phase | Host | NetBox | Token | |---|---|---|---| -| dump (read upstream) | `vcloud` | `netbox.baldurkeep.com` (apex, READ-ONLY) | upstream, `~/.vr1-netbox.env` | +| dump (read upstream) | `vcloud` | `netbox.baldurkeep.com` (apex, READ-ONLY) | upstream, `~/vr1-office1-creds/vr1-netbox.env` | | seed / carve / import / verify | `office1-netbox` (`10.10.1.201`) | sandbox `localhost:8000` | sandbox (operator-held on the box) | `pynetbox` is on `office1-netbox`, NOT on vcloud -- the two pynetbox importers