Add fleet hostnaming
This commit is contained in:
@@ -296,6 +296,15 @@ build {
|
|||||||
# ---- Diskless adaptations ------------------------------------------
|
# ---- Diskless adaptations ------------------------------------------
|
||||||
# Per-host machine-id is generated at boot into the overlay
|
# Per-host machine-id is generated at boot into the overlay
|
||||||
"truncate -s 0 /etc/machine-id",
|
"truncate -s 0 /etc/machine-id",
|
||||||
|
# No per-host hostname in the image (image stays fleet-generic): empty
|
||||||
|
# /etc/hostname so the static hostname is unset and NetworkManager sets the
|
||||||
|
# transient hostname from the DHCP lease — DHCP option 12 (the dnsmasq
|
||||||
|
# fleet-namer assigns a creative name per MAC, server-side) or a reverse-DNS
|
||||||
|
# PTR lookup. hostname-mode=default makes that explicit so a stricter global
|
||||||
|
# config can't switch it to 'none' and leave hosts unnamed.
|
||||||
|
"truncate -s 0 /etc/hostname",
|
||||||
|
"install -D -m0644 /tmp/etc-stage/NetworkManager/conf.d/20-hostname.conf /etc/NetworkManager/conf.d/20-hostname.conf",
|
||||||
|
"restorecon /etc/NetworkManager/conf.d/20-hostname.conf",
|
||||||
# Read-only erofs/verity root; runtime state on a tmpfs overlay provided
|
# Read-only erofs/verity root; runtime state on a tmpfs overlay provided
|
||||||
# by systemd.volatile=overlay (set in the cmdline by extract-artifacts.sh).
|
# by systemd.volatile=overlay (set in the cmdline by extract-artifacts.sh).
|
||||||
# RHEL/Rocky dracut has no 'overlayfs'/'rootovl' module.
|
# RHEL/Rocky dracut has no 'overlayfs'/'rootovl' module.
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ flipped back for a secure/production build (consolidated as item #8):
|
|||||||
test target. Production needs CI/HSM signing.
|
test target. Production needs CI/HSM signing.
|
||||||
- `enable_kiosk=false` by default (CI may set true); `enable_stig=true`;
|
- `enable_kiosk=false` by default (CI may set true); `enable_stig=true`;
|
||||||
`boot_splash=true`; NTP via DHCP→serving-host chrony.
|
`boot_splash=true`; NTP via DHCP→serving-host chrony.
|
||||||
|
- **Hostname:** image is hostname-neutral (empty `/etc/hostname`,
|
||||||
|
`hostname-mode=default`); the serving dnsmasq's `fleet-namer` (`--dhcp-script`)
|
||||||
|
assigns a stable creative starship name per MAC (e.g. `nebula-vanguard-07`) via
|
||||||
|
DHCP option 12 + DNS A/PTR. Host picks it up on next DHCP renewal/reboot.
|
||||||
|
|
||||||
What's working/verified vs awaiting bench is per-item below. Cross-cutting
|
What's working/verified vs awaiting bench is per-item below. Cross-cutting
|
||||||
bench-verify checklist lives in `CLAUDE.md` → "Known bench-verify items".
|
bench-verify checklist lives in `CLAUDE.md` → "Known bench-verify items".
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Derive the system hostname from the network at boot.
|
||||||
|
#
|
||||||
|
# The image carries NO per-host hostname (invariant: no per-host state in the
|
||||||
|
# artifact) — /etc/hostname is emptied at build, so the static hostname is unset
|
||||||
|
# and the host comes up as "localhost". With hostname-mode=default, NetworkManager
|
||||||
|
# sets the *transient* hostname once it has a DHCP lease, in this order:
|
||||||
|
# 1. DHCP option 12 (host-name) if the server sends one — the lab dnsmasq's
|
||||||
|
# fleet-namer assigns a creative starship name per MAC, server-side.
|
||||||
|
# 2. otherwise a reverse-DNS (PTR) lookup of the leased address.
|
||||||
|
# This is already NM's default, but we set it explicitly so a stricter
|
||||||
|
# global/STIG config can't silently switch it to "none" and leave hosts unnamed.
|
||||||
|
[main]
|
||||||
|
hostname-mode=default
|
||||||
@@ -3,4 +3,9 @@
|
|||||||
# entirely as --flags from docker-compose.yml.
|
# entirely as --flags from docker-compose.yml.
|
||||||
FROM harbor.bitasylum.org/dockerhub/library/alpine:3.20
|
FROM harbor.bitasylum.org/dockerhub/library/alpine:3.20
|
||||||
RUN apk add --no-cache dnsmasq
|
RUN apk add --no-cache dnsmasq
|
||||||
|
# fleet-namer: --dhcp-script that assigns creative starship hostnames per MAC,
|
||||||
|
# server-side (keeps the workstation image neutral). COPY'd in root-owned, 0755
|
||||||
|
# so dnsmasq will execute it. Uses busybox sha256sum/cut/wc — all in the image.
|
||||||
|
COPY fleet-namer /usr/local/bin/fleet-namer
|
||||||
|
RUN chmod 0755 /usr/local/bin/fleet-namer
|
||||||
ENTRYPOINT ["dnsmasq"]
|
ENTRYPOINT ["dnsmasq"]
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# DHCP reservations for the diskless fleet (dnsmasq --dhcp-hostsfile).
|
||||||
|
#
|
||||||
|
# Two ways entries land here, both SERVER-SIDE (the workstation image carries no
|
||||||
|
# hostname and stays fleet-generic):
|
||||||
|
#
|
||||||
|
# 1. AUTO (default): fleet-namer (the --dhcp-script) assigns a stable, creative
|
||||||
|
# starship-themed name per MAC on first contact (e.g. "nebula-vanguard-07")
|
||||||
|
# and appends a line below, then reloads dnsmasq. Same MAC -> same name
|
||||||
|
# forever. The host picks it up on its next DHCP renewal/reboot.
|
||||||
|
#
|
||||||
|
# 2. MANUAL (override): add your own line to pin a specific host. fleet-namer
|
||||||
|
# skips any MAC already listed, so manual entries always win.
|
||||||
|
#
|
||||||
|
# Format (fields after the MAC are order-independent):
|
||||||
|
# <MAC>,<hostname> # name only; IP from --dhcp-range
|
||||||
|
# <MAC>,<IPv4>,<hostname> # also pin the IP
|
||||||
|
# Names become <hostname>.${BOOT_DOMAIN} in DNS (A + PTR).
|
||||||
|
#
|
||||||
|
# Manual examples (uncomment + edit):
|
||||||
|
# 52:54:00:11:22:01,192.168.50.101,bridge-command
|
||||||
|
# 52:54:00:11:22:02,reactor-control
|
||||||
|
#
|
||||||
|
# --- fleet-namer auto-assigned entries are appended below this line ---
|
||||||
Executable
+45
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# fleet-namer — dnsmasq --dhcp-script that assigns a stable, creative cyber
|
||||||
|
# sci-fi hostname per host, SERVER-SIDE, so the workstation image stays neutral.
|
||||||
|
#
|
||||||
|
# dnsmasq calls this on every lease event with: $1=action(add|old|del)
|
||||||
|
# $2=MAC $3=IP [$4=hostname]. For a MAC we haven't named yet, we derive a name
|
||||||
|
# DETERMINISTICALLY from the MAC (sha256 -> curated wordlists), append a
|
||||||
|
# reservation (MAC,name) to the dhcp-hostsfile, and SIGHUP dnsmasq so it re-reads
|
||||||
|
# it. dnsmasq then hands the name out as DHCP option 12 + registers DNS A/PTR;
|
||||||
|
# NetworkManager on the host sets it as the hostname.
|
||||||
|
#
|
||||||
|
# Same MAC -> same name forever (idempotent). The host picks up the name on its
|
||||||
|
# next DHCP transaction (renewal at ~T1, or reboot) — see the lease-time note in
|
||||||
|
# docker-compose.yml. Manual reservations in the hostsfile always win (we skip a
|
||||||
|
# MAC that's already listed).
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
action="$1"; mac="$2"
|
||||||
|
case "$action" in add|old) ;; *) exit 0 ;; esac
|
||||||
|
[ -n "$mac" ] || exit 0
|
||||||
|
|
||||||
|
HOSTSFILE="${FLEET_HOSTSFILE:-/etc/dnsmasq-hosts}"
|
||||||
|
|
||||||
|
# Already named (auto or manual)? leave it.
|
||||||
|
grep -qi "^$mac," "$HOSTSFILE" 2>/dev/null && exit 0
|
||||||
|
|
||||||
|
# Curated DNS-safe wordlists (single line, single-spaced — indexed with cut).
|
||||||
|
PREFIX="nova nebula quasar pulsar comet vega rigel orion lyra cygnus draco hydra phoenix titan atlas helios aurora zenith vortex ion plasma photon quantum warp aether astral stellar lunar solar cobalt obsidian halcyon"
|
||||||
|
NOUN="cruiser frigate vanguard sentinel ranger voyager pioneer horizon beacon spectre nomad drifter wraith talon raptor falcon meridian apex helm reactor relay array lance aegis ember corsair seeker harbinger paragon warden monolith"
|
||||||
|
np=$(echo "$PREFIX" | wc -w)
|
||||||
|
nn=$(echo "$NOUN" | wc -w)
|
||||||
|
|
||||||
|
# Deterministic indices from the MAC's sha256 (hex chunks mod list length).
|
||||||
|
h=$(printf '%s' "$mac" | sha256sum | cut -c1-12)
|
||||||
|
pi=$(( 0x$(echo "$h" | cut -c1-4) % np + 1 ))
|
||||||
|
ni=$(( 0x$(echo "$h" | cut -c5-8) % nn + 1 ))
|
||||||
|
num=$(( 0x$(echo "$h" | cut -c9-12) % 100 ))
|
||||||
|
name=$(printf '%s-%s-%02d' "$(echo "$PREFIX" | cut -d' ' -f"$pi")" "$(echo "$NOUN" | cut -d' ' -f"$ni")" "$num")
|
||||||
|
|
||||||
|
# Register + reload dnsmasq (re-reads --dhcp-hostsfile on SIGHUP). dnsmasq is
|
||||||
|
# PID 1 in this container; the script runs as root (--user=root in compose).
|
||||||
|
echo "$mac,$name" >> "$HOSTSFILE"
|
||||||
|
kill -HUP 1 2>/dev/null || true
|
||||||
|
echo "fleet-namer: $mac -> $name" >&2
|
||||||
|
exit 0
|
||||||
@@ -43,6 +43,11 @@ services:
|
|||||||
- NET_ADMIN # DHCP raw sockets
|
- NET_ADMIN # DHCP raw sockets
|
||||||
- NET_RAW
|
- NET_RAW
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
# DHCP reservations (MAC -> name [-> IP]); drives DHCP option 12 (hostname)
|
||||||
|
# + DNS A/PTR. Auto-populated by fleet-namer (creative names per MAC); add
|
||||||
|
# manual lines to pin specific hosts. RW so fleet-namer can append.
|
||||||
|
- ./dnsmasq/dhcp-hosts:/etc/dnsmasq-hosts
|
||||||
command:
|
command:
|
||||||
- --keep-in-foreground
|
- --keep-in-foreground
|
||||||
# Log to stderr so `docker compose logs` captures output. Without this,
|
# Log to stderr so `docker compose logs` captures output. Without this,
|
||||||
@@ -84,6 +89,18 @@ services:
|
|||||||
# below so they sync time internally and never reach the public NTP pool.
|
# below so they sync time internally and never reach the public NTP pool.
|
||||||
# The image's NM dispatcher (10-chrony-dhcp-ntp) feeds this into chrony.
|
# The image's NM dispatcher (10-chrony-dhcp-ntp) feeds this into chrony.
|
||||||
- --dhcp-option=option:ntp-server,${SERVER_IP}
|
- --dhcp-option=option:ntp-server,${SERVER_IP}
|
||||||
|
# ---- Per-host names (creative starship hostnames, server-assigned) ----
|
||||||
|
# The workstation image is hostname-neutral; naming is done HERE. fleet-namer
|
||||||
|
# (the --dhcp-script) derives a stable themed name per MAC (e.g.
|
||||||
|
# nebula-vanguard-07), appends it to the hostsfile, and SIGHUPs dnsmasq;
|
||||||
|
# dnsmasq then sends it as DHCP option 12 (-> NetworkManager sets the
|
||||||
|
# hostname) and registers DNS A/PTR under ${BOOT_DOMAIN}. The host picks up
|
||||||
|
# its name on the NEXT DHCP transaction (renewal at ~half lease, or reboot)
|
||||||
|
# — shorten DHCP_LEASE if you want naming to settle faster on first contact.
|
||||||
|
# --user=root so the dhcp-script can write the hostsfile + signal dnsmasq.
|
||||||
|
- --user=root
|
||||||
|
- --dhcp-hostsfile=/etc/dnsmasq-hosts
|
||||||
|
- --dhcp-script=/usr/local/bin/fleet-namer
|
||||||
# ---- UEFI HTTP Boot ----
|
# ---- UEFI HTTP Boot ----
|
||||||
# Firmware sends vendor-class "HTTPClient"; we must echo it back (opt 60)
|
# Firmware sends vendor-class "HTTPClient"; we must echo it back (opt 60)
|
||||||
# and hand over the UKI URL (opt 67). IP in the URL avoids a DNS round-trip
|
# and hand over the UKI URL (opt 67). IP in the URL avoids a DNS round-trip
|
||||||
|
|||||||
Reference in New Issue
Block a user