c8cc2271a7ea1b73ed227f419dcac5bb187e8381
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c8cc2271a7 |
Support providers with multiple inbound signaling IPs (e.g. Anveo Direct)
VoIP.ms's one-POP-one-hostname model meant a single resolved IP was enough for the trunk's identify match=, but Anveo Direct (researched as an alternative after finding VoIP.ms's ToS selectively requires government ID at signup) sends inbound signaling from a fixed set of published IPs instead. Generalizes the pjsip include to accept multiple match= lines in one identify object (PJSIP allows repeating it to build one match set - no separate config objects needed per IP), with a new install prompt for any additional known source IPs beyond the auto-resolved one. No dial-prefix support was added despite earlier research suggesting Anveo Direct needed one - verified against their current official FAQ (fetched directly rather than trusting a stale search-result claim) that plain number dialing works with no prefix. Fixed a real bug caught while testing the update-mode round trip: .pstn-trunk.env was written with unquoted values, which breaks `source` entirely once any value contains a space - a multi-IP list or a multi-word provider name (e.g. "Anveo Direct") would make bash try to run the second word as a command. Predates this change (a multi-extension ring group has the same shape); only surfaced by actually re-sourcing the file and reapplying, not by inspecting generator output. Fixed by quoting every value. Updated docs/pstn-calling-voipms-plan.md with the Anveo Direct/DIDLogic/ SIP.US research findings and what's now implemented vs. still open (real-time balance enforcement at Anveo specifically is unconfirmed). |
||
|
|
876fd6553b |
Add inbound concurrency cap, bump defaults to 10/10, make caps live-editable
Adds an inbound concurrent-call cap mirroring the existing outbound one - outbound alone didn't protect against an inbound call-flood, which also costs money per-minute on VoIP.ms. Both defaults bumped from 3 to 10. Moves the cap numbers themselves out of static dialplan text and into a new pstn-limits.conf, read live via AST_CONFIG() the same way permission tiers already are - changing either cap takes effect on the next call, no Asterisk restart, no reinstall. "update in place" never touches this file, matching the existing protection for pstn-permissions.conf/.env/firewall/ Caddy config. Adds a concurrency-caps card to security-dashboard.sh's "PSTN Trunk" tab, above the existing permissions table, so both caps are visible and editable from the same web page. Tested against a real running instance of the Python app: default fallback when the file doesn't exist yet, save/persist, invalid-input rejection, and a bash-to-Python round trip on the generated file format. Inbound dialplan ordering mirrors outbound's existing pattern: permission check (is any ring-group member authorized for this caller) before the concurrency check, consistent with outbound's tier-check-then-busy-check order. |
||
|
|
3bd952e55d |
PSTN trunk: 3-tier live permissions + Security Dashboard web UI + dual target
Reworks the outbound permission model from a flat allow-list into three per-extension tiers (internal / restricted / full), addressing the ask for extensions that can only reach pre-approved numbers plus extensions with full US calling, while internal extension-to-extension dialing and ring groups stay ungated for everyone regardless of tier. Permissions now live in pstn-permissions.conf, read by the dialplan via Asterisk's AST_CONFIG() on every call instead of being baked into static dialplan text - editing that file takes effect on the next call, no Asterisk restart and no re-running the installer. "update in place" mode never touches this file (same protection this repo's update-mode convention already gives .env/firewall/Caddy config); only a "fresh" reinstall (with confirmation) or the web UI change it. Adds a "PSTN Trunk" tab to services/security-dashboard.sh: lists every extension (parsed from pjsip.conf) with its live tier and approved numbers, editable with no restart - this is what makes the tier model actually manageable day to day. Extracted the dashboard's systemd-unit writing into its own function so "update" mode refreshes it too (previously only fresh installs did), and generalized both the dashboard and the trunk service to detect either asterisk-digital-ocean or the home/LAN asterisk install. Inbound ring-group membership now checks each member's tier live per call via an unrolled per-member dialplan block (full always rings, restricted only if the caller's number is approved, internal never rings) rather than a single static Dial() string. Caught and fixed two real bugs during testing against a sandboxed vendor copy and a live instance of the (stdlib-only) Python dashboard app: - Asterisk Goto/GotoIf argument parsing: ring<ext>/skip<ext> are named priorities within the same extension (declared via "same => n(label),..."), not separate exten => entries, so jumping to them needs the single-argument Goto(label) form - the two-argument Goto(label,1) form used initially addresses a different, nonexistent extension named "label" instead. - A security-relevant REGEX() direction issue: the inbound Caller-ID check initially interpolated attacker-influenced call data into the PATTERN side of a REGEX() match rather than the tested-string side, which would let a crafted Caller-ID forge a match against an unrelated approved-numbers entry. Fixed by keeping the admin-controlled approved-list as the pattern and the live call data as the string being tested, consistently on both the outbound and inbound checks. Verified end-to-end: dialplan/pjsip generation and vendor-file patching (idempotent, syntax-checked) as before, plus the new permission-file round-trip between bash and Python, and the dashboard's new API endpoints exercised against a real running Python server (extension parsing, tier changes, number normalization, invalid-input rejection, atomic file writes). |
||
|
|
1e2a3743ab |
Rework PSTN trunk: role-based permissions, ring-groups, ntfy spend alerts
Generalizes services/pstn-trunk.sh (renamed from voipms-trunk.sh in the prior commit) away from VoIP.ms specifics - any IP-authenticated SIP provider works, VoIP.ms is just the suggested default. Adds: - Role-based outbound permission: a configurable allow-list of extensions that may dial PSTN numbers (regex-gated on CHANNEL(peername)), separate from internal extension-to-extension dialing which stays open to everyone regardless. Blank list preserves the original "everyone can dial out" behavior. - Inbound ring-group: rings a configurable list of extensions instead of a single hardcoded one. - ntfy alerts: immediate on denied (unauthorized extension) or rejected (concurrency cap hit) calls, plus an hourly cron-driven check that alerts once per month when estimated spend crosses a threshold and every hour call volume looks like a burst. Uses a self-contained pipe-delimited call log rather than Asterisk's CDR, to avoid depending on CDR module availability and CSV comma-quoting. - Settings persisted to .pstn-trunk.env so "update in place" reapplies everything from that file instead of fragile re-parsing out of generated Asterisk config (which had a real bug: update mode was extracting the wrong Dial(PJSIP/...) line). Tested end-to-end against a sandboxed copy of the real vendor files: permission-gate regex, ring-group dial-string construction, ntfy line injection/removal, and the usage-alert script's threshold/burst/monthly- dedup logic all verified with synthetic data. Caught and fixed a sed `&` escaping bug in the ring-group substitution before it shipped (RING_DIAL contains literal `&` join characters, which sed's replacement syntax otherwise treats as "insert the match"). |
||
|
|
6b0e378123 |
Rework PSTN trunk: role-based permissions, ring-groups, ntfy spend alerts
Renames services/voipms-trunk.sh to services/pstn-trunk.sh and generalizes it away from VoIP.ms specifics - any IP-authenticated SIP provider works, VoIP.ms is just the suggested default. Adds: - Role-based outbound permission: a configurable allow-list of extensions that may dial PSTN numbers (regex-gated on CHANNEL(peername)), separate from internal extension-to-extension dialing which stays open to everyone regardless. Blank list preserves the original "everyone can dial out" behavior. - Inbound ring-group: rings a configurable list of extensions instead of a single hardcoded one. - ntfy alerts: immediate on denied (unauthorized extension) or rejected (concurrency cap hit) calls, plus an hourly cron-driven check that alerts once per month when estimated spend crosses a threshold and every hour call volume looks like a burst. Uses a self-contained pipe-delimited call log rather than Asterisk's CDR, to avoid depending on CDR module availability and CSV comma-quoting. - Settings persisted to .pstn-trunk.env so "update in place" reapplies everything from that file instead of fragile re-parsing out of generated Asterisk config (which had a real bug: update mode was extracting the wrong Dial(PJSIP/...) line). Tested end-to-end against a sandboxed copy of the real vendor files: permission-gate regex, ring-group dial-string construction, ntfy line injection/removal, and the usage-alert script's threshold/burst/monthly- dedup logic all verified with synthetic data. Caught and fixed a sed `&` escaping bug in the ring-group substitution before it shipped (RING_DIAL contains literal `&` join characters, which sed's replacement syntax otherwise treats as "insert the match"). |