Skip to content

Repository files, collaboration, releases and project activity.

archforge-external-status / scripts/fix-https.shmain
▤ scripts/fix-https.sh6174 bytes
144 lines · Select a line number for a permalink
1#!/usr/bin/env bash
2# Fix the stock :80 Caddy site which can leave status.archforge.nl unreachable on HTTPS.
3# Intentional: never touch the status app, its environment, SQLite, or admin credentials.
4set -Eeuo pipefail
5umask 077
6log() { printf '[*] %s\n' "$*"; }
7error() { printf '[ERROR] %s\n' "$*" >&2; exit 1; }
8[[ $EUID -eq 0 ]] || error 'Run as root: sudo bash ./fix-https.sh'
9[[ -r /etc/os-release ]] || error 'Cannot determine Linux distribution'
10# shellcheck disable=SC1091
11source /etc/os-release
12[[ ${ID:-} == debian && ${VERSION_ID:-} == 13 ]] || error 'This repair is for Debian 13 only'
13for binary in caddy systemctl ss curl python3; do
14 command -v "$binary" >/dev/null 2>&1 || error "Missing required program: $binary"
15done
17ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
18# shellcheck source=domain-lib.sh
19source "$ROOT/scripts/domain-lib.sh"
20CFG=/etc/archforge-status/status.env
21CADDYFILE=/etc/caddy/Caddyfile
22SITEFILE=/etc/caddy/conf.d/archforge-status.caddy
23[[ -f "$CFG" && ! -L "$CFG" ]] || error 'Status configuration not found or symbolic link'
24[[ -f "$CADDYFILE" && ! -L "$CADDYFILE" ]] || error 'Caddyfile not found or symbolic link'
25[[ ! -L "$SITEFILE" ]] || error 'Refusing symlinked Caddy site file'
26DOMAIN=$(af_read_public_domain "$CFG") || error 'Invalid STATUS_PUBLIC_URL in existing status configuration'
27[[ "$DOMAIN" == status.archforge.nl ]] || error "Configured domain is '$DOMAIN', expected status.archforge.nl; no changes made"
29# The user has only the Debian default :80 site and our status snippet. Never
30# replace multi-site or otherwise customized Caddy instances on a shared VPS.
31if [[ -d /etc/caddy/conf.d ]]; then
32 OTHER=$(find /etc/caddy/conf.d -maxdepth 1 -type f -name '*.caddy' ! -name 'archforge-status.caddy' -print -quit)
33 [[ -z "$OTHER" ]] || error "Other Caddy site exists: $OTHER. No changes made."
34fi
35CURRENT_JSON=$(mktemp /tmp/archforge-caddy-adapt.XXXXXXXX)
36trap 'rm -f "$CURRENT_JSON"' EXIT
37if ! caddy adapt --config "$CADDYFILE" > "$CURRENT_JSON" 2>/dev/null; then
38 error 'Existing Caddy config does not parse. No changes made.'
39fi
40if ! python3 - "$CURRENT_JSON" "$DOMAIN" <<'PY'
41import json, sys
42conf=json.load(open(sys.argv[1]))
43domain=sys.argv[2]
44http=conf.get('apps',{}).get('http',{})
45for server in http.get('servers',{}).values():
46 for route in server.get('routes',[]):
47 for matcher in route.get('match',[]):
48 for host in matcher.get('host',[]):
49 if host != domain:
50 print(f'Another HTTPS site exists: {host}',file=sys.stderr)
51 sys.exit(1)
52PY
53then
54 error 'Caddy serves another named site. Refusing to overwrite shared config.'
55fi
57if ! curl --silent --show-error --fail --max-time 5 http://127.0.0.1:3000/healthz -o /dev/null; then
58 error 'ArchForge Status is not running on local port 3000. No Caddy changes made.'
59fi
61log "Repairing HTTPS for $DOMAIN (Node.js backend is healthy)."
62BACKUP="/root/archforge-caddy-before-https-fix-$(date -u +%Y%m%d-%H%M%S)"
63mkdir -m 700 -- "$BACKUP"
64cp -a -- "$CADDYFILE" "$BACKUP/Caddyfile"
65if [[ -f "$SITEFILE" ]]; then cp -a -- "$SITEFILE" "$BACKUP/archforge-status.caddy"; fi
66printf 'Backup directory: %s\n' "$BACKUP"
68RESTORED=false
69rollback() {
70 printf '[!] Restoring previous Caddy configuration from %s\n' "$BACKUP" >&2
71 cp -a -- "$BACKUP/Caddyfile" "$CADDYFILE"
72 systemctl restart caddy >/dev/null 2>&1 || true
73 RESTORED=true
76# Use the existing configured site if it is a straightforward reverse proxy;
77# otherwise generate the minimal known-safe Caddyfile without any extra directives.
78TMP=$(mktemp /etc/caddy/.archforge-caddy.XXXXXXXX)
79if [[ -f "$SITEFILE" ]] && \
80 grep -Eq '^status\.archforge\.nl[[:space:]]*\{' "$SITEFILE" && \
81 grep -Eq '^[[:space:]]*reverse_proxy[[:space:]]+127\.0\.0\.1:3000[[:space:]]*$' "$SITEFILE"; then
82 cp -- "$SITEFILE" "$TMP"
83else
84 af_render_caddy "$DOMAIN" '' > "$TMP"
85fi
86chmod 644 "$TMP"
87chown root:root "$TMP"
88if ! caddy validate --config "$TMP" --adapter caddyfile >/dev/null; then
89 rm -f "$TMP"
90 error 'New direct HTTPS configuration did not validate. Old files untouched.'
91fi
93# Force the domain block directly into the main Caddyfile. This intentionally
94# replaces only the stock HTTP :80 page on this dedicated status VPS.
95if ! mv -f -- "$TMP" "$CADDYFILE"; then
96 rm -f "$TMP" || true
97 error 'Could not write Caddyfile. Backup remains available.'
98fi
99if ! systemctl restart caddy; then
100 log 'Caddy restart failed; printing errors and rolling back.'
101 journalctl -u caddy -n 30 --no-pager || true
102 rollback
103 error 'Caddy failed to restart with HTTPS config.'
104fi
105sleep 2
106has_443() { ss -ltn | grep -Eq '(:|\])443[[:space:]]'; }
107if ! has_443; then
108 log 'Service did not bind 443. Trying explicit reload of the new Caddyfile.'
109 caddy reload --config "$CADDYFILE" >/dev/null 2>&1 || true
110 sleep 2
111fi
112if ! has_443; then
113 printf '\n[!] Caddy STILL DOES NOT LISTEN on TCP 443. This is not a firewall issue.\n' >&2
114 systemctl show caddy -p ExecStart -p FragmentPath --no-pager >&2 || true
115 journalctl -u caddy -n 40 --no-pager >&2 || true
116 rollback
117 error 'HTTPS listener missing. Previous configuration restored. Above is the actual Caddy failure.'
118fi
120log 'SUCCESS: Caddy now listens on TCP 443.'
121log 'Checking whether an ACME certificate is ready...'
122TLS_OK=false
123for _ in 1 2 3 4 5 6; do
124 if curl --silent --show-error --fail --max-time 7 --resolve "$DOMAIN:443:127.0.0.1" "https://$DOMAIN/healthz" -o /dev/null 2>/dev/null; then
125 TLS_OK=true
126 break
127 fi
128 sleep 3
129done
130printf '\n===============================================\n'
131printf ' Domain: https://%s\n' "$DOMAIN"
132printf ' Admin: https://%s/admin/login\n' "$DOMAIN"
133printf ' HTTPS port: LISTENING (443/tcp)\n'
134if [[ "$TLS_OK" == true ]]; then
135 printf ' Certificate: VERIFIED (valid TLS and status endpoint)\n'
136else
137 printf ' Certificate: PENDING / NOT YET VERIFIED\n'
138 printf ' Caddy is listening, but ACME or public DNS/connectivity must finish.\n'
139 printf ' Recent Caddy logs:\n'
140 journalctl -u caddy -n 25 --no-pager || true
141fi
142printf ' Backup: %s\n' "$BACKUP"
143printf '===============================================\n'