fix(fips,iso): match upstream fips schema + guard ISO against stale binary
1. FIPS daemon config schema drifted: upstream jmcorgan/fips now takes `node.identity.persistent: true` (keys read from config-dir/fips.key) and `transports.udp.bind_addr: "0.0.0.0:PORT"` instead of `identity.key_file/pub_file` + `transports.udp.enabled/port`. The `tor:` transport was dropped entirely; archipelago handles Tor fallback itself. fips.yaml generated by archipelago::fips::config now matches the upstream schema, and archipelago-fips.service stops crashlooping on Activate. Observed on .198: 52 restarts with "data did not match any variant of untagged enum TransportInstances at line 7 column 3". 2. ISO backend-binary capture didn't verify that the captured binary matched the checked-out Cargo.toml version. Today's 14:40 ISO shipped a stale 1.4.0 binary because `core/target/release/archipelago` pre-dated the 1.5.0-alpha bump — the build grabbed it via the first-priority "local release build" path without looking at it. All four capture sources now go through verify_backend_version() which greps the binary for the expected version string; mismatches are skipped so the build falls through to the source-build path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,26 +24,28 @@ use super::{DAEMON_CONFIG_PATH, DAEMON_KEY_PATH, DAEMON_PUB_PATH, DEFAULT_UDP_PO
|
||||
/// IPv6 TUN + DNS on defaults. Static peer list is empty — archipelago
|
||||
/// feeds peers dynamically via federation updates.
|
||||
pub fn render_config_yaml() -> String {
|
||||
// Schema matches upstream jmcorgan/fips as of 2026-04. With
|
||||
// `node.identity.persistent: true` the daemon reuses the key file at
|
||||
// config-dir/fips.key (= DAEMON_KEY_PATH). Transports take `bind_addr`
|
||||
// rather than `enabled: true / port: N`, and the upstream no longer
|
||||
// has a `tor:` transport — archipelago's own Tor fallback handles that.
|
||||
format!(
|
||||
"# Generated by archipelago — do not edit by hand.\n\
|
||||
# Regenerated on every key change and daemon upgrade.\n\
|
||||
identity:\n \
|
||||
key_file: {key_path}\n \
|
||||
pub_file: {pub_path}\n\
|
||||
transports:\n \
|
||||
udp:\n \
|
||||
enabled: true\n \
|
||||
port: {port}\n \
|
||||
tor:\n \
|
||||
enabled: true\n\
|
||||
node:\n \
|
||||
identity:\n \
|
||||
persistent: true\n\
|
||||
tun:\n \
|
||||
enabled: true\n\
|
||||
enabled: true\n \
|
||||
name: fips0\n \
|
||||
mtu: 1280\n\
|
||||
dns:\n \
|
||||
enabled: true\n \
|
||||
suffix: .fips\n\
|
||||
bind_addr: \"127.0.0.1\"\n\
|
||||
transports:\n \
|
||||
udp:\n \
|
||||
bind_addr: \"0.0.0.0:{port}\"\n\
|
||||
peers: []\n",
|
||||
key_path = DAEMON_KEY_PATH,
|
||||
pub_path = DAEMON_PUB_PATH,
|
||||
port = DEFAULT_UDP_PORT,
|
||||
)
|
||||
}
|
||||
@@ -125,14 +127,16 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rendered_yaml_contains_paths_and_port() {
|
||||
fn test_rendered_yaml_matches_upstream_schema() {
|
||||
let yaml = render_config_yaml();
|
||||
assert!(yaml.contains(DAEMON_KEY_PATH));
|
||||
assert!(yaml.contains(DAEMON_PUB_PATH));
|
||||
assert!(yaml.contains(&DEFAULT_UDP_PORT.to_string()));
|
||||
assert!(yaml.contains("persistent: true"));
|
||||
assert!(yaml.contains(&format!("0.0.0.0:{}", DEFAULT_UDP_PORT)));
|
||||
assert!(yaml.contains("udp:"));
|
||||
assert!(yaml.contains("tor:"));
|
||||
assert!(yaml.contains("tun:"));
|
||||
assert!(yaml.contains("name: fips0"));
|
||||
// Upstream fips dropped the `tor:` transport variant; archipelago
|
||||
// handles Tor fallback itself. Make sure we didn't regress.
|
||||
assert!(!yaml.contains("tor:"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -1013,19 +1013,47 @@ fi
|
||||
# Try to get backend binary: local release build → local install → remote → container build
|
||||
BACKEND_CAPTURED=0
|
||||
|
||||
# The captured binary MUST report the same version as the checked-out
|
||||
# core/archipelago/Cargo.toml, otherwise we're shipping a stale binary
|
||||
# from an earlier version bump (which is what happened with the 14:40
|
||||
# ISO — it grabbed an Apr-18 1.4.0 binary and the fleet rejected the
|
||||
# fips.yaml it wrote out on Activate). The expected version is the one
|
||||
# compiled into this build run.
|
||||
EXPECTED_VERSION="$(grep '^version' "$(cd "$SCRIPT_DIR/.." && pwd)/core/archipelago/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')"
|
||||
echo " Expected backend version (from Cargo.toml): $EXPECTED_VERSION"
|
||||
|
||||
verify_backend_version() {
|
||||
local bin="$1"
|
||||
local embedded
|
||||
# CARGO_PKG_VERSION is compiled into the binary as a string literal;
|
||||
# the easiest way to recover it without running the daemon is to grep
|
||||
# the binary for an anchored version string. This is cheap and safe.
|
||||
embedded=$(strings "$bin" 2>/dev/null | grep -E "^${EXPECTED_VERSION}$" | head -1)
|
||||
if [ -z "$embedded" ]; then
|
||||
echo " ⚠️ Captured binary does NOT contain expected version $EXPECTED_VERSION — it is stale"
|
||||
return 1
|
||||
fi
|
||||
echo " ✅ Version match: binary contains $EXPECTED_VERSION"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check for local release binary first (works for both BUILD_FROM_SOURCE and normal mode)
|
||||
LOCAL_RELEASE="$(cd "$SCRIPT_DIR/.." && pwd)/core/target/release/archipelago"
|
||||
if [ -f "$LOCAL_RELEASE" ]; then
|
||||
cp "$LOCAL_RELEASE" "$ARCH_DIR/bin/archipelago"
|
||||
chmod +x "$ARCH_DIR/bin/archipelago"
|
||||
echo " ✅ Backend from local release build ($(du -h "$ARCH_DIR/bin/archipelago" | cut -f1))"
|
||||
BACKEND_CAPTURED=1
|
||||
if verify_backend_version "$LOCAL_RELEASE"; then
|
||||
cp "$LOCAL_RELEASE" "$ARCH_DIR/bin/archipelago"
|
||||
chmod +x "$ARCH_DIR/bin/archipelago"
|
||||
echo " ✅ Backend from local release build ($(du -h "$ARCH_DIR/bin/archipelago" | cut -f1))"
|
||||
BACKEND_CAPTURED=1
|
||||
else
|
||||
echo " Skipping stale local release binary; trying next source"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$BACKEND_CAPTURED" = "0" ] && [ "$BUILD_FROM_SOURCE" != "1" ]; then
|
||||
# Direct copy from ARCHIPELAGO_BIN env or local install
|
||||
BIN="${ARCHIPELAGO_BIN:-/usr/local/bin/archipelago}"
|
||||
if [ -f "$BIN" ]; then
|
||||
if [ -f "$BIN" ] && verify_backend_version "$BIN"; then
|
||||
cp "$BIN" "$ARCH_DIR/bin/archipelago"
|
||||
chmod +x "$ARCH_DIR/bin/archipelago"
|
||||
echo " ✅ Backend captured from local system ($(du -h "$ARCH_DIR/bin/archipelago" | cut -f1))"
|
||||
@@ -1033,10 +1061,12 @@ if [ "$BACKEND_CAPTURED" = "0" ] && [ "$BUILD_FROM_SOURCE" != "1" ]; then
|
||||
fi
|
||||
# Remote copy via SCP if local failed
|
||||
if [ "$BACKEND_CAPTURED" = "0" ] && [ "$DEV_SERVER" != "localhost" ] && [ "$DEV_SERVER" != "127.0.0.1" ]; then
|
||||
if scp "$DEV_SERVER:/usr/local/bin/archipelago" "$ARCH_DIR/bin/archipelago" 2>/dev/null; then
|
||||
if scp "$DEV_SERVER:/usr/local/bin/archipelago" "$ARCH_DIR/bin/archipelago" 2>/dev/null && verify_backend_version "$ARCH_DIR/bin/archipelago"; then
|
||||
chmod +x "$ARCH_DIR/bin/archipelago"
|
||||
echo " ✅ Backend captured from remote server ($(du -h "$ARCH_DIR/bin/archipelago" | cut -f1))"
|
||||
BACKEND_CAPTURED=1
|
||||
else
|
||||
rm -f "$ARCH_DIR/bin/archipelago"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user