fix: filebrowser port bind, CSRF in tests, console-setup, auto-test scope
FileBrowser crash fix: - Add --cap-add=NET_BIND_SERVICE (port 80 needs it with --cap-drop=ALL) - Add --cap-add=DAC_OVERRIDE for rootless volume access - Both in first-boot script and backend config.rs Test script fixes: - Extract csrf_token cookie and send as X-CSRF-Token header on RPC calls - Add --phase1-only flag for safe install-only checks (no side effects) - Auto-test service uses --phase1-only so it doesn't steal onboarding Install fixes: - Pre-create ~/.local/share/containers (ReadWritePaths mount namespace error) - Fix console-setup.service: add After=tmp.mount + ExecStartPre mkdir /tmp Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
316dc4875a
commit
9741e73824
@ -97,9 +97,10 @@ pub(super) fn get_app_capabilities(app_id: &str) -> Vec<String> {
|
|||||||
"--cap-add=SETUID".to_string(),
|
"--cap-add=SETUID".to_string(),
|
||||||
"--cap-add=SETGID".to_string(),
|
"--cap-add=SETGID".to_string(),
|
||||||
],
|
],
|
||||||
// FileBrowser needs DAC_OVERRIDE to read/write volume files under rootless podman
|
// FileBrowser needs DAC_OVERRIDE for volume access + NET_BIND_SERVICE to bind port 80
|
||||||
"filebrowser" => vec![
|
"filebrowser" => vec![
|
||||||
"--cap-add=DAC_OVERRIDE".to_string(),
|
"--cap-add=DAC_OVERRIDE".to_string(),
|
||||||
|
"--cap-add=NET_BIND_SERVICE".to_string(),
|
||||||
],
|
],
|
||||||
// Minimal apps (searxng, etc.) need no extra caps
|
// Minimal apps (searxng, etc.) need no extra caps
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
|
|||||||
@ -961,6 +961,7 @@ if ! runuser -u archipelago -- bash -c 'export XDG_RUNTIME_DIR=/run/user/1000 &&
|
|||||||
runuser -u archipelago -- bash -c "export XDG_RUNTIME_DIR=/run/user/1000 && podman run -d --name filebrowser --restart unless-stopped \
|
runuser -u archipelago -- bash -c "export XDG_RUNTIME_DIR=/run/user/1000 && podman run -d --name filebrowser --restart unless-stopped \
|
||||||
--cap-drop=ALL \
|
--cap-drop=ALL \
|
||||||
--cap-add=DAC_OVERRIDE \
|
--cap-add=DAC_OVERRIDE \
|
||||||
|
--cap-add=NET_BIND_SERVICE \
|
||||||
--security-opt=no-new-privileges:true \
|
--security-opt=no-new-privileges:true \
|
||||||
--read-only \
|
--read-only \
|
||||||
--tmpfs=/tmp:rw,noexec,nosuid,size=64m \
|
--tmpfs=/tmp:rw,noexec,nosuid,size=64m \
|
||||||
@ -1364,8 +1365,12 @@ cat > /mnt/target/etc/hosts <<EOF
|
|||||||
EOF
|
EOF
|
||||||
chmod 644 /mnt/target/etc/hosts
|
chmod 644 /mnt/target/etc/hosts
|
||||||
|
|
||||||
# Configure Archipelago app registry (HTTP, insecure)
|
# Pre-create container storage dirs (ReadWritePaths needs these to exist)
|
||||||
|
mkdir -p /mnt/target/home/archipelago/.local/share/containers
|
||||||
mkdir -p /mnt/target/home/archipelago/.config/containers
|
mkdir -p /mnt/target/home/archipelago/.config/containers
|
||||||
|
chown -R 1000:1000 /mnt/target/home/archipelago/.local
|
||||||
|
|
||||||
|
# Configure Archipelago app registry (HTTP, insecure)
|
||||||
cat > /mnt/target/home/archipelago/.config/containers/registries.conf <<'REGCONF'
|
cat > /mnt/target/home/archipelago/.config/containers/registries.conf <<'REGCONF'
|
||||||
[[registry]]
|
[[registry]]
|
||||||
location = "80.71.235.15:3000"
|
location = "80.71.235.15:3000"
|
||||||
@ -1998,6 +2003,17 @@ chroot /mnt/target systemctl enable archipelago-setup-tor.service 2>/dev/null ||
|
|||||||
chroot /mnt/target systemctl enable archipelago-first-boot-containers.service 2>/dev/null || true
|
chroot /mnt/target systemctl enable archipelago-first-boot-containers.service 2>/dev/null || true
|
||||||
chroot /mnt/target systemctl enable archipelago-kiosk.service 2>/dev/null || true
|
chroot /mnt/target systemctl enable archipelago-kiosk.service 2>/dev/null || true
|
||||||
|
|
||||||
|
# Fix console-setup: setupcon needs /tmp writable, add ordering dependency
|
||||||
|
mkdir -p /mnt/target/etc/systemd/system/console-setup.service.d
|
||||||
|
cat > /mnt/target/etc/systemd/system/console-setup.service.d/fix-tmp.conf <<'CONSOLEFIX'
|
||||||
|
[Unit]
|
||||||
|
After=tmp.mount systemd-tmpfiles-setup.service
|
||||||
|
Wants=tmp.mount
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStartPre=/bin/mkdir -p /tmp
|
||||||
|
CONSOLEFIX
|
||||||
|
|
||||||
# Auto-login on tty1 — no password prompt on console
|
# Auto-login on tty1 — no password prompt on console
|
||||||
mkdir -p /mnt/target/etc/systemd/system/getty@tty1.service.d
|
mkdir -p /mnt/target/etc/systemd/system/getty@tty1.service.d
|
||||||
cat > /mnt/target/etc/systemd/system/getty@tty1.service.d/autologin.conf <<'AUTOLOGIN'
|
cat > /mnt/target/etc/systemd/system/getty@tty1.service.d/autologin.conf <<'AUTOLOGIN'
|
||||||
@ -2007,24 +2023,23 @@ ExecStart=-/sbin/agetty --autologin archipelago --noclear %I $TERM
|
|||||||
AUTOLOGIN
|
AUTOLOGIN
|
||||||
chroot /mnt/target systemctl enable archipelago-diagnostics.service 2>/dev/null || true
|
chroot /mnt/target systemctl enable archipelago-diagnostics.service 2>/dev/null || true
|
||||||
|
|
||||||
# Post-install test runner — runs once on first boot after all services are up
|
# Post-install smoke test — runs Phase 1 (install verification) only on first boot
|
||||||
|
# Does NOT run onboarding or create passwords — lets user do that via the UI
|
||||||
cat > /mnt/target/etc/systemd/system/archipelago-post-install-tests.service <<'PITSERVICE'
|
cat > /mnt/target/etc/systemd/system/archipelago-post-install-tests.service <<'PITSERVICE'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Archipelago Post-Install Test Suite (first boot)
|
Description=Archipelago Install Verification (first boot)
|
||||||
After=archipelago.service archipelago-first-boot-containers.service nginx.service
|
After=archipelago.service archipelago-first-boot-containers.service nginx.service
|
||||||
Wants=archipelago.service nginx.service
|
Wants=archipelago.service nginx.service
|
||||||
ConditionPathExists=/opt/archipelago/scripts/run-post-install-tests.sh
|
|
||||||
ConditionPathExists=!/var/lib/archipelago/.post-install-tests-done
|
ConditionPathExists=!/var/lib/archipelago/.post-install-tests-done
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
# Wait for backend to fully initialize
|
|
||||||
ExecStartPre=/bin/bash -c 'for i in $(seq 1 30); do curl -sf http://127.0.0.1:5678/health >/dev/null 2>&1 && exit 0; sleep 2; done; exit 0'
|
ExecStartPre=/bin/bash -c 'for i in $(seq 1 30); do curl -sf http://127.0.0.1:5678/health >/dev/null 2>&1 && exit 0; sleep 2; done; exit 0'
|
||||||
ExecStart=/bin/bash -c '/opt/archipelago/scripts/run-post-install-tests.sh "password123" 2>&1 | tee /var/log/archipelago-post-install-tests.log; touch /var/lib/archipelago/.post-install-tests-done'
|
ExecStart=/bin/bash -c '/opt/archipelago/scripts/run-post-install-tests.sh --phase1-only 2>&1 | tee /var/log/archipelago-post-install-tests.log; touch /var/lib/archipelago/.post-install-tests-done'
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
StandardOutput=journal+console
|
StandardOutput=journal+console
|
||||||
StandardError=journal+console
|
StandardError=journal+console
|
||||||
TimeoutStartSec=300
|
TimeoutStartSec=120
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@ -3,15 +3,24 @@
|
|||||||
# Run on an installed Archipelago node (SSH or local).
|
# Run on an installed Archipelago node (SSH or local).
|
||||||
#
|
#
|
||||||
# Usage: bash run-post-install-tests.sh [password]
|
# Usage: bash run-post-install-tests.sh [password]
|
||||||
# password defaults to "testpass123!" for fresh installs
|
# bash run-post-install-tests.sh --phase1-only # Install checks only (no auth)
|
||||||
#
|
#
|
||||||
# Tests:
|
# Tests:
|
||||||
# Phase 1: Install verification (services, files, logs)
|
# Phase 1: Install verification (services, files, logs) — safe, no side effects
|
||||||
# Phase 2: Onboarding (password setup, auth flow)
|
# Phase 2: Onboarding (password setup, auth flow) — creates user account
|
||||||
# Phase 3: Container lifecycle (install 3 apps, start/stop/health)
|
# Phase 3: Container lifecycle (install 3 apps, start/stop/health) — needs auth
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
PASSWORD="${1:-testpass123!}"
|
PHASE1_ONLY=false
|
||||||
|
PASSWORD="testpass123!"
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--phase1-only) PHASE1_ONLY=true ;;
|
||||||
|
*) PASSWORD="$arg" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
BASE="http://127.0.0.1:5678"
|
BASE="http://127.0.0.1:5678"
|
||||||
JAR="/tmp/e2e-cookies.txt"
|
JAR="/tmp/e2e-cookies.txt"
|
||||||
rm -f "$JAR"
|
rm -f "$JAR"
|
||||||
@ -22,11 +31,23 @@ fail() { FC=$((FC + 1)); printf "\033[31m ✗ %s — %s\033[0m\n" "$1" "${2:-}"
|
|||||||
skip() { SC=$((SC + 1)); printf "\033[33m ⊘ %s\033[0m\n" "$1"; }
|
skip() { SC=$((SC + 1)); printf "\033[33m ⊘ %s\033[0m\n" "$1"; }
|
||||||
section() { printf "\n\033[1m━━━ %s ━━━\033[0m\n" "$1"; }
|
section() { printf "\n\033[1m━━━ %s ━━━\033[0m\n" "$1"; }
|
||||||
|
|
||||||
|
# Extract CSRF token from cookie jar
|
||||||
|
get_csrf() {
|
||||||
|
grep 'csrf_token' "$JAR" 2>/dev/null | awk '{print $NF}'
|
||||||
|
}
|
||||||
|
|
||||||
rpc() {
|
rpc() {
|
||||||
local method="$1"
|
local method="$1"
|
||||||
local params="${2:-"{}"}"
|
local params="${2:-"{}"}"
|
||||||
|
local csrf
|
||||||
|
csrf=$(get_csrf)
|
||||||
|
local csrf_header=""
|
||||||
|
if [ -n "$csrf" ]; then
|
||||||
|
csrf_header="-H X-CSRF-Token:${csrf}"
|
||||||
|
fi
|
||||||
curl -s -b "$JAR" -c "$JAR" \
|
curl -s -b "$JAR" -c "$JAR" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
$csrf_header \
|
||||||
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$method\",\"params\":$params}" \
|
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$method\",\"params\":$params}" \
|
||||||
"${BASE}/rpc/v1" 2>/dev/null
|
"${BASE}/rpc/v1" 2>/dev/null
|
||||||
}
|
}
|
||||||
@ -153,6 +174,17 @@ else
|
|||||||
fail "Nginx config" "nginx -t failed"
|
fail "Nginx config" "nginx -t failed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Phase 1 exit point ──
|
||||||
|
if [ "$PHASE1_ONLY" = "true" ]; then
|
||||||
|
section "Results (Phase 1 only)"
|
||||||
|
TOTAL=$((PC + FC + SC))
|
||||||
|
printf "\n \033[32mPassed: %d\033[0m \033[31mFailed: %d\033[0m \033[33mSkipped: %d\033[0m Total: %d\n\n" "$PC" "$FC" "$SC" "$TOTAL"
|
||||||
|
[ "$FC" -gt 0 ] && echo " Phase 1: SOME CHECKS FAILED" && exit 1
|
||||||
|
echo " Phase 1: ALL CHECKS PASSED"
|
||||||
|
echo " Run without --phase1-only to test onboarding + containers"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
# PHASE 2: Onboarding & Auth
|
# PHASE 2: Onboarding & Auth
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user