asterisk.sh: proactive stack health check in update mode
"update" mode's whole promise is leaving already-configured things alone — but that assumption silently breaks when something was configured but never fully wired up, and update never re-asks the questions that would reveal it. This session hit three separate instances of exactly that on one droplet revert: a domain set with no Caddy block, a Caddy block with no synced TLS cert (transport-tls fails to bind — "Unable to retrieve PJSIP transport 'transport-tls'", breaking every call), and a baked-in external IP left over from before the box moved. sms-inbound and (potentially) Security Dashboard/ntfy can have the identical "domain set, nothing serving it" gap with no way to discover it either, since their own update modes don't re-ask. _asterisk_run_stack_health_check(), called every "update", replaces the narrower Caddy-only check added last time: - Compares pjsip.conf's baked external_signaling_address against this box's actual current public IP; offers to rewrite it and restart. - Checks Asterisk's own DOMAIN_NAME has both a Caddy site block and a matching TLS cert in the container; offers to fix each independently. - Checks Security Dashboard / sms-inbound / ntfy (whichever are installed) for a matching Caddy site block, via a new lib/common.sh helper (caddy_domain_for_upstream) that finds the block without needing to already know the domain — none of these three services persist it anywhere. Points at that service's own "Full reinstall" (the only mode that re-asks) since fixing their config isn't this file's to script. The cert-sync fix needed a non-interactive hook into the vendored easy-asterisk CLI, which only exposed it as an interactive menu item (Server Settings -> Force re-sync Caddy certs). Added a --sync-caddy-cert flag via _asterisk_patch_cert_sync_cli(), patching the deployed vendor copy the same way _asterisk_patch_voicemail_vendor_files and friends already do — never vendor/ in git. Every check runs unconditionally (never opt-in, so a gap is never missed by nobody thinking to ask); every fix is individually opt-in and named as a real config change, unlike the rest of "update"'s no-side-effects default. Also factored the DO-metadata/ifconfig.me/hostname-I public-IP detection chain (previously duplicated 3 times) into _asterisk_current_public_ip(). Verified: caddy_domain_for_upstream against a multi-block Caddyfile (distinguishes same-prefix upstreams correctly); the full health check against fake docker/curl across every combination (all wired, IP mismatch declined/accepted, cert mismatch declined/accepted, dashboard unwired, sms-inbound wired vs. placeholder-domain, multi-instance ntfy with one wired and one not); and _asterisk_patch_cert_sync_cli's idempotency + resulting syntax against a real copy of the vendor script. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpKTLpwAgZNooTacWeQLuc
This commit is contained in:
@@ -416,6 +416,42 @@ _remove_caddy_site_block() {
|
||||
' "$caddy_file"
|
||||
}
|
||||
|
||||
# Read-only counterpart to _remove_caddy_site_block: returns (on stdout) the
|
||||
# domain of the local Caddy site block whose body contains
|
||||
# "reverse_proxy <upstream>" (same substring-match convention), or nothing
|
||||
# if there's no local Caddy, no Caddyfile, or no matching block. Never
|
||||
# modifies the Caddyfile — for services/asterisk.sh's stack health check
|
||||
# (and any future caller) to answer "is X actually wired into Caddy?"
|
||||
# without needing to already know the domain, since several services here
|
||||
# (security-dashboard, sms-inbound) never persist the domain they were
|
||||
# configured with anywhere — the Caddyfile is the only record of it.
|
||||
caddy_domain_for_upstream() {
|
||||
local upstream="$1"
|
||||
local caddyfile="$DOCKER_DIR/caddy/Caddyfile"
|
||||
[ -f "$caddyfile" ] || return 0
|
||||
awk -v upstream="$upstream" '
|
||||
BEGIN { depth = 0; candidate = ""; domain = ""; found = 0 }
|
||||
{
|
||||
line = $0
|
||||
opens = gsub(/\{/, "{", line)
|
||||
closes = gsub(/\}/, "}", line)
|
||||
if (depth == 0 && opens > 0) {
|
||||
header = $0
|
||||
sub(/[[:space:]]*\{.*$/, "", header)
|
||||
candidate = header
|
||||
depth += opens - closes
|
||||
next
|
||||
}
|
||||
if (depth > 0) {
|
||||
if (index($0, "reverse_proxy " upstream) > 0) { found = 1; domain = candidate }
|
||||
depth += opens - closes
|
||||
next
|
||||
}
|
||||
}
|
||||
END { if (found) print domain }
|
||||
' "$caddyfile"
|
||||
}
|
||||
|
||||
# Generic per-service removal: stops/removes its containers, its Caddy site
|
||||
# block (if any), any UFW rule tagged with its name, and optionally its
|
||||
# ~/docker/<name> directory. Scoped to the common case (a Docker service
|
||||
|
||||
Reference in New Issue
Block a user