Fix ntfy deny-all default, add crowdsec update mode, presence alerts, embedded Asterisk admin tab
- ntfy.sh: auth-default-access was deny-all, silently blocking both publish and anonymous subscribe on every topic once an auth-file is present. Default to read-write (same model as public ntfy.sh) so alerts from crowdsec.sh/pstn-trunk.sh actually get delivered. - crowdsec.sh: add update/fresh/cancel reinstall gating — reruns previously re-asked every optional prompt (ASN exempt, geo-allowlist, ntfy, remote LAPI) unconditionally with no way to just refresh in place. - asterisk-digital-ocean.sh: add optional ntfy alerts on extension registration going offline/online, checked every 2 minutes via systemd timer (cron.d fallback), offered on both fresh install and update. - security-dashboard.sh: replace the outbound-only Asterisk Web Admin link with an embedded, lazy-loaded iframe tab, with a best-effort Caddy frame-ancestors patch and an always-available "open in new tab" fallback.
This commit is contained in:
@@ -293,6 +293,190 @@ $_ea_dir/logs/full {
|
||||
LOGROTATE
|
||||
}
|
||||
|
||||
# ── Shared: extension presence (online/offline) ntfy alerts ────────────────
|
||||
# Polls PJSIP registration state and alerts only on a CHANGE from the last
|
||||
# check (never on every poll) — same periodic-check shape as pstn-trunk.sh's
|
||||
# usage-alert script, but purely informational, so a looser 2-minute
|
||||
# interval is fine here (nothing enforces/blocks anything off the back of
|
||||
# this one). UNVERIFIED: the `pjsip show contacts` column layout below is
|
||||
# parsed defensively (grep for the Avail/Unavail keyword rather than a fixed
|
||||
# column position) specifically because it hasn't been confirmed against a
|
||||
# live install's actual output yet — run
|
||||
# `docker exec easy-asterisk-do asterisk -rx "pjsip show contacts"` yourself
|
||||
# after enabling this to confirm extensions/status actually show up as
|
||||
# expected, same as any other not-yet-live-tested piece in this project.
|
||||
_asterisk_do_write_presence_alert_script() {
|
||||
local FILE="$1" CONTAINER_NAME="$2" NTFY_URL="$3" STATE_FILE="$4"
|
||||
cat > "$FILE" << 'SCRIPT'
|
||||
#!/bin/bash
|
||||
# Auto-generated by services/asterisk-digital-ocean.sh — rerun the installer's
|
||||
# presence-alert step to change settings instead of editing this directly.
|
||||
CONTAINER_NAME="__PRESENCE_CONTAINER__"
|
||||
NTFY_URL="__PRESENCE_NTFY_URL__"
|
||||
STATE_FILE="__PRESENCE_STATE_FILE__"
|
||||
|
||||
[[ -z "$NTFY_URL" ]] && exit 0
|
||||
|
||||
send_ntfy() {
|
||||
curl -m 5 -s -d "$1" "$NTFY_URL" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
CURRENT="$(docker exec "$CONTAINER_NAME" asterisk -rx "pjsip show contacts" 2>/dev/null | grep '^ Contact:' | while read -r _ aor rest; do
|
||||
ext="${aor%%/*}"
|
||||
status="Unknown"
|
||||
case "$rest" in
|
||||
*Unavail*) status="Unavail" ;;
|
||||
*Avail*) status="Avail" ;;
|
||||
esac
|
||||
echo "${ext}:${status}"
|
||||
done)"
|
||||
|
||||
[[ -z "$CURRENT" ]] && exit 0
|
||||
|
||||
touch "$STATE_FILE"
|
||||
declare -A OLD_STATE
|
||||
while IFS=: read -r ext status; do
|
||||
[[ -n "$ext" ]] && OLD_STATE["$ext"]="$status"
|
||||
done < "$STATE_FILE"
|
||||
|
||||
: > "${STATE_FILE}.new"
|
||||
while IFS=: read -r ext status; do
|
||||
[[ -z "$ext" ]] && continue
|
||||
echo "${ext}:${status}" >> "${STATE_FILE}.new"
|
||||
old="${OLD_STATE[$ext]:-}"
|
||||
if [[ -n "$old" && "$old" != "$status" && "$status" != "Unknown" ]]; then
|
||||
if [[ "$status" == "Avail" ]]; then
|
||||
send_ntfy "Extension $ext is back online."
|
||||
elif [[ "$old" == "Avail" ]]; then
|
||||
send_ntfy "Extension $ext went offline."
|
||||
fi
|
||||
fi
|
||||
done <<< "$CURRENT"
|
||||
mv "${STATE_FILE}.new" "$STATE_FILE"
|
||||
SCRIPT
|
||||
sed -i "s#__PRESENCE_CONTAINER__#${CONTAINER_NAME}#g; s#__PRESENCE_NTFY_URL__#${NTFY_URL}#g; s#__PRESENCE_STATE_FILE__#${STATE_FILE}#g" "$FILE"
|
||||
chmod 755 "$FILE"
|
||||
}
|
||||
|
||||
_asterisk_do_install_presence_timer() {
|
||||
local EA_DIR="$1"
|
||||
mkdir -p "$EA_DIR/logs"
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system ]]; then
|
||||
cat > /etc/systemd/system/asterisk-presence-alert.service << SVCEOF
|
||||
[Unit]
|
||||
Description=Asterisk extension presence (online/offline) check
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/bash $EA_DIR/asterisk-presence-alert.sh
|
||||
StandardOutput=append:$EA_DIR/logs/asterisk-presence-alert.log
|
||||
StandardError=append:$EA_DIR/logs/asterisk-presence-alert.log
|
||||
SVCEOF
|
||||
|
||||
cat > /etc/systemd/system/asterisk-presence-alert.timer << SVCEOF
|
||||
[Unit]
|
||||
Description=Run the Asterisk presence check every 2 minutes
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=2min
|
||||
AccuracySec=10s
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
SVCEOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now asterisk-presence-alert.timer
|
||||
log_success "Presence check installed (systemd timer, every 2 minutes)."
|
||||
elif command -v cron >/dev/null 2>&1 || [[ -d /etc/cron.d ]]; then
|
||||
cat > /etc/cron.d/asterisk-presence-alert << CRON
|
||||
*/2 * * * * root /bin/bash $EA_DIR/asterisk-presence-alert.sh >> $EA_DIR/logs/asterisk-presence-alert.log 2>&1
|
||||
CRON
|
||||
log_success "Presence check installed (cron.d fallback — systemd not detected)."
|
||||
else
|
||||
log_warning "Neither systemd nor cron available — run $EA_DIR/asterisk-presence-alert.sh manually/periodically."
|
||||
fi
|
||||
}
|
||||
|
||||
_asterisk_do_remove_presence_timer() {
|
||||
systemctl disable --now asterisk-presence-alert.timer 2>/dev/null || true
|
||||
rm -f /etc/systemd/system/asterisk-presence-alert.timer /etc/systemd/system/asterisk-presence-alert.service
|
||||
rm -f /etc/cron.d/asterisk-presence-alert
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Interactive step — called from both the fresh-install flow and "update in
|
||||
# place" (always asked either way, same reasoning as pstn-trunk.sh's
|
||||
# international-calling step: this is a live-editable extra, not a
|
||||
# structural setting, so it doesn't belong exclusively to one path).
|
||||
_asterisk_do_run_presence_step() {
|
||||
local EA_DIR="$1"
|
||||
local SETTINGS_FILE="$EA_DIR/.presence-alert.env"
|
||||
local STATE_FILE="$EA_DIR/.presence-alert.state"
|
||||
|
||||
echo ""
|
||||
local _CUR_ENABLED="n" _CUR_NTFY=""
|
||||
if [[ -f "$SETTINGS_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$SETTINGS_FILE"
|
||||
_CUR_ENABLED="${PRESENCE_ENABLED:-n}"
|
||||
_CUR_NTFY="${PRESENCE_NTFY_URL:-}"
|
||||
fi
|
||||
|
||||
if [[ "$_CUR_ENABLED" == "y" ]]; then
|
||||
echo " Extension online/offline ntfy alerts are ON (topic: $_CUR_NTFY)."
|
||||
local _CHANGE=""
|
||||
prompt_yn " Change or disable this? (y/n):" "n" _CHANGE
|
||||
[[ "$_CHANGE" =~ ^[Yy]$ ]] || return 0
|
||||
local _DISABLE=""
|
||||
prompt_yn " Disable presence alerts entirely? (y/n):" "n" _DISABLE
|
||||
if [[ "$_DISABLE" =~ ^[Yy]$ ]]; then
|
||||
_asterisk_do_remove_presence_timer
|
||||
rm -f "$EA_DIR/asterisk-presence-alert.sh" "$STATE_FILE"
|
||||
cat > "$SETTINGS_FILE" << ENV
|
||||
PRESENCE_ENABLED="n"
|
||||
PRESENCE_NTFY_URL=""
|
||||
ENV
|
||||
log_success "Presence alerts disabled."
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
local _WANT=""
|
||||
prompt_yn "Send an ntfy alert when an extension's SIP registration goes offline / comes back online? (y/n):" "n" _WANT
|
||||
[[ "$_WANT" =~ ^[Yy]$ ]] || return 0
|
||||
fi
|
||||
|
||||
local _ntfy_default="${_CUR_NTFY:-https://ntfy.sh/asterisk-presence}"
|
||||
if [[ -z "$_CUR_NTFY" ]] && [[ -f "$DOCKER_DIR/ntfy/config/server.yml" ]]; then
|
||||
local _local_base_url
|
||||
_local_base_url="$(grep -oP '(?<=base-url: ")[^"]+' "$DOCKER_DIR/ntfy/config/server.yml" 2>/dev/null || true)"
|
||||
if [[ -n "$_local_base_url" ]] && [[ "$_local_base_url" != "https://ntfy.example.com" ]]; then
|
||||
_ntfy_default="${_local_base_url}/asterisk-presence"
|
||||
log_info "Detected a configured local ntfy instance at $_local_base_url — using it as the default."
|
||||
fi
|
||||
fi
|
||||
local PRESENCE_NTFY_URL=""
|
||||
prompt_text " ntfy topic URL:" "$_ntfy_default" PRESENCE_NTFY_URL
|
||||
if [[ -z "$PRESENCE_NTFY_URL" ]]; then
|
||||
log_warning "No topic entered — presence alerts not enabled."
|
||||
return 0
|
||||
fi
|
||||
|
||||
_asterisk_do_write_presence_alert_script "$EA_DIR/asterisk-presence-alert.sh" "easy-asterisk-do" "$PRESENCE_NTFY_URL" "$STATE_FILE"
|
||||
_asterisk_do_install_presence_timer "$EA_DIR"
|
||||
|
||||
cat > "$SETTINGS_FILE" << ENV
|
||||
PRESENCE_ENABLED="y"
|
||||
PRESENCE_NTFY_URL="${PRESENCE_NTFY_URL}"
|
||||
ENV
|
||||
chown "$ACTUAL_USER:$ACTUAL_USER" "$SETTINGS_FILE" 2>/dev/null || true
|
||||
log_success "Presence alerts enabled (checked every 2 minutes) — topic: $PRESENCE_NTFY_URL"
|
||||
log_info "Fires only on a state CHANGE, never every check — the first check after enabling"
|
||||
log_info "never alerts by itself, since there's no prior state to compare against yet."
|
||||
}
|
||||
|
||||
# ── Shared: docker-compose.yml ─────────────────────────────────────────────
|
||||
# Same reasoning as above — one copy of the template used by both fresh
|
||||
# installs and updates. Must be called with $PWD already at $EA_DIR.
|
||||
@@ -380,6 +564,9 @@ install_asterisk-digital-ocean() {
|
||||
echo "[DRY-RUN] Would reverse-proxy the web admin on the SAME FQDN used for SIP if Caddy is already installed (needed for cert sync)"
|
||||
echo "[DRY-RUN] Would offer local OR remote Authelia to protect the web admin, if either is already available"
|
||||
echo "[DRY-RUN] Would offer 'update in place' instead of a fresh install if $EA_DIR already exists"
|
||||
echo "[DRY-RUN] Would offer optional ntfy alerts on extension registration going offline/online"
|
||||
echo "[DRY-RUN] (checked every 2 minutes via systemd timer, cron.d fallback; always asked,"
|
||||
echo "[DRY-RUN] update mode included)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -411,6 +598,8 @@ install_asterisk-digital-ocean() {
|
||||
log_warning "docker compose up failed — check: docker compose -f $EA_DIR/docker-compose.yml logs"
|
||||
fi
|
||||
|
||||
_asterisk_do_run_presence_step "$EA_DIR"
|
||||
|
||||
local _EXISTING_DOMAIN _EXISTING_PORT
|
||||
_EXISTING_DOMAIN="$(grep -E '^DOMAIN_NAME=' .env | cut -d= -f2-)"
|
||||
_EXISTING_PORT="$(grep -E '^WEB_ADMIN_PORT=' .env | cut -d= -f2-)"
|
||||
@@ -839,6 +1028,9 @@ CADDY_BLOCK
|
||||
log_info "It auto-detects this asterisk-digital-ocean install and wires up SIP protection on its own."
|
||||
fi
|
||||
|
||||
# ── Extension presence (online/offline) ntfy alerts ────────────────────────
|
||||
_asterisk_do_run_presence_step "$EA_DIR"
|
||||
|
||||
# ── README ────────────────────────────────────────────────────────────────
|
||||
write_readme "$EA_DIR" << MD
|
||||
# Easy Asterisk PBX + coturn — DigitalOcean droplet edition
|
||||
|
||||
Reference in New Issue
Block a user