diff --git a/CLAUDE.md b/CLAUDE.md index 9ed1803..03244f2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -302,7 +302,7 @@ 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-do.sh` (and their `_asterisk_*` counterparts in +`services/asterisk-digital-ocean.sh` (and their `_asterisk_*` counterparts in `services/asterisk.sh`) for the reference pattern. `cancel` must leave the install completely untouched — it's the default for @@ -338,7 +338,7 @@ it, so as long as your `install_()` 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-do`) don't join +**`network_mode: host` services (e.g. `asterisk`/`asterisk-digital-ocean`) 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 diff --git a/README.md b/README.md index dcedb7d..d67c406 100644 --- a/README.md +++ b/README.md @@ -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-do`, `sunshine` | +| `homelab` | `caddy`, `crowdsec`, `authelia`, `homeassistant`, `asterisk`, `asterisk-digital-ocean`, `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,7 @@ homelab authelia homeassistant asterisk - asterisk-do + asterisk-digital-ocean sunshine utilities diff --git a/services/asterisk-do.sh b/services/asterisk-digital-ocean.sh similarity index 96% rename from services/asterisk-do.sh rename to services/asterisk-digital-ocean.sh index 0a13ebe..0f93e63 100755 --- a/services/asterisk-do.sh +++ b/services/asterisk-digital-ocean.sh @@ -1,11 +1,12 @@ #!/bin/bash -# services/asterisk-do.sh — Easy Asterisk PBX + coturn, tuned for a public -# DigitalOcean droplet (public-IP FQDN by default, DO Cloud Firewall setup, -# no LAN/VLAN prompts). For a home/LAN box use services/asterisk.sh instead. +# services/asterisk-digital-ocean.sh — Easy Asterisk PBX + coturn, tuned for a +# public DigitalOcean droplet (public-IP FQDN by default, DO Cloud Firewall +# setup, no LAN/VLAN prompts). For a home/LAN box use services/asterisk.sh +# instead. # Part of the modular post-install system (sourced by setup.sh). # # Can also be run standalone on a fresh droplet: -# sudo bash asterisk-do.sh +# sudo bash asterisk-digital-ocean.sh # (Docker must already be installed when run standalone) # ── Standalone bootstrap ────────────────────────────────────────────────────── @@ -220,7 +221,7 @@ CBLOCK fi # ───────────────────────────────────────────────────────────────────────────── -register_service asterisk-do homelab "Easy Asterisk PBX + coturn, tuned for a public DigitalOcean droplet" 5061 +register_service asterisk-digital-ocean homelab "Easy Asterisk PBX + coturn, tuned for a public DigitalOcean droplet" 5061 # ── Shared: vendor file refresh ──────────────────────────────────────────── # Called from both a fresh install and an "update in place" run, so a single @@ -339,11 +340,11 @@ EOF fi } -install_asterisk-do() { +install_asterisk-digital-ocean() { require_docker || return 1 log_info "Installing Easy Asterisk PBX + coturn (DigitalOcean droplet edition)..." - local EA_DIR="$DOCKER_DIR/asterisk-do" + local EA_DIR="$DOCKER_DIR/asterisk-digital-ocean" if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Would add a swapfile if RAM <= 2048MB and none exists" @@ -534,7 +535,7 @@ VLAN_SUBNETS= # ── Web admin ───────────────────────────────────────────────── # Picked automatically at install time (first free port starting at 8081) — -# see WEB_ADMIN_PORT_VAL in services/asterisk-do.sh if this ever needs to +# see WEB_ADMIN_PORT_VAL in services/asterisk-digital-ocean.sh if this ever needs to # change again; don't hand-edit without also updating Caddy's Caddyfile and # both firewall layers to match. WEB_ADMIN_PORT=${WEB_ADMIN_PORT_VAL} @@ -590,12 +591,12 @@ ENV prompt_yn "Create a DigitalOcean Cloud Firewall for this droplet via doctl now? (y/n):" "y" DO_FW if [[ "$DO_FW" =~ ^[Yy]$ ]]; then if doctl compute firewall create \ - --name "asterisk-do" \ + --name "asterisk-digital-ocean" \ --droplet-ids "$DROPLET_ID" \ --inbound-rules "$(IFS=' '; echo "${DO_FW_RULES[*]}")" \ --outbound-rules "protocol:tcp,ports:all,address:0.0.0.0/0,address:::/0 protocol:udp,ports:all,address:0.0.0.0/0,address:::/0 protocol:icmp,ports:0,address:0.0.0.0/0,address:::/0" \ &>/dev/null; then - log_success "Cloud Firewall 'asterisk-do' created and attached (SSH/22 included so you don't get locked out)." + log_success "Cloud Firewall 'asterisk-digital-ocean' created and attached (SSH/22 included so you don't get locked out)." log_info "Verify it in the DO console — adjust the SSH rule if you use a non-default SSH port." else log_warning "doctl firewall create failed — add the rules manually (see README)." @@ -660,7 +661,7 @@ ENV # which only lands on $DOMAIN_NAME if SITE_DOMAIN happens to be set to # match, and silently shows a useless blank/wrong default otherwise # (real-world confirmed: SITE_DOMAIN is never set when this service is - # run by name, e.g. `sudo ./setup.sh asterisk-do`, since that skips + # run by name, e.g. `sudo ./setup.sh asterisk-digital-ocean`, since that skips # setup.sh's own site-defaults wizard entirely). There is exactly one # correct domain for this site block — $DOMAIN_NAME — so it's written # directly, with no domain prompt to get wrong. @@ -734,10 +735,10 @@ CADDY_BLOCK else local _SNIPPET_DIR="$DOCKER_DIR/caddy-snippets" mkdir -p "$_SNIPPET_DIR" - printf '%s\n' "$_SITE_BLOCK" > "$_SNIPPET_DIR/asterisk-do.caddy" - chown "$ACTUAL_USER:$ACTUAL_USER" "$_SNIPPET_DIR/asterisk-do.caddy" 2>/dev/null || true - log_success "Snippet saved: $_SNIPPET_DIR/asterisk-do.caddy" - log_info "Copy to your Caddy machine: scp $_SNIPPET_DIR/asterisk-do.caddy caddy-host:~/caddy-snippets/" + printf '%s\n' "$_SITE_BLOCK" > "$_SNIPPET_DIR/asterisk-digital-ocean.caddy" + chown "$ACTUAL_USER:$ACTUAL_USER" "$_SNIPPET_DIR/asterisk-digital-ocean.caddy" 2>/dev/null || true + log_success "Snippet saved: $_SNIPPET_DIR/asterisk-digital-ocean.caddy" + log_info "Copy to your Caddy machine: scp $_SNIPPET_DIR/asterisk-digital-ocean.caddy caddy-host:~/caddy-snippets/" fi fi fi @@ -745,15 +746,15 @@ CADDY_BLOCK # ── CrowdSec note ────────────────────────────────────────────────────────── # Not installed here — select it separately from the whiptail menu, or # `sudo ./setup.sh crowdsec`. Its own installer (services/crowdsec.sh) - # auto-detects an asterisk-do install and wires up SIP brute-force - # protection on its own, in either install order. + # auto-detects an asterisk-digital-ocean install and wires up SIP + # brute-force protection on its own, in either install order. if command -v cscli &>/dev/null; then log_info "CrowdSec is already installed — rerun it to pick up SIP protection for this install:" log_info " sudo ./setup.sh crowdsec" else log_info "CrowdSec not installed. Recommended for SSH + SIP intrusion prevention on a public" log_info "droplet — install it separately (whiptail menu, or 'sudo ./setup.sh crowdsec')." - log_info "It auto-detects this asterisk-do install and wires up SIP protection on its own." + log_info "It auto-detects this asterisk-digital-ocean install and wires up SIP protection on its own." fi # ── README ──────────────────────────────────────────────────────────────── @@ -821,7 +822,7 @@ plan for the admin panel. - **CrowdSec** — SIP brute-force/enumeration protection (\`crowdsecurity/asterisk\` collection). Not installed by this script — install it separately (whiptail menu, or \`sudo ./setup.sh crowdsec\`); its own installer auto-detects this - asterisk-do install and wires up SIP protection regardless of install order. + asterisk-digital-ocean install and wires up SIP protection regardless of install order. - DO's paid Droplet Backups, or \`services/borg-backup.sh\` installed separately, are both options for a rollback path. @@ -919,7 +920,7 @@ accept it manually). Access the Easy Asterisk web interface at http://:${WEB_ADMIN_PORT_VAL} or via your configured reverse-proxy domain. -## Data directories (all inside ~/docker/asterisk-do/, included in backup) +## Data directories (all inside ~/docker/asterisk-digital-ocean/, included in backup) | Directory | Contents | |-----------------------|----------------------------------| @@ -967,4 +968,4 @@ MD } # Run immediately when executed directly (deferred until after function definition) -[[ "${_RUN_STANDALONE:-0}" == 1 ]] && install_asterisk-do +[[ "${_RUN_STANDALONE:-0}" == 1 ]] && install_asterisk-digital-ocean diff --git a/services/caddy.sh b/services/caddy.sh index dbc5cac..aeb83b7 100644 --- a/services/caddy.sh +++ b/services/caddy.sh @@ -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-do) via "host.docker.internal:PORT" — Caddy + # (e.g. asterisk/asterisk-digital-ocean) 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: diff --git a/services/crowdsec.sh b/services/crowdsec.sh index 123d0dd..38b81fb 100644 --- a/services/crowdsec.sh +++ b/services/crowdsec.sh @@ -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-do is installed" + echo "[DRY-RUN] Would install crowdsecurity/asterisk + write an acquisition if asterisk-digital-ocean 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" @@ -159,18 +159,18 @@ labels: echo " ✓ Caddy acquisition already exists" fi - # ── 5b. SIP brute-force/enumeration protection, if asterisk-do is installed - # (services/asterisk-do.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-do/logs" + # ── 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-do — installing SIP brute-force/enumeration protection..." + echo " Detected asterisk-digital-ocean — installing SIP brute-force/enumeration protection..." sudo cscli collections install crowdsecurity/asterisk 2>/dev/null || \ echo " ⚠ crowdsecurity/asterisk collection may already be installed" - local ASTERISK_ACQUIS="/etc/crowdsec/acquis.d/asterisk-do.yaml" + local ASTERISK_ACQUIS="/etc/crowdsec/acquis.d/asterisk-digital-ocean.yaml" if [ ! -f "$ASTERISK_ACQUIS" ]; then local ASTERISK_ACQUIS_CONTENT="filenames: - $ASTERISK_LOG_DIR/full @@ -343,7 +343,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-do` is installed) by parsing logs. + force/enumeration if `asterisk-digital-ocean` 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. @@ -365,9 +365,9 @@ 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-do` is installed): - `/etc/crowdsec/acquis.d/asterisk-do.yaml` - (`~/docker/asterisk-do/logs/full` — auth failures, registration scans) + - 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) - Notifications: `/etc/crowdsec/notifications/` - ntfy ban alerts (if enabled): `/etc/crowdsec/notifications/ntfy.yaml`, wired into `/etc/crowdsec/profiles.yaml`