Simplify pstn-trunk.sh: point to the dashboard instead of CLI permission prompts

Removed the full/restricted-extension, approved-numbers pool/whiptail,
messaging-extensions, and personal-DID-assignment prompts from the
installer. All four are already live-editable, no-restart-needed settings
the Security Dashboard's PSTN Trunk tab manages end to end — the CLI wall
of prompts (that then needed a full reinstall to change) just duplicated
that with more friction. Every extension now starts at internal tier (no
PSTN, no messaging) until granted via the dashboard. Kept: provider/DID
setup, concurrency caps, and the inbound ring-group (basic trunk wiring,
not a permission). Also updated the generated README and CLI summary to
match, and refreshed the stale "known gap" messaging section that predated
the dialplan enforcement built earlier this session.
This commit is contained in:
Claude
2026-07-23 17:16:42 +00:00
parent 3f033da635
commit a33b8e7b23
+40 -163
View File
@@ -1224,14 +1224,13 @@ install_pstn-trunk() {
echo "[DRY-RUN] Would require an existing asterisk-digital-ocean OR asterisk (LAN) install" echo "[DRY-RUN] Would require an existing asterisk-digital-ocean OR asterisk (LAN) install"
echo "[DRY-RUN] Would prompt for: known-provider quick-pick (Anveo Direct/VoIP.ms pre-fill known" echo "[DRY-RUN] Would prompt for: known-provider quick-pick (Anveo Direct/VoIP.ms pre-fill known"
echo "[DRY-RUN] server/signaling-IP values; still editable) or manual entry, SIP provider name, DID," echo "[DRY-RUN] server/signaling-IP values; still editable) or manual entry, SIP provider name, DID,"
echo "[DRY-RUN] full-PSTN extensions, restricted-PSTN extensions + their approved numbers," echo "[DRY-RUN] max concurrent outbound/inbound calls (default 10/10), inbound ring-group extensions,"
echo "[DRY-RUN] internal SIP messaging extensions (separate from PSTN calling permission),"
echo "[DRY-RUN] optional personal-number assignments (DID -> owner extension, additive to the"
echo "[DRY-RUN] shared trunk DID), max concurrent outbound/inbound calls (default 10/10),"
echo "[DRY-RUN] inbound ring-group extensions,"
echo "[DRY-RUN] ntfy alert topic (optional), international-calling allow-list (CLI-only," echo "[DRY-RUN] ntfy alert topic (optional), international-calling allow-list (CLI-only,"
echo "[DRY-RUN] always asked, never on the web dashboard), per-minute rate + monthly/hourly" echo "[DRY-RUN] always asked, never on the web dashboard), per-minute rate + monthly/hourly"
echo "[DRY-RUN] alert thresholds, and an optional hard monthly spend-cap kill-switch" echo "[DRY-RUN] alert thresholds, and an optional hard monthly spend-cap kill-switch"
echo "[DRY-RUN] Would NOT prompt for who can call/be called, messaging, or personal numbers —"
echo "[DRY-RUN] all managed live via the Security Dashboard's PSTN Trunk tab instead; every"
echo "[DRY-RUN] extension defaults to 'internal' (no PSTN, no messaging) until granted there"
echo "[DRY-RUN] Would resolve the server hostname to an IP, plus prompt for any additional" echo "[DRY-RUN] Would resolve the server hostname to an IP, plus prompt for any additional"
echo "[DRY-RUN] known source IPs (some providers publish a fixed list), for inbound call matching" echo "[DRY-RUN] known source IPs (some providers publish a fixed list), for inbound call matching"
echo "[DRY-RUN] Would patch vendor generator functions to #include the trunk config" echo "[DRY-RUN] Would patch vendor generator functions to #include the trunk config"
@@ -1427,78 +1426,25 @@ install_pstn-trunk() {
return 1 return 1
fi fi
# ── Permission tiers ─────────────────────────────────────────────────── # ── Permission tiers, messaging, personal numbers — all managed via the
# Security Dashboard, not prompted here ────────────────────────────────
# This used to prompt for full/restricted extensions, approved numbers,
# messaging extensions, and personal-DID assignments right here at
# install time. All four are live-editable, no-restart-needed settings
# in pstn-permissions.conf / pstn-personal-dids.conf that the Security
# Dashboard's PSTN Trunk tab already manages end to end — duplicating
# that as a wall of CLI prompts (that you'd then have to redo via a full
# reinstall to change) added friction the dashboard already solves
# better. Every extension defaults to "internal" (no PSTN, no
# messaging, no personal number) until granted otherwise there.
echo "" echo ""
echo " Three PSTN permission tiers. Below, you'll enter EXTENSION NUMBERS at each" log_info "Who can call/be called, internal SIP messaging, and personal numbers are"
echo " prompt (e.g. 999, 213) — never the tier name itself:" log_info "all managed from the Security Dashboard's PSTN Trunk tab (not here) — install"
echo " internal — call/receive other Asterisk extensions + internal ring" log_info "it if you haven't: sudo ./setup.sh security-dashboard. Every extension starts"
echo " groups only. No PSTN at all. The default for any extension" log_info "at 'internal' (no PSTN, no messaging) until you grant it there; changes apply"
echo " not entered at either prompt below — nothing to type for it." log_info "live, no restart or reinstall needed."
echo " restricted — internal, PLUS call/receive ONLY pre-approved US numbers." local FULL_EXTS="" RESTRICTED_EXTS="" RESTRICTED_ARGS=()
echo " full — internal, PLUS call/receive ANY US number." local MESSAGING_EXTS="" PERSONAL_DID_PAIRS=() PERSONAL_DID_ASSIGNMENTS=""
echo " Live-editable after install (pstn-permissions.conf) — via the Security"
echo " Dashboard web UI if installed, or by hand — no restart/reinstall needed."
local FULL_EXTS=""
prompt_text "Extension NUMBERS to grant FULL PSTN access (space-separated, e.g. '999 213', blank = none):" "" FULL_EXTS
local RESTRICTED_EXTS=""
prompt_text "Extension NUMBERS to grant RESTRICTED PSTN access (space-separated, e.g. '301', blank = none):" "" RESTRICTED_EXTS
local RESTRICTED_ARGS=()
if [[ -n "$RESTRICTED_EXTS" ]]; then
# Shared pool, entered once — faster than retyping the same numbers
# per extension when several extensions overlap. Picking per
# extension then uses a whiptail checklist (multi-select, toggle
# with space) against this pool if whiptail is available and this
# isn't an unattended run; otherwise falls back to typing numbers
# directly (or "all" for the whole pool) per extension, same as
# before this existed.
echo ""
echo " Optional: enter a shared pool of approved numbers ONCE below, then pick"
echo " which ones apply to each restricted extension next — instead of retyping"
echo " the same numbers for every extension that shares them."
local MASTER_NUMS_RAW="" MASTER_NUMS=()
prompt_text " Approved-numbers pool (comma/space-separated, 11-digit US numbers, e.g. '15551234567 15559876543', blank = enter per-extension instead):" "" MASTER_NUMS_RAW
if [[ -n "$MASTER_NUMS_RAW" ]]; then
local _pool_n
while IFS= read -r _pool_n; do
[[ -n "$_pool_n" ]] && MASTER_NUMS+=("$_pool_n")
done < <(echo "$MASTER_NUMS_RAW" | tr ', ' '\n\n' | grep -E '^[0-9]{11}$' | sort -u)
if [[ ${#MASTER_NUMS[@]} -eq 0 ]]; then
log_warning "No valid 11-digit numbers found in that pool — falling back to per-extension entry."
else
log_success "Pool: ${#MASTER_NUMS[@]} number(s) — ${MASTER_NUMS[*]}"
fi
fi
local _ext _raw_nums _clean_nums
for _ext in $RESTRICTED_EXTS; do
_clean_nums=""
if [[ ${#MASTER_NUMS[@]} -gt 0 ]] && command -v whiptail >/dev/null 2>&1 && [[ "$UNATTENDED" != true ]]; then
local _wt_args=() _wt_n _selected
for _wt_n in "${MASTER_NUMS[@]}"; do
_wt_args+=("$_wt_n" "" "off")
done
_selected="$(whiptail --title "Extension $_ext" --checklist \
"Approved numbers for extension $_ext (space to toggle, Enter to confirm):" \
20 70 10 "${_wt_args[@]}" 3>&1 1>&2 2>&3)"
[[ -n "$_selected" ]] && _clean_nums="$(echo "$_selected" | tr -d '"' | tr ' ' '\n' | paste -sd'|' -)"
else
prompt_text " Approved numbers for extension $_ext (comma/space-separated, 11-digit US numbers, e.g. 15551234567, or 'all' for the whole pool above):" "" _raw_nums
if [[ "$_raw_nums" == "all" && ${#MASTER_NUMS[@]} -gt 0 ]]; then
_clean_nums="$(printf '%s\n' "${MASTER_NUMS[@]}" | paste -sd'|' -)"
else
_clean_nums="$(echo "$_raw_nums" | tr ', ' '\n\n' | grep -E '^[0-9]{11}$' | paste -sd'|' - 2>/dev/null)"
fi
fi
if [[ -z "$_clean_nums" ]]; then
log_warning "No valid 11-digit numbers entered for $_ext — it will be restricted with an EMPTY"
log_warning "approved list, meaning no PSTN number can currently reach/be reached by it until"
log_warning "you add some (via the Security Dashboard or by editing pstn-permissions.conf)."
fi
RESTRICTED_ARGS+=("$_ext" "$_clean_nums")
done
fi
echo "" echo ""
echo " Concurrent-call caps (both directions) are also live — changeable later via" echo " Concurrent-call caps (both directions) are also live — changeable later via"
@@ -1516,70 +1462,13 @@ install_pstn-trunk() {
MAX_INBOUND=10 MAX_INBOUND=10
fi fi
local _suggested_ring
_suggested_ring="$(echo "$FULL_EXTS $RESTRICTED_EXTS" | xargs)"
local RING_EXTS="" local RING_EXTS=""
prompt_text "Extensions to ring for inbound PSTN calls (space-separated — one, or several for a ring group; only full/restricted-tier members will actually ring):" "$_suggested_ring" RING_EXTS prompt_text "Extensions to ring for inbound PSTN calls (space-separated — one, or several for a ring group; only full/restricted-tier members will actually ring, once granted via the dashboard):" "" RING_EXTS
if [[ -z "$RING_EXTS" ]]; then if [[ -z "$RING_EXTS" ]]; then
log_error "At least one extension is required for inbound routing — aborting." log_error "At least one extension is required for inbound routing — aborting."
return 1 return 1
fi fi
# ── Internal SIP messaging — a separate axis from PSTN calling ─────────
# Asterisk's native SIP MESSAGE (extension-to-extension texting) has no
# cost/carrier involvement at all, unlike PSTN calling, so it gets its
# own independent flag in pstn-permissions.conf rather than being folded
# into the internal/restricted/full tiers above — an extension can be
# "internal" for calling (no PSTN) and still messaging-enabled, or vice
# versa. Off by default, same "opt in" posture as PSTN access.
echo ""
echo " Asterisk also supports native SIP texting between extensions (no carrier"
echo " SMS, no PSTN, no cost) — a separate permission from PSTN calling above."
local MESSAGING_EXTS=""
prompt_text "Extensions allowed to use internal SIP messaging (space-separated, blank = none):" "" MESSAGING_EXTS
# ── Personal numbers — optional, additive to the shared trunk DID ──────
# Multiple DIDs can share this one trunk/account. Assigning one to a
# specific extension makes inbound calls to it ring ONLY that extension
# (still gated by that extension's own tier/approved-numbers — a
# personal DID doesn't bypass PSTN permission, it just narrows routing
# from "the shared ring group" to "this one owner"), and makes that
# extension's outbound calls show its own DID as Caller-ID instead of
# the shared one. The shared DID/ring-group above is unaffected either
# way — this is purely additive.
echo ""
echo " Personal numbers (optional): assign specific DIDs to specific extensions."
echo " Inbound calls to that DID ring only its owner; outbound calls from that"
echo " extension show its own DID as Caller-ID. Requires the owner to also be"
echo " full or restricted tier to actually receive anything on it."
local WANT_PERSONAL_DIDS=""
prompt_yn "Assign any personal DIDs now? (y/n):" "n" WANT_PERSONAL_DIDS
local PERSONAL_DID_PAIRS=() PERSONAL_DID_ASSIGNMENTS=""
if [[ "$WANT_PERSONAL_DIDS" =~ ^[Yy]$ ]]; then
local _pd_more="y"
while [[ "$_pd_more" =~ ^[Yy]$ ]]; do
local _pd_did="" _pd_owner=""
prompt_text " DID (10-digit US number, digits only):" "" _pd_did
if [[ "$_pd_did" =~ ^[0-9]{10}$ ]]; then
prompt_text " Owner extension for $_pd_did:" "" _pd_owner
if [[ "$_pd_owner" =~ ^[0-9]+$ ]]; then
PERSONAL_DID_PAIRS+=("$_pd_did" "$_pd_owner")
PERSONAL_DID_ASSIGNMENTS="${PERSONAL_DID_ASSIGNMENTS} ${_pd_owner}=${_pd_did}"
if [[ " $FULL_EXTS $RESTRICTED_EXTS " != *" $_pd_owner "* ]]; then
log_warning "Extension $_pd_owner isn't full/restricted tier yet — it won't actually"
log_warning "receive calls on $_pd_did until you also grant it one of those tiers."
fi
log_success "Will assign $_pd_did to extension $_pd_owner."
else
log_warning "Not a valid extension — skipped."
fi
else
log_warning "Not a valid 10-digit DID — skipped."
fi
prompt_yn " Assign another? (y/n):" "n" _pd_more
done
fi
echo "" echo ""
local WANT_NTFY="" local WANT_NTFY=""
prompt_yn "Send an ntfy alert when a call is denied (permission tier/approved-number check failed) or rejected (concurrency cap hit)? (y/n):" "y" WANT_NTFY prompt_yn "Send an ntfy alert when a call is denied (permission tier/approved-number check failed) or rejected (concurrency cap hit)? (y/n):" "y" WANT_NTFY
@@ -1748,11 +1637,9 @@ background, cost estimate, and toll-fraud reasoning.
| Server/POP | ${TRUNK_SERVER} (inbound match IPs: ${TRUNK_SERVER_IPS}) | | Server/POP | ${TRUNK_SERVER} (inbound match IPs: ${TRUNK_SERVER_IPS}) |
| DID | ${TRUNK_DID} | | DID | ${TRUNK_DID} |
| Outbound scope | US/NANP only — \`_1NXXNXXXXX\` / \`_NXXNXXXXX\` patterns, no catch-all, minus 27 non-US/premium NANP area codes (see below) | | Outbound scope | US/NANP only — \`_1NXXNXXXXX\` / \`_NXXNXXXXX\` patterns, no catch-all, minus 27 non-US/premium NANP area codes (see below) |
| Full-PSTN extensions | ${FULL_EXTS:-none} | | Permission tiers, messaging, personal numbers | Managed live via the Security Dashboard's PSTN Trunk tab — not set at install, so not shown here (this file isn't regenerated when you change them there). Everyone starts at \`internal\` (no PSTN, no messaging) until granted. |
| Restricted-PSTN extensions | ${RESTRICTED_EXTS:-none} |
| Concurrency caps | ${MAX_OUTBOUND} outbound / ${MAX_INBOUND} inbound simultaneous calls (live — see \`pstn-limits.conf\` below) | | Concurrency caps | ${MAX_OUTBOUND} outbound / ${MAX_INBOUND} inbound simultaneous calls (live — see \`pstn-limits.conf\` below) |
| Inbound ring-group | ${RING_EXTS} | | Inbound ring-group | ${RING_EXTS} |
| Internal SIP messaging extensions | ${MESSAGING_EXTS:-none} (separate from PSTN calling permission — see below) |
| ntfy alerts | ${NTFY_URL:-disabled} | | ntfy alerts | ${NTFY_URL:-disabled} |
| Estimated rate | \$${RATE_PER_MIN}/min | | Estimated rate | \$${RATE_PER_MIN}/min |
| Monthly spend alert threshold | \$${MONTH_THRESHOLD} | | Monthly spend alert threshold | \$${MONTH_THRESHOLD} |
@@ -1916,31 +1803,19 @@ permission tiers.
Asterisk's native SIP \`MESSAGE\` support (extension-to-extension texting — Asterisk's native SIP \`MESSAGE\` support (extension-to-extension texting —
no carrier SMS, no PSTN, no cost) is gated by a \`messaging=yes\` flag per no carrier SMS, no PSTN, no cost) is gated by a \`messaging=yes\` flag per
extension in \`pstn-permissions.conf\`, independent of the PSTN calling extension in \`pstn-permissions.conf\`, independent of the PSTN calling
tiers above — off by default, same "opt in" posture. Currently enabled for: tiers above — off by default, same "opt in" posture. Live-editable any
${MESSAGING_EXTS:-none}. Live-editable any time via the Security time via the Security Dashboard's "PSTN Trunk" tab, in its own
Dashboard's "PSTN Trunk" tab, in its own always-available "Internal SIP always-available "Internal SIP messaging" card — no dependency on this
messaging" card — no need to re-run this installer, and no dependency on trunk (or any PSTN trunk at all) being installed.
this trunk (or any PSTN trunk at all) being installed, unlike the
calling-permissions table below it in that same tab.
**Won't show up in Easy Asterisk's own web admin, by design** — same as Actually enforced, not just a flag — \`services/asterisk-digital-ocean.sh\`
the PSTN calling tiers, this is a permission this repo layers on top, (and \`services/asterisk.sh\` for the LAN edition) routes messages through a
not an Easy Asterisk feature, so it's only manageable here or via the dedicated \`[sip-messaging]\` dialplan context (separate from \`[intercom]\`'s
Security Dashboard. own per-device call routing, so there's no collision risk) and checks this
same flag via \`AST_CONFIG()\` before delivering. One caveat still flagged
**Known gap:** the flag above is real and live-editable, but the actual rather than papered over: the \`MESSAGE(from)\` sender-extraction hasn't
SIP \`MESSAGE\` routing dialplan wiring — does Asterisk actually deliver/ been confirmed against real MESSAGE traffic on a live install — it fails
gate a message using this flag — depends on how Easy Asterisk's own closed (denies) if it ever parses wrong, but worth a live test.
generated \`extensions.conf\`/\`pjsip.conf\` route inbound messages, which
needs to be verified against a live install before it's safely automated
here. Shipping a guessed pattern risked either silently not working or
interfering with call-routing precedence in the same \`[intercom]\`
context, so it hasn't been guessed at. If you want this working end to
end, the fastest path is checking a few things on a live box (e.g.
whether an endpoint has \`message_context\` set, and what happens when you
send a test SIP MESSAGE to one) so the dialplan gate can be built against
real behavior instead of assumption — ask if you want to walk through
that.
## Personal numbers ## Personal numbers
@@ -2026,10 +1901,12 @@ MD
echo " DID: $TRUNK_DID" echo " DID: $TRUNK_DID"
echo " Outbound: US/NANP only, max $MAX_OUTBOUND concurrent calls" echo " Outbound: US/NANP only, max $MAX_OUTBOUND concurrent calls"
echo " Inbound: max $MAX_INBOUND concurrent calls" echo " Inbound: max $MAX_INBOUND concurrent calls"
echo " Full-PSTN extensions: ${FULL_EXTS:-none}"
echo " Restricted extensions: ${RESTRICTED_EXTS:-none}"
echo " Inbound ring-group: $RING_EXTS" echo " Inbound ring-group: $RING_EXTS"
echo " ntfy alerts: ${NTFY_URL:-disabled}" echo " ntfy alerts: ${NTFY_URL:-disabled}"
echo " Docs: $DOC_FILE" echo " Docs: $DOC_FILE"
echo "" echo ""
log_info "Everyone's at 'internal' tier (no PSTN, no messaging) until you grant access"
log_info "via the Security Dashboard's PSTN Trunk tab — sudo ./setup.sh security-dashboard"
log_info "if it isn't installed yet."
echo ""
} }