Merge pull request #177 from outis1one/claude/asterisk-digital-ocean-w22kk8
Claude/asterisk digital ocean w22kk8
This commit is contained in:
+55
-3
@@ -214,10 +214,30 @@ install_asterisk-do() {
|
|||||||
echo "[DRY-RUN] Would open 51820/udp (not 51821) if wg-easy was selected"
|
echo "[DRY-RUN] Would open 51820/udp (not 51821) if wg-easy was selected"
|
||||||
echo "[DRY-RUN] Would offer to create a DigitalOcean Cloud Firewall via doctl"
|
echo "[DRY-RUN] Would offer to create a DigitalOcean Cloud Firewall via doctl"
|
||||||
echo "[DRY-RUN] Would reverse-proxy the web admin on the SAME FQDN used for SIP (needed for cert sync)"
|
echo "[DRY-RUN] Would reverse-proxy the web admin on the SAME FQDN used for SIP (needed for cert sync)"
|
||||||
|
echo "[DRY-RUN] Would offer local OR remote Authelia to protect the web admin"
|
||||||
echo "[DRY-RUN] Would offer to install CrowdSec if not already present (full repo only)"
|
echo "[DRY-RUN] Would offer to install CrowdSec if not already present (full repo only)"
|
||||||
|
echo "[DRY-RUN] Would offer to run base setup first if not already done (full repo only)"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Bring in base first, if this is a genuinely fresh box ─────────────────
|
||||||
|
# Naming a service directly (sudo ./setup.sh asterisk-do) skips setup.sh's
|
||||||
|
# own first-run base step — essential packages, SSH key import, disabling
|
||||||
|
# password auth. That's a real gap on a fresh droplet: everything below
|
||||||
|
# still works without it, but the SSH-hardening part of this setup's
|
||||||
|
# security story wouldn't actually have happened. Same marker setup.sh
|
||||||
|
# itself uses to detect base (command -v ncdu).
|
||||||
|
if ! command -v ncdu &>/dev/null; then
|
||||||
|
if declare -F install_base &>/dev/null; then
|
||||||
|
local WANT_BASE=""
|
||||||
|
prompt_yn "Base setup not detected (essential packages, SSH hardening) — run it first? (y/n):" "y" WANT_BASE
|
||||||
|
[[ "$WANT_BASE" =~ ^[Yy]$ ]] && install_base
|
||||||
|
else
|
||||||
|
log_warning "Base setup not detected, and this looks like a standalone copy of asterisk-do.sh."
|
||||||
|
log_warning "Run services/base.sh yourself first, or grab the full repo."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Swap file (insurance for low-RAM droplets, e.g. the $4/mo 512MB plan) ──
|
# ── Swap file (insurance for low-RAM droplets, e.g. the $4/mo 512MB plan) ──
|
||||||
# DigitalOcean doesn't provision swap by default. Docker + Asterisk + coturn
|
# DigitalOcean doesn't provision swap by default. Docker + Asterisk + coturn
|
||||||
# fit in 512MB-1GB at idle with little headroom; a swapfile absorbs spikes
|
# fit in 512MB-1GB at idle with little headroom; a swapfile absorbs spikes
|
||||||
@@ -580,6 +600,31 @@ ENV
|
|||||||
# Disable built-in auth since Authelia handles it
|
# Disable built-in auth since Authelia handles it
|
||||||
sed -i "s/^WEB_ADMIN_AUTH_DISABLED=.*/WEB_ADMIN_AUTH_DISABLED=true/" .env
|
sed -i "s/^WEB_ADMIN_AUTH_DISABLED=.*/WEB_ADMIN_AUTH_DISABLED=true/" .env
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
# No local Authelia — offer one running elsewhere (e.g. a homelab).
|
||||||
|
# There's no shared "(authelia)" Caddy snippet to import in that
|
||||||
|
# case (authelia.sh only writes one when installing locally), so
|
||||||
|
# this builds the same forward_auth block inline, targeting the
|
||||||
|
# remote instance directly instead of the local "authelia:9091"
|
||||||
|
# container reference.
|
||||||
|
local _use_remote_auth=""
|
||||||
|
prompt_yn "Protect the web admin with a remote Authelia instance (e.g. on a homelab)? (y/n):" "n" _use_remote_auth
|
||||||
|
if [[ "$_use_remote_auth" =~ ^[Yy]$ ]]; then
|
||||||
|
local _remote_authelia=""
|
||||||
|
prompt_text " Remote Authelia address — a bare host:port over a private network (e.g. a NetBird mesh IP:9091), or a full https:// URL if it's on its own public domain+TLS:" "" _remote_authelia
|
||||||
|
if [[ -n "$_remote_authelia" ]]; then
|
||||||
|
EXTRA_BLOCK=" forward_auth ${_remote_authelia} {
|
||||||
|
uri /api/authz/forward-auth
|
||||||
|
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
|
||||||
|
}"
|
||||||
|
sed -i "s/^WEB_ADMIN_AUTH_DISABLED=.*/WEB_ADMIN_AUTH_DISABLED=true/" .env
|
||||||
|
log_info "Using remote Authelia at ${_remote_authelia}."
|
||||||
|
log_info "Verify it's reachable from this droplet before relying on it — e.g.:"
|
||||||
|
log_info " curl -I ${_remote_authelia}"
|
||||||
|
else
|
||||||
|
log_info "No address entered — skipping Authelia protection."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Reconstruct the subdomain-only fragment so configure_caddy_for_service's
|
# Reconstruct the subdomain-only fragment so configure_caddy_for_service's
|
||||||
@@ -740,9 +785,16 @@ plan for the admin panel.
|
|||||||
Offered during install (space-separated at the "Install:" prompt); can also
|
Offered during install (space-separated at the "Install:" prompt); can also
|
||||||
be added later by running \`sudo ./setup.sh <name>\` from the repo.
|
be added later by running \`sudo ./setup.sh <name>\` from the repo.
|
||||||
|
|
||||||
- **authelia** — SSO/2FA in front of the web admin. Needs Caddy. Once
|
- **authelia** — SSO/2FA in front of the web admin. Needs Caddy locally to
|
||||||
installed, re-running \`asterisk-do\` will offer to protect the web admin
|
install here. If it's already installed (locally or picked up by
|
||||||
with it.
|
re-running \`asterisk-do\`), that instance protects the web admin
|
||||||
|
automatically. **No local Authelia?** The web-admin step separately offers
|
||||||
|
a **remote Authelia** option instead — point it at an instance already
|
||||||
|
running elsewhere (e.g. a homelab) via a bare \`host:port\` over a private
|
||||||
|
network (a NetBird mesh IP works well here) or a full \`https://\` URL if
|
||||||
|
it has its own public domain+TLS. Every web-admin page load then does a
|
||||||
|
round trip to that address, so if it's unreachable, the panel fails closed
|
||||||
|
— SIP/calling on this droplet is unaffected either way, only the admin UI.
|
||||||
- **ntfy** — self-hosted push notifications. Useful as a destination for
|
- **ntfy** — self-hosted push notifications. Useful as a destination for
|
||||||
CrowdSec ban alerts (\`services/crowdsec.sh\` prompts for an ntfy URL —
|
CrowdSec ban alerts (\`services/crowdsec.sh\` prompts for an ntfy URL —
|
||||||
point it at this instance instead of the public ntfy.sh if you'd rather
|
point it at this instance instead of the public ntfy.sh if you'd rather
|
||||||
|
|||||||
+88
-13
@@ -104,6 +104,7 @@ install_crowdsec() {
|
|||||||
echo "[DRY-RUN] Would write Caddy acquisition /etc/crowdsec/acquis.d/caddy.yaml"
|
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-do is installed"
|
||||||
echo "[DRY-RUN] Would optionally wire ntfy ban alerts into the default profile"
|
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"
|
echo "[DRY-RUN] Would enable + restart crowdsec and crowdsec-firewall-bouncer"
|
||||||
echo "[DRY-RUN] Would write $DOCS_DIR/README.md (docs-only folder)"
|
echo "[DRY-RUN] Would write $DOCS_DIR/README.md (docs-only folder)"
|
||||||
return 0
|
return 0
|
||||||
@@ -235,20 +236,79 @@ headers:
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 8. Restart services to apply ─────────────────────────────────────────
|
# ── 7b. Optional: point this agent at a remote/central LAPI ──────────────
|
||||||
local RESTART_CS=""
|
# CrowdSec's real multi-server support: parsers/scenarios/bouncer still
|
||||||
prompt_yn "Restart CrowdSec to apply changes? (y/n):" "y" RESTART_CS
|
# run locally (banning only works where traffic actually arrives), but
|
||||||
if [ "$RESTART_CS" = "y" ] || [ "$RESTART_CS" = "Y" ]; then
|
# the decision database (LAPI) can live on one central machine instead
|
||||||
sudo systemctl enable crowdsec 2>/dev/null || true
|
# of every box running its own. Useful if you already have CrowdSec on
|
||||||
if sudo systemctl restart crowdsec; then
|
# a homelab and don't want a second LAPI+SQLite DB on this droplet.
|
||||||
echo " ✓ CrowdSec restarted successfully"
|
echo ""
|
||||||
sudo systemctl enable crowdsec-firewall-bouncer 2>/dev/null || true
|
local USE_REMOTE_LAPI="" _REMOTE_LAPI_PENDING=""
|
||||||
sudo systemctl restart crowdsec-firewall-bouncer 2>/dev/null || true
|
prompt_yn "Point this agent at a remote/central LAPI instead of running its own (e.g. one already on a homelab)? (y/n):" "n" USE_REMOTE_LAPI
|
||||||
sleep 2
|
if [ "$USE_REMOTE_LAPI" = "y" ] || [ "$USE_REMOTE_LAPI" = "Y" ]; then
|
||||||
sudo cscli metrics 2>/dev/null | head -20 || true
|
echo ""
|
||||||
|
echo " This registers this machine and disables its local API server."
|
||||||
|
echo " The registration is PENDING until approved on the central LAPI"
|
||||||
|
echo " machine — that approval step can't be automated from here."
|
||||||
|
echo ""
|
||||||
|
local LAPI_URL="" LAPI_MACHINE=""
|
||||||
|
prompt_text " Central LAPI URL (e.g. http://homelab-ip:8080):" "" LAPI_URL
|
||||||
|
prompt_text " Machine name to register as:" "$(hostname)" LAPI_MACHINE
|
||||||
|
if [ -n "$LAPI_URL" ]; then
|
||||||
|
if sudo cscli lapi register -u "$LAPI_URL" --machine "$LAPI_MACHINE"; then
|
||||||
|
echo " ✓ Registered with $LAPI_URL as '$LAPI_MACHINE'"
|
||||||
|
|
||||||
|
# Disable the local API server (remove the 'api.server:' block
|
||||||
|
# from config.yaml) now that this agent forwards to the
|
||||||
|
# central one instead. Backed up first — this is a direct
|
||||||
|
# edit to CrowdSec's core config.
|
||||||
|
local CS_CONFIG="/etc/crowdsec/config.yaml"
|
||||||
|
local CS_BACKUP="$CS_CONFIG.backup.$(date +%Y%m%d-%H%M%S)"
|
||||||
|
sudo cp "$CS_CONFIG" "$CS_BACKUP"
|
||||||
|
sudo awk '
|
||||||
|
/^ server:/ { skip=1; next }
|
||||||
|
skip && /^([a-zA-Z]| [a-zA-Z])/ { skip=0 }
|
||||||
|
!skip { print }
|
||||||
|
' "$CS_CONFIG" | sudo tee "$CS_CONFIG.new" > /dev/null \
|
||||||
|
&& sudo mv "$CS_CONFIG.new" "$CS_CONFIG"
|
||||||
|
echo " ✓ Local API server disabled in config.yaml (backup: $(basename "$CS_BACKUP"))"
|
||||||
|
echo ""
|
||||||
|
echo " ⚠ Not usable yet — on the CENTRAL LAPI machine, run:"
|
||||||
|
echo " sudo cscli machines validate $LAPI_MACHINE"
|
||||||
|
echo " Then restart this agent: sudo systemctl restart crowdsec"
|
||||||
|
echo " If it fails to start afterward, restore the backup and check logs:"
|
||||||
|
echo " sudo cp $CS_BACKUP $CS_CONFIG && sudo systemctl restart crowdsec"
|
||||||
|
_REMOTE_LAPI_PENDING="y"
|
||||||
|
else
|
||||||
|
echo " ⚠ cscli lapi register failed — keeping the local LAPI. See:"
|
||||||
|
echo " sudo cscli lapi register -u $LAPI_URL --machine $LAPI_MACHINE"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo " ⚠ Failed to restart CrowdSec"
|
echo " No URL entered — keeping the local LAPI."
|
||||||
echo " Check logs: sudo journalctl -u crowdsec -n 50"
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 8. Restart services to apply ─────────────────────────────────────────
|
||||||
|
if [ "$_REMOTE_LAPI_PENDING" = "y" ]; then
|
||||||
|
echo ""
|
||||||
|
echo " Skipping the restart below — it would fail until the machine is"
|
||||||
|
echo " validated on the central LAPI (see above). Restart manually after:"
|
||||||
|
echo " sudo systemctl restart crowdsec"
|
||||||
|
else
|
||||||
|
local RESTART_CS=""
|
||||||
|
prompt_yn "Restart CrowdSec to apply changes? (y/n):" "y" RESTART_CS
|
||||||
|
if [ "$RESTART_CS" = "y" ] || [ "$RESTART_CS" = "Y" ]; then
|
||||||
|
sudo systemctl enable crowdsec 2>/dev/null || true
|
||||||
|
if sudo systemctl restart crowdsec; then
|
||||||
|
echo " ✓ CrowdSec restarted successfully"
|
||||||
|
sudo systemctl enable crowdsec-firewall-bouncer 2>/dev/null || true
|
||||||
|
sudo systemctl restart crowdsec-firewall-bouncer 2>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
sudo cscli metrics 2>/dev/null | head -20 || true
|
||||||
|
else
|
||||||
|
echo " ⚠ Failed to restart CrowdSec"
|
||||||
|
echo " Check logs: sudo journalctl -u crowdsec -n 50"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -292,6 +352,21 @@ sudo cscli collections list # installed detection collections
|
|||||||
- ntfy ban alerts (if enabled): `/etc/crowdsec/notifications/ntfy.yaml`,
|
- ntfy ban alerts (if enabled): `/etc/crowdsec/notifications/ntfy.yaml`,
|
||||||
wired into `/etc/crowdsec/profiles.yaml`
|
wired into `/etc/crowdsec/profiles.yaml`
|
||||||
- Bouncer config: `/etc/crowdsec/bouncers/`
|
- Bouncer config: `/etc/crowdsec/bouncers/`
|
||||||
|
- Remote/central LAPI (if enabled): `/etc/crowdsec/local_api_credentials.yaml`
|
||||||
|
points at the remote URL; the local API server block is removed from
|
||||||
|
`/etc/crowdsec/config.yaml` (backed up as `config.yaml.backup.<timestamp>`
|
||||||
|
next to it before editing). Parsers, scenarios, and the firewall bouncer
|
||||||
|
still run locally regardless — only the decision database is centralized.
|
||||||
|
|
||||||
|
## Multi-server (remote LAPI) notes
|
||||||
|
|
||||||
|
- On THIS machine: `sudo cscli lapi register -u <url> --machine <name>`
|
||||||
|
registers and disables the local API server.
|
||||||
|
- On the CENTRAL machine: `sudo cscli machines validate <name>` approves it —
|
||||||
|
not automated, since that's a different box.
|
||||||
|
- Check registration status here: `sudo cscli lapi status`
|
||||||
|
- Revert: restore the `config.yaml` backup and
|
||||||
|
`sudo systemctl restart crowdsec`.
|
||||||
|
|
||||||
## Geo + reputation notes
|
## Geo + reputation notes
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user