Skip to content

Repository files, collaboration, releases and project activity.

archforge-external-status / scripts/repair-admin-login.shmain
▤ scripts/repair-admin-login.sh4219 bytes
67 lines · Select a line number for a permalink
1#!/usr/bin/env bash
2# ArchForge External Status 1.0.6 - ArchForge-based admin login recovery.
3# Never resets admin accounts, sessions, tokens, Caddy or SQLite data.
4set -Eeuo pipefail
5umask 077
6log(){ printf '[*] %s\n' "$*"; }
7fail(){ printf '[ERROR] %s\n' "$*" >&2; exit 1; }
8[[ $EUID -eq 0 ]] || fail 'Run as root: bash ./repair-admin-login.sh'
9ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
10[[ -f "$ROOT/package.json" && -f "$ROOT/src/server.js" ]] || fail 'Start from your ArchForge checkout'
11[[ "$(/usr/bin/node -p 'require(process.argv[1]).version' "$ROOT/package.json")" == '1.0.6' ]] || fail 'ArchForge checkout is not 1.0.6; commit/push/pull the update first'
12[[ "$ROOT" != '/opt/archforge-status/app' ]] || fail 'Run this from the ArchForge checkout, not from /opt'
13CFG=/etc/archforge-status/status.env
14[[ -f "$CFG" && ! -L "$CFG" ]] || fail 'Missing /etc/archforge-status/status.env'
15[[ -r "$ROOT/scripts/domain-lib.sh" ]] || fail 'Missing domain configuration helper'
16source "$ROOT/scripts/domain-lib.sh"
17DOMAIN='status.archforge.nl'
18if ! grep -Eqs "^[[:space:]]*${DOMAIN//./\\.}[[:space:]]*\\{" /etc/caddy/Caddyfile /etc/caddy/conf.d/*.caddy 2>/dev/null; then
19 fail 'Caddy is not configured for status.archforge.nl; no changes made'
20fi
21if ! curl --fail --silent --show-error --max-time 5 http://127.0.0.1:3000/healthz >/dev/null;then
22 fail 'Status service unavailable on 127.0.0.1:3000; check systemctl status archforge-status'
23fi
24SNAP=$(mktemp -d /root/.archforge-login-repair.XXXXXXXX)
25cp -a -- "$CFG" "$SNAP/status.env"
26log "Private .env backup created ($SNAP/status.env)"
27CURRENT=$(grep -m1 '^STATUS_PUBLIC_URL=' "$CFG" | cut -d= -f2- || true)
28if [[ "$CURRENT" != "https://$DOMAIN" && "$CURRENT" != "https://$DOMAIN/" ]];then
29 log 'Synchronizing public URL with the Caddy HTTPS domain'
30 af_set_public_domain "$CFG" "$DOMAIN" || fail 'Cannot safely update public URL'
31fi
32log 'Deploying version 1.0.6 via the existing backup/rollback installer'
33if ! bash "$ROOT/scripts/deploy.sh" "$ROOT";then
34 log 'Deployment failed; restoring the previous status.env'
35 cp -a -- "$SNAP/status.env" "$CFG"
36 systemctl restart archforge-status || true
37 fail 'Deployment was rolled back. The old release should still be in service.'
38fi
39version=$(curl --fail --silent --show-error --max-time 5 http://127.0.0.1:3000/healthz | /usr/bin/node -p 'try { JSON.parse(require("fs").readFileSync(0,"utf8")).version } catch { "" }')
40[[ "$version" == '1.0.6' ]] || fail "Installed version verification failed (${version:-unknown}); inspect journalctl -u archforge-status"
41log 'Running safe login tests locally (no passwords or 2FA codes needed)'
42# Reuse a freshly issued double-submit cookie, with a dummy username.
43curl --silent --show-error --max-time 5 -D "$SNAP/headers" -o /dev/null http://127.0.0.1:3000/admin/login
44CSRF=$(sed -nE 's/^[Ss]et-[Cc]ookie: af_csrf=([^;]+).*/\1/p' "$SNAP/headers" | tr -d '\r' | head -n 1)
45[[ "$CSRF" =~ ^[A-Za-z0-9_-]{32,100}$ ]] || fail 'Could not obtain form token from local status app'
46post_login(){
47 local output_name=$1 site=$2 origin=$3 expected=$4 code
48 code=$(curl --silent --show-error --max-time 6 -o "$SNAP/$output_name" -w '%{http_code}' \
49 -H "Cookie: af_csrf=$CSRF" \
50 -H "Origin: $origin" \
51 -H "Sec-Fetch-Site: $site" \
52 -H 'Content-Type: application/x-www-form-urlencoded' \
53 --data-urlencode "csrf=$CSRF" \
54 --data-urlencode 'username=__archforge_test_nonexistent__' \
55 --data-urlencode 'password=never-a-valid-password' \
56 --data-urlencode 'totp=000000' \
57 http://127.0.0.1:3000/admin/login)
58 [[ "$code" == "$expected" ]] || fail "Login $output_name returned HTTP $code (expected $expected); inspect installed version and logs"
60post_login canonical 'same-origin' "https://$DOMAIN" 401
61post_login privacy 'same-origin' 'null' 401
62post_login blocked 'cross-site' "https://$DOMAIN" 403
63log 'PASS: canonical and privacy-protected same-origin login reach credential validation'
64log 'PASS: cross-site login is still blocked; CSRF verification remains mandatory'
65log 'Installed release: 1.0.6. Refresh https://status.archforge.nl/admin/login and try again.'
66log 'No credentials, monitoring database or Caddy configuration changed.'