diff --git a/Containerfile b/Containerfile index fdf6871..1a9f73e 100644 --- a/Containerfile +++ b/Containerfile @@ -51,6 +51,11 @@ RUN groupadd -g ${GROUP_ID} marvin || true && \ RUN printf "marvin:100000:65536\n" > /etc/subuid && \ printf "marvin:100000:65536\n" > /etc/subgid +# NOTE: don't bake /etc/hostname or /etc/hosts here — podman regenerates +# both when boco creates the build container, so they never reach the +# qcow2. The entrypoint sets the hostname live; node identity comes from +# TURNSTONE_NODE_ID. + # SSH access for marvin (pubkey generated by scripts/build.sh) COPY configs/ssh/authorized_keys /tmp/authorized_keys RUN install -o ${USER_ID} -g ${GROUP_ID} -m 700 -d /home/marvin/.ssh && \ diff --git a/start-turnstone.sh b/start-turnstone.sh index 2eaa727..1b3c069 100755 --- a/start-turnstone.sh +++ b/start-turnstone.sh @@ -6,6 +6,12 @@ set -euo pipefail export HOME=/home/marvin +# The boco kernel boots with hostname "(none)" (no systemd to read +# /etc/hostname) — set it live so turnstone node IDs and service URLs +# are sane (turnstone-vm_xxxx instead of (none)_xxxx). The Arch base +# has no `hostname` binary, so use the syscall via python. +python3 -c 'import socket; socket.sethostname("turnstone-vm")' || true + # sshd: host keys baked per-boot, ops channel on guest port 22 ssh-keygen -A mkdir -p /run/sshd @@ -50,6 +56,17 @@ setpriv --reuid=marvin --regid=marvin --init-groups \ /usr/local/bin/turnstone-console --host 0.0.0.0 --port 8090 & echo "start-turnstone: console starting on :8090" -# NOTE: turnstone reads $HOME/.config/turnstone/config.toml as marvin +# NOTE: turnstone reads $HOME/.config/turnstone/config.toml as marvin. +# +# Stable node identity: node_id defaults to {hostname}_{random-per-boot}, +# so every boot inserts a NEW services row and the console probes stale +# ones (409 node_id mismatch → "unreachable"). Pin both: +# - TURNSTONE_NODE_ID: one registry row, upserted every boot +# - TURNSTONE_ADVERTISE_URL: loopback — the console's collector probes +# the registered URL, and the guest's slirp IPv6 (fec0::/fe80::) +# lands in the SSRF guard's never-allowed lane, which got the node +# refused outright. +export TURNSTONE_NODE_ID="${TURNSTONE_NODE_ID:-turnstone-vm-main}" +export TURNSTONE_ADVERTISE_URL="${TURNSTONE_ADVERTISE_URL:-http://127.0.0.1:8080}" exec setpriv --reuid=marvin --regid=marvin --init-groups \ /usr/local/bin/turnstone-server --host 0.0.0.0 --port 8080