Skip to content

Repository files, collaboration, releases and project activity.

archforge-external-status / scripts/fix-admin-origin.shmain
▤ scripts/fix-admin-origin.sh3365 bytes
60 lines · Select a line number for a permalink
1#!/usr/bin/env bash
2# v1.0.5 one-shot safe repair of admin POST 403 Invalid origin.
3# Does not delete/recreate admin accounts, SQLite, monitoring or Caddy.
4set -Eeuo pipefail
5umask 077
6say(){ printf '[*] %s\n' "$*"; }
7fail(){ printf '[ERROR] %s\n' "$*" >&2; exit 1; }
8[[ $EUID -eq 0 ]] || fail 'Run this fix as root: bash ./fix-admin-origin.sh'
9ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
10source "$ROOT/scripts/domain-lib.sh"
11[[ -r "$ROOT/package.json" && -r "$ROOT/src/server.js" ]] || fail 'Missing status project source checkout'
12/usr/bin/node -e 'if(require(process.argv[1]).version!=="1.0.5")process.exit(1)' "$ROOT/package.json" || fail 'Expected ArchForge checkout v1.0.5. Run git pull --ff-only first.'
13CFG=/etc/archforge-status/status.env
14[[ -f "$CFG" && ! -L "$CFG" ]] || fail 'Existing status.env not found (or unsafe symlink)'
15[[ -f /etc/caddy/Caddyfile ]] || fail 'Caddyfile not found'
16DOMAIN=status.archforge.nl
17# Verify that this VPS actually has an intended HTTPS site for our domain.
18if ! grep -Eqs "^[[:space:]]*${DOMAIN//./\\.}[[:space:]]*\\{" /etc/caddy/Caddyfile /etc/caddy/conf.d/*.caddy 2>/dev/null; then
19 fail 'Caddy has no configured site for status.archforge.nl. Refusing to change public URL.'
20fi
21if ! curl -fsS --max-time 6 http://127.0.0.1:3000/healthz >/dev/null;then
22 fail 'Installed status app is not running at localhost:3000. No changes made.'
23fi
24BACKUP=$(mktemp -d /root/.archforge-origin-fix.XXXXXXXX)
25cp -a -- "$CFG" "$BACKUP/status.env"
26say "Configuration backup: $BACKUP/status.env (private)"
27CHANGED=false
28CURRENT=$(grep -m1 '^STATUS_PUBLIC_URL=' "$CFG" | cut -d= -f2- || true)
29if [[ "$CURRENT" != "https://$DOMAIN" && "$CURRENT" != "https://$DOMAIN/" ]]; then
30 say "Synchronizing STATUS_PUBLIC_URL with Caddy's HTTPS domain."
31 af_set_public_domain "$CFG" "$DOMAIN" || fail 'Failed updating STATUS_PUBLIC_URL'
32 CHANGED=true
33else
34 say 'Configured public hostname already matches the domain.'
35fi
36say 'Deploying tested v1.0.5 code with built-in database backup and rollback...'
37if ! bash "$ROOT/scripts/deploy.sh" "$ROOT";then
38 say 'Code deployment failed. Restoring original status configuration.'
39 cp -a -- "$BACKUP/status.env" "$CFG"
40 systemctl restart archforge-status || true
41 fail 'Deployment failed and configuration restored.'
42fi
43say 'Checking login POST through HTTPS without sending any credentials...'
44# Invalid CSRF MUST be rejected, but valid Origin MUST NOT receive Invalid origin.
45OUT=$(mktemp "$BACKUP/response.XXXXXXXX")
46if ! curl -sS --max-time 12 --resolve "$DOMAIN:443:127.0.0.1" \
47 -H "Origin: https://$DOMAIN" \
48 -H 'Content-Type: application/x-www-form-urlencoded' \
49 --data 'csrf=deliberately-invalid' "https://$DOMAIN/admin/login" > "$OUT";then
50 say 'HTTPS local POST could not be verified; inspect Caddy/SSL separately.'
51elif grep -q 'Invalid origin' "$OUT"; then
52 fail 'Origin check still rejects canonical host; see service journal: journalctl -u archforge-status -n 50'
53elif grep -q 'Invalid or expired form token' "$OUT"; then
54 say 'PASS: same-origin login form reaches CSRF validation (as intended).'
55else
56 say 'The HTTPS response was received but its form result was unexpected; inspect the login page.'
57fi
58say 'Finished. Open https://status.archforge.nl/admin/login and sign in again.'
59say 'All admin credentials, SQLite data and Caddy settings were preserved.'