Fold asterisk-digital-ocean into asterisk with droplet auto-detection
services/asterisk-digital-ocean.sh was a near-verbatim copy of services/asterisk.sh — same vendor refresh, compose template, messaging dialplan, presence alerts, UFW rules and dashboard/trunk chaining, with the helper functions renamed _asterisk_do_*. Two copies meant every fix had to land twice, and several never did. There is now one `asterisk` service. It reads the DigitalOcean metadata service and asks either way (so a droplet with metadata blocked, or another provider's public VM, can still opt in), then gates the genuinely droplet-specific behaviour on that one answer: swapfile for low-RAM plans, public-FQDN-only setup with no LAN/VLAN prompts, a Caddy site block pinned to that FQDN, the remote-Authelia option, and the doctl Cloud Firewall. Two things that were droplet-only for no real reason now apply everywhere: the entrypoint patch that writes security-level events to logs/full, and the logrotate config for that file. Without them the Security Dashboard's Security Log tab and CrowdSec's Asterisk acquisition were silently empty on every home/LAN install; crowdsec.sh now detects either install directory. Existing droplets are left alone: an install at ~/docker/asterisk-digital-ocean keeps its directory and easy-asterisk-do container names, since its Caddyfile block, UFW rules, Cloud Firewall, CrowdSec acquisition and PSTN trunk all name those exactly. New installs use ~/docker/asterisk / easy-asterisk. `sudo ./setup.sh asterisk-digital-ocean` still works via a new SERVICE_ALIAS map in setup.sh, without a second menu entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAddJGE1G6eGaPzmScG5Vh
This commit is contained in:
@@ -18,7 +18,7 @@ checklist per group, and calls `install_<name>()` for each selected item.
|
||||
1. Create `services/<name>.sh` (kebab-case filename)
|
||||
2. Call `register_service` at the top of the file
|
||||
3. Define `install_<name>()` — keep hyphens **literal** in the function name
|
||||
(`install_asterisk-digital-ocean`, not `install_asterisk_digital_ocean`).
|
||||
(`install_pstn-trunk`, not `install_pstn_trunk`).
|
||||
`setup.sh`'s dispatcher calls `install_${name}` with no hyphen→underscore
|
||||
conversion, so the function name must match the service name exactly.
|
||||
Confirmed live: a mismatched underscore here produces
|
||||
@@ -29,6 +29,33 @@ That's it. The menu picks it up on the next run.
|
||||
Also update the **Services table in `README.md`** — add the service name to the
|
||||
appropriate group row so the README stays current.
|
||||
|
||||
## Retiring a service name (merging two services)
|
||||
|
||||
Deleting `services/<name>.sh` removes it from the menu, but `sudo ./setup.sh
|
||||
<name>` then fails outright for anyone with that name in their notes, docs, or
|
||||
shell history. Add the old name to `SERVICE_ALIAS` in `setup.sh` instead —
|
||||
`run_service` resolves it to the surviving service, says so once, and runs
|
||||
that. The alias never gets its own menu entry, which is the whole point.
|
||||
|
||||
`services/asterisk-digital-ocean.sh` was merged into `services/asterisk.sh`
|
||||
this way: one installer that detects a DigitalOcean droplet (metadata service,
|
||||
with a y/n either way) and applies the droplet-only extras — swapfile,
|
||||
public-FQDN-only flow, hand-built Caddy site block, remote Authelia, Cloud
|
||||
Firewall — behind that one answer. Two lessons worth reusing:
|
||||
|
||||
- **Don't rename a live install's directory or containers.** New installs
|
||||
land in `~/docker/asterisk` with `easy-asterisk`; a pre-merge droplet keeps
|
||||
`~/docker/asterisk-digital-ocean` and `easy-asterisk-do`, because its
|
||||
Caddyfile block, UFW rules, Cloud Firewall, CrowdSec acquisition and PSTN
|
||||
trunk all name those exact paths. `_asterisk_resolve_layout()` picks
|
||||
whichever exists, and every sibling service probes both.
|
||||
- **Check whether a "flavor-specific" behavior was actually flavor-specific.**
|
||||
The Asterisk security-logging patch and the `logs/full` logrotate config
|
||||
were droplet-only purely because that's where they got written first — the
|
||||
Security Dashboard's Security Log and CrowdSec's Asterisk acquisition were
|
||||
silently empty on every home/LAN install as a result. Both now apply
|
||||
everywhere.
|
||||
|
||||
## Minimal Docker service template
|
||||
|
||||
```bash
|
||||
@@ -175,15 +202,16 @@ the auth server's own access-control rules say. Confirmed live: this was
|
||||
the actual cause of a "Caddy proxies fine but Authelia never prompts for
|
||||
login" bug, on a site block that otherwise looked completely correct. If a
|
||||
service builds its own site block instead of using this helper (e.g.
|
||||
`services/asterisk-digital-ocean.sh` does, deliberately, see its own
|
||||
comment for why), put its auth block first there too.
|
||||
`services/asterisk.sh` does in droplet mode, deliberately — see
|
||||
`_asterisk_configure_caddy_public`'s comment for why), put its auth block
|
||||
first there too.
|
||||
|
||||
**`forward_auth` to a remote Authelia over a scheme-qualified URL needs
|
||||
explicit `header_up` pins.** A bare `forward_auth authelia:9091` (Authelia on
|
||||
the same Docker network, one hop) is fine relying on Caddy's default
|
||||
`X-Forwarded-*` headers. But `forward_auth https://auth.example.com { ... }`
|
||||
(Authelia on a *different* machine, reached over its own public domain+TLS —
|
||||
see `services/asterisk-digital-ocean.sh`'s remote-Authelia prompt) is a
|
||||
see `services/asterisk.sh`'s droplet-mode remote-Authelia prompt) is a
|
||||
second Caddy hop: Caddy rewrites the outgoing request's `Host` header to
|
||||
`auth.example.com` so the remote Caddy can route/SNI-match it, and without an
|
||||
override `X-Forwarded-Host` picks up that rewritten value instead of the
|
||||
@@ -221,9 +249,10 @@ Use this to skip opening a host firewall port for a service Caddy already
|
||||
fronts *locally* (it reaches the service over `host.docker.internal`, not
|
||||
the network) — but still open it when `CADDY_SERVICE_MODE` is `"remote"`,
|
||||
since a remote Caddy machine needs to reach this host over the network
|
||||
instead. See `services/asterisk.sh` and `services/asterisk-digital-ocean.sh`
|
||||
for the reference pattern: call `configure_caddy_for_service` *before*
|
||||
building firewall rules, not after, so the decision is known in time.
|
||||
instead. See `services/asterisk.sh` for the reference pattern: it decides
|
||||
the Caddy question *before* building firewall rules, not after, so the
|
||||
answer is known in time (in droplet mode it hand-builds its own site block
|
||||
and sets the same flag itself, for the reasons noted above).
|
||||
|
||||
### UFW enable
|
||||
|
||||
@@ -252,8 +281,7 @@ blocks that too and silently breaks the service (confirmed live: closing
|
||||
the web admin port outright took Caddy down with it). Call
|
||||
`ufw_allow_from_caddy_net` right after the `delete` to re-open the port
|
||||
scoped to just `caddy_net`'s subnet — reachable from Caddy, not from the
|
||||
internet. See `services/asterisk-digital-ocean.sh` and
|
||||
`services/asterisk.sh` for the pattern.
|
||||
internet. See `services/asterisk.sh` for the pattern.
|
||||
|
||||
### README generation
|
||||
|
||||
@@ -298,7 +326,7 @@ on the same machine anyway. See `add_authelia_domain()` in `services/authelia.sh
|
||||
|
||||
**Running a genuinely separate instance (e.g. one per machine).** `services/authelia.sh`
|
||||
runs standalone on any box (`sudo bash authelia.sh`, same pattern as `crowdsec.sh`) and
|
||||
`asterisk-digital-ocean.sh` already auto-detects a local install (`if [ -d
|
||||
`asterisk.sh` already auto-detects a local install (`if [ -d
|
||||
"$DOCKER_DIR/authelia" ]`), switching from the remote-Authelia `forward_auth` flow to the
|
||||
local `import authelia` snippet automatically — so a second, fully independent instance on
|
||||
another machine (e.g. a droplet, for resilience if the first machine goes down) works with
|
||||
@@ -429,9 +457,8 @@ rules, or reverse-proxy/SSO config that's already in place. If the
|
||||
vendor-copy or `docker-compose.yml`-generation logic is more than a few
|
||||
lines, factor it into a helper function so the fresh-install path and the
|
||||
update path share one copy instead of drifting apart — see
|
||||
`_asterisk_do_refresh_vendor_files`/`_asterisk_do_write_compose` in
|
||||
`services/asterisk-digital-ocean.sh` (and their `_asterisk_*` counterparts in
|
||||
`services/asterisk.sh`) for the reference pattern.
|
||||
`_asterisk_refresh_vendor_files`/`_asterisk_write_compose` in
|
||||
`services/asterisk.sh` for the reference pattern.
|
||||
|
||||
`cancel` must leave the install completely untouched — it's the default for
|
||||
a reason (a stray Enter on a service you're just checking on shouldn't
|
||||
@@ -443,9 +470,9 @@ would, prompts included.
|
||||
A service can call another service's `install_<name>()` directly as a
|
||||
convenience step at the end of its own flow, instead of making the user
|
||||
remember to separately run `sudo ./setup.sh <other-name>` afterward.
|
||||
`services/asterisk.sh`/`services/asterisk-digital-ocean.sh` do this for
|
||||
`services/asterisk.sh` does this for
|
||||
`services/security-dashboard.sh` and `services/pstn-trunk.sh` — after
|
||||
Asterisk itself is installed/updated, each asks once whether to also set up
|
||||
Asterisk itself is installed/updated, it asks once whether to also set up
|
||||
the dashboard and/or a PSTN trunk (or, if either is already installed,
|
||||
silently re-invokes it so it gets refreshed as part of the same run — its
|
||||
own `prompt_reinstall_mode` gate decides update vs. skip, so this never
|
||||
@@ -501,7 +528,7 @@ it, so as long as your `install_<name>()` calls `require_docker` before
|
||||
`docker compose up` (it always should), the network is guaranteed to exist
|
||||
regardless of whether Caddy itself has been installed yet.
|
||||
|
||||
**`network_mode: host` services (e.g. `asterisk`/`asterisk-digital-ocean`) don't join
|
||||
**`network_mode: host` services (e.g. `asterisk`) don't join
|
||||
`caddy_net` at all** — Caddy reaching them (or anything else on the host
|
||||
network) needs `host.docker.internal:PORT` in the Caddyfile, not
|
||||
`localhost:PORT` or a container name. Caddy's own compose file
|
||||
|
||||
@@ -67,7 +67,7 @@ a ready-to-copy Caddy config snippet to `~/docker/caddy-snippets/`.
|
||||
| Group | Services |
|
||||
|-------|---------|
|
||||
| `base` | `net-tools`, `ncdu`, `git`, `curl`, `wget`, `htop`, `tree`, `zip`/`unzip`, `ca-certificates`, `gnupg`, `jq`, `rsync`; `glow` (terminal markdown reader, Charm apt repo); Docker CE + Compose plugin; `openssh-server` with GitHub/Launchpad SSH key import, optional password-auth lockdown, and SSH Host aliases; optional NetBird overlay network |
|
||||
| `homelab` | `caddy`, `crowdsec`, `authelia`, `homeassistant`, `asterisk`, `asterisk-digital-ocean`, `pstn-trunk`, `security-dashboard`, `sunshine` |
|
||||
| `homelab` | `caddy`, `crowdsec`, `authelia`, `homeassistant`, `asterisk`, `pstn-trunk`, `security-dashboard`, `sunshine` |
|
||||
| `utilities` | `actualbudget`, `ai-gpu`, `ai-stack`, `archivebox`, `changedetection`, `ddclient`, `filebrowser`, `fmd`, `gatus`, `homebox`, `iopaint`, `joplin`, `koha`, `magicmirror`, `mail-archiver`, `mattermost`, `mealie`, `meshcentral`, `n8n`, `nextcloud`, `ntfy`, `onlyoffice`, `paintplus`, `portainer`, `rustdesk`, `stirling-pdf`, `syncthing`, `traccar`, `unifi`, `uptimekuma`, `vaultwarden`, `watchyourlan`, `watchtower`, `wg-easy` |
|
||||
| `media` | `arm`, `audiobookshelf`, `calibre-web`, `emby`, `immich`, `jellyfin`, `lyrion` |
|
||||
| `cameras` | `frigate`, `frigate-audio`, `frigate-notify`, `sky-cam` |
|
||||
@@ -91,7 +91,6 @@ homelab
|
||||
authelia
|
||||
homeassistant
|
||||
asterisk
|
||||
asterisk-digital-ocean
|
||||
pstn-trunk
|
||||
security-dashboard
|
||||
sunshine
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Anveo Direct + Easy Asterisk — confirmed working setup guide
|
||||
|
||||
This is the exact sequence that got a real Anveo Direct DID working end to
|
||||
end (both outbound and inbound) with `asterisk-digital-ocean.sh` +
|
||||
`pstn-trunk.sh`, confirmed live on a real droplet.
|
||||
end (both outbound and inbound) with `asterisk.sh` + `pstn-trunk.sh`,
|
||||
confirmed live on a real droplet.
|
||||
|
||||
**Steps 1, 3 and 4 are one-time account setup** — the outbound Service
|
||||
Trunk (step 3) and the inbound SIP Trunk (step 4) each cover every DID on
|
||||
@@ -13,8 +13,8 @@ in the dashboard, and test.
|
||||
|
||||
## 0. Prerequisites
|
||||
|
||||
- `asterisk-digital-ocean.sh` (or `asterisk.sh` for a LAN box) already
|
||||
installed and running, with at least one extension configured.
|
||||
- `asterisk.sh` already installed and running (droplet or home/LAN — the
|
||||
installer detects which), with at least one extension configured.
|
||||
- This box's public IP address (`curl -4 ifconfig.me`).
|
||||
|
||||
## 1. Anveo Direct account (one-time)
|
||||
@@ -194,7 +194,9 @@ In the Security Dashboard's PSTN Trunk tab:
|
||||
answers or 20 seconds pass.
|
||||
- Watch the live console while testing either direction:
|
||||
```
|
||||
docker exec -it easy-asterisk-do asterisk -rvvv
|
||||
docker exec -it easy-asterisk asterisk -rvvv
|
||||
# on a droplet set up before the two Asterisk services were merged, the
|
||||
# container is named easy-asterisk-do instead
|
||||
```
|
||||
|
||||
## Bugs hit and fixed along the way (informational — already fixed)
|
||||
|
||||
@@ -10,9 +10,8 @@ file** — this doc is the design/decision log; that one is the clean
|
||||
how-to.
|
||||
|
||||
**Implemented** — see `services/pstn-trunk.sh` (run `sudo ./setup.sh
|
||||
pstn-trunk` after `asterisk-digital-ocean` **or** `asterisk` (home/LAN) is
|
||||
installed — both are supported, see the file for the static-IP caveat on the
|
||||
LAN variant). Generic SIP trunk add-on that defaults to VoIP.ms but isn't
|
||||
pstn-trunk` after `asterisk` is installed — droplet or home/LAN, both are
|
||||
supported; see the file for the static-IP caveat on the LAN variant). Generic SIP trunk add-on that defaults to VoIP.ms but isn't
|
||||
hardcoded to it — any provider supporting IP authentication works. Covers:
|
||||
|
||||
- IP-authenticated trunk, US/NANP-only outbound dialplan, no catch-all.
|
||||
@@ -65,7 +64,7 @@ file.
|
||||
- **Provider: VoIP.ms.** Chosen for its prepaid-balance model: turn off
|
||||
auto-recharge in the account's Finances settings and outbound calls simply
|
||||
fail once the balance hits $0 — that's the toll-fraud backstop if the
|
||||
droplet's Asterisk (`asterisk-digital-ocean`) is ever compromised.
|
||||
droplet's Asterisk is ever compromised.
|
||||
|
||||
**Update — read VoIP.ms's actual ToS (not just the wiki) on this.** The
|
||||
wiki says plainly "only accounts with a balance over $0 are able to send
|
||||
@@ -173,7 +172,7 @@ estimated spend crosses a threshold, and every hour that call volume in the
|
||||
last hour looks like a burst. Denied/rejected calls alert immediately,
|
||||
separately from that hourly check.
|
||||
|
||||
## What it takes technically (asterisk-digital-ocean)
|
||||
## What it takes technically (asterisk, droplet mode)
|
||||
- A PJSIP trunk: `endpoint` / `aor` / `identify` sections in the pjsip
|
||||
config. **Implemented with IP authentication** (no `auth` section, no SIP
|
||||
password stored anywhere) — see `services/pstn-trunk.sh`. Provider name,
|
||||
@@ -357,9 +356,9 @@ generator output. Fixed by quoting every value in that heredoc.
|
||||
model (internal/restricted/full) managed live via
|
||||
`pstn-permissions.conf` + the Security Dashboard web UI, no reinstall
|
||||
needed to change. ~~Generic Asterisk target~~ Done —
|
||||
`services/pstn-trunk.sh` now supports either `asterisk-digital-ocean` or
|
||||
the home/LAN `asterisk` install (the latter with a static-IP caveat for
|
||||
the provider's IP authentication). Still unresolved: pick pay-per-minute
|
||||
`services/pstn-trunk.sh` supports the `asterisk` install in either mode,
|
||||
droplet or home/LAN (the latter with a static-IP caveat for the
|
||||
provider's IP authentication). Still unresolved: pick pay-per-minute
|
||||
vs. unlimited DID plan on VoIP.ms's side based on real expected volume,
|
||||
and decide on E911 (see cost estimate).
|
||||
5. ~~Concurrent-call cap~~ Done — both directions now (inbound was a real
|
||||
@@ -444,7 +443,7 @@ generator output. Fixed by quoting every value in that heredoc.
|
||||
`exten => <ext>,1,...` per device, freshly regenerated by Easy
|
||||
Asterisk's own `rebuild_dialplan()` on every dialplan rebuild — exactly
|
||||
the collision this doc worried about. Solved by NOT sharing
|
||||
`[intercom]`: `services/asterisk-digital-ocean.sh` now explicitly sets
|
||||
`[intercom]`: `services/asterisk.sh` now explicitly sets
|
||||
`message_context=sip-messaging` on every endpoint (patched into both of
|
||||
Easy Asterisk's device-creation code paths — the CLI menu's bash
|
||||
heredoc and the web admin's Python `add_device()` — so new devices pick
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+849
-202
File diff suppressed because it is too large
Load Diff
@@ -435,7 +435,7 @@ auth.${AUTHELIA_DOMAIN} {
|
||||
# own incoming request (always auth.${AUTHELIA_DOMAIN} itself) and
|
||||
# overwrites the value a forward_auth caller (e.g. a remote site's
|
||||
# "forward_auth https://auth.${AUTHELIA_DOMAIN}" block, see
|
||||
# services/asterisk-digital-ocean.sh) set for its own domain. Confirmed
|
||||
# services/asterisk.sh's droplet-mode Caddy block) set for its own domain. Confirmed
|
||||
# live: every forward-auth check evaluated as if it were for
|
||||
# auth.${AUTHELIA_DOMAIN} itself (which has policy: bypass in
|
||||
# access_control.rules so its own login portal isn't gated behind
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ services:
|
||||
labels:
|
||||
- "io.podman.annotations.label/crowdsec.enable=true"
|
||||
# Lets Caddyfile blocks reach services that use network_mode: host
|
||||
# (e.g. asterisk/asterisk-digital-ocean) via "host.docker.internal:PORT" — Caddy
|
||||
# (e.g. asterisk) via "host.docker.internal:PORT" — Caddy
|
||||
# itself is on the caddy_net bridge network below, so plain "localhost"
|
||||
# in a site block resolves to Caddy's own container, not the host.
|
||||
extra_hosts:
|
||||
|
||||
+26
-14
@@ -102,7 +102,7 @@ install_crowdsec() {
|
||||
echo "[DRY-RUN] Would ensure /var/log/caddy exists for log acquisition"
|
||||
echo "[DRY-RUN] Would install collections: sshd, linux, caddy, base-http-scenarios"
|
||||
echo "[DRY-RUN] Would write Caddy acquisition /etc/crowdsec/acquis.d/caddy.yaml"
|
||||
echo "[DRY-RUN] Would install crowdsecurity/asterisk + write an acquisition if asterisk-digital-ocean is installed"
|
||||
echo "[DRY-RUN] Would install crowdsecurity/asterisk + write an acquisition if asterisk is installed"
|
||||
echo "[DRY-RUN] Would optionally wire ntfy ban alerts into the default profile"
|
||||
echo "[DRY-RUN] Would optionally register with a remote/central LAPI and disable the local one"
|
||||
echo "[DRY-RUN] Would enable + restart crowdsec and crowdsec-firewall-bouncer"
|
||||
@@ -194,17 +194,26 @@ labels:
|
||||
echo " ✓ Caddy acquisition already exists"
|
||||
fi
|
||||
|
||||
# ── 5b. SIP brute-force/enumeration protection, if asterisk-digital-ocean
|
||||
# is installed (services/asterisk-digital-ocean.sh patches Asterisk to log
|
||||
# security events — auth failures, registration scanning — to
|
||||
# $EA_DIR/logs/full. The plain LAN asterisk.sh doesn't emit that file yet,
|
||||
# so it's intentionally not detected here.)
|
||||
local ASTERISK_LOG_DIR="$DOCKER_DIR/asterisk-digital-ocean/logs"
|
||||
if [ -d "$ASTERISK_LOG_DIR" ]; then
|
||||
echo " Detected asterisk-digital-ocean — installing SIP brute-force/enumeration protection..."
|
||||
# ── 5b. SIP brute-force/enumeration protection, if Asterisk is installed.
|
||||
# services/asterisk.sh patches Asterisk to log security events — auth
|
||||
# failures, registration scanning — to $EA_DIR/logs/full, which is what
|
||||
# the acquisition below tails. Both directories are probed: a box set up
|
||||
# before the droplet edition was merged back into `asterisk` still runs
|
||||
# out of ~/docker/asterisk-digital-ocean. The logging patch used to be
|
||||
# droplet-only; it now applies to every install, so a home/LAN box gets
|
||||
# SIP protection here too.
|
||||
local ASTERISK_LOG_DIR=""
|
||||
local _ea_candidate
|
||||
for _ea_candidate in "$DOCKER_DIR/asterisk-digital-ocean" "$DOCKER_DIR/asterisk"; do
|
||||
[ -d "$_ea_candidate/logs" ] && { ASTERISK_LOG_DIR="$_ea_candidate/logs"; break; }
|
||||
done
|
||||
if [ -n "$ASTERISK_LOG_DIR" ]; then
|
||||
echo " Detected Asterisk at ${ASTERISK_LOG_DIR%/logs} — installing SIP brute-force/enumeration protection..."
|
||||
sudo cscli collections install crowdsecurity/asterisk 2>/dev/null || \
|
||||
echo " ⚠ crowdsecurity/asterisk collection may already be installed"
|
||||
|
||||
# Filename kept as-is so a droplet that already has this acquisition
|
||||
# isn't given a second one pointing at the same log.
|
||||
local ASTERISK_ACQUIS="/etc/crowdsec/acquis.d/asterisk-digital-ocean.yaml"
|
||||
if [ ! -f "$ASTERISK_ACQUIS" ]; then
|
||||
local ASTERISK_ACQUIS_CONTENT="filenames:
|
||||
@@ -543,7 +552,7 @@ install. The real configuration lives under `/etc/crowdsec`.
|
||||
## What it does
|
||||
|
||||
- Detects malicious behaviour (SSH brute force, web scans, SIP brute
|
||||
force/enumeration if `asterisk-digital-ocean` is installed) by parsing logs.
|
||||
force/enumeration if `asterisk` is installed) by parsing logs.
|
||||
- Bans offending IPs via the **firewall bouncer** (iptables/nftables).
|
||||
- Pulls **community IP reputation** blocklists so known-bad IPs are blocked
|
||||
before they ever touch your services.
|
||||
@@ -573,9 +582,12 @@ sudo cscli collections list # installed detection collections
|
||||
- Log acquisition (what to watch): `/etc/crowdsec/acquis.d/`
|
||||
- Caddy access logs: `/etc/crowdsec/acquis.d/caddy.yaml`
|
||||
(`/var/log/caddy/*.log` — Caddy writes JSON access logs there)
|
||||
- Asterisk SIP auth events (if `asterisk-digital-ocean` is installed):
|
||||
`/etc/crowdsec/acquis.d/asterisk-digital-ocean.yaml`
|
||||
(`~/docker/asterisk-digital-ocean/logs/full` — auth failures, registration scans)
|
||||
- Asterisk SIP auth events (if `asterisk` is installed):
|
||||
`/etc/crowdsec/acquis.d/asterisk-digital-ocean.yaml` (filename kept from
|
||||
when the droplet edition was its own service, so existing droplets aren't
|
||||
given a duplicate acquisition)
|
||||
(`~/docker/asterisk/logs/full`, or `~/docker/asterisk-digital-ocean/logs/full`
|
||||
on a pre-merge droplet — auth failures, registration scans)
|
||||
- Notifications: `/etc/crowdsec/notifications/`
|
||||
- ntfy ban alerts (if enabled): `/etc/crowdsec/notifications/ntfy.yaml`,
|
||||
wired into `/etc/crowdsec/profiles.yaml`
|
||||
@@ -609,7 +621,7 @@ sudo cscli collections list # installed detection collections
|
||||
list directly in that file, then `sudo systemctl restart crowdsec`. This
|
||||
can block Let's Encrypt's out-of-region ACME validation checks; if a cert
|
||||
renewal fails mysteriously, check here first.
|
||||
- ASN-exempt Asterisk brute-force scenarios (if enabled, asterisk-digital-ocean
|
||||
- ASN-exempt Asterisk brute-force scenarios (if enabled, Asterisk installs
|
||||
only): `/etc/crowdsec/scenarios/local-asterisk_bf.yaml` and
|
||||
`local-asterisk_user_enum.yaml` — local forks of the stock hub scenarios with
|
||||
specific carrier ASNs excluded from their filter (the hub originals get
|
||||
|
||||
+24
-23
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# services/pstn-trunk.sh — SIP PSTN trunk add-on for asterisk-digital-ocean
|
||||
# (or the home/LAN asterisk install): US-only outbound (NANP dialplan
|
||||
# services/pstn-trunk.sh — SIP PSTN trunk add-on for services/asterisk.sh:
|
||||
# US-only outbound (NANP dialplan
|
||||
# restriction), independent outbound/inbound concurrent-call caps, a 3-tier
|
||||
# permission model per extension (internal-only / restricted to pre-approved
|
||||
# numbers / full US calling), a configurable inbound ring-group,
|
||||
@@ -15,19 +15,19 @@
|
||||
# works the same way. VoIP.ms and Anveo Direct are both confirmed working;
|
||||
# see docs/pstn-calling-voipms-plan.md for the design/cost background.
|
||||
#
|
||||
# Requires an existing services/asterisk-digital-ocean.sh OR services/asterisk.sh
|
||||
# install — this adds a PSTN trunk on top of one of them and does not stand
|
||||
# alone. Permission tiers AND concurrency caps are managed live (no restart
|
||||
# Requires an existing services/asterisk.sh install (either directory layout —
|
||||
# ~/docker/asterisk, or ~/docker/asterisk-digital-ocean on a box set up before
|
||||
# the droplet edition was merged back in) — this adds a PSTN trunk on top and
|
||||
# does not stand alone. Permission tiers AND concurrency caps are managed live (no restart
|
||||
# needed) via pstn-permissions.conf / pstn-limits.conf — editable by hand, or
|
||||
# from services/security-dashboard.sh's "PSTN Trunk" tab if that's installed.
|
||||
# from services/security-dashboard.sh's Extensions tab if that's installed.
|
||||
#
|
||||
# Part of the modular post-install system (sourced by setup.sh).
|
||||
|
||||
register_service pstn-trunk homelab "SIP PSTN trunk for asterisk-digital-ocean/asterisk — US-only, per-extension permission tiers, spend/volume alerts (any IP-authenticated provider — VoIP.ms and Anveo Direct both confirmed)"
|
||||
register_service pstn-trunk homelab "SIP PSTN trunk for asterisk — US-only, per-extension permission tiers, spend/volume alerts (any IP-authenticated provider — VoIP.ms and Anveo Direct both confirmed)"
|
||||
|
||||
# ── Surviving Easy Asterisk's regeneration ──────────────────────────────────
|
||||
# Easy Asterisk (the vendor project asterisk-digital-ocean.sh/asterisk.sh
|
||||
# build on) fully OVERWRITES both pjsip.conf and extensions.conf from its own
|
||||
# Easy Asterisk (the vendor project services/asterisk.sh builds on) fully OVERWRITES both pjsip.conf and extensions.conf from its own
|
||||
# internal state:
|
||||
# - extensions.conf: rebuilt by rebuild_dialplan() on every container start,
|
||||
# and whenever a device/room is added or removed via the web admin.
|
||||
@@ -41,9 +41,9 @@ register_service pstn-trunk homelab "SIP PSTN trunk for asterisk-digital-ocean/a
|
||||
# the #include itself survive regeneration too, _pstn_patch_vendor_files
|
||||
# (below) patches it into the vendor's *generator functions* — the same
|
||||
# technique this repo already uses for the logger.conf security-logging fix
|
||||
# in _asterisk_do_refresh_vendor_files (see services/asterisk-digital-ocean.sh).
|
||||
# in _asterisk_refresh_vendor_files (see services/asterisk.sh).
|
||||
#
|
||||
# Caveat: if the base asterisk-digital-ocean/asterisk install is later
|
||||
# Caveat: if the base asterisk install is later
|
||||
# refreshed ("update in place", which re-copies fresh vendor files)
|
||||
# independently of this service, the patch is wiped along with it and needs
|
||||
# reapplying — run this service again (fresh or update mode both reapply it)
|
||||
@@ -459,7 +459,7 @@ EOF
|
||||
# zero of the outbound NANP patterns either, and `dialplan show
|
||||
# from-pstn-trunk` reported the context didn't exist at all, with no
|
||||
# warning or error anywhere (config log, full log, or the reload command's
|
||||
# own output) pointing at why. Meanwhile services/asterisk-digital-ocean.sh's
|
||||
# own output) pointing at why. Meanwhile services/asterisk.sh's
|
||||
# messaging-dialplan.conf — #include'd via the exact same mechanism, right
|
||||
# after [intercom] in the same extensions.conf — loaded fine every time.
|
||||
# The one structural difference: messaging-dialplan.conf's first real line
|
||||
@@ -1608,7 +1608,7 @@ install_pstn-trunk() {
|
||||
[[ "$ASTERISK_KIND" == "asterisk-digital-ocean" ]] && CONTAINER_NAME="easy-asterisk-do"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Would require an existing asterisk-digital-ocean OR asterisk (LAN) install"
|
||||
echo "[DRY-RUN] Would require an existing asterisk install (droplet or home/LAN)"
|
||||
echo "[DRY-RUN] Would prompt for: known-provider quick-pick (Anveo Direct runs a full 5-step"
|
||||
echo "[DRY-RUN] interactive portal walkthrough — account/funding, DID ordering, both trunk"
|
||||
echo "[DRY-RUN] objects, confirmed rate — pausing for Enter between each; VoIP.ms pre-fills known"
|
||||
@@ -1640,10 +1640,12 @@ install_pstn-trunk() {
|
||||
fi
|
||||
|
||||
if [[ -z "$EA_DIR" ]]; then
|
||||
log_error "Neither asterisk-digital-ocean nor asterisk (LAN) is installed — install one first:"
|
||||
log_error " sudo ./setup.sh asterisk-digital-ocean (recommended — public droplet, static IP)"
|
||||
log_error " sudo ./setup.sh asterisk (home/LAN — see the static-IP caveat below)"
|
||||
log_error "This service adds a PSTN trunk on top of one of them; it doesn't stand alone."
|
||||
log_error "Asterisk is not installed — install it first:"
|
||||
log_error " sudo ./setup.sh asterisk"
|
||||
log_error "A public droplet (which that installer detects and tunes for) is the"
|
||||
log_error "recommended host, since IP authentication wants a static IP — see the"
|
||||
log_error "caveat below for what that means on a home/LAN box."
|
||||
log_error "This service adds a PSTN trunk on top of it; it doesn't stand alone."
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -1652,7 +1654,7 @@ install_pstn-trunk() {
|
||||
log_warning "Using the home/LAN asterisk install. IP authentication needs a STABLE public IP —"
|
||||
log_warning "if this box is behind a dynamic home IP, your provider's IP allow-list goes stale"
|
||||
log_warning "whenever your ISP rotates it, breaking calls until you update it there yourself."
|
||||
log_warning "A static IP from your ISP avoids that; asterisk-digital-ocean sidesteps it entirely."
|
||||
log_warning "A static IP from your ISP avoids that; a cloud droplet sidesteps it entirely."
|
||||
fi
|
||||
|
||||
log_info "Configuring a SIP PSTN trunk for $ASTERISK_KIND (any IP-authenticated provider —"
|
||||
@@ -2213,12 +2215,11 @@ 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
|
||||
extension in \`pstn-permissions.conf\`, independent of the PSTN calling
|
||||
tiers above — off by default, same "opt in" posture. Live-editable any
|
||||
time via the Security Dashboard's "PSTN Trunk" tab, in its own
|
||||
always-available "Internal SIP messaging" card — no dependency on this
|
||||
trunk (or any PSTN trunk at all) being installed.
|
||||
time via the Security Dashboard's Extensions tab, in the Messaging column of
|
||||
its always-available extensions table — no dependency on this trunk (or any
|
||||
PSTN trunk at all) being installed.
|
||||
|
||||
Actually enforced, not just a flag — \`services/asterisk-digital-ocean.sh\`
|
||||
(and \`services/asterisk.sh\` for the LAN edition) routes messages through a
|
||||
Actually enforced, not just a flag — \`services/asterisk.sh\` routes messages through a
|
||||
dedicated \`[sip-messaging]\` dialplan context (separate from \`[intercom]\`'s
|
||||
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
|
||||
|
||||
@@ -27,6 +27,10 @@ export TERM="${TERM:-xterm-256color}"
|
||||
CATEGORY_ORDER=(base homelab utilities media cameras gaming extras backup)
|
||||
# Service ordering hint within a category (lower = earlier). Default 50.
|
||||
declare -A SERVICE_PRIORITY=( [caddy]=1 [crowdsec]=2 [authelia]=3 )
|
||||
# Retired service names that now resolve to another service. Keeps a name
|
||||
# that used to work on the command line (and in docs/muscle memory) working
|
||||
# after a merge, without giving it a second menu entry of its own.
|
||||
declare -A SERVICE_ALIAS=( [asterisk-digital-ocean]=asterisk )
|
||||
|
||||
# ── Parse flags / collect service names ──────────────────────────────────────
|
||||
DRY_RUN=false; UNATTENDED=false; DO_LIST=false
|
||||
@@ -89,6 +93,9 @@ is_installed() {
|
||||
sync-cc) [ -f "$ACTUAL_HOME/sync-cc/sync_cc.py" ] ;;
|
||||
sky-cam) [ -d "$ACTUAL_HOME/sky-cam/.git" ] ;;
|
||||
sky-cam-frigate) [ -d "$ACTUAL_HOME/sky-cam/.git" ] && [ -f "$ACTUAL_HOME/sky-cam/frigate-retime.sh" ] ;;
|
||||
# Either directory counts: boxes set up before the droplet edition was
|
||||
# merged back into `asterisk` still run out of ~/docker/asterisk-digital-ocean.
|
||||
asterisk) [ -e "$DOCKER_DIR/asterisk" ] || [ -e "$DOCKER_DIR/asterisk-digital-ocean" ] ;;
|
||||
pstn-trunk) [ -f "$DOCKER_DIR/asterisk-digital-ocean/config/asterisk/pstn-trunk-pjsip.conf" ] || [ -f "$DOCKER_DIR/asterisk/config/asterisk/pstn-trunk-pjsip.conf" ] ;;
|
||||
ssh-config) false ;; # repeatable management tool, never shows [installed]
|
||||
*) [ -e "$DOCKER_DIR/$1" ] ;;
|
||||
@@ -97,6 +104,10 @@ is_installed() {
|
||||
|
||||
run_service() {
|
||||
local name="$1"
|
||||
if [ -n "${SERVICE_ALIAS[$name]:-}" ]; then
|
||||
log_info "'$name' is now part of '${SERVICE_ALIAS[$name]}' — running that instead."
|
||||
name="${SERVICE_ALIAS[$name]}"
|
||||
fi
|
||||
if [ -z "${SERVICE_GROUP[$name]:-}" ]; then log_error "Unknown service: $name (try --list)"; return 1; fi
|
||||
declare -F "install_${name}" >/dev/null || { log_error "Service '$name' has no install_${name}"; return 1; }
|
||||
log_info "=== ${name} (${SERVICE_DESC[$name]}) ==="
|
||||
|
||||
Reference in New Issue
Block a user