#!/usr/bin/env bash
# scripts/opnsense-render-config.sh <output-config.xml>
#
# Renders opentofu/templates/opnsense-config.xml.tmpl into a real config.xml
# by substituting {{TOKEN}} markers (this repo's existing clientdocs
# convention, reused here) from required environment variables -- no
# invented defaults except NTP_UPSTREAM_SERVERS/NTP_PREFER_SERVER, which
# default to the SAME public pool OPNsense's own shipped config.xml.sample
# uses (a real, confirmed default, not an invention -- see
# opentofu/templates/README.md). Everything else fails loud if unset.
#
# Feed the result to scripts/opnsense-build-config-iso.sh next.
# Exit: 0 rendered | 1 required token/template missing.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATE="$HERE/../opentofu/templates/opnsense-config.xml.tmpl"
OUT="${1:?usage: opnsense-render-config.sh <output-config.xml>}"
: "${NTP_UPSTREAM_SERVERS:=0.opnsense.pool.ntp.org 1.opnsense.pool.ntp.org 2.opnsense.pool.ntp.org 3.opnsense.pool.ntp.org}"
: "${NTP_PREFER_SERVER:=0.opnsense.pool.ntp.org}"
req() { # req VARNAME -- fail loud if unset, no inferred fallback
local name="$1"
[ -n "${!name:-}" ] || { echo "FAIL: \$$name not set -- see opentofu/templates/README.md"; exit 1; }
}
REQUIRED_VARS=(
OPNSENSE_HOSTNAME DOMAIN ROOT_PASSWORD_HASH
WAN_IF WAN_IPADDR WAN_SUBNET_BITS WAN_GATEWAY
LAN_IF LAN_IPADDR LAN_SUBNET_BITS
MIRROR_SYNC_PROTOCOL MIRROR_UPSTREAM_NET MIRROR_SYNC_PORT
)
for v in "${REQUIRED_VARS[@]}"; do
req "$v"
done
[ -r "$TEMPLATE" ] || { echo "FAIL: template not found: $TEMPLATE"; exit 1; }
route_block() { # route_block <network-var-name> <gateway-var-name> <descr-var-name>
local net="${!1:-}" gw="${!2:-}" descr="${!3:-}"
if [ -z "$net" ]; then
return 0 # no route needed at this site -- emit nothing, not a placeholder
fi
[ -n "$gw" ] || { echo "FAIL: \$$1 is set but \$$2 is not"; exit 1; }
printf ' <route>\n <network>%s</network>\n <gateway>%s</gateway>\n <descr>%s</descr>\n <enabled>1</enabled>\n </route>\n' \
"$net" "$gw" "${descr:-static route}"
}
ROUTE1_BLOCK="$(route_block ROUTE1_NETWORK ROUTE1_GATEWAY ROUTE1_DESCR)"
ROUTE2_BLOCK="$(route_block ROUTE2_NETWORK ROUTE2_GATEWAY ROUTE2_DESCR)"
content="$(cat "$TEMPLATE")"
for v in "${REQUIRED_VARS[@]}" NTP_UPSTREAM_SERVERS NTP_PREFER_SERVER; do
content="${content//"{{$v}}"/${!v}}"
done
content="${content//"{{ROUTE1_BLOCK}}"/$ROUTE1_BLOCK}"
content="${content//"{{ROUTE2_BLOCK}}"/$ROUTE2_BLOCK}"
if grep -qE '\{\{[A-Z0-9_]+\}\}' <<<"$content"; then
echo "FAIL: unresolved {{TOKEN}} remains in rendered output -- template/script token lists are out of sync:"
grep -oE '\{\{[A-Z0-9_]+\}\}' <<<"$content" | sort -u
exit 1
fi
printf '%s' "$content" > "$OUT"
echo "PASS: rendered $OUT"