feat(dns): party-DNS records generator + online/airgap modes
DNS is now your own party server. tooling/dns-records.sh prints the records (online = service names; airgap = + Mojang/launcher/NeoForge spoofs) in several formats. build-stack --up online|airgap selects the mirror layer and prints the matching records. dnsmasq moved to an optional override (docker-compose.dnsmasq.yml). Default domain switched to .lan (.local is hijacked by mDNS on unicast DNS). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,11 @@ prep() {
|
||||
echo " downloaded $(basename "$ai_url")"
|
||||
fi
|
||||
|
||||
if [ "${ENABLE_MDNS:-false}" = true ]; then
|
||||
log "build avahi mDNS image (cache it for offline use)"
|
||||
"${COMPOSE[@]}" -f docker-compose.yml -f docker-compose.avahi.yml build avahi
|
||||
fi
|
||||
|
||||
log "pre-bake server volume (TYPE=NEOFORGE install, one-shot, online)"
|
||||
# Bring minecraft up once so itzg installs NeoForge + mods into the volume,
|
||||
# then stop it. The party then runs offline against the baked volume.
|
||||
@@ -90,23 +95,29 @@ prep() {
|
||||
warn "watch logs until 'Done' then re-run with --up; or automate a readiness probe"
|
||||
}
|
||||
|
||||
# ---- offline bring-up ------------------------------------------------------
|
||||
# Modes select which optional layers come up (override files):
|
||||
# core base only (dnsmasq, drasl, minecraft, mc-backup, caddy auth+packwiz)
|
||||
# static + apex landing page + launcher downloads
|
||||
# mirror + full air-gap upstream mirror (tls internal on :443)
|
||||
# full static + mirror (default)
|
||||
# ---- bring-up --------------------------------------------------------------
|
||||
# Modes (DNS is YOUR party server — see tooling/dns-records.sh for the records):
|
||||
# online base + landing/launcher; internet present, no mirror
|
||||
# airgap online + full upstream mirror (tls internal :443) for no-internet
|
||||
# core base only (no landing, no mirror) — debugging
|
||||
up() {
|
||||
local mode="${1:-full}"
|
||||
local mode="${1:-airgap}"
|
||||
local -a files=(-f docker-compose.yml)
|
||||
case "$mode" in
|
||||
core) ;;
|
||||
static) files+=(-f docker-compose.static.yml) ;;
|
||||
mirror) files+=(-f docker-compose.mirror.yml) ;;
|
||||
full) files+=(-f docker-compose.static.yml -f docker-compose.mirror.yml) ;;
|
||||
*) die "unknown up mode: '$mode' (use core|static|mirror|full)" ;;
|
||||
online) files+=(-f docker-compose.static.yml) ;;
|
||||
airgap) files+=(-f docker-compose.static.yml -f docker-compose.mirror.yml) ;;
|
||||
*) die "unknown up mode: '$mode' (use online|airgap|core)" ;;
|
||||
esac
|
||||
log "PHASE up (mode=$mode)"
|
||||
# Optional mDNS (.local) responder — see ENABLE_MDNS in .env.
|
||||
if [ "${ENABLE_MDNS:-false}" = true ]; then
|
||||
files+=(-f docker-compose.avahi.yml)
|
||||
case "$BASE_DOMAIN" in
|
||||
*.local) ;;
|
||||
*) warn "ENABLE_MDNS=true but BASE_DOMAIN=$BASE_DOMAIN is not *.local — mDNS won't resolve it" ;;
|
||||
esac
|
||||
fi
|
||||
log "PHASE up (mode=$mode${ENABLE_MDNS:+, mdns=$ENABLE_MDNS})"
|
||||
have_script render-config.sh
|
||||
tooling/render-config.sh # cheap; ensures configs match current .env
|
||||
|
||||
@@ -114,7 +125,7 @@ up() {
|
||||
# Pre-create it (empty) so Docker doesn't make a directory at that path; the
|
||||
# real cert is exported below once Caddy's local CA exists.
|
||||
case "$mode" in
|
||||
static|full) [ -e caddy/caddy-root-ca.crt ] || : > caddy/caddy-root-ca.crt ;;
|
||||
online|airgap) [ -e caddy/caddy-root-ca.crt ] || : > caddy/caddy-root-ca.crt ;;
|
||||
esac
|
||||
|
||||
"${COMPOSE[@]}" "${files[@]}" up -d
|
||||
@@ -122,7 +133,7 @@ up() {
|
||||
# Mirror/full provision Caddy's local CA — export it so guests can trust the
|
||||
# HTTPS asset mirror (served at http://${BASE_DOMAIN}/ca.crt in static/full).
|
||||
case "$mode" in
|
||||
mirror|full)
|
||||
airgap)
|
||||
local i
|
||||
for i in 1 2 3 4 5; do
|
||||
docker exec caddy cat /data/caddy/pki/authorities/local/root.crt \
|
||||
@@ -136,26 +147,28 @@ up() {
|
||||
esac
|
||||
|
||||
log "stack up (mode=$mode). apex: http://${BASE_DOMAIN} auth: http://auth.${BASE_DOMAIN}"
|
||||
echo
|
||||
log "Add these to your party DNS (mode=$mode):"
|
||||
[ -x tooling/dns-records.sh ] && tooling/dns-records.sh "$mode" | sed -n '/## Plain/,/^$/p'
|
||||
}
|
||||
|
||||
# ---- dispatch --------------------------------------------------------------
|
||||
preflight
|
||||
case "${1:-}" in
|
||||
--prep) prep ;;
|
||||
--up) up "${2:-full}" ;;
|
||||
--up) up "${2:-airgap}" ;;
|
||||
"") cat <<EOF
|
||||
|
||||
Nothing run. Choose a phase:
|
||||
$0 --prep # ONLINE: render, build landing, mirror everything, pre-bake server
|
||||
$0 --up [mode] # bring the stack up; mode = core|static|mirror|full (default full)
|
||||
# core = no static site, no mirror
|
||||
# static = + apex landing + launcher downloads
|
||||
# mirror = + full air-gap upstream mirror (:443)
|
||||
# full = static + mirror
|
||||
$0 --prep # ONLINE, once: render, build landing, mirror, fetch, pre-bake
|
||||
$0 --up [mode] # bring the stack up; mode = online|airgap|core (default airgap)
|
||||
# online = landing + launcher; internet present, no mirror
|
||||
# airgap = online + full upstream mirror (:443), no internet
|
||||
# core = base only (debugging)
|
||||
|
||||
Sub-scripts expected in tooling/: render-config.sh, mirror-mods.sh,
|
||||
mirror-airgap.sh, fetch-launcher.sh (built in their own commits — see plan/12).
|
||||
DNS: configure your party DNS with the records from tooling/dns-records.sh [mode].
|
||||
mDNS (.local) alternative: set ENABLE_MDNS=true + BASE_DOMAIN=*.local in .env.
|
||||
EOF
|
||||
;;
|
||||
*) die "unknown arg: $1 (use --prep or --up [core|static|mirror|full])" ;;
|
||||
*) die "unknown arg: $1 (use --prep or --up [online|airgap|core])" ;;
|
||||
esac
|
||||
|
||||
72
tooling/dns-records.sh
Executable file
72
tooling/dns-records.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# dns-records.sh — print the DNS records to configure on YOUR party DNS server.
|
||||
#
|
||||
# Usage: tooling/dns-records.sh [online|airgap] (default: airgap)
|
||||
# online service names only — your DNS forwards everything else to the internet
|
||||
# airgap service names + the spoofed upstream hosts (Mojang/launcher/NeoForge),
|
||||
# so guest laptops fetch game files from the LAN mirror with no internet
|
||||
#
|
||||
# All names point to HOST_LAN_IP. Reads BASE_DOMAIN / HOST_LAN_IP from .env.
|
||||
# Prints several formats; use whichever your DNS UI wants.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
[ -f .env ] && { set -a; . ./.env; set +a; }
|
||||
: "${BASE_DOMAIN:?BASE_DOMAIN unset (set in .env)}"
|
||||
: "${HOST_LAN_IP:?HOST_LAN_IP unset (set in .env)}"
|
||||
|
||||
mode="${1:-airgap}"
|
||||
|
||||
# Stack service names (always).
|
||||
services=(
|
||||
"${BASE_DOMAIN}"
|
||||
"auth.${BASE_DOMAIN}"
|
||||
"packwiz.${BASE_DOMAIN}"
|
||||
"mc.${BASE_DOMAIN}"
|
||||
)
|
||||
|
||||
# Upstream hosts spoofed onto the LAN mirror (AIRGAP only). Keep in sync with
|
||||
# caddy/conf.d/20-mirror.caddy and docker-compose.mirror.yml.
|
||||
spoof=(
|
||||
"meta.prismlauncher.org"
|
||||
"piston-meta.mojang.com"
|
||||
"piston-data.mojang.com"
|
||||
"libraries.minecraft.net"
|
||||
"maven.neoforged.net"
|
||||
"resources.download.minecraft.net"
|
||||
)
|
||||
|
||||
names=("${services[@]}")
|
||||
case "$mode" in
|
||||
online) ;;
|
||||
airgap) names+=("${spoof[@]}") ;;
|
||||
*) echo "usage: $0 [online|airgap]" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo "# DNS records for mode=${mode} -> ${HOST_LAN_IP}"
|
||||
echo "# Configure these on your party DNS server (all A records to the host)."
|
||||
echo
|
||||
|
||||
echo "## Plain (name -> ip)"
|
||||
for n in "${names[@]}"; do printf '%-36s %s\n' "$n" "$HOST_LAN_IP"; done
|
||||
echo
|
||||
|
||||
echo "## hosts file (/etc/hosts style)"
|
||||
for n in "${names[@]}"; do printf '%s %s\n' "$HOST_LAN_IP" "$n"; done
|
||||
echo
|
||||
|
||||
echo "## BIND zone (A records)"
|
||||
for n in "${names[@]}"; do printf '%-36s IN A %s\n' "${n}." "$HOST_LAN_IP"; done
|
||||
echo
|
||||
|
||||
echo "## dnsmasq (address=)"
|
||||
echo "address=/${BASE_DOMAIN}/${HOST_LAN_IP} # wildcard covers all *.${BASE_DOMAIN}"
|
||||
if [ "$mode" = airgap ]; then
|
||||
for n in "${spoof[@]}"; do printf 'address=/%s/%s\n' "$n" "$HOST_LAN_IP"; done
|
||||
fi
|
||||
echo
|
||||
|
||||
echo "## Minecraft (no port needed)"
|
||||
echo "# Clients connect to mc.${BASE_DOMAIN} (defaults to :25565)."
|
||||
echo "# Optional SRV if your DNS supports it:"
|
||||
echo "_minecraft._tcp.mc.${BASE_DOMAIN}. IN SRV 0 0 25565 mc.${BASE_DOMAIN}."
|
||||
Reference in New Issue
Block a user