From a00711d46093a4dda96267030e36164eda5da9ac Mon Sep 17 00:00:00 2001 From: Alexandru Macocian Date: Mon, 22 Jun 2026 15:43:38 +0200 Subject: [PATCH] Setup provisioning docs and plans --- docs/provisioning.md | 84 ++++++++++++++++++++++++++++++++++++++++++++ docs/roadmap.md | 3 +- docs/shared-homes.md | 4 +-- docs/toc.md | 1 + 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 docs/provisioning.md diff --git a/docs/provisioning.md b/docs/provisioning.md new file mode 100644 index 0000000..35a7ec3 --- /dev/null +++ b/docs/provisioning.md @@ -0,0 +1,84 @@ +# Host provisioning + +Status: **planned**. Tracking doc for the host auto-provisioning tool that takes +a node from bare to a fully working Charlie deploy target, step by step. Pairs +with [shared home directories](shared-homes.md), whose autofs `/Users` work is +one provisioning role. + +## Decisions + +| Area | Decision | +| ---- | -------- | +| Tool | Ansible (declarative, idempotent, cross-platform incl. macOS). | +| Phases | Phase 0 trust bootstrap is operator-run; Phase 1+ convergence is CI-driven. | +| Phase 1 invocation | Gitea Actions, push from runner over step-ca SSH cert, mirroring `deploy-stack.yml`. | +| Repo | New `Charlie/provisioning` (playbooks, roles, inventory, Phase-1 workflow). | +| Inventory | Ansible inventory is authoritative; [hosts](hosts.md) links to it instead of duplicating host facts. | +| Secrets | SOPS/age, matching the rest of Charlie. | + +## Provisioning steps + +Dependency-ordered. "Bootstrap" steps must exist before the node can be a normal +CI/SSH-cert target. + +| # | Step | Depends on | Phase | +| - | ---- | ---------- | ----- | +| 1 | Hostname + Marika DNS/DHCP entry | — | off-host | +| 2 | CA trust: X.509 root + SSH user CA | 1 | 0 | +| 3 | `/etc/charlie/age.key` | — | 0 | +| 4 | NFS `/mnt/nas` autofs mount | 1 | 1 | +| 5 | LDAP/SSSD users, groups, sudo | 2, 3 | 1 | +| 6 | Docker / Compose | — | 1 | +| 7 | `charlie-deploy` group + Docker socket access | 6 | 1 | +| 8 | `sys-gitea-runner` Git + registry creds | 2, 3 | 1 | +| 9 | otelcollector | 4, 7 | 1 | +| 10 | autofs `/Users` homes ([shared-homes](shared-homes.md)) | 4, 5 | 1 | + +## Two-phase model + +```mermaid +flowchart LR + Op[Operator + Sherlock] -->|Phase 0: out-of-band SSH| Bare[Bare host] + Bare -->|trust: CA roots, SSH CA, age key| Trusted[Trusted host] + Runner[Gitea runner] -->|Phase 1: step-ca SSH cert| Trusted + Trusted -->|converged| Target[Deploy target] +``` + +- **Phase 0 — trust bootstrap (operator-run).** Resolves the chicken-and-egg: + a bare node has no age key, no CA trust, no LDAP identity, so it cannot be a + CI target yet. Operator connects out-of-band (console / initial key) and lays + down CA roots, the SSH user CA, and `/etc/charlie/age.key`. Minimal, run once. +- **Phase 1+ — convergence (CI-driven).** Once trust and LDAP land, the host is + a normal step-ca SSH target. A Gitea workflow runs the playbook idempotently; + re-runnable on every host forever. + +## Proposed roles + +| Role | Provides | Phase | +| ---- | -------- | ----- | +| `trust` | CA roots, SSH user CA, `/etc/charlie/age.key` | 0 | +| `nfs` | autofs `/mnt/nas` mount | 1 | +| `identity` | SSSD + LDAP client config; sudo via groups | 1 | +| `docker` | Docker/Compose engine | 1 | +| `deploy-group` | `charlie-deploy`, Docker socket access | 1 | +| `gitea-access` | `sys-gitea-runner` Git + registry creds | 1 | +| `observability` | otelcollector | 1 | +| `homes` | autofs `/Users` + LDAP automount map | 1 | + +## Inventory & secrets + +- `inventory/` holds the authoritative host list; `host_vars/` carries + role, OS family, SOPS recipient pubkey, and whether the host runs stacks. +- Per-host/group secrets are SOPS-encrypted under the repo's `.sops.yaml`, + decrypted at run time with the host age key (Phase 1) or the operator key + (Phase 0). Same pattern as stack repos. + +## Open questions + +| Item | Note | +| ---- | ---- | +| macOS (Malenia) | Ansible supports it, but roles branch on OS (launchd vs systemd, no apt, native `/Users`). Decide per-role branching vs a separate playbook. | +| Existing hosts | Converge current hosts in place, or apply only to new nodes first? | +| First-run-only steps | Hostname/DNS and Phase-0 trust are not idempotent CI work; keep them operator-gated. | +| Home creation owner | Per-user home provisioning may belong to the `homes` role or DirectoryAdmin at account creation. See [shared-homes](shared-homes.md). | +| Break-glass | Keep a local sudoer with a local home so LDAP/NFS outages don't lock out provisioning. | diff --git a/docs/roadmap.md b/docs/roadmap.md index c55d924..f2bad1c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -22,9 +22,10 @@ context. | Build-time image scanning | Source/image repos | | Dashboards as code | `Charlie/victoriametrics` | | Cert issuer hardening | `Charlie/cert-issuer` | -| Bootstrap tooling | Host onboarding | +| [Host provisioning tool][provisioning] | Host onboarding | | [Shared home directories][shared-homes] | Host onboarding | +[provisioning]: provisioning.md [shared-homes]: shared-homes.md Done work stays in Git history, not this doc. diff --git a/docs/shared-homes.md b/docs/shared-homes.md index 479e349..f955820 100644 --- a/docs/shared-homes.md +++ b/docs/shared-homes.md @@ -2,8 +2,8 @@ Status: **planned**. Tracking doc for moving host home directories onto the NAS via LDAP-backed autofs, so per-user config (shells, `nvim`, etc.) follows the -user across every host. This is the first slice of host auto-provisioning; see -[roadmap → Bootstrap tooling](roadmap.md). Fold the implemented parts into +user across every host. This is the first slice of host +[auto-provisioning](provisioning.md). Fold the implemented parts into [identity](identity.md) and [onboarding](onboarding.md) once shipped. ## Goal diff --git a/docs/toc.md b/docs/toc.md index 890f1c8..6a6f1d0 100644 --- a/docs/toc.md +++ b/docs/toc.md @@ -11,6 +11,7 @@ Repository: [Charlie/project-charlie](https://gitea.alexandru.macocian.me/Charli | hosts | [Hosts](hosts.md) | Fleet roles and SOPS recipients. | | inventory | [Inventory](inventory.md) | Stack ownership, hosts, source/image repos, operator tooling. | | onboarding | [Host onboarding](onboarding.md) | Required host state and deploy-target prerequisites. | +| provisioning | [Host provisioning](provisioning.md) | Planned Ansible host auto-provisioning tool. | | identity | [Identity & access](identity.md) | Authentik, LDAP, step-ca, Gssh, Sherlock, service identities. | | shared-homes | [Shared home directories](shared-homes.md) | Planned LDAP/autofs NFS-backed `/Users` homes. | | secrets | [Secrets](secrets.md) | SOPS, age keys, PAT identities, secret placement. |