commit 1d41970b8dc5909afcac0a4cebde2de819e304a2 Author: Myron Blair Date: Tue Jun 9 04:07:27 2026 +0000 Initial: backup/restore scripts + README for FusionPBX - backup.sh: weekly PostgreSQL dump + FreeSWITCH/nginx/fail2ban configs - restore.sh: 10-phase interactive restore wizard - README.md: full rebuild guide, PBX reference, SSH relay instructions Co-Authored-By: Claude Sonnet 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcf33e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.log +*.tmp +__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..28fb6a1 --- /dev/null +++ b/README.md @@ -0,0 +1,164 @@ +# FusionPBX Config Backup & Restore + +Weekly backup of the FusionPBX/FreeSWITCH PBX server (`fusion`, 134.209.72.226). +Debian 12, FreeSWITCH, FusionPBX, PostgreSQL 18, nginx, PHP 8.2. + +--- + +## Critical: The Database IS the Config + +FusionPBX stores **everything** in PostgreSQL (`fusionpbx` database): +- Extensions, voicemail boxes, passwords +- Dialplans and call routing +- SIP gateways (SignalWire trunk) +- IVR menus +- Ring groups +- Device provisioning (Yealink T48S settings) +- Users and permissions +- Call recordings index + +The `database/fusionpbx.sql` dump in this repo is the heart of the backup. + +--- + +## What's Backed Up + +| Directory | Source | Contents | +|-----------|--------|----------| +| `database/` | `pg_dump fusionpbx` | Complete FusionPBX database — extensions, dialplans, SIP, IVR, everything | +| `database/postgres_globals.sql` | `pg_dumpall --globals` | PostgreSQL roles and passwords | +| `freeswitch/` | `/etc/freeswitch/` | vars.xml, sip_profiles, key autoload configs | +| `fusionpbx-app/` | `/var/www/fusionpbx/resources/` | config.php (DB credentials) | +| `nginx/` | `/etc/nginx/sites-enabled/` | fusionpbx nginx config (incl. all provisioning URL rewrites) | +| `fail2ban/` | `/etc/fail2ban/` | jail.local (trusted IPs: 107.178.2.130, 97.154.109.245) | +| `network/` | `/etc/netplan/` | 50-cloud-init.yaml, hosts, hostname | +| `systemd/` | `/etc/systemd/system/` | All FusionPBX service units | +| `ssh/` | `/root/.ssh/` | authorized_keys | +| `recordings/` | `/var/lib/freeswitch/recordings/` | Call recordings (~3.7MB) | + +**Backup schedule:** Every Sunday at 5:00 AM +**Log:** `/var/log/fusionpbx-backup.log` +**Manual trigger:** `/usr/local/bin/fusionpbx-backup` + +--- + +## SSH Access — Important + +Port 22 is **firewalled** from the internet. Only accessible from: +- `107.178.2.130` (your home FortiGate external IP) +- `97.154.109.245` (FortiGate secondary) + +**From anywhere else, relay through DO:** +```bash +ssh root@165.22.1.228 # DO server (always accessible) +ssh root@134.209.72.226 # then hop to FusionPBX +``` + +--- + +## Disaster Recovery — Full Rebuild + +**Estimated time: 30–45 minutes** + +### Step 1 — New Debian 12 Droplet + +Create a fresh Debian 12 droplet on DigitalOcean, same region as original. +Get SSH access, then relay via the DO server if needed. + +### Step 2 — Clone this repo and run restore + +```bash +apt update && apt install -y git +git clone https://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@github.com/myronblair/fusionpbx-config.git /opt/fusionpbx-config +bash /opt/fusionpbx-config/restore.sh +``` + +The restore script walks through all phases interactively. + +### Step 3 — Update fail2ban trusted IPs if FortiGate IP changed + +The FortiGate DDNS (`orbisne.fortiddns.com`) resolves to your home IP. +If your external IP changed, update `fail2ban/jail.local`: +``` +ignoreip = 127.0.0.1/8 ::1 97.154.109.245 +``` + +### Step 4 — Update Yealink provisioning URL if server IP changed + +If the new droplet has a different IP, update the Yealink T48S provisioning: +``` +Old URL: http://134.209.72.226/app/provision/?mac={mac} +New URL: http:///app/provision/?mac={mac} +``` +Set via Yealink web UI or FusionPBX → Apps → Provision → Devices. + +--- + +## PBX Quick Reference + +| Item | Value | +|------|-------| +| FusionPBX web | https://fusion.orbishosting.com | +| Admin login | admin / fY7XP5swgtpbzrYLhkeVYkA4744 | +| Root SSH | root / Joker1974!@# | +| PostgreSQL DB | fusionpbx (user: fusionpbx) | +| FreeSWITCH CLI | `fs_cli` | +| SIP trunk | SignalWire (mod_signalwire) | +| SIP profile | Use `internal` profile on port 5060 | +| NAT setting | `aggressive-nat-detection=true` | + +## Extensions + +| Ext | Name | Device | Location | +|-----|------|--------|----------| +| 1000 | Myron | Yealink T48S | 10.48.200.43 (FortiGate LAN) | +| 1001 | Tommy | Softphone | Remote | +| 1002 | (spare) | — | — | +| 900 | IVR | Main number auto-attendant | — | + +## Key Commands + +```bash +# FreeSWITCH CLI +fs_cli + +# Inside fs_cli: +sofia status # show SIP profiles +sofia status gateway # show gateway registrations +show registrations # show registered extensions +reload mod_sofia # reload SIP after config change +reloadxml # reload dialplan XML + +# Force FusionPBX to regenerate FreeSWITCH XML cache +rm /var/cache/fusionpbx/FusionPBX.configuration.sofia.conf +systemctl restart freeswitch + +# Check all FusionPBX services +systemctl status active_calls active_conferences email_queue event_guard \ + fax_queue system_status transcribe_queue websockets xml_cdr + +# Logs +journalctl -u freeswitch -f +tail -f /var/log/freeswitch/freeswitch.log +tail -f /var/log/nginx/access.log +``` + +--- + +## What Is NOT Backed Up + +| Item | Notes | +|------|-------| +| SSL certs | `/etc/letsencrypt/` — re-issue: `certbot --nginx -d fusion.orbishosting.com` | +| FusionPBX web app | `/var/www/fusionpbx/` — reinstalled by official installer | +| FreeSWITCH binary | Installed by FusionPBX installer | +| Voicemail audio files | `/var/lib/freeswitch/storage/voicemail/` — small, not critical | + +--- + +## Architecture Notes + +- FusionPBX uses a **Lua XML handler** — FreeSWITCH queries PostgreSQL via PHP/Lua for all routing decisions. No static XML dialplan files in production. +- **SignalWire** SIP trunk uses `transport=udp` — critical, TCP caused re-INVITE issues. +- **ext 1000 (Yealink T48S)** registers from behind FortiGate using `aggressive-nat-detection=true` on port 5080 to bypass SIP ALG. +- **FusionPBX cache**: `/var/cache/fusionpbx/FusionPBX.configuration.sofia.conf` — delete this file to force a full Sofia reload after changes. diff --git a/backup.sh b/backup.sh new file mode 100644 index 0000000..ae6a86a --- /dev/null +++ b/backup.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# ============================================================================= +# FusionPBX Config Backup — runs on fusion (134.209.72.226) +# Critical: PostgreSQL fusionpbx DB dump + FreeSWITCH configs +# Install: /usr/local/bin/fusionpbx-backup +# Cron: 0 5 * * 0 /usr/local/bin/fusionpbx-backup >> /var/log/fusionpbx-backup.log 2>&1 +# ============================================================================= +set -euo pipefail + +PAT="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" +REPO_URL="https://${PAT}@github.com/myronblair/fusionpbx-config.git" +REPO_DIR="/opt/fusionpbx-config" +LOG_PREFIX="[$(date '+%Y-%m-%d %H:%M:%S')] [fusion]" + +log() { echo "$LOG_PREFIX $*"; } + +# --------------------------------------------------------------------------- +# 1. Clone or pull repo +# --------------------------------------------------------------------------- +if [[ -d "$REPO_DIR/.git" ]]; then + log "Pulling latest from GitHub" + cd "$REPO_DIR" + git config user.email "backup@orbishosting.com" + git config user.name "FusionPBX Backup" + git pull --rebase origin main -q || true +else + log "Cloning repo to $REPO_DIR" + git clone "$REPO_URL" "$REPO_DIR" + cd "$REPO_DIR" + git config user.email "backup@orbishosting.com" + git config user.name "FusionPBX Backup" +fi + +cd "$REPO_DIR" +mkdir -p database freeswitch/autoload_configs freeswitch/sip_profiles \ + nginx fail2ban network systemd ssh recordings + +# --------------------------------------------------------------------------- +# 2. PostgreSQL dump — THE most critical piece +# Everything (extensions, dialplans, SIP gateways, IVR, ring groups, +# devices, voicemail, users) lives here. +# --------------------------------------------------------------------------- +log "Dumping PostgreSQL fusionpbx database" +su -c "pg_dump --clean --if-exists fusionpbx" postgres > database/fusionpbx.sql 2>/dev/null +DUMP_SIZE=$(wc -c < database/fusionpbx.sql) +log " DB dump: $(numfmt --to=iec $DUMP_SIZE 2>/dev/null || echo ${DUMP_SIZE}B)" + +# Also dump roles/users +su -c "pg_dumpall --globals-only" postgres > database/postgres_globals.sql 2>/dev/null + +# --------------------------------------------------------------------------- +# 3. FreeSWITCH config (key files — FusionPBX dynamically generates the rest) +# --------------------------------------------------------------------------- +log "Backing up FreeSWITCH config" +cp /etc/freeswitch/vars.xml freeswitch/ 2>/dev/null || true +cp /etc/freeswitch/freeswitch.xml freeswitch/ 2>/dev/null || true +cp /etc/freeswitch/extensions.conf freeswitch/ 2>/dev/null || true +# SIP profiles (noload — managed by FusionPBX, but useful as reference) +rsync -a --delete /etc/freeswitch/sip_profiles/ freeswitch/sip_profiles/ 2>/dev/null || true +# Autoload configs that may have been customized +for f in switch.conf.xml sofia.conf.xml event_socket.conf.xml modules.conf.xml \ + lua.conf.xml conference.conf logfile.conf.xml; do + [[ -f "/etc/freeswitch/autoload_configs/$f" ]] && \ + cp "/etc/freeswitch/autoload_configs/$f" freeswitch/autoload_configs/ || true +done + +# --------------------------------------------------------------------------- +# 4. FusionPBX application config (has DB credentials + domain settings) +# --------------------------------------------------------------------------- +log "Backing up FusionPBX app config" +mkdir -p fusionpbx-app +[[ -f /var/www/fusionpbx/resources/config.php ]] && \ + cp /var/www/fusionpbx/resources/config.php fusionpbx-app/ || true + +# --------------------------------------------------------------------------- +# 5. nginx config +# --------------------------------------------------------------------------- +log "Backing up nginx config" +[[ -f /etc/nginx/sites-enabled/fusionpbx ]] && \ + cp /etc/nginx/sites-enabled/fusionpbx nginx/fusionpbx.conf || true + +# --------------------------------------------------------------------------- +# 6. fail2ban +# --------------------------------------------------------------------------- +log "Backing up fail2ban" +[[ -f /etc/fail2ban/jail.local ]] && cp /etc/fail2ban/jail.local fail2ban/ || true +rsync -a --delete /etc/fail2ban/jail.d/ fail2ban/jail.d/ 2>/dev/null || true + +# --------------------------------------------------------------------------- +# 7. Network / netplan +# --------------------------------------------------------------------------- +log "Backing up network" +for f in /etc/netplan/*.yaml; do + [[ -f "$f" ]] && cp "$f" network/ || true +done +cp /etc/hosts network/hosts 2>/dev/null || true +cp /etc/hostname network/hostname 2>/dev/null || true + +# --------------------------------------------------------------------------- +# 8. Systemd units (FusionPBX service daemons) +# --------------------------------------------------------------------------- +log "Backing up systemd units" +FPBX_UNITS="active_calls active_conferences email_queue event_guard fax_queue system_status transcribe_queue websockets xml_cdr" +for unit in $FPBX_UNITS; do + [[ -f "/etc/systemd/system/$unit.service" ]] && \ + cp "/etc/systemd/system/$unit.service" systemd/ || true +done + +# --------------------------------------------------------------------------- +# 9. SSH authorized_keys +# --------------------------------------------------------------------------- +log "Backing up SSH keys" +[[ -f /root/.ssh/authorized_keys ]] && cp /root/.ssh/authorized_keys ssh/ || true + +# --------------------------------------------------------------------------- +# 10. Recordings (small — 3.7MB) +# --------------------------------------------------------------------------- +log "Backing up recordings" +if [[ -d /var/lib/freeswitch/recordings ]]; then + rsync -a --delete /var/lib/freeswitch/recordings/ recordings/ 2>/dev/null || true + RECSIZE=$(du -sh recordings/ 2>/dev/null | cut -f1) + log " Recordings: $RECSIZE" +fi + +# --------------------------------------------------------------------------- +# 11. Commit and push +# --------------------------------------------------------------------------- +log "Committing changes" +git add -A +if git diff --cached --quiet; then + log "No changes to commit" +else + CHANGES=$(git diff --cached --stat | tail -1) + git commit -m "[fusion] Weekly backup $(date '+%Y-%m-%d') — $CHANGES" + log "Pushing to GitHub" + git push origin main + log "Backup complete" +fi diff --git a/restore.sh b/restore.sh new file mode 100644 index 0000000..87951cd --- /dev/null +++ b/restore.sh @@ -0,0 +1,283 @@ +#!/usr/bin/env bash +# ============================================================================= +# FusionPBX Restore — run on a fresh Debian 12 droplet +# +# Usage: +# bash restore.sh +# +# NOTE: SSH to this server is firewalled — access via DO server relay: +# ssh root@165.22.1.228 +# ssh root@134.209.72.226 +# ============================================================================= + +REPO_DIR="$(cd "$(dirname "$0")" && pwd)" + +RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' + +header() { echo -e "\n${CYAN}══════════════════════════════════════════════════${NC}"; echo -e "${CYAN}${BOLD} $*${NC}"; echo -e "${CYAN}══════════════════════════════════════════════════${NC}"; } +success() { echo -e "${GREEN} ✓ $*${NC}"; } +warn() { echo -e "${YELLOW} ⚠ $*${NC}"; } +info() { echo -e " → $*"; } +die() { echo -e "${RED} ✗ $*${NC}" >&2; exit 1; } + +confirm() { + echo -e "\n${YELLOW} $1${NC}" + read -rp " Apply? [Y/n] " ans + [[ "${ans:-Y}" =~ ^[Yy]$ ]] +} + +[[ $(id -u) -eq 0 ]] || die "Must run as root" + +clear +echo -e "${CYAN}" +cat << 'BANNER' + ╔══════════════════════════════════════════════════════╗ + ║ FUSIONPBX RESTORE — fusion.orbishosting.com ║ + ║ 134.209.72.226 | Debian 12 | FreeSWITCH ║ + ╚══════════════════════════════════════════════════════╝ +BANNER +echo -e "${NC}" +echo " Source : $REPO_DIR" +echo " Date : $(date)" +echo "" +warn "SSH to this server is port-restricted." +warn "Access via: ssh root@165.22.1.228, then ssh root@134.209.72.226" +echo "" +read -rp " Type 'yes' to continue: " c +[[ "$c" == "yes" ]] || { echo "Aborted."; exit 0; } + +APPLIED=(); SKIPPED=() + +# --------------------------------------------------------------------------- +# PHASE 1: SSH & Network +# --------------------------------------------------------------------------- +header "PHASE 1 — SSH & Network" + +if [[ -f "$REPO_DIR/ssh/authorized_keys" ]] && confirm "Restore SSH authorized_keys?"; then + mkdir -p /root/.ssh && chmod 700 /root/.ssh + cp "$REPO_DIR/ssh/authorized_keys" /root/.ssh/authorized_keys + chmod 600 /root/.ssh/authorized_keys + success "authorized_keys restored" + APPLIED+=("ssh") +else; SKIPPED+=("ssh"); fi + +if confirm "Set hostname to 'fusion' (fusion.orbishosting.com)?"; then + hostnamectl set-hostname fusion + echo "127.0.1.1 fusion.orbishosting.com" >> /etc/hosts + success "Hostname set" + APPLIED+=("hostname") +else; SKIPPED+=("hostname"); fi + +# --------------------------------------------------------------------------- +# PHASE 2: Install FusionPBX + FreeSWITCH +# --------------------------------------------------------------------------- +header "PHASE 2 — Install FusionPBX (official installer)" + +echo "" +warn "The official FusionPBX installer handles everything:" +warn "FreeSWITCH, PostgreSQL, nginx, PHP, fail2ban, all systemd units." +echo "" +info "Run this in a separate terminal and come back when done (~10-15 min):" +echo "" +echo -e " ${BOLD}wget -O - https://raw.githubusercontent.com/fusionpbx/fusionpbx-install.sh/master/debian/install.sh | bash${NC}" +echo "" +warn "During install, when asked for the domain: enter fusion.orbishosting.com" +warn "Note the admin username/password it generates — you'll change it after DB restore." +echo "" +read -rp " Press ENTER once FusionPBX is fully installed... " + +# Verify install +if [[ ! -f /var/www/fusionpbx/index.php ]]; then + warn "FusionPBX doesn't appear to be installed at /var/www/fusionpbx/" + read -rp " Continue anyway? [y/N] " ans + [[ "${ans:-N}" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 1; } +fi + +# --------------------------------------------------------------------------- +# PHASE 3: Restore PostgreSQL Database — THE critical step +# --------------------------------------------------------------------------- +header "PHASE 3 — Restore PostgreSQL Database (ALL CONFIG)" + +echo "" +warn "This restores ALL FusionPBX config: extensions, dialplans, SIP gateways," +warn "IVR menus, ring groups, devices, voicemail settings, users — everything." +echo "" +info "Backup DB dump: $REPO_DIR/database/fusionpbx.sql" +info "Size: $(wc -l < $REPO_DIR/database/fusionpbx.sql 2>/dev/null) lines" +echo "" + +if confirm "Stop services and restore PostgreSQL fusionpbx database?"; then + # Stop FusionPBX services before restoring + for svc in active_calls active_conferences email_queue event_guard fax_queue \ + system_status transcribe_queue websockets xml_cdr freeswitch; do + systemctl stop "$svc" 2>/dev/null || true + done + info "Services stopped" + + # Drop and recreate the database + su -c "psql -c 'DROP DATABASE IF EXISTS fusionpbx;'" postgres + su -c "psql -c 'CREATE DATABASE fusionpbx OWNER fusionpbx;'" postgres + su -c "psql fusionpbx < '$REPO_DIR/database/fusionpbx.sql'" postgres + success "Database restored" + + # Restore postgres globals (roles/passwords) + if [[ -f "$REPO_DIR/database/postgres_globals.sql" ]]; then + su -c "psql < '$REPO_DIR/database/postgres_globals.sql'" postgres 2>/dev/null || true + success "PostgreSQL globals restored" + fi + + # Restart FreeSWITCH and FusionPBX services + systemctl start freeswitch + sleep 3 + for svc in active_calls active_conferences email_queue event_guard fax_queue \ + system_status transcribe_queue websockets xml_cdr; do + systemctl start "$svc" 2>/dev/null || true + done + success "Services restarted" + APPLIED+=("database") +else; SKIPPED+=("database"); fi + +# --------------------------------------------------------------------------- +# PHASE 4: FreeSWITCH Config +# --------------------------------------------------------------------------- +header "PHASE 4 — FreeSWITCH Config" + +if [[ -d "$REPO_DIR/freeswitch" ]] && confirm "Restore FreeSWITCH config files?"; then + [[ -f "$REPO_DIR/freeswitch/vars.xml" ]] && \ + cp "$REPO_DIR/freeswitch/vars.xml" /etc/freeswitch/ + [[ -f "$REPO_DIR/freeswitch/freeswitch.xml" ]] && \ + cp "$REPO_DIR/freeswitch/freeswitch.xml" /etc/freeswitch/ + # Autoload configs + if [[ -d "$REPO_DIR/freeswitch/autoload_configs" ]]; then + rsync -a "$REPO_DIR/freeswitch/autoload_configs/" \ + /etc/freeswitch/autoload_configs/ + fi + # Fix ownership + chown -R freeswitch:freeswitch /etc/freeswitch/ 2>/dev/null || true + # Force reload of FusionPBX XML cache + rm -f /var/cache/fusionpbx/FusionPBX.configuration.sofia.conf 2>/dev/null || true + systemctl reload freeswitch 2>/dev/null || systemctl restart freeswitch + success "FreeSWITCH config restored and reloaded" + APPLIED+=("freeswitch-config") +else; SKIPPED+=("freeswitch-config"); fi + +# --------------------------------------------------------------------------- +# PHASE 5: FusionPBX App Config +# --------------------------------------------------------------------------- +header "PHASE 5 — FusionPBX App Config (DB credentials)" + +if [[ -f "$REPO_DIR/fusionpbx-app/config.php" ]] && \ + confirm "Restore FusionPBX resources/config.php (contains DB credentials)?"; then + cp "$REPO_DIR/fusionpbx-app/config.php" /var/www/fusionpbx/resources/config.php + chown www-data:www-data /var/www/fusionpbx/resources/config.php + success "FusionPBX config.php restored" + APPLIED+=("fusionpbx-config") +else; SKIPPED+=("fusionpbx-config"); fi + +# --------------------------------------------------------------------------- +# PHASE 6: nginx Config +# --------------------------------------------------------------------------- +header "PHASE 6 — nginx Config" + +if [[ -f "$REPO_DIR/nginx/fusionpbx.conf" ]] && confirm "Restore nginx fusionpbx config?"; then + cp "$REPO_DIR/nginx/fusionpbx.conf" /etc/nginx/sites-enabled/fusionpbx + nginx -t && systemctl reload nginx + success "nginx config restored and reloaded" + APPLIED+=("nginx") +else + SKIPPED+=("nginx") + warn "Skipped — installer's nginx config will work; restore if provisioning URLs break" +fi + +# --------------------------------------------------------------------------- +# PHASE 7: fail2ban +# --------------------------------------------------------------------------- +header "PHASE 7 — fail2ban" + +if [[ -f "$REPO_DIR/fail2ban/jail.local" ]] && confirm "Restore fail2ban jail.local (includes trusted IPs)?"; then + cp "$REPO_DIR/fail2ban/jail.local" /etc/fail2ban/jail.local + [[ -d "$REPO_DIR/fail2ban/jail.d" ]] && \ + rsync -a "$REPO_DIR/fail2ban/jail.d/" /etc/fail2ban/jail.d/ + systemctl restart fail2ban + success "fail2ban restored — trusted IPs: 107.178.2.130, 97.154.109.245" + APPLIED+=("fail2ban") +else; SKIPPED+=("fail2ban"); fi + +# --------------------------------------------------------------------------- +# PHASE 8: Recordings +# --------------------------------------------------------------------------- +header "PHASE 8 — Recordings" + +if [[ -d "$REPO_DIR/recordings" ]] && \ + [[ -n "$(ls -A $REPO_DIR/recordings 2>/dev/null)" ]] && \ + confirm "Restore call recordings?"; then + rsync -a "$REPO_DIR/recordings/" /var/lib/freeswitch/recordings/ + chown -R freeswitch:freeswitch /var/lib/freeswitch/recordings/ + success "Recordings restored" + APPLIED+=("recordings") +else; SKIPPED+=("recordings"); fi + +# --------------------------------------------------------------------------- +# PHASE 9: Backup script + cron +# --------------------------------------------------------------------------- +header "PHASE 9 — Backup Script & Cron" + +if confirm "Install fusionpbx-backup cron (weekly Sunday 5am)?"; then + cp "$REPO_DIR/backup.sh" /usr/local/bin/fusionpbx-backup + chmod +x /usr/local/bin/fusionpbx-backup + (crontab -l 2>/dev/null | grep -v fusionpbx-backup; \ + echo "0 5 * * 0 /usr/local/bin/fusionpbx-backup >> /var/log/fusionpbx-backup.log 2>&1") | crontab - + success "Backup cron installed" + APPLIED+=("backup-cron") +else; SKIPPED+=("backup-cron"); fi + +# --------------------------------------------------------------------------- +# PHASE 10: SSL (manual) +# --------------------------------------------------------------------------- +header "PHASE 10 — SSL Certificate" + +echo "" +info "Re-issue SSL cert after DNS is pointing to this server:" +info " certbot --nginx -d fusion.orbishosting.com" +info " OR: FusionPBX web UI → Admin → Certificate Manager" +echo "" +read -rp " Press ENTER to continue... " + +# --------------------------------------------------------------------------- +# PHASE 11: SignalWire SIP Trunk +# --------------------------------------------------------------------------- +header "PHASE 11 — SignalWire SIP Trunk" + +echo "" +warn "The SignalWire SIP gateway is stored in the PostgreSQL DB and will" +warn "be restored automatically with the DB dump." +echo "" +info "After restore, verify in FusionPBX → Accounts → Gateways:" +info " Gateway: signalwire-inbound (or similar)" +info " SIP profile: external (transport=udp — required to avoid re-INVITE issues)" +info " If calls don't work: Admin → FreeSWITCH → Reload XML" +info " Also delete Sofia XML cache: rm /var/cache/fusionpbx/FusionPBX.configuration.sofia.conf" +echo "" + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +header "Restore Complete" +echo "" +[[ ${#APPLIED[@]} -gt 0 ]] && success "Applied: ${APPLIED[*]}" +[[ ${#SKIPPED[@]} -gt 0 ]] && warn "Skipped: ${SKIPPED[*]}" +echo "" +echo -e "${YELLOW} Final checklist:${NC}" +echo " • FusionPBX web UI: https://fusion.orbishosting.com (admin / fY7XP5swgtpbzrYLhkeVYkA4744)" +echo " • Check FreeSWITCH status: systemctl status freeswitch" +echo " • Check gateways registered: fs_cli -x 'sofia status gateway'" +echo " • Check extensions load: fs_cli -x 'show registrations'" +echo " • Check fail2ban trusted IPs: fail2ban-client status" +echo " • Test ext 1000 (Myron Yealink T48S at 10.48.200.43) — update provisioning URL if IP changed" +echo " • Test ext 1001 (Tommy) registration" +echo " • Test IVR 900 (main number)" +echo " • Verify SignalWire trunk: make outbound test call" +echo "" +echo " SSH access: only from 107.178.2.130 and 97.154.109.245 (FortiGate)" +echo " Or relay: ssh root@165.22.1.228 → ssh root@134.209.72.226" +echo ""