openstack-caracal-dc-dc / opentofu / templates /
..
README.md DOCFIX-149: OPNsense config.xml template + tested renderer 6 hours ago
opnsense-config.xml.tmpl DOCFIX-149: OPNsense config.xml template + tested renderer 6 hours ago
README.md

opentofu/templates/ -- OPNsense config.xml template

opnsense-config.xml.tmpl is the {{TOKEN}}-parameterized OPNsense config.xml for a DC-DC edge (Office1, DC1, or DC2 -- one instantiation per site). Built directly from OPNsense's own real, currently-shipped config.xml.sample (opnsense/core, fetched 2026-07-09 -- see .claude/skills/openstack-cloud-ops/references/opentofu-provider-docs.md for the fetch method) plus the confirmed staticroutes schema (opnsense/core's Route.xml model). This is real design work, not a placeholder -- but several tokens genuinely cannot be filled with a real value yet because the decisions they depend on (D-100/D-101/D-107) are still PROPOSED, or because the value can only be measured on a real boot. Never invent a value for those; leave the token required and unfilled until the real one exists.

Pipeline

opnsense-config.xml.tmpl
  --(scripts/opnsense-render-config.sh, env-var tokens)-->
real config.xml
  --(scripts/opnsense-build-config-iso.sh)-->
config.iso (plain ISO9660, /conf/config.xml)
  --(opentofu/modules/opnsense-edge, config_iso_path variable)-->
attached as a secondary cdrom disk to the domain

Token legend

Token Status Notes
{{OPNSENSE_HOSTNAME}} Design choice, not yet made e.g. opnsense. NOT named HOSTNAME -- that collides with bash's own built-in $HOSTNAME variable (caught by this delivery's own test harness: unset HOSTNAME doesn't actually clear it, since bash repopulates it).
{{DOMAIN}} Real, applies D-106's naming convention e.g. omega.dc1.vr1.cloud.neumatrix.local, following the (still-PROPOSED, but already-settled-as-design-intent) <service>.omega.<dc>.vr1.cloud.neumatrix.local pattern -- same status as DC1's plane CIDRs elsewhere in this repo: carried-forward design intent, not invented.
{{ROOT_PASSWORD_HASH}} MUST be freshly generated, never reused The real config.xml.sample ships a bcrypt hash ($2y$10$...) for a well-known default OPNsense password -- reusing it verbatim would mean every edge shares a public default root password, a real exposure. Generate a fresh one per deployment, e.g. `htpasswd -bnBC 10 "" '' \ tr -d ':\n'(produces a$2y$10$...`-compatible bcrypt hash) -- NOT independently verified against a real OPNsense login this session; confirm login works before relying on it operationally.
{{NTP_UPSTREAM_SERVERS}} / {{NTP_PREFER_SERVER}} Real default supplied (optional to override) Defaults to the exact same public pool OPNsense's own sample ships (N.opnsense.pool.ntp.org) -- a real, confirmed default, not invented. D-107 says the edge "syncs upstream to internet NTP pools," consistent with keeping this default. Override only if a different upstream is decided.
{{WAN_IF}} / {{LAN_IF}} MUST be measured on a real boot The <if> value is the actual vtnetN (or similar) device name FreeBRSD assigns -- confirmed from the real sample that this, not interface declaration order, is what assigns the LAN/WAN role (see opentofu/README.md's "Audit pass" section for the full account of this correction). Boot the domain, run ifconfig, and set these to match opentofu/modules/opnsense-edge's actual lan_network_name/wan_network_name wiring.
{{WAN_IPADDR}} / {{WAN_SUBNET_BITS}} / {{WAN_GATEWAY}} Pending D-100/D-101 The "simulated ISP uplink" network's real addressing isn't assigned yet (NetBox-pending).
{{LAN_IPADDR}} / {{LAN_SUBNET_BITS}} Pending a design decision WHICH plane(s) OPNsense's LAN interface actually serves (metal-admin? provider-public? something else?) is not fully specified in the buildout design as read this session -- confirm before filling this in, don't assume.
{{MIRROR_SYNC_PROTOCOL}} / {{MIRROR_UPSTREAM_NET}} / {{MIRROR_SYNC_PORT}} Pending -- mirror software not chosen D-107 requires the per-DC artifact mirror's own upstream sync as one of exactly two allowed WAN egress paths (the other is NTP), but doesn't specify which mirror software (apt-mirror? aptly? a generic reverse proxy?) -- the port/protocol depend on that choice. Tooling gap register item 3 territory.
{{ROUTE1_NETWORK}}/{{ROUTE1_GATEWAY}}/{{ROUTE1_DESCR}}, {{ROUTE2_*}} Optional, pending DC2 CIDR assignment At most 2 static routes needed per the known topology (a peer-DC replication-plane route at DC1/DC2; an Office1-management route). Leave a slot's *_NETWORK var unset to omit it entirely -- scripts/opnsense-render-config.sh drops an unset slot rather than emitting an empty/invalid <route>. DC2's own CIDRs aren't assigned yet (D-101 open item), so DC1's route to DC2 can't be filled in until then.

Design choices baked into the template (not tokens -- read before assuming these are wrong)

  • Firewall: default-deny WAN egress, explicit NTP + mirror-sync allow rules (D-107's "controlled egress ... narrowly scoped: NTP-only, plus the mirror's upstream sync"). The two default LAN-allow rules are kept exactly as OPNsense's own sample ships them (basic operation depends on them).
  • gateways.gateway_item is new versus the stock sample (which only has an implicit DHCP-derived gateway) -- required because the WAN side here is a static "simulated ISP uplink," not DHCP, and staticroutes.route.gateway must reference a named gateway object (confirmed from the real Route.xml model: gateway is a JsonKeyValueStoreField needing "a valid gateway from the list"). Named WAN_GW -- change consistently in both places if you rename it.
  • unbound/nat/rrd/legacy <filter> sections are left exactly as the real stock sample ships them -- minimal, working defaults, not independently redesigned.

What this delivery does NOT do

Write the actual, final, per-site config.xml files (Office1/DC1/DC2) -- that needs the pending values above filled in with real ones, which in turn needs Stage 0 ratification (D-100/D-101/D-107) and a real boot to measure vtnetN assignment. This delivery is the mechanism (template + a tested renderer), consistent with everything else built this session: parameterized and reusable, not three files with invented IP addresses.