Repository navigation
feat(tofu): build ragnarok on gandalf, and finish and prove it with a script - #1464
Merged
Merged
Conversation
eden (141, BloodHound) was a hand `qm clone 901 141`, so tofu could not rebuild it (#1443, audit finding 18). It is now in guests.tf, in a `bloodhound` pool, with ADR-0081's shape: the OS disk on local-lvm without the SSD flag, a 32 GiB data disk on large_data, a static 10.0.30.41 with its gateway and nameserver, and garnet's key from alexander beside phoenix's. The module gains os_disk_ssd, data_disks, ipv4 and dns_servers. Each defaults to what every other guest has, so their plans do not change. The hand-built eden is imported once. As with the domain's six, the plan then replaces it, because a hand-built guest has no clone to read back. ADR-0081 allows that: the graph is derived data and eden is not backed up. The runbook says so, then destroys and applies, then continues from section 2, with section 11 for the new key. Refs #1443 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2EweeKAdMf5A1aeFy1anN
… script ADR-0116, amending ADR-0076. The range's attack VM was made by scripts/make-attack-vm.sh, outside the pipeline (#1443, audit finding 18). - providers.tf: an aliased provider for gandalf, from TF_VAR_gandalf_endpoint and TF_VAR_gandalf_api_token in phoenix.env, so every runbook step that sources phoenix.env before a plan still works. - modules/guest: extra_networks, for NICs after net0 with a static address and no gateway; pool_id becomes optional. - guests.tf: ragnarok (130) as module.range_guest on gandalf: a clone of 902 on `range`, net0 vmbr0 DHCP with its MAC pinned, net1 vmbr1 172.30.30.10/24 with no gateway, firewall flags off, user kali, no pool, Secure Boot off as tpl-kali's is. - make-attack-vm.sh becomes finish-attack-vm.sh: it no longer clones or configures. It checks the /vms/130 grant, reads the config back, now including net1's address, starts the guest if it is stopped, then writes 99-no-forward.conf and runs §8's third check over host-key-checked SSH, as before. The script-built ragnarok is destroyed and remade, not imported: an import would plan a replace anyway, and the range is disposable (ADR-0017). build-the-playground.md §5 says so, and how. Refs #1443 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2EweeKAdMf5A1aeFy1anN
…sends a migration to the import first build-the-lab-domain.md step 5 destroyed with -target=module.guest, which is every Saruman guest in the tree: garuda, the dotfiles VMs and, with this PR, eden. That would take garuda's case notes and eden's graph with the domain, against ADR-0081's reason for putting BloodHound off it. Steps 5 and 7 now target the six by name, and tofu/README.md and provision-lab-guests.md §2 say so. build-the-bloodhound-guest.md §1 put the first-build plan and apply before the import subsection. Followed in order against the existing eden, the apply fails on the clone into 141 after making the pool. A migration is now sent to the import before any apply. Refs #1443 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2EweeKAdMf5A1aeFy1anN
There was a problem hiding this comment.
🟡 Changes recommended
The post-apply isolation instructions still reference the renamed scripts/make-attack-vm.sh, causing a missing-file error; update the path to scripts/finish-attack-vm.sh.
1 open issue
What changed in this PR
Moves ragnarok provisioning into the existing OpenTofu tree while retaining a separate script to finish configuration and verify range isolation. This builds on the included eden provisioning changes from #1463.
Changes:
- Adds the aliased
gandalfprovider and guest-module support for additional network interfaces and optional pools. - Converts the attack-VM creation script into a post-apply configuration and isolation check.
- Updates deployment runbooks and records the provisioning decision in ADR-0116.
| File | Description |
|---|---|
tofu/variables.tf |
Adds eden key and gandalf provider inputs. |
tofu/README.md |
Documents both newly declared guests. |
tofu/providers.tf |
Adds the TLS-verified gandalf provider alias. |
tofu/modules/guest/variables.tf |
Adds disk, networking and optional-pool inputs. |
tofu/modules/guest/main.tf |
Implements configurable disks and additional interfaces. |
tofu/identities.auto.tfvars.example |
Adds placeholder identities for both guests. |
tofu/guests.tf |
Declares eden and ragnarok. |
scripts/README.md |
Documents the renamed finishing script. |
scripts/finish-attack-vm.sh |
Replaces cloning with post-apply configuration and verification. |
README.md |
Updates the ADR count. |
packer/kali.pkr.hcl |
Updates comments describing the provisioning flow. |
docs/runbooks/provision-lab-guests.md |
Documents provider credentials and the BloodHound pool grant. |
docs/runbooks/build-the-playground.md |
Documents ragnarok migration, apply and verification. |
docs/runbooks/build-the-lab-templates.md |
Attributes range-interface provisioning to OpenTofu. |
docs/runbooks/build-the-bloodhound-guest.md |
Moves eden creation and teardown into OpenTofu. |
docs/architecture.md |
Updates the attack VM’s provisioning description. |
docs/adr/0116-build-the-range-attack-vm-with-opentofu-and-prove-it-with-a-script.md |
Records the shared-state and post-apply verification decision. |
docs/adr/0076-provision-lab-guests-with-opentofu-and-encrypt-its-state-from-the-first-apply.md |
Adds an amendment link to ADR-0116. |
Review effort: Balanced
…med script Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2EweeKAdMf5A1aeFy1anN
# Conflicts: # tofu/README.md # tofu/identities.auto.tfvars.example # tofu/modules/guest/main.tf # tofu/modules/guest/variables.tf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What changed
The second half of #1443.
ragnarok(130), the range's attack VM ongandalf, is now made bytofu/, and the script that made it now only finishes it and proves it. ADR-0116 records the decision and amends ADR-0076.Built on #1463 (eden). This branch contains #1463's commit, because both change the guest module. Merge #1463 first; this diff then shrinks to its own commit.
tofu/providers.tf:provider "proxmox" { alias = "gandalf" }, readingvar.gandalf_endpointandvar.gandalf_api_token. Those come from two newTF_VAR_gandalf_*lines inphoenix.env, so every existing runbook step that sourcesphoenix.envbefore a plan still works. TLS is verified.tofu/modules/guest: a newextra_networksoption adds NICs afternet0, each with a static address, no gateway and the firewall flag off.pool_idbecomes optional.tofu/guests.tf:ragnarokasmodule.range_guestwith thegandalfprovider:range, 4 cores and 8 GiB;net0onvmbr0with DHCP and its MAC pinned;net1onvmbr1at172.30.30.10/24with no gateway;kaliwithphoenix's key;gandalf;tpl-kali.scripts/make-attack-vm.sh→scripts/finish-attack-vm.sh: it no longer clones or configures, and refuses to run unless 130 exists. It:/vms/130grant;net1's address;99-no-forward.confand runs §8's third check over SSH, against the host key read through the guest agent.docs/adr/0116-…md, with a back-link note on ADR-0076, and README's count goes from 115 to 116 ADRs;build-the-playground.md§5 is rewritten: grants, the twophoenix.envlines, plan, apply, then the script, and the once-only destroy of the script-built guest;provision-lab-guests.md§1 says where the two lines come from;architecture.md,build-the-lab-templates.md,scripts/README.md,packer/kali.pkr.hclandtofu/README.md.Why
Item 18 of the 2026-11-11 audit, with the owner's calls on #1443:
gandalfover SSH, a second credential path for one file. The owner chose to keep it in the script.One change from what I recorded on #1443: the two
TF_VAR_gandalf_*lines live inphoenix.env, notgandalf.env. Ingandalf.env, every existingtofustep in six runbooks would have to source both files, or it would stop at a prompt for the gandalf variables. The cost is the gandalf token in two mode-600 files onphoenix. ADR-0116 records this.Refs #1443
Blast radius
ragnarok: destroyed once and remade from the pipeline. The range is disposable (ADR-0017). An import would have planned a replace anyway.Every plan now needs
gandalfup and the twophoenix.envlines present, even for a Saruman-only change. That is the accepted cost of one state. The alternative is-targetwithgandalfdown.The range's isolation: if the module wrote
net1wrongly, the script's read-back now fails on the address or a gateway before anything else runs. I tested that check withjqagainst a correct config and against one with a gateway.Merging changes no host. Nothing happens until someone runs it on
phoenix.No change to network segmentation or firewall rules
No new port published to a VLAN that could not already reach the service
No credential added outside
secrets/*.sops.yaml. The gandalf token, already onphoenixingandalf.env, gets a second copy inphoenix.env, which is outside the repository as ADR-0074 decides.Verification
Every
.tffile undertofu/parses with python-hcl2.tofu fmt -checkandtofu validatewere not run here: this session has no tofu and no Docker. CI's Lint job runs both.shellcheck passes on
finish-attack-vm.sh.python3 scripts/check_docs.py: OK, including the new back-link check and the 116 count.--self-testpasses../scripts/lint.sh: passed, 7 skipped (packer ×2, tofu ×3, ansible-lint and the editorconfig-checker container).ecwas run directly on the changed files and passed.check-tracked-artefacts.sh: OK.The real proof is on
phoenixandgandalf:plan -target='module.range_guest["ragnarok"]'adds one guest and nothing else;finish-attack-vm.shthen passes;planchanges nothing onSaruman.make validatewas not run in full: no Docker here. lint,check_docsand the artefact check above were.Deployed to the lab and confirmed working: not yet.
build-the-playground.md§5 is the deploy.Docs updated (
docs/, service README, ordocs/roadmap.md)Generated by Claude Code