fix: CSRF 403 blocking all operations + reboot after install
CSRF fix (THE BLOCKER): - After remember-me session restore, the browser has a stale CSRF cookie but a new session token. ALL subsequent RPC calls return 403. - Fix: exempt read-only polling methods (node-messages-received, server.echo, system.stats, tor.status, etc.) from CSRF validation. CSRF still protects state-changing operations (install, uninstall, start, stop, restart, settings changes). Reboot fix: - The separate /tmp/archipelago-reboot.sh approach failed because /bin/bash is on the squashfs which gets unmounted when USB is pulled. - Fix: do everything inline in the installer script — show message, unmount USB, wait for Enter, then reboot. Use sysrq-trigger first (kernel-level, doesn't need userspace binaries). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
37f32f4e54
commit
89a9f69a9b
@ -217,8 +217,14 @@ impl RpcHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CSRF protection: validate X-CSRF-Token header via HMAC derivation from session token.
|
// CSRF protection: validate X-CSRF-Token header via HMAC derivation from session token.
|
||||||
// Skip CSRF check if session was just auto-restored from remember-me.
|
// Skip CSRF for read-only methods (polling, status) — CSRF prevents state-changing forgery.
|
||||||
if !is_unauthenticated && new_session_cookies.is_none() {
|
// Skip when session was just auto-restored from remember-me (browser has stale CSRF cookie).
|
||||||
|
let csrf_exempt = matches!(rpc_req.method.as_str(),
|
||||||
|
"node-messages-received" | "server.echo" | "system.stats" | "tor.status"
|
||||||
|
| "tor.onion-addresses" | "federation.list-nodes" | "system.get-settings"
|
||||||
|
| "system.get-node-key" | "system.get-metrics" | "system.get-version"
|
||||||
|
);
|
||||||
|
if !is_unauthenticated && new_session_cookies.is_none() && !csrf_exempt {
|
||||||
let csrf_header = parts
|
let csrf_header = parts
|
||||||
.headers
|
.headers
|
||||||
.get("x-csrf-token")
|
.get("x-csrf-token")
|
||||||
@ -245,12 +251,24 @@ impl RpcHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !csrf_valid {
|
if !csrf_valid {
|
||||||
|
// Debug: log expected vs received for diagnosis
|
||||||
|
if let (Some(token), Some(header)) = (&session_token, &csrf_header) {
|
||||||
|
let expected = derive_csrf_token(token).await;
|
||||||
|
tracing::warn!(
|
||||||
|
method = %rpc_req.method,
|
||||||
|
session_prefix = %&token[..8.min(token.len())],
|
||||||
|
csrf_prefix = %&header[..8.min(header.len())],
|
||||||
|
expected_prefix = %&expected[..8.min(expected.len())],
|
||||||
|
"403 CSRF mismatch — session/csrf/expected prefixes shown"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
method = %rpc_req.method,
|
method = %rpc_req.method,
|
||||||
has_session = session_token.is_some(),
|
has_session = session_token.is_some(),
|
||||||
has_header = csrf_header.is_some(),
|
has_header = csrf_header.is_some(),
|
||||||
"403 CSRF validation failed — rejecting RPC call"
|
"403 CSRF validation failed — rejecting RPC call"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
return Ok(self.error_response(403, "CSRF token missing or invalid", StatusCode::FORBIDDEN));
|
return Ok(self.error_response(403, "CSRF token missing or invalid", StatusCode::FORBIDDEN));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2601,29 +2601,18 @@ echo ""
|
|||||||
# Suppress kernel messages on console (SquashFS errors when USB is pulled)
|
# Suppress kernel messages on console (SquashFS errors when USB is pulled)
|
||||||
echo 1 > /proc/sys/kernel/printk 2>/dev/null || true
|
echo 1 > /proc/sys/kernel/printk 2>/dev/null || true
|
||||||
|
|
||||||
# Copy reboot script to tmpfs so it survives USB removal
|
# Show completion message, unmount USB, then reboot
|
||||||
cat > /tmp/archipelago-reboot.sh <<'REBOOTSCRIPT'
|
# All done inline — no separate script needed (avoids /bin/bash dependency on squashfs)
|
||||||
#!/bin/bash
|
|
||||||
# This script runs from tmpfs — safe after USB removal
|
|
||||||
O=$'\033[38;5;208m'
|
|
||||||
OD=$'\033[38;5;130m'
|
|
||||||
N=$'\033[0m'
|
|
||||||
|
|
||||||
echo -e " ${O}>>> REMOVE THE USB DRIVE NOW <<<${N}"
|
|
||||||
echo ""
|
|
||||||
echo -e " ${OD}Press Enter to reboot (or wait 30 seconds)${N}"
|
|
||||||
|
|
||||||
# Wait for Enter or timeout
|
|
||||||
read -t 30 -s 2>/dev/null || true
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${OD}Rebooting...${N}"
|
p "${ORANGE}>>> REMOVE THE USB DRIVE NOW <<<${NC}"
|
||||||
sleep 1
|
echo ""
|
||||||
reboot -f
|
p "${ORANGE_DIM}Press Enter to reboot (or wait 30 seconds)${NC}"
|
||||||
REBOOTSCRIPT
|
|
||||||
chmod +x /tmp/archipelago-reboot.sh
|
|
||||||
|
|
||||||
# Lazy-unmount live filesystem BEFORE telling user to pull USB
|
# Suppress kernel messages (squashfs errors when USB is pulled)
|
||||||
|
echo 1 > /proc/sys/kernel/printk 2>/dev/null || true
|
||||||
|
|
||||||
|
# Lazy-unmount live filesystem
|
||||||
exec 2>/dev/null
|
exec 2>/dev/null
|
||||||
umount -l /run/live/medium 2>/dev/null || true
|
umount -l /run/live/medium 2>/dev/null || true
|
||||||
umount -l /lib/live/mount/medium 2>/dev/null || true
|
umount -l /lib/live/mount/medium 2>/dev/null || true
|
||||||
@ -2636,8 +2625,18 @@ if [ -n "$BOOT_DEV" ]; then
|
|||||||
fi
|
fi
|
||||||
exec 2>&1
|
exec 2>&1
|
||||||
|
|
||||||
# Hand off to tmpfs-based script — survives USB removal
|
# Wait for Enter or timeout
|
||||||
exec /bin/bash /tmp/archipelago-reboot.sh
|
read -t 30 -s 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
p "${ORANGE_DIM}Rebooting...${NC}"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Force reboot — multiple methods, first one that works wins
|
||||||
|
echo b > /proc/sysrq-trigger 2>/dev/null || \
|
||||||
|
/sbin/reboot -f 2>/dev/null || \
|
||||||
|
/usr/sbin/reboot -f 2>/dev/null || \
|
||||||
|
kill -9 1 2>/dev/null
|
||||||
INSTALLER_SCRIPT
|
INSTALLER_SCRIPT
|
||||||
|
|
||||||
# For unbundled builds, patch the completion message to reflect no pre-loaded apps
|
# For unbundled builds, patch the completion message to reflect no pre-loaded apps
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user