Add ntp time source and browser policy
This commit is contained in:
@@ -152,6 +152,13 @@ instead of working around it.
|
|||||||
build; the supplicant config carries both options.
|
build; the supplicant config carries both options.
|
||||||
- erofs module availability on the target RHEL kernel (fallback:
|
- erofs module availability on the target RHEL kernel (fallback:
|
||||||
journal-less ext4 in `extract-artifacts.sh`).
|
journal-less ext4 in `extract-artifacts.sh`).
|
||||||
|
- NTP via DHCP option 42 → chrony (no public-pool egress): confirm on a booted
|
||||||
|
host `chronyc sources` lists the DHCP-handed lab server (not `*.pool.ntp.org`).
|
||||||
|
Assumes (a) NM exports the lease's NTP servers to the dispatcher as
|
||||||
|
`$DHCP4_NTP_SERVERS`, and (b) RHEL 9 `/etc/chrony.conf` honours
|
||||||
|
`sourcedir /run/chrony-dhcp` (the provisioner appends it if absent). If sources
|
||||||
|
are empty, the dispatcher didn't fire or the env var name differs — check
|
||||||
|
`journalctl -u NetworkManager` and `ls /run/chrony-dhcp/`.
|
||||||
- `fleet` Plymouth theme: confirm (a) the script plugin package actually
|
- `fleet` Plymouth theme: confirm (a) the script plugin package actually
|
||||||
installed (`/usr/lib64/plymouth/script.so` — else the build fell back to
|
installed (`/usr/lib64/plymouth/script.so` — else the build fell back to
|
||||||
`spinner`, logged as a WARNING), (b) the scripted theme + a font got bundled
|
`spinner`, logged as a WARNING), (b) the scripted theme + a font got bundled
|
||||||
|
|||||||
+15
-2
@@ -262,6 +262,16 @@ build {
|
|||||||
# secure mode, reserving the physical NIC for wpa_supplicant.
|
# secure mode, reserving the physical NIC for wpa_supplicant.
|
||||||
"chmod 0755 /usr/lib/dracut/modules.d/95dot1x-macsec/*.sh",
|
"chmod 0755 /usr/lib/dracut/modules.d/95dot1x-macsec/*.sh",
|
||||||
|
|
||||||
|
# ---- NTP from DHCP (option 42) -> chrony, no public-pool egress --------
|
||||||
|
# NM dispatcher writes DHCP-provided NTP servers into chrony's sourcedir
|
||||||
|
# (replaces the removed dhclient.d/chrony.sh bridge). Strip the baked public
|
||||||
|
# pool so the host syncs ONLY from the lab NTP server handed via DHCP — the
|
||||||
|
# workstation never reaches *.pool.ntp.org.
|
||||||
|
"install -D -m0755 /tmp/etc-stage/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp /etc/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp",
|
||||||
|
"restorecon /etc/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp",
|
||||||
|
"sed -i -E 's/^[[:space:]]*(pool|server)[[:space:]]/#&/' /etc/chrony.conf",
|
||||||
|
"grep -q '^sourcedir /run/chrony-dhcp' /etc/chrony.conf || echo 'sourcedir /run/chrony-dhcp' >> /etc/chrony.conf",
|
||||||
|
|
||||||
# Inject the fleet NIC drivers into the dracut config
|
# Inject the fleet NIC drivers into the dracut config
|
||||||
"sed -i 's/@NIC_DRIVERS@/${var.nic_drivers}/' /etc/dracut.conf.d/90-diskless.conf",
|
"sed -i 's/@NIC_DRIVERS@/${var.nic_drivers}/' /etc/dracut.conf.d/90-diskless.conf",
|
||||||
|
|
||||||
@@ -341,15 +351,18 @@ build {
|
|||||||
"install -D -m0644 /tmp/kiosk/weston.ini /etc/xdg/weston/weston.ini",
|
"install -D -m0644 /tmp/kiosk/weston.ini /etc/xdg/weston/weston.ini",
|
||||||
# Chromium managed policy: stops the browser phoning home to Google
|
# Chromium managed policy: stops the browser phoning home to Google
|
||||||
# (sign-in, variations, optimization-guide, component/auto update, metrics,
|
# (sign-in, variations, optimization-guide, component/auto update, metrics,
|
||||||
# safe browsing). EPEL chromium reads /etc/chromium/policies/managed/*.json.
|
# safe browsing). The managed-policy dir differs across chromium builds
|
||||||
|
# (/etc/chromium vs /etc/chromium-browser), and a policy in the wrong dir is
|
||||||
|
# silently ignored — so install to BOTH. Verify at runtime via chrome://policy.
|
||||||
# Pairs with the hardening flags in kiosk-session.
|
# Pairs with the hardening flags in kiosk-session.
|
||||||
"install -D -m0644 /tmp/kiosk/chromium-kiosk-policy.json /etc/chromium/policies/managed/10-kiosk-lockdown.json",
|
"install -D -m0644 /tmp/kiosk/chromium-kiosk-policy.json /etc/chromium/policies/managed/10-kiosk-lockdown.json",
|
||||||
|
"install -D -m0644 /tmp/kiosk/chromium-kiosk-policy.json /etc/chromium-browser/policies/managed/10-kiosk-lockdown.json",
|
||||||
# URL from the var (weston renderer is set via --use-pixman in the unit).
|
# URL from the var (weston renderer is set via --use-pixman in the unit).
|
||||||
"install -d -m0755 /etc/kiosk",
|
"install -d -m0755 /etc/kiosk",
|
||||||
"printf 'KIOSK_URL=%s\\n' \"$KIOSK_URL\" > /etc/kiosk/kiosk.env",
|
"printf 'KIOSK_URL=%s\\n' \"$KIOSK_URL\" > /etc/kiosk/kiosk.env",
|
||||||
"chmod 0644 /etc/kiosk/kiosk.env",
|
"chmod 0644 /etc/kiosk/kiosk.env",
|
||||||
# Label the new files so they survive into the enforcing erofs.
|
# Label the new files so they survive into the enforcing erofs.
|
||||||
"restorecon -R /usr/local/bin/kiosk-session /etc/systemd/system/kiosk.service /etc/xdg/weston /etc/kiosk /etc/chromium",
|
"restorecon -R /usr/local/bin/kiosk-session /etc/systemd/system/kiosk.service /etc/xdg/weston /etc/kiosk /etc/chromium /etc/chromium-browser",
|
||||||
# Boot graphical and start the kiosk (replaces the tty1 getty).
|
# Boot graphical and start the kiosk (replaces the tty1 getty).
|
||||||
"systemctl enable kiosk.service",
|
"systemctl enable kiosk.service",
|
||||||
"systemctl set-default graphical.target",
|
"systemctl set-default graphical.target",
|
||||||
|
|||||||
+25
-8
@@ -155,15 +155,32 @@ Path to getting here (don't re-discover):
|
|||||||
avc`; the kiosk user is unconfined so likely none — but do NOT set permissive
|
avc`; the kiosk user is unconfined so likely none — but do NOT set permissive
|
||||||
if they appear; add a policy module). Admin SSH (the key user) still works for
|
if they appear; add a policy module). Admin SSH (the key user) still works for
|
||||||
debug even if the kiosk fails (it lands at graphical.target either way).
|
debug even if the kiosk fails (it lands at graphical.target either way).
|
||||||
- **Phone-home lockdown (done 2026-06-17):** Chromium was dialing Google
|
- **Phone-home lockdown (done 2026-06-17/18):** Chromium was dialing Google
|
||||||
(accounts/variations/optimization-guide/component+auto update/safe browsing —
|
(accounts/variations/optimization-guide/component+auto update/safe browsing —
|
||||||
seen in dnsmasq logs). Stopped via a managed policy
|
seen in dnsmasq logs). Two layers:
|
||||||
(`files/kiosk/chromium-kiosk-policy.json` → `/etc/chromium/policies/managed/`)
|
- **Flags** in `kiosk-session` (`--disable-background-networking`,
|
||||||
plus hardening flags in `kiosk-session` (`--disable-background-networking`,
|
`--disable-component-update`, `--disable-features=OptimizationHints,...`) —
|
||||||
`--disable-component-update`, `--disable-features=OptimizationHints,...`, etc.).
|
confirmed killing variations/optimization-guide/update queries.
|
||||||
Verify with the dnsmasq query log after a rebuild — only `boot.fleet.lab`
|
- **Managed policy** `files/kiosk/chromium-kiosk-policy.json` for the
|
||||||
(kiosk page) + chrony NTP should remain. NTP is the OS (chrony), not Chromium;
|
policy-governed bits (sign-in `accounts.google.com`, sync). FIX 2026-06-18:
|
||||||
optionally point it at the serving host to fully contain egress.
|
the managed-policy dir differs by build, so it's installed to BOTH
|
||||||
|
`/etc/chromium/policies/managed/` AND `/etc/chromium-browser/policies/managed/`
|
||||||
|
(a policy in the wrong dir is silently ignored — the reason
|
||||||
|
`accounts.google.com`/`android.clients.google.com` persisted). Verify via
|
||||||
|
`chrome://policy` (Status: OK per entry).
|
||||||
|
- **Serving-side sinkhole** (defense-in-depth, `serving/docker-compose.yml`
|
||||||
|
dnsmasq `--address=/google.com|googleapis.com|gstatic.com/0.0.0.0`): the
|
||||||
|
kiosk is local-only, so block egress at the resolver. Stops the *leak*; the
|
||||||
|
query still logs (answered 0.0.0.0, not forwarded) — eliminating the query
|
||||||
|
is the browser policy's job. Production: do this at the boot-LAN firewall.
|
||||||
|
- **NTP egress closed (done 2026-06-18):** the `*.pool.ntp.org` lookups were
|
||||||
|
the OS (chrony), not Chromium. Now: a `chrony` NTP server in the serving
|
||||||
|
stack (`serving/chrony/`), dnsmasq hands it via **DHCP option 42**, and the
|
||||||
|
image's NM dispatcher (`files/etc/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp`)
|
||||||
|
feeds it into chrony's `sourcedir /run/chrony-dhcp`; the baked public pool is
|
||||||
|
stripped from `/etc/chrony.conf`. Result: the workstation syncs time only
|
||||||
|
from the lab server — no external NTP egress. BENCH-VERIFY: on a booted host
|
||||||
|
`chronyc sources` shows the lab server (the DHCP one), not a public pool.
|
||||||
- **Image size:** Chromium+Mesa add hundreds of MB to the *uncompressed* erofs —
|
- **Image size:** Chromium+Mesa add hundreds of MB to the *uncompressed* erofs —
|
||||||
feeds the "shrink the rootfs" concern (and #7); reconsider whether a lighter
|
feeds the "shrink the rootfs" concern (and #7); reconsider whether a lighter
|
||||||
browser/render path is viable.
|
browser/render path is viable.
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# NetworkManager dispatcher: feed DHCP option 42 (NTP servers) into chrony.
|
||||||
|
#
|
||||||
|
# Replaces the old dhclient.d/chrony.sh bridge — we use NetworkManager's internal
|
||||||
|
# DHCP now (the network-manager dracut port), and NM exports the lease's NTP
|
||||||
|
# servers to dispatcher scripts as $DHCP4_NTP_SERVERS. chrony.conf carries
|
||||||
|
# `sourcedir /run/chrony-dhcp`, so writing <iface>.sources there and reloading
|
||||||
|
# makes chronyd use the DHCP-provided server. The image's baked public pool is
|
||||||
|
# stripped (see the provisioner), so the host syncs ONLY from the lab NTP server
|
||||||
|
# — no time egress from the workstation.
|
||||||
|
#
|
||||||
|
# Installed 0755 root:root at /etc/NetworkManager/dispatcher.d/. Lenient on
|
||||||
|
# purpose: a hiccup here must never break networking.
|
||||||
|
|
||||||
|
interface="$1"
|
||||||
|
action="$2"
|
||||||
|
|
||||||
|
case "$action" in
|
||||||
|
up|dhcp4-change|reapply) ;;
|
||||||
|
*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
dir=/run/chrony-dhcp
|
||||||
|
file="$dir/${interface}.sources"
|
||||||
|
mkdir -p "$dir"
|
||||||
|
|
||||||
|
if [ -n "${DHCP4_NTP_SERVERS:-}" ]; then
|
||||||
|
: > "$file"
|
||||||
|
for s in $DHCP4_NTP_SERVERS; do
|
||||||
|
printf 'server %s iburst\n' "$s" >> "$file"
|
||||||
|
done
|
||||||
|
# Pick up the new sources without restarting chronyd.
|
||||||
|
chronyc reload sources >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
"DeviceMetricsReportingEnabled": false,
|
"DeviceMetricsReportingEnabled": false,
|
||||||
|
|
||||||
"BrowserSignin": 0,
|
"BrowserSignin": 0,
|
||||||
|
"SigninAllowed": false,
|
||||||
"SyncDisabled": true,
|
"SyncDisabled": true,
|
||||||
|
|
||||||
"SafeBrowsingProtectionLevel": 0,
|
"SafeBrowsingProtectionLevel": 0,
|
||||||
|
|||||||
Executable
+79
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# preview-plymouth.sh — fast local preview of the 'fleet' boot splash WITHOUT
|
||||||
|
# building/booting the full diskless image. The splash is just plymouthd + the
|
||||||
|
# theme + status tokens, so it runs on any Linux box with plymouth installed
|
||||||
|
# (root), driven by the same `plymouth update --status=` tokens the dracut hooks
|
||||||
|
# emit (dot1x-macsec-up.sh / nvmf-connect.sh).
|
||||||
|
#
|
||||||
|
# sudo bash scripts/preview-plymouth.sh [secure|test]
|
||||||
|
#
|
||||||
|
# Seeing it:
|
||||||
|
# - Windowed: needs the X11 renderer (Debian/Ubuntu: `apt install plymouth-x11`,
|
||||||
|
# run inside an X session). Fedora/RHEL often don't package it.
|
||||||
|
# - No X11 renderer: switch to a spare VT (Ctrl+Alt+F3), run there, watch,
|
||||||
|
# Ctrl+Alt+F1 back — plymouthd grabs DRM on that VT.
|
||||||
|
# - Headless/CI screenshot: Xvfb + x11 renderer + a grab tool.
|
||||||
|
#
|
||||||
|
# Iterate: edit files/plymouth/themes/fleet/fleet.script, re-run this. Seconds,
|
||||||
|
# not a 30-min image build. (On an already-booted diskless host the root overlay
|
||||||
|
# is writable, so you can scp the theme to /usr/share/plymouth/themes/fleet and
|
||||||
|
# run this there for a real distro/font/plugin stack.)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
[ "$(id -u)" -eq 0 ] || { echo "run as root (plymouthd needs it)" >&2; exit 1; }
|
||||||
|
|
||||||
|
mode=${1:-secure}
|
||||||
|
theme_src=${THEME_SRC:-files/plymouth/themes/fleet}
|
||||||
|
dest=/usr/share/plymouth/themes/fleet
|
||||||
|
|
||||||
|
command -v plymouthd >/dev/null 2>&1 || {
|
||||||
|
echo "ERROR: plymouthd not found — install plymouth, plymouth-scripts," >&2
|
||||||
|
echo " plymouth-plugin-label, and the script plugin first." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
[ -f "$theme_src/fleet.script" ] || {
|
||||||
|
echo "ERROR: $theme_src/fleet.script not found (run from the repo root, or set THEME_SRC)." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "==> installing theme: $theme_src -> $dest"
|
||||||
|
mkdir -p "$dest"
|
||||||
|
cp -a "$theme_src"/. "$dest"/
|
||||||
|
plymouth-set-default-theme fleet 2>/dev/null || \
|
||||||
|
echo "WARNING: plymouth-set-default-theme failed (plymouth-scripts missing?) — continuing" >&2
|
||||||
|
|
||||||
|
if [ ! -e /usr/lib64/plymouth/script.so ] && [ ! -e /usr/lib/plymouth/script.so ]; then
|
||||||
|
echo "WARNING: plymouth script plugin (script.so) not found — the theme will not render." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> starting plymouthd (debug log: /tmp/plymouth-preview.log)"
|
||||||
|
plymouth quit 2>/dev/null || true
|
||||||
|
plymouthd --no-daemon --debug --debug-file=/tmp/plymouth-preview.log &
|
||||||
|
ply_pid=$!
|
||||||
|
trap 'plymouth quit 2>/dev/null || true; kill "$ply_pid" 2>/dev/null || true' EXIT
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
plymouth show-splash
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
emit() {
|
||||||
|
echo " --status=$1"
|
||||||
|
plymouth update --status="$1" || true
|
||||||
|
sleep "${2:-1.2}"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "==> driving the '$mode' handshake sequence"
|
||||||
|
if [ "$mode" = test ]; then
|
||||||
|
emit mode-test 0.6
|
||||||
|
emit net-active; emit net-done
|
||||||
|
emit nvme-active; emit nvme-done 1.5
|
||||||
|
else
|
||||||
|
emit tpm-active; emit tpm-done
|
||||||
|
emit dot1x-active 1.6; emit dot1x-done
|
||||||
|
emit macsec-active; emit macsec-done
|
||||||
|
emit net-active 1.6; emit net-done
|
||||||
|
emit nvme-active 1.6; emit nvme-done 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> sequence complete; press Enter to quit the splash"
|
||||||
|
read -r _ || true
|
||||||
@@ -61,6 +61,9 @@ curl -sI http://${SERVER_IP}/fleet-${IMAGE_VERSION}.efi | head -1 # 200 OK
|
|||||||
# HTTP: kiosk test page is served (what the diskless kiosk loads by default)
|
# HTTP: kiosk test page is served (what the diskless kiosk loads by default)
|
||||||
curl -sI http://${SERVER_IP}/kiosk/ | head -1 # 200 OK
|
curl -sI http://${SERVER_IP}/kiosk/ | head -1 # 200 OK
|
||||||
|
|
||||||
|
# NTP: lab time server answers (diskless hosts get it via DHCP option 42)
|
||||||
|
chronyc -h ${SERVER_IP} tracking 2>/dev/null || ntpdate -q ${SERVER_IP}
|
||||||
|
|
||||||
# NVMe/TCP: target is listening + discoverable
|
# NVMe/TCP: target is listening + discoverable
|
||||||
ss -lntp | grep 4420
|
ss -lntp | grep 4420
|
||||||
sudo nvme discover -t tcp -a ${SERVER_IP} -s 4420 # lists the subsystem
|
sudo nvme discover -t tcp -a ${SERVER_IP} -s 4420 # lists the subsystem
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Minimal chrony NTP server for the lab boot LAN. Diskless workstations get this
|
||||||
|
# host as their time source via DHCP option 42 (dnsmasq), so they never reach the
|
||||||
|
# public NTP pool (no workstation egress). Config is bind-mounted from
|
||||||
|
# chrony.conf in docker-compose.yml. Build our own so the version is pinned.
|
||||||
|
FROM alpine:3.20
|
||||||
|
RUN apk add --no-cache chrony
|
||||||
|
# -d: run in the foreground (don't daemonise) so the container stays up and logs
|
||||||
|
# to stdout for `docker compose logs`.
|
||||||
|
ENTRYPOINT ["chronyd", "-d", "-f", "/etc/chrony/chrony.conf"]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Lab NTP server (serving stack). Syncs from upstream and serves the boot LAN so
|
||||||
|
# diskless workstations get time INTERNALLY (handed out as DHCP option 42 by
|
||||||
|
# dnsmasq) instead of egressing to the public pool. Only this host reaches the
|
||||||
|
# internet for time; the workstations never do.
|
||||||
|
|
||||||
|
# Upstream time source (this serving host has internet; the workstations don't).
|
||||||
|
pool pool.ntp.org iburst
|
||||||
|
|
||||||
|
# Serve the boot LAN. This is an isolated lab/boot network, so allow all; scope
|
||||||
|
# to your subnet (e.g. `allow 192.168.50.0/24`) for anything less trusted.
|
||||||
|
allow all
|
||||||
|
|
||||||
|
# Serve our own clock at a high stratum if upstream is unreachable, so a diskless
|
||||||
|
# boot never blocks waiting for time.
|
||||||
|
local stratum 10
|
||||||
|
|
||||||
|
# Step the clock on large initial offsets (containers can start far off), then
|
||||||
|
# slew. driftfile is best-effort (container fs is ephemeral).
|
||||||
|
makestep 1.0 3
|
||||||
|
driftfile /var/lib/chrony/drift
|
||||||
@@ -63,11 +63,24 @@ services:
|
|||||||
# resolve the per-environment DNS name to this serving host.
|
# resolve the per-environment DNS name to this serving host.
|
||||||
- --address=/${NVME_HOST}/${SERVER_IP}
|
- --address=/${NVME_HOST}/${SERVER_IP}
|
||||||
- --address=/${BOOT_HOST}/${SERVER_IP}
|
- --address=/${BOOT_HOST}/${SERVER_IP}
|
||||||
|
# Kiosk is LOCAL-ONLY (loads boot.fleet.lab), so sinkhole Chromium's
|
||||||
|
# call-home to 0.0.0.0 — telemetry can't leave the lab even if the browser
|
||||||
|
# tries. Defense-in-depth behind the in-image Chromium policy/flags; in
|
||||||
|
# production this belongs in the boot-LAN firewall's egress rules. NOTE: the
|
||||||
|
# client still *emits* the query (you'll see it in this log answered locally,
|
||||||
|
# not "forwarded"); stopping the query itself is the browser policy's job.
|
||||||
|
- --address=/google.com/0.0.0.0
|
||||||
|
- --address=/googleapis.com/0.0.0.0
|
||||||
|
- --address=/gstatic.com/0.0.0.0
|
||||||
# ---- DHCP ----
|
# ---- DHCP ----
|
||||||
- --dhcp-authoritative
|
- --dhcp-authoritative
|
||||||
- --dhcp-range=${DHCP_RANGE_START},${DHCP_RANGE_END},${DHCP_LEASE}
|
- --dhcp-range=${DHCP_RANGE_START},${DHCP_RANGE_END},${DHCP_LEASE}
|
||||||
- --dhcp-option=option:router,${GATEWAY_IP}
|
- --dhcp-option=option:router,${GATEWAY_IP}
|
||||||
- --dhcp-option=option:dns-server,${SERVER_IP}
|
- --dhcp-option=option:dns-server,${SERVER_IP}
|
||||||
|
# NTP server (option 42): point diskless hosts at the lab 'ntp' service
|
||||||
|
# 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.
|
||||||
|
- --dhcp-option=option:ntp-server,${SERVER_IP}
|
||||||
# ---- 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
|
||||||
@@ -96,6 +109,20 @@ services:
|
|||||||
# For HTTPS boot (production), terminate TLS here with the boot-infra CA's
|
# For HTTPS boot (production), terminate TLS here with the boot-infra CA's
|
||||||
# cert and switch the dhcp-boot URL above to https://. Left HTTP for the lab.
|
# cert and switch the dhcp-boot URL above to https://. Left HTTP for the lab.
|
||||||
|
|
||||||
|
# --- NTP server (serves time to the boot LAN; handed out via DHCP opt 42) ---
|
||||||
|
ntp:
|
||||||
|
build: ./chrony
|
||||||
|
network_mode: host # serve NTP on udp/123 across the boot LAN
|
||||||
|
cap_add:
|
||||||
|
- SYS_TIME # discipline the (host-shared) clock it serves
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./chrony/chrony.conf:/etc/chrony/chrony.conf:ro
|
||||||
|
# NOTE: with host networking + SYS_TIME this chronyd disciplines the Docker
|
||||||
|
# HOST clock (the clock isn't namespaced) — intended: the serving host is the
|
||||||
|
# lab time source. Drop SYS_TIME (and add `chronyd -x`) if the host already
|
||||||
|
# runs its own NTP and you only want this to relay.
|
||||||
|
|
||||||
# --- NVMe/TCP target (serves rootfs.erofs, read-only) ----------------------
|
# --- NVMe/TCP target (serves rootfs.erofs, read-only) ----------------------
|
||||||
nvmeof:
|
nvmeof:
|
||||||
# Prebuilt by CI (.gitea/workflows/spdk-image.yaml) and pulled here — no
|
# Prebuilt by CI (.gitea/workflows/spdk-image.yaml) and pulled here — no
|
||||||
|
|||||||
Reference in New Issue
Block a user