Initial infrastructure mono-repo scaffold

Consolidates nixos, docker, raspi, and debian-configuration into a single
infrastructure-as-code repo. Includes:

- ansible/: full inventory + proxmox-hardening, freeipa, and raspberrypi
  roles (converted from debian-configuration bash scripts)
- terraform/: Proxmox VMs, Dynu DNS, Pi-hole (decommissioned stub),
  Docker container catalog — migrated from docker/infrastructure/terraform/
- stacks/docker/, stacks/raspi/, nixos/: placeholder READMEs pending
  git subtree population (see implementation plan)
- docs/: internal MkDocs site with architecture, network topology, runbooks,
  and drift-detection guide; external sanitized site
- scripts/: drift-detect.sh, docs-build.sh, install-hooks.sh, check-secrets.sh
- CI: secret-scan (push/PR), drift-detect (daily), docs-build (on change)
- Pi-hole removed throughout — DNS is FreeIPA, DHCP is router

See docs/internal/implementation-plan.md for the phased rollout after
pushing to Gitea.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UvNjoxTWEDkhXsd1Dq2ETP
This commit is contained in:
beatzaplentyandClaude Sonnet 4.6 committed 2026-07-30 07:07:47 +10:00
commit f85c65870f
125 files changed
+8186

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
# Runbook: Drift Detection
How to interpret and respond to drift detected by the CI pipeline or local script.
## What is drift?
**Terraform drift** — a Proxmox VM, DNS record, or Pi-hole entry exists in a different
state than what's declared in the Terraform workspace. Common causes:
- A VM was resized or reconfigured manually in the PVE web UI
- A DNS record was added directly via Dynu or Pi-hole admin
- A VM was deleted outside of Terraform
**Ansible drift** — a host's configuration differs from what the Ansible role declares.
Common causes:
- A config file was edited by hand on the host
- A package was installed/removed manually
- A service was stopped/disabled without updating the role
## CI workflow
The `drift-detect` workflow runs daily at 06:00 AEST. If drift is detected, a Gotify
notification is sent to the push notification dashboard. The step summary in Gitea CI
contains the full `terraform plan` diff.
## Responding to Terraform drift
```bash
# See exactly what changed
cd terraform/proxmox
terraform plan
# Option A: The manual change was intentional — update the .tf file to match reality,
# then run terraform apply to reconcile state
vim proxmox/docker.tf # update the resource to match current state
terraform apply
# Option B: The manual change was accidental — apply to restore declared state
terraform apply # will revert the manual change
```
## Responding to Ansible drift
```bash
# See exactly what would change
cd ansible
ansible-playbook playbooks/site.yml --check --diff --limit <drifted_host>
# Apply to restore declared state
ansible-playbook playbooks/site.yml --limit <drifted_host>
```
## Required Gitea secrets
The following secrets must be configured in the Gitea repo for CI drift detection to work:
| Secret | Used by |
|--------|---------|
| `PROXMOX_ENDPOINT` | Terraform proxmox workspace |
| `PROXMOX_API_TOKEN_ID` | Terraform proxmox workspace |
| `PROXMOX_API_TOKEN_SECRET` | Terraform proxmox workspace |
| `DYNU_API_KEY` | Terraform dns workspace |
| `ANSIBLE_SSH_KEY` | Ansible drift check |
| `GOTIFY_URL` | Drift notifications |
| `GOTIFY_TOKEN` | Drift notifications |
+59
View File
@@ -0,0 +1,59 @@
# Runbook: Provisioning a New Host
Steps for adding a new machine to the infrastructure.
## NixOS host on Proxmox
```bash
# 1. Use the PXE boot menu or installer ISO
# The auto-installer at pxe-boot.sweet.home presents all flake targets.
# 2. Add to nixos/ flake if a new build-type is needed
# Otherwise pick the closest existing target (e.g. proxmox-minimal)
# 3. Add SSH host key to clan vars (enables SOPS decryption)
cd nixos
./scripts/sync-host-keys.sh <target>
# 4. Create the VM/LXC via Terraform
cd ../terraform/proxmox
# Add a resource block in the appropriate .tf file
terraform plan
terraform apply
# 5. Deploy NixOS
nixos-rebuild switch --flake ./nixos#<target> --target-host <hostname>
```
## Non-NixOS host (Proxmox VM)
```bash
# 1. Create VM in Proxmox (manually or via terraform)
# 2. Add to ansible/inventory/hosts.yml with correct group
# 3. Add SSH key, verify connectivity:
ansible-playbook ansible/playbooks/ping.yml --limit <hostname>
# 4. Apply the role:
ansible-playbook ansible/playbooks/<role>.yml --limit <hostname> --check --diff
ansible-playbook ansible/playbooks/<role>.yml --limit <hostname>
```
## Adding Ansible inventory entry
```yaml
# ansible/inventory/hosts.yml — under the appropriate group:
new-host.sweet.home:
ansible_user: wayne
# any role-specific variables
```
## IPA enrollment (for any new host)
```bash
# On the new host (after OS install):
sudo ipa-client-install \
--server=domain-controller.sweet.home \
--domain=sweet.home \
--realm=SWEET.HOME \
--principal=admin \
--mkhomedir
```
@@ -0,0 +1,52 @@
# Runbook: Proxmox VE Hardening
Applies Stage 1 hardening to a fresh Proxmox VE node using the `proxmox-hardening` Ansible role.
## Pre-conditions
- Fresh PVE install with root SSH access
- SSH key already added to root's `authorized_keys`
- Host added to `ansible/inventory/hosts.yml` under the `proxmox` group
## Steps
```bash
# 1. Verify connectivity
cd ansible
ansible-playbook playbooks/ping.yml --limit <hostname>
# 2. Dry-run to preview changes
ansible-playbook playbooks/proxmox.yml --limit <hostname> --check --diff
# 3. Review the diff output, then apply
ansible-playbook playbooks/proxmox.yml --limit <hostname>
# 4. Verify hardening state
ansible-playbook playbooks/proxmox.yml --limit <hostname> --tags audit --check
```
## What gets applied
1. **APT repos** — enterprise source disabled, no-subscription source enabled
2. **SSH hardening** — `PermitRootLogin prohibit-password`, `PasswordAuthentication no`, fail2ban
3. **Datacenter firewall** — default-deny inbound; SSH (22) and web UI (8006) from management CIDR only
4. **Unattended upgrades** — security-only origins, no auto-reboot
5. **Named admin user** — `<proxmox_admin_username>@pve` with Administrator role (one-time)
6. **IPA sudo** — `admins` group gets NOPASSWD sudo for shell and PVE tools
7. **Subscription nag** — cosmetic patch to remove the nag dialog
## Variables to set per-host
In `ansible/inventory/hosts.yml`:
```yaml
pve1.sweet.home:
proxmox_mgmt_cidr: "192.168.2.0/24"
proxmox_admin_username: wayne
```
## After hardening
- Log into the PVE web UI as the named admin, change the initial password
- Enable TOTP/2FA for both the named admin and root@pam
- Verify with the audit script: `cd /home/wayne/repos/debian-configuration && proxmox/scripts/audit.sh`
(will migrate to an Ansible audit task in a future iteration)