Bootstrap tree for the media box Linux conversion
Autoinstall lays down a thin base (sshd, key, DHCP, Docker CE, /srv) and hands
off to this repo on first boot. Everything interesting stays in git so it is
reviewable and re-runnable, rather than frozen onto a USB nobody can diff.
Stages, all idempotent:
00-preflight asserts hardware/BIOS state, changes nothing. Catches a BIOS
update having silently re-enabled Secure Boot, which would stop
the NVIDIA DKMS module loading on a box with no keyboard.
10-secrets ADD-ONLY seeder for /srv/secrets/stacks.env. Never overwrites an
existing key. Verified against a pre-populated file: existing
values, unrelated keys, the operator tier and existing manifest
lines all survive byte-for-byte; a second run is a no-op.
20-cifs the 8 shares Plex actually uses (Share is excluded, it is not a
library root). \040 escaping, nofail + x-systemd.automount +
_netdev. Managed-block rewrite verified not to duplicate or to
drop the root fstab entry.
30-nvidia nvidia-driver-580 explicitly: 580 is the LAST branch supporting
Pascal, and the -open modules need Turing+. Pins against newer
branches. Not in late-commands because DKMS needs the installed
kernel, not the installer's.
40-shell-mcp builds the native MCP locally for amd64; refuses to finish
unless /sse returns 401 without a token.
50-plex run by hand: PLEX_CLAIM expires in 4 minutes. Refuses to start
against missing mounts and disables autoEmptyTrash, which with
read-write NAS credentials is the most dangerous default here.
shell-mcp was built on arm64 originally. It builds clean on amd64 (whole dep
tree resolves to prebuilt manylinux x86_64 wheels, no compiler needed), but
dependencies are now pinned - the original installed mcp/starlette/uvicorn
unpinned and starlette has since gone 1.x. Port moved to 8103 so NPM host 42
can simply be repointed, and the tool description now says media box rather
than arrsstack.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+118
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# 10-secrets — idempotent, ADD-ONLY seeder for /srv/secrets/stacks.env
|
||||
#
|
||||
# Mirrors the arrsstack convention: one master file, three tiers.
|
||||
# [OPERATOR] secrets for operating the host; never emitted to a container
|
||||
# [VALUES] flat, deduplicated KEY=value
|
||||
# [MANIFEST] "#@stack <name> = VAR VAR" lines consumed by genenv.sh
|
||||
#
|
||||
# GUARANTEES:
|
||||
# * An existing key is NEVER touched. Not its value, not its position,
|
||||
# not its comment. If KEY= exists, this script skips it entirely.
|
||||
# * A missing key is appended with either a generated value (for tokens we
|
||||
# can safely generate) or the literal FILL_ME (for anything a human must
|
||||
# supply). genenv.sh already fails closed on FILL_ME.
|
||||
# * Values are never printed. Only key names and add/skip decisions.
|
||||
# * Running this twice in a row produces zero changes the second time.
|
||||
#
|
||||
# This is a SEPARATE secrets file from arrsstack's. Per the project decision:
|
||||
# no shared or mounted secrets between hosts. Canonical copy is Vaultwarden.
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
MASTER="/srv/secrets/stacks.env"
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
umask 077
|
||||
install -d -m 0700 /srv/secrets
|
||||
|
||||
added=0; skipped=0
|
||||
|
||||
# --- create the skeleton only if the file does not exist at all -------------
|
||||
if [ ! -f "$MASTER" ]; then
|
||||
echo " creating $MASTER from template"
|
||||
install -m 0600 "$REPO_DIR/secrets/stacks.env.example" "$MASTER"
|
||||
fi
|
||||
|
||||
# Ensure the tier markers exist, so insertion has something to anchor to.
|
||||
grep -q '^# ----- \[VALUES\]' "$MASTER" || printf '\n# ----- [VALUES] -----\n' >> "$MASTER"
|
||||
grep -q '^# ----- \[MANIFEST\]' "$MASTER" || printf '\n# ----- [MANIFEST] -----\n' >> "$MASTER"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# add_value <KEY> <generator>
|
||||
# generator: "hex32" -> openssl rand -hex 32
|
||||
# "fill" -> literal FILL_ME (human must supply)
|
||||
# anything else -> used as the literal default value
|
||||
# Inserts immediately BEFORE the [MANIFEST] marker so it lands inside [VALUES].
|
||||
# ---------------------------------------------------------------------------
|
||||
add_value() {
|
||||
local key="$1" gen="$2" val
|
||||
|
||||
if grep -qE "^${key}=" "$MASTER"; then
|
||||
printf ' skip %-32s (already present)\n' "$key"
|
||||
skipped=$((skipped+1))
|
||||
return 0
|
||||
fi
|
||||
|
||||
case "$gen" in
|
||||
hex32) val="$(openssl rand -hex 32)" ;;
|
||||
fill) val="FILL_ME" ;;
|
||||
*) val="$gen" ;;
|
||||
esac
|
||||
|
||||
# Insert before the [MANIFEST] header, preserving everything else byte-for-byte.
|
||||
local tmp; tmp="$(mktemp)"
|
||||
awk -v line="${key}=${val}" '
|
||||
/^# ----- \[MANIFEST\]/ && !done { print line; print ""; done=1 }
|
||||
{ print }
|
||||
' "$MASTER" > "$tmp"
|
||||
install -m 0600 "$tmp" "$MASTER"; rm -f "$tmp"
|
||||
|
||||
printf ' ADD %-32s (%s)\n' "$key" "$([ "$gen" = fill ] && echo 'needs a human' || echo generated)"
|
||||
added=$((added+1))
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# add_manifest <stack> <vars...>
|
||||
# Appends a "#@stack" line only if one does not already exist for that stack.
|
||||
# ---------------------------------------------------------------------------
|
||||
add_manifest() {
|
||||
local stack="$1"; shift
|
||||
if grep -qE "^#@stack[[:space:]]+${stack}[[:space:]]*=" "$MASTER"; then
|
||||
printf ' skip %-32s (manifest present)\n' "#@stack ${stack}"
|
||||
skipped=$((skipped+1))
|
||||
return 0
|
||||
fi
|
||||
printf '#@stack %s = %s\n' "$stack" "$*" >> "$MASTER"
|
||||
printf ' ADD %-32s\n' "#@stack ${stack}"
|
||||
added=$((added+1))
|
||||
}
|
||||
|
||||
echo " seeding $MASTER (add-only)"
|
||||
|
||||
# --- [VALUES] ---------------------------------------------------------------
|
||||
add_value TZ "America/New_York"
|
||||
# Reuses the SAME token value that arrsstack's mediabox-mcp already serves, so
|
||||
# the Claude connector entry does not change when NPM host 42 is repointed.
|
||||
# Left as FILL_ME: copy it across by hand rather than minting a new one.
|
||||
add_value MEDIABOX_MCP_BEARER_TOKEN fill
|
||||
add_value PLEX_ADVERTISE_URL "http://10.0.1.20:32400"
|
||||
|
||||
# --- [MANIFEST] -------------------------------------------------------------
|
||||
add_manifest shell-mcp MEDIABOX_MCP_BEARER_TOKEN TZ
|
||||
add_manifest plex TZ PLEX_ADVERTISE_URL
|
||||
|
||||
chmod 600 "$MASTER"
|
||||
chown root:root "$MASTER"
|
||||
|
||||
echo " result: ${added} added, ${skipped} left untouched"
|
||||
|
||||
# Report unfilled keys by NAME only — never values.
|
||||
if grep -qE '^[A-Za-z_][A-Za-z0-9_]*=FILL_ME$' "$MASTER"; then
|
||||
echo
|
||||
echo " keys still needing a value (fill by hand, then re-run):"
|
||||
grep -E '^[A-Za-z_][A-Za-z0-9_]*=FILL_ME$' "$MASTER" | cut -d= -f1 | sed 's/^/ - /'
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user