docs: add architecture, IaC pipeline, and disaster recovery write-ups
Completes the docs/ set - all 16 systems now have a full deep-dive page in the diagram+callout+table style. Architecture overview covers the 4 trust boundaries and 2 traffic planes; IaC pipeline is a fresh Packer->Terraform->Ansible writeup replacing the stale old-content/ snapshot; disaster recovery covers the 3 independent backup layers and the deliberately decoupled OS/app upgrade systems.
This commit is contained in:
3 files changed
+182
No files matched your search
@@ -0,0 +1,47 @@
|
||||
# 🏗️ Infrastructure as Code
|
||||
|
||||
> [!NOTE]
|
||||
> Three tools, three responsibilities, one thread running through all of them: a host's tag list, generated once and consumed by every stage after it.
|
||||
|
||||
## The pipeline
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Packer["Packer<br/>builds the base image"] --> Terraform["Terraform<br/>provisions the VM"]
|
||||
Terraform -->|generates| Tags["host_tags.yml<br/>the tag list"]
|
||||
Tags --> Ansible["Ansible<br/>configures & hardens"]
|
||||
Ansible -->|re-applied every run| Compliance["Compliance stage<br/>not just once at creation"]
|
||||
```
|
||||
|
||||
## Packer: hardening baked in at install time, not bolted on after
|
||||
|
||||
A single base image is built from a minimal Rocky Linux install with CIS-oriented hardening baked into the kickstart itself — disk partitioning and package selection decided at install time, not patched in afterward. Some controls (like mount-option restrictions on specific partitions) are genuinely easier and more reliable to get right at image-build time than to retrofit with a configuration tool after the fact.
|
||||
|
||||
## Terraform: one resource type, tag generation as the real output
|
||||
|
||||
> [!TIP]
|
||||
> Every VM is one entry in a single map — VM ID, target node, template, resources, network, disk, and its own tag list. Two supporting pieces do the real connective work: a wait step that blocks completion until the new VM actually answers SSH (so nothing downstream races a host that isn't up yet), and a template render that writes that VM's tag list directly into Ansible's own host-variable tree. **This is the actual link between provisioning and configuration** — the tags Terraform assigns to a VM are the exact same tags every later Ansible stage matches against.
|
||||
|
||||
Terraform only manages virtual machines, deliberately — containers are handled differently (below).
|
||||
|
||||
## Ansible: tag-driven, re-applied continuously
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Tags["Per-host tag list"] --> Init["Initialization stage<br/>runs once, at creation"]
|
||||
Tags --> Compliance["Compliance stage<br/>re-applied every run"]
|
||||
Compliance --> Sub["9 independently-tagged<br/>hardening sub-roles"]
|
||||
```
|
||||
|
||||
Compliance hardening runs as a set of independently-tagged sub-roles, selected per host via that same tag list — which controls apply to a given host is explicit and auditable in that host's own config, not an implicit "all or nothing." All 9 current sub-roles are active fleet-wide; none are disabled or pending.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Deliberate deviations from the hardening baseline, with real reasoning attached**: one kernel-level control is flipped from the stricter default on container-hosting machines specifically, because the container platform's own networking genuinely requires it. One SSH control stays permissive fleet-wide because real remote access depends on it. Both are documented as accepted, permanent trade-offs — not gaps waiting to be closed, and not silently ignored either.
|
||||
|
||||
## The honest exception: not everything fits the clean model
|
||||
|
||||
> [!WARNING]
|
||||
> Two hosts don't follow the pipeline above cleanly, and that history is preserved rather than erased. Both were originally provisioned as Terraform-managed virtual machines, then at some point converted directly into containers outside Terraform's management — a resource type mismatch that Terraform's VM-only tooling can't represent. Rather than deleting their old Terraform entries, they were commented out, preserving the historical record. Ansible still configures both today through the same tag-driven mechanism as everything else — only the initial provisioning step is now manual instead of automated.
|
||||
|
||||
---
|
||||
Related: [`docs/disaster-recovery.md`](disaster-recovery.md)
|
||||
Reference in new issue
Block a user