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 bash2
# 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.4
set -Eeuo pipefail5
umask 0776
log() { printf '[*] %s\n' "$*"; }7
error() { 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=SC109111
source /etc/os-release12
[[ ${ID:-} == debian && ${VERSION_ID:-} == 13 ]] || error 'This repair is for Debian 13 only'13
for binary in caddy systemctl ss curl python3; do14
command -v "$binary" >/dev/null 2>&1 || error "Missing required program: $binary"15
done17
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)18
# shellcheck source=domain-lib.sh19
source "$ROOT/scripts/domain-lib.sh"20
CFG=/etc/archforge-status/status.env21
CADDYFILE=/etc/caddy/Caddyfile22
SITEFILE=/etc/caddy/conf.d/archforge-status.caddy23
[[ -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'26
DOMAIN=$(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. Never30
# replace multi-site or otherwise customized Caddy instances on a shared VPS.31
if [[ -d /etc/caddy/conf.d ]]; then32
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."34
fi35
CURRENT_JSON=$(mktemp /tmp/archforge-caddy-adapt.XXXXXXXX)36
trap 'rm -f "$CURRENT_JSON"' EXIT37
if ! caddy adapt --config "$CADDYFILE" > "$CURRENT_JSON" 2>/dev/null; then38
error 'Existing Caddy config does not parse. No changes made.'39
fi40
if ! python3 - "$CURRENT_JSON" "$DOMAIN" <<'PY'41
import json, sys42
conf=json.load(open(sys.argv[1]))43
domain=sys.argv[2]44
http=conf.get('apps',{}).get('http',{})45
for 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)52
PY53
then54
error 'Caddy serves another named site. Refusing to overwrite shared config.'55
fi57
if ! curl --silent --show-error --fail --max-time 5 http://127.0.0.1:3000/healthz -o /dev/null; then58
error 'ArchForge Status is not running on local port 3000. No Caddy changes made.'59
fi61
log "Repairing HTTPS for $DOMAIN (Node.js backend is healthy)."62
BACKUP="/root/archforge-caddy-before-https-fix-$(date -u +%Y%m%d-%H%M%S)"63
mkdir -m 700 -- "$BACKUP"64
cp -a -- "$CADDYFILE" "$BACKUP/Caddyfile"65
if [[ -f "$SITEFILE" ]]; then cp -a -- "$SITEFILE" "$BACKUP/archforge-status.caddy"; fi66
printf 'Backup directory: %s\n' "$BACKUP"68
RESTORED=false69
rollback() {70
printf '[!] Restoring previous Caddy configuration from %s\n' "$BACKUP" >&271
cp -a -- "$BACKUP/Caddyfile" "$CADDYFILE"72
systemctl restart caddy >/dev/null 2>&1 || true73
RESTORED=true74
}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.78
TMP=$(mktemp /etc/caddy/.archforge-caddy.XXXXXXXX)79
if [[ -f "$SITEFILE" ]] && \80
grep -Eq '^status\.archforge\.nl[[:space:]]*\{' "$SITEFILE" && \81
grep -Eq '^[[:space:]]*reverse_proxy[[:space:]]+127\.0\.0\.1:3000[[:space:]]*$' "$SITEFILE"; then82
cp -- "$SITEFILE" "$TMP"83
else84
af_render_caddy "$DOMAIN" '' > "$TMP"85
fi86
chmod 644 "$TMP"87
chown root:root "$TMP"88
if ! caddy validate --config "$TMP" --adapter caddyfile >/dev/null; then89
rm -f "$TMP"90
error 'New direct HTTPS configuration did not validate. Old files untouched.'91
fi93
# Force the domain block directly into the main Caddyfile. This intentionally94
# replaces only the stock HTTP :80 page on this dedicated status VPS.95
if ! mv -f -- "$TMP" "$CADDYFILE"; then96
rm -f "$TMP" || true97
error 'Could not write Caddyfile. Backup remains available.'98
fi99
if ! systemctl restart caddy; then100
log 'Caddy restart failed; printing errors and rolling back.'101
journalctl -u caddy -n 30 --no-pager || true102
rollback103
error 'Caddy failed to restart with HTTPS config.'104
fi105
sleep 2106
has_443() { ss -ltn | grep -Eq '(:|\])443[[:space:]]'; }107
if ! has_443; then108
log 'Service did not bind 443. Trying explicit reload of the new Caddyfile.'109
caddy reload --config "$CADDYFILE" >/dev/null 2>&1 || true110
sleep 2111
fi112
if ! has_443; then113
printf '\n[!] Caddy STILL DOES NOT LISTEN on TCP 443. This is not a firewall issue.\n' >&2114
systemctl show caddy -p ExecStart -p FragmentPath --no-pager >&2 || true115
journalctl -u caddy -n 40 --no-pager >&2 || true116
rollback117
error 'HTTPS listener missing. Previous configuration restored. Above is the actual Caddy failure.'118
fi120
log 'SUCCESS: Caddy now listens on TCP 443.'121
log 'Checking whether an ACME certificate is ready...'122
TLS_OK=false123
for _ in 1 2 3 4 5 6; do124
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; then125
TLS_OK=true126
break127
fi128
sleep 3129
done130
printf '\n===============================================\n'131
printf ' Domain: https://%s\n' "$DOMAIN"132
printf ' Admin: https://%s/admin/login\n' "$DOMAIN"133
printf ' HTTPS port: LISTENING (443/tcp)\n'134
if [[ "$TLS_OK" == true ]]; then135
printf ' Certificate: VERIFIED (valid TLS and status endpoint)\n'136
else137
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 || true141
fi142
printf ' Backup: %s\n' "$BACKUP"143
printf '===============================================\n'