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