docs: admin guide (install, voip.ms, network, E911, troubleshooting, updating)
docs/admin-guide/: install and the setup wizard, voip.ms with screenshots, NAT and SIP ALG, firewall (ufw, nftables limited to the voip.ms POP), fail2ban and CrowdSec, E911 duties, recording consent, troubleshooting (08 §3.3), updating, backup and restore, monitoring and log rotation. scripts/update.sh as 08 §3.1 describes. The e2e checks fail2ban's and CrowdSec's stock Asterisk rules against a real failed sign-in, takes the screenshots and checks every link and anchor. Task: M11-T7
This commit is contained in:
1 parent
29ee4ba105
commit
324f9d9fe2
17 files changed
+1002
-1
No files matched your search
@@ -0,0 +1,114 @@
|
||||
# SelfPhoneNG admin guide
|
||||
|
||||
This guide takes you from an empty server to working inbound and outbound calls, then covers
|
||||
keeping the system safe, legal and up to date.
|
||||
|
||||
1. **Install** (this page)
|
||||
2. [voip.ms setup](voipms.md): the sub-account, numbers and the trunk
|
||||
3. [Network, firewall and attacks](network.md): NAT, SIP ALG, ports, firewall, fail2ban / CrowdSec
|
||||
4. [Emergency calls and recording law](legal.md): E911 duties and call recording consent
|
||||
5. [Troubleshooting](troubleshooting.md)
|
||||
6. [Updating, backups and monitoring](maintenance.md)
|
||||
|
||||
---
|
||||
|
||||
## What you need
|
||||
|
||||
- A Linux server (a VM is fine) with a **public IP** or a router you can forward ports on.
|
||||
A small office needs very little: 1–2 CPUs and 2 GB of memory is plenty.
|
||||
- **Docker Engine with the compose plugin**: <https://docs.docker.com/engine/install/>.
|
||||
Check with `docker compose version`.
|
||||
- `git` and `openssl` (both are on almost every Linux install).
|
||||
- A **DNS name** for the server, e.g. `pbx.example.com`, with an A record pointing at its public IP.
|
||||
Browsers need HTTPS for the microphone, so the browser phone only works through this name.
|
||||
- A **voip.ms account** with at least one phone number (DID). Other SIP providers work as a
|
||||
"generic" trunk, but this guide uses voip.ms.
|
||||
|
||||
## Ports
|
||||
|
||||
Open these to the internet (on the server's firewall, and forwarded on your router if the
|
||||
server is behind one; see [Network](network.md)):
|
||||
|
||||
| Port | Protocol | For |
|
||||
|---|---|---|
|
||||
| 443 | TCP | Web app, browser phone, phone setup files |
|
||||
| 80 | TCP | Only with the bundled Caddy: getting the HTTPS certificate |
|
||||
| 5060 | UDP (+TCP) | SIP: voip.ms and desk phones |
|
||||
| 5061 | TCP | Only if you turn on SIP over TLS |
|
||||
| 10000–20000 | UDP | Call audio (RTP) |
|
||||
|
||||
Everything else (the database, the api on 8080, Asterisk's 5038 and 8088) listens on
|
||||
127.0.0.1 only. Don't open them.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
git clone https://git.dou.bet/spng/selfphoneng.git
|
||||
cd selfphoneng
|
||||
./scripts/install.sh
|
||||
```
|
||||
|
||||
The script:
|
||||
|
||||
1. Checks Docker is there.
|
||||
2. Writes `.env` with random passwords and asks for:
|
||||
- the **public host name** (`pbx.example.com`),
|
||||
- the **first admin's email and password** (at least 10 characters).
|
||||
3. Makes the `data/` folders.
|
||||
4. Asks **"Use bundled Caddy for HTTPS?"**
|
||||
- **Yes**: Caddy gets a Let's Encrypt certificate for your host name by itself. Ports 80 and
|
||||
443 must reach the server and nothing else may use them.
|
||||
- **No**: you already run a reverse proxy. Copy `deploy/nginx/selfphoneng.conf.example`,
|
||||
put in your host name and certificate paths, and reload nginx. Any proxy works if it sends
|
||||
`/ws` to `127.0.0.1:8088` (the browser phone's WebSocket) and everything else to
|
||||
`127.0.0.1:8080`, with WebSocket upgrades allowed on both.
|
||||
5. Builds and starts everything. The first build compiles Asterisk, so it takes a while.
|
||||
|
||||
When it's done, check the health URL on the server:
|
||||
|
||||
```sh
|
||||
curl http://127.0.0.1:8080/healthz # {"ok":true}
|
||||
```
|
||||
|
||||
> **Keep `.env` safe and never change `APP_SECRET`.** It encrypts the SIP and trunk passwords in
|
||||
> the database, and backups can only be restored with it. Copy `.env` somewhere safe (a password
|
||||
> manager) now.
|
||||
|
||||
## First sign-in: the setup wizard
|
||||
|
||||
Open `https://pbx.example.com/` and sign in with the admin email and password you gave the
|
||||
script. You're asked to pick a new password, then the setup wizard walks you through:
|
||||
|
||||
| Step | What to do |
|
||||
|---|---|
|
||||
| Company | Name and time zone (office hours and reports use it). |
|
||||
| Network | The **public IP** (Detect fills it in) and the **local networks**. Do the router steps shown there: SIP ALG off and the port forwards ([Network](network.md)). |
|
||||
| Email | Your SMTP server, then **Send test**. Voicemail-to-email and alerts need it. |
|
||||
| Trunk | Your voip.ms sub-account ([voip.ms setup](voipms.md)). Wait for **Registered**. |
|
||||
| Phone numbers | Add your DIDs, or **Import numbers from voip.ms** if you gave API details. |
|
||||
| Users | People and their extensions. |
|
||||
| Main number | Where calls to your main number go. |
|
||||
| Emergency | The **default emergency caller ID** (required once a trunk exists) and who's told when someone dials 911 ([E911](legal.md#emergency-calls-e911)). |
|
||||
|
||||
You can leave the wizard and come back; everything it sets is also in the admin pages.
|
||||
|
||||
Then:
|
||||
|
||||
- **Place a test call out.** From a desk phone, or from the browser phone (a user with an
|
||||
extension signs in and calls from **My phone**), call your mobile.
|
||||
- **Place a test call in.** From your mobile, call your main number.
|
||||
|
||||
If either fails, see [Troubleshooting](troubleshooting.md).
|
||||
|
||||
## Phones
|
||||
|
||||
- **Desk phones (Yealink, Grandstream, Polycom/Poly, Snom, Fanvil):** add them on **Phones** with
|
||||
their MAC address. The page shows the setup URL and password to enter on the phone.
|
||||
- **Any other SIP phone:** use the details on the user's **Credentials** tab.
|
||||
- **Browser phone:** every user with an extension gets one on **My phone** after signing in. There
|
||||
is nothing to set up.
|
||||
|
||||
## Turning on two-step sign-in
|
||||
|
||||
Admins can (and should) turn on two-step sign-in: header → **Account** → **Turn on two-step sign-in**. Keep
|
||||
the recovery codes somewhere safe.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,95 @@
|
||||
# Emergency calls and recording law
|
||||
|
||||
> This page explains what SelfPhoneNG does and what the law expects of you as the operator of a
|
||||
> phone system. It is **not legal advice**. Rules differ by country, state and type of business:
|
||||
> check with counsel for your situation.
|
||||
|
||||
## Emergency calls (E911)
|
||||
|
||||
In the US, two federal rules apply to multi-line phone systems like this one:
|
||||
|
||||
- **Kari's Law** (47 CFR 9.16):
|
||||
- anyone must be able to dial **911 directly**, with no prefix like 9 first;
|
||||
- when someone does, the system must **notify** someone on site (front desk, security) where
|
||||
that's set up.
|
||||
- **RAY BAUM'S Act** (47 CFR 9.16(b)): a 911 call must carry a **dispatchable location**: the street
|
||||
address plus floor or room where that's needed to find the caller.
|
||||
|
||||
### What SelfPhoneNG does
|
||||
|
||||
- **911 and 9911 always work** from every extension.
|
||||
- This holds even if the extension may only call internally, is on do-not-disturb, or every
|
||||
trunk is busy.
|
||||
- Nothing you set can turn this off.
|
||||
- **It tells people.** When someone dials 911, SelfPhoneNG:
|
||||
- emails the addresses in **Settings → Emergency → Email these people**;
|
||||
- rings the phones under **Ring these phones**;
|
||||
- shows a red banner on every admin's dashboard;
|
||||
- fires the `emergency.call` webhook.
|
||||
- **It sends a caller ID that has an address.**
|
||||
- voip.ms gives the 911 operator the street address **registered on the number used as caller ID**.
|
||||
- That number is the extension's own **Emergency caller ID** (user → **Emergency** tab), or else
|
||||
the company's **Default emergency caller ID** (Settings → Emergency).
|
||||
|
||||

|
||||
|
||||
### What you must do
|
||||
|
||||
1. **Register an E911 address at voip.ms** for every number used as an emergency caller ID (DID →
|
||||
e911). This address is what the 911 operator gets.
|
||||
2. **Pick a default emergency caller ID** on Settings → Emergency. The dashboard stays red until you
|
||||
do, and until that number's **E911 address** note is filled in on Phone numbers (a reminder that
|
||||
you registered it).
|
||||
3. **Give people in other places their own number.**
|
||||
- Anyone who works somewhere else (another building, at home) needs an emergency caller ID whose
|
||||
E911 address is *where they are*.
|
||||
- Otherwise 911 is sent to the main office. Remote workers without such a number should call
|
||||
911 from a mobile phone, and must be told so.
|
||||
4. **Fill in each person's Location** (e.g. "2nd floor, room 210") on their **Emergency** tab.
|
||||
- It goes into the notification email, so whoever is on site can meet the responders.
|
||||
- The 911 operator **does not** receive it. voip.ms only passes the registered street address.
|
||||
If a room number is needed to find someone in your building, register it in the voip.ms
|
||||
address itself.
|
||||
5. **Test the notifications, never 911 itself.**
|
||||
- **Settings → Emergency → Test notifications** sends the email, rings the phones and shows the
|
||||
banner, all marked TEST. It dials nothing.
|
||||
- **Never place a real call to 911 to test.**
|
||||
6. **Canada:** 911 works the same way through voip.ms. Check voip.ms's E911 documentation for
|
||||
Canadian rules.
|
||||
|
||||
## Call recording
|
||||
|
||||
Recording calls without the right consent can be a crime. In the US:
|
||||
|
||||
- Federal law and most states are **one-party consent**: one person on the call (you) agreeing
|
||||
is enough.
|
||||
- Several states need **everyone on the call** to agree, for example California, Florida,
|
||||
Illinois, Maryland, Massachusetts, Montana, New Hampshire, Pennsylvania and Washington. A call
|
||||
between states can bring the stricter rule in.
|
||||
- Other countries have their own rules; in the EU and UK, recordings are also personal data under
|
||||
the GDPR.
|
||||
|
||||
### What SelfPhoneNG does
|
||||
|
||||
- **Nothing is recorded unless you turn it on.** You can record:
|
||||
- every call to a number (**Phone numbers → Record every call**);
|
||||
- a queue's calls (the queue's **Record calls** switch).
|
||||
The setup wizard never turns recording on.
|
||||
- **Settings → Recording → "Notice played before a recorded call"** plays a sound you upload
|
||||
(e.g. "This call may be recorded for quality and training") to everyone who calls a number set
|
||||
to record, before anything else. Upload it on **Sounds & music**.
|
||||
- **Queue recordings play no notice of their own.** If you record a queue, send its callers
|
||||
through a number that plays the notice, or through an announcement that says it
|
||||
(**Sounds & music → Announcements**: play a sound, then go to the queue).
|
||||
- **Retention.** Recordings and voicemail are deleted after the number of days you set there.
|
||||
0 keeps them.
|
||||
|
||||

|
||||
|
||||
### What you should do
|
||||
|
||||
- **Set the notice** whenever anything is recorded, even in one-party states. It's the simplest
|
||||
way to have consent from everyone.
|
||||
- **Tell your staff** their calls are recorded (a written policy).
|
||||
- **Keep recordings no longer than you need them**, and limit who can sign in as an admin:
|
||||
admins can play every recording.
|
||||
@@ -0,0 +1,140 @@
|
||||
# Updating, backups and monitoring
|
||||
|
||||
## Updating SelfPhoneNG
|
||||
|
||||
When nobody is on a call, run:
|
||||
|
||||
```sh
|
||||
./scripts/update.sh
|
||||
```
|
||||
|
||||
It:
|
||||
|
||||
1. pulls the latest code (`git pull --ff-only`);
|
||||
2. pulls newer PostgreSQL and Caddy images;
|
||||
3. rebuilds the api and Asterisk images;
|
||||
4. restarts what changed.
|
||||
|
||||
Database changes apply by themselves when the api starts. **Calls in progress drop** if Asterisk is
|
||||
rebuilt. Make a backup first (**Settings → Backups → Back up now**).
|
||||
|
||||
If `git pull` refuses because you changed files in the folder, put your changes somewhere else.
|
||||
Settings belong in the web app and `.env`, not in the code.
|
||||
|
||||
### Asterisk security releases
|
||||
|
||||
SelfPhoneNG builds Asterisk from source, checking the download against Asterisk's published
|
||||
checksum. New Asterisk versions come with SelfPhoneNG updates.
|
||||
|
||||
To take an Asterisk fix before that, build the version you need yourself. Release numbers are
|
||||
listed at <https://downloads.asterisk.org/pub/telephony/asterisk/releases/>.
|
||||
|
||||
```sh
|
||||
docker compose build --build-arg ASTERISK_VERSION=24.0.1 asterisk
|
||||
docker compose up -d asterisk
|
||||
docker compose exec asterisk asterisk -rx "core show version"
|
||||
```
|
||||
|
||||
The next `update.sh` goes back to the version the code names.
|
||||
|
||||
## Backups
|
||||
|
||||
**Settings → Backups:**
|
||||
|
||||
- **Back up every day** at a time you choose, keeping the newest few.
|
||||
- **Back up now** makes one right away.
|
||||
- The list downloads any of them.
|
||||
|
||||

|
||||
|
||||
A backup (`data/backups/selfphoneng-YYYYMMDD-HHMM.tar.gz`) holds:
|
||||
|
||||
- the database: users, extensions, numbers, settings, call history;
|
||||
- voicemail;
|
||||
- your sounds and music on hold;
|
||||
- call recordings, if you tick **"Include call recordings"** (they can be large).
|
||||
|
||||
If a daily backup fails, the admins get an email, and the dashboard warns while today's backup is
|
||||
missing.
|
||||
|
||||
**Two things a backup doesn't hold:**
|
||||
|
||||
1. **`.env`.** Its `APP_SECRET` encrypts the SIP and trunk passwords inside the backup.
|
||||
- **A backup can only be restored with the `.env` it was made with**, so keep a copy of `.env`
|
||||
in a password manager.
|
||||
- The backup itself holds no plain passwords.
|
||||
2. **A copy anywhere else.** Backups sit on the same disk as the system. Copy them off the server
|
||||
regularly, e.g. with a nightly cron job:
|
||||
`rsync -a /opt/selfphoneng/data/backups/ backup-host:selfphoneng/`.
|
||||
|
||||
## Restoring
|
||||
|
||||
```sh
|
||||
./scripts/restore.sh /path/to/selfphoneng-20261019-0300.tar.gz
|
||||
```
|
||||
|
||||
The script:
|
||||
|
||||
- refuses if `.env`'s `APP_SECRET` isn't the one the backup was made with;
|
||||
- shows what the backup holds and asks you to type `yes`;
|
||||
- replaces the database and those folders, then starts everything again.
|
||||
|
||||
Asterisk's config is rebuilt from the restored database. Sign in with the passwords from the
|
||||
backup.
|
||||
|
||||
### Moving to a new server
|
||||
|
||||
1. Clone SelfPhoneNG on the new server.
|
||||
2. **Copy your old `.env`** into the folder.
|
||||
3. Run `./scripts/install.sh`. It keeps the existing `.env`.
|
||||
4. Run `./scripts/restore.sh <backup>`.
|
||||
5. If the public IP changed: update it on **Settings → Network**, change your DNS record and any
|
||||
IP-based settings at voip.ms (IP authentication, API whitelist), then
|
||||
`docker compose restart asterisk`.
|
||||
6. Desk phones fetch their setup from the host name, so they follow the DNS change.
|
||||
|
||||
## Monitoring
|
||||
|
||||
**Emails to admins.** SelfPhoneNG emails every admin when:
|
||||
|
||||
- a trunk has been down for 5 minutes (one email per outage);
|
||||
- the disk is under 10% free (once a day);
|
||||
- the public IP changed (with the re-check turned on);
|
||||
- a daily backup failed.
|
||||
|
||||
These need **Settings → Email** working.
|
||||
|
||||
**Health check** for an uptime monitor (Uptime Kuma or similar), on the server itself:
|
||||
|
||||
```sh
|
||||
curl http://127.0.0.1:8080/healthz # 200 {"ok":true}, or 503 when the database is down
|
||||
```
|
||||
|
||||
From outside, monitor `https://pbx.example.com/healthz`.
|
||||
|
||||
**The dashboard** shows trunks, active calls, queues and warnings (emergency setup, certificate,
|
||||
backups, restart needed) live.
|
||||
|
||||
### Keeping logs small
|
||||
|
||||
Asterisk's log files in `data/asterisk-logs/` grow forever. Rotate them with logrotate. Create
|
||||
`/etc/logrotate.d/selfphoneng`:
|
||||
|
||||
```
|
||||
/opt/selfphoneng/data/asterisk-logs/*.log {
|
||||
weekly
|
||||
rotate 8
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
```
|
||||
|
||||
`copytruncate` lets Asterisk keep writing to the same file. fail2ban and CrowdSec notice the file
|
||||
shrinking.
|
||||
|
||||
The api's logs are Docker's. Limit them in `/etc/docker/daemon.json` with
|
||||
`"log-opts": {"max-size": "20m", "max-file": "5"}`, restart Docker, then
|
||||
`docker compose up -d --force-recreate` (the limit only applies to new containers).
|
||||
@@ -0,0 +1,164 @@
|
||||
# Network, firewall and attacks
|
||||
|
||||
Most call problems come from the network between the server and the internet, and any PBX on the
|
||||
internet is attacked within hours of going up. This page covers both.
|
||||
|
||||
## NAT: a server behind a router
|
||||
|
||||
Calls carry the server's address inside the SIP messages. Behind a router (NAT), the server has to
|
||||
know its **public IP** and which networks are **local**, or the other side sends the audio to
|
||||
the wrong place.
|
||||
|
||||
1. **Turn off SIP ALG** (also called "SIP helper" or "SIP fixup") on every router between the
|
||||
server and the internet. It rewrites SIP messages and is the classic cause of one-way audio and
|
||||
calls dropping after 30 seconds. Where to find it depends on the router; search for
|
||||
"<router model> disable SIP ALG".
|
||||
2. **Forward ports** to the server's LAN IP:
|
||||
- UDP 5060 (SIP);
|
||||
- UDP 10000–20000 (audio);
|
||||
- TCP 443 (and 80 with the bundled Caddy);
|
||||
- TCP 5061 only if you use SIP over TLS.
|
||||
3. **Double NAT** (the ISP's box in front of your own router): forward the ports on both, or put
|
||||
your router in the ISP box's DMZ / bridge mode.
|
||||
4. **Public IP and local networks.**
|
||||
- The wizard's Network step sets both (go back to it at `/setup?step=3`).
|
||||
- The public IP is also on **Settings → Network**: **Detect** fills it in.
|
||||
- Local networks are the networks that reach the server without NAT. The default
|
||||
(`192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12`) fits most offices.
|
||||
5. **Dynamic IP?** Tick **"Re-check the public IP every 5 minutes"** on Settings → Network.
|
||||
- When the IP changes, SelfPhoneNG updates its settings and emails the admins
|
||||
("Public IP changed to …").
|
||||
- **Then restart telephony** so Asterisk uses the new address: on the server, run
|
||||
`docker compose restart asterisk` (calls in progress drop). The dashboard shows "SIP port or
|
||||
transport changes need a telephony restart" until you do.
|
||||
|
||||

|
||||
|
||||
## Firewall
|
||||
|
||||
Allow only the ports in the [port table](README.md#ports). All SelfPhoneNG containers use the
|
||||
host's network, so the host firewall (ufw, firewalld, nftables) applies to them directly.
|
||||
|
||||
With **ufw**:
|
||||
|
||||
```sh
|
||||
ufw allow OpenSSH
|
||||
ufw allow 443/tcp
|
||||
ufw allow 80/tcp # only with the bundled Caddy
|
||||
ufw allow 5060/udp
|
||||
ufw allow 10000:20000/udp
|
||||
ufw enable
|
||||
```
|
||||
|
||||
### Only voip.ms may reach SIP (no remote phones)
|
||||
|
||||
If every desk phone is in the office, nothing else on the internet needs port 5060. Limit it to
|
||||
voip.ms and your local networks.
|
||||
|
||||
1. **Look up your POP's address** (the server you put on the trunk):
|
||||
|
||||
```sh
|
||||
dig +short chicago1.voip.ms
|
||||
```
|
||||
|
||||
2. **Save this as `/etc/nftables.d/selfphoneng.nft`**, with that address in `sip_ok`. Add any
|
||||
other office that has phones (the same networks as Settings → Security → "Other networks
|
||||
allowed").
|
||||
|
||||
```nft
|
||||
table inet selfphoneng {
|
||||
set sip_ok {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 208.100.60.8 }
|
||||
}
|
||||
chain input {
|
||||
type filter hook input priority 0; policy accept;
|
||||
meta l4proto { tcp, udp } th dport { 5060, 5061 } ip saddr != @sip_ok drop
|
||||
meta nfproto ipv6 meta l4proto { tcp, udp } th dport { 5060, 5061 } drop
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Load it:** `nft -f /etc/nftables.d/selfphoneng.nft`. To keep it after a reboot, include it
|
||||
from `/etc/nftables.conf`.
|
||||
|
||||
This table only drops SIP from elsewhere and leaves your other rules alone. voip.ms can change
|
||||
its addresses: if the trunk stops registering one day, run `dig` again.
|
||||
|
||||
## Desk phones only from the office
|
||||
|
||||
**Settings → Security → "Only accept desk phones from the networks below"** makes Asterisk refuse
|
||||
desk phones signing in from anywhere but your local networks and the ones you list. A stolen phone
|
||||
password is then useless from outside.
|
||||
|
||||
- Browser phones aren't affected: they come in through HTTPS.
|
||||
- **"Only to phones on the local networks"** does the same for the phone setup files.
|
||||
|
||||
## Blocking password guessers: fail2ban or CrowdSec
|
||||
|
||||
Asterisk writes every failed sign-in to `data/asterisk-logs/security.log`. Use one of the two tools
|
||||
below; both read that file on the host.
|
||||
|
||||
### fail2ban
|
||||
|
||||
Install it (`apt install fail2ban`) and create `/etc/fail2ban/jail.d/selfphoneng.conf`. Change
|
||||
the path to where you cloned SelfPhoneNG:
|
||||
|
||||
```ini
|
||||
[asterisk]
|
||||
enabled = true
|
||||
logpath = /opt/selfphoneng/data/asterisk-logs/security.log
|
||||
maxretry = 5
|
||||
findtime = 10m
|
||||
bantime = 1d
|
||||
# never ban your own offices
|
||||
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```sh
|
||||
systemctl restart fail2ban
|
||||
fail2ban-client status asterisk
|
||||
```
|
||||
|
||||
The stock `asterisk` filter already understands the log. We test it against real failed
|
||||
sign-ins (`scripts/e2e/m11-t7.mjs`).
|
||||
|
||||
### CrowdSec
|
||||
|
||||
Install CrowdSec and a bouncer (<https://docs.crowdsec.net/>), then:
|
||||
|
||||
```sh
|
||||
cscli collections install crowdsecurity/asterisk
|
||||
```
|
||||
|
||||
Add `/etc/crowdsec/acquis.d/selfphoneng.yaml`:
|
||||
|
||||
```yaml
|
||||
filenames:
|
||||
- /opt/selfphoneng/data/asterisk-logs/security.log
|
||||
labels:
|
||||
type: asterisk
|
||||
```
|
||||
|
||||
and run `systemctl reload crowdsec`.
|
||||
|
||||
## Web sign-in attacks
|
||||
|
||||
The web sign-in is rate limited. Failed attempts are written to the api's log as
|
||||
`"event":"auth.failed"` with the `ip`, so you can feed them to fail2ban or CrowdSec too
|
||||
(`docker compose logs api`). Turn on two-step sign-in for every admin.
|
||||
|
||||
## Toll fraud
|
||||
|
||||
Someone with a stolen SIP password calling expensive international numbers is the attack that costs
|
||||
real money. Keep:
|
||||
|
||||
- international calling **off** at voip.ms and in **Outbound rules** unless you need it;
|
||||
- the voip.ms **balance alerts and spending limits** on;
|
||||
- the **desk phone network limit** above on if your phones are all in the office.
|
||||
|
||||
Never reuse a SIP password. SelfPhoneNG generates long random ones; phones get them from the
|
||||
setup files.
|
||||
@@ -0,0 +1,152 @@
|
||||
# Troubleshooting
|
||||
|
||||
Run the commands on this page on the server, from the folder you cloned SelfPhoneNG into.
|
||||
|
||||
## Looking inside Asterisk
|
||||
|
||||
Ask Asterisk directly:
|
||||
|
||||
```sh
|
||||
docker compose exec asterisk asterisk -rx "pjsip show registrations" # the trunk: Registered?
|
||||
docker compose exec asterisk asterisk -rx "pjsip show endpoints" # phones and trunks
|
||||
docker compose exec asterisk asterisk -rx "pjsip show contacts" # who is signed in, from where
|
||||
docker compose exec asterisk asterisk -rx "core show channels" # calls right now
|
||||
docker compose exec asterisk asterisk -rx "queue show" # queues and agents
|
||||
```
|
||||
|
||||
### Seeing the SIP messages themselves
|
||||
|
||||
Open Asterisk's console and turn on the SIP logger:
|
||||
|
||||
```sh
|
||||
docker compose exec asterisk asterisk -rvvv
|
||||
pjsip set logger on
|
||||
```
|
||||
|
||||
Make the failing call and read what scrolls by. To stop:
|
||||
|
||||
```
|
||||
pjsip set logger off
|
||||
exit
|
||||
```
|
||||
|
||||
The logger prints passwords' hashes and phone numbers; don't paste it publicly without editing.
|
||||
|
||||
### Logs
|
||||
|
||||
| What | Where |
|
||||
|---|---|
|
||||
| Asterisk notices, warnings, errors | `data/asterisk-logs/messages.log` |
|
||||
| Failed sign-ins (fail2ban reads this) | `data/asterisk-logs/security.log` |
|
||||
| The api (web app, config changes, emails) | `docker compose logs api` |
|
||||
| Everything, live | `docker compose logs -f` |
|
||||
|
||||
## One-way audio, or no audio
|
||||
|
||||
The call connects but one side (or both) hears nothing. It's almost always NAT
|
||||
([Network](network.md#nat-a-server-behind-a-router)).
|
||||
|
||||
1. **SIP ALG** must be off on every router in the path. This is the first suspect.
|
||||
2. **Public IP:** Settings → Network must show the server's current public IP. If it changed,
|
||||
update it and restart telephony (`docker compose restart asterisk`).
|
||||
3. **Audio ports:** UDP 10000–20000 must be forwarded to the server and open on its firewall.
|
||||
4. **Local networks** (setup wizard, Network step) must list your office networks, and only them.
|
||||
5. **Calls drop after about 30 seconds:** same causes. The answer never reaches the other side,
|
||||
so it gives up.
|
||||
|
||||
## The trunk won't register
|
||||
|
||||
Trunks shows **Rejected** or **Not registered**.
|
||||
|
||||
- **Rejected:** voip.ms said no.
|
||||
- The username must be the sub-account (`123456_pbx`) and the password its password, not your
|
||||
voip.ms portal password.
|
||||
- Retype the password on the trunk and save.
|
||||
- **Not registered / Unreachable:** Asterisk gets no answer.
|
||||
- Check the server name (`chicago1.voip.ms`, no `sip:` or port).
|
||||
- Check the server can reach it: `ping chicago1.voip.ms`.
|
||||
- Check outbound UDP 5060 isn't blocked by a firewall.
|
||||
- If you limited SIP to voip.ms with nftables, the POP's address may have changed
|
||||
([Firewall](network.md#only-voipms-may-reach-sip-no-remote-phones)).
|
||||
- **IP authentication:** there's nothing to register.
|
||||
- The trunk shows **Reachable** when voip.ms answers.
|
||||
- The sub-account's allowed IP must be your server's current public IP.
|
||||
- **TLS:**
|
||||
- TLS must be turned on in Settings → Security, with a valid certificate.
|
||||
- Encryption must also be on at the voip.ms sub-account.
|
||||
- Port 5061.
|
||||
|
||||
Then run `pjsip show registrations`, and use the SIP logger to see voip.ms's answer.
|
||||
|
||||
## Incoming calls don't arrive
|
||||
|
||||
The trunk is **Registered** and outgoing calls work, but calls to your number fail or reach the
|
||||
wrong place.
|
||||
|
||||
1. **At voip.ms** (DID Numbers → Manage DIDs), the number must be routed to **SIP/IAX → your
|
||||
sub-account**, on the **same POP** the trunk uses. A different POP is the most common cause.
|
||||
2. **In SelfPhoneNG**, the number must be on **Phone numbers** with a destination. Imported numbers
|
||||
hang up until you route them.
|
||||
3. **Office hours or a holiday** may be sending the call elsewhere: check the number's schedule.
|
||||
4. Still nothing? Run the SIP logger, call from your mobile, and check an INVITE arrives.
|
||||
- **No INVITE:** the problem is at voip.ms or the firewall.
|
||||
- **An INVITE, then an error:** see what Asterisk replied.
|
||||
|
||||
## The browser phone won't connect
|
||||
|
||||
On **My phone**, the phone stays "connecting" or calls fail.
|
||||
|
||||
- **Use the address with HTTPS and the real host name.** Browsers block the microphone on plain
|
||||
HTTP or an IP address.
|
||||
- **Microphone permission:** the browser must be allowed to use it. Look for the camera/mic icon
|
||||
in the address bar.
|
||||
- **The reverse proxy must pass `/ws`** to `127.0.0.1:8088` with WebSocket upgrades.
|
||||
- The bundled Caddy and `deploy/nginx/selfphoneng.conf.example` both do.
|
||||
- On your own proxy, check: `curl -i https://pbx.example.com/ws` should answer
|
||||
`426 Upgrade Required` with `Server: Asterisk`, not 404 or the web app.
|
||||
- **Certificate:** the browser must trust the HTTPS certificate (no warning page).
|
||||
- **No audio on browser calls:** see [one-way audio](#one-way-audio-or-no-audio). Browsers use the
|
||||
same audio ports.
|
||||
|
||||
## Voicemail emails (or alerts) aren't sent
|
||||
|
||||
1. **Settings → Email → Send test** to your own address. The error shown says what the mail server
|
||||
answered.
|
||||
2. Check the server, port and security setting match your provider:
|
||||
- 587 with STARTTLS;
|
||||
- or 465 with TLS from the start.
|
||||
Many providers need an app password.
|
||||
3. The user's email address must be right, and voicemail-to-email on for them.
|
||||
4. The api's log has every send error: `docker compose logs api | grep -i mail`.
|
||||
|
||||
## "SIP port or transport changes need a telephony restart"
|
||||
|
||||
Changes to the public IP, SIP ports or TLS only take effect when Asterisk restarts. Run this when
|
||||
nobody is on a call:
|
||||
|
||||
```sh
|
||||
docker compose restart asterisk
|
||||
```
|
||||
|
||||
The banner goes away a few seconds after Asterisk is back.
|
||||
|
||||
## Locked out
|
||||
|
||||
- **Forgot the admin password:** another admin can reset it on **Users & Extensions**.
|
||||
- **Lost the phone with the two-step codes:** use a recovery code, or have another admin turn
|
||||
two-step sign-in off for you (Users & Extensions → the user → turn off two-step sign-in).
|
||||
- **The only admin is locked out:** keep two admins so this can't happen. If it does, the api
|
||||
makes a new admin from `.env` when no admin is left:
|
||||
1. Make the locked-out account a plain user:
|
||||
|
||||
```sh
|
||||
docker compose exec postgres psql -U selfphoneng \
|
||||
-c "update users set role = 'user' where email = 'you@example.com'"
|
||||
```
|
||||
|
||||
2. In `.env`, set `INITIAL_ADMIN_EMAIL` to an email **no account uses yet** and
|
||||
`INITIAL_ADMIN_PASSWORD` to a temporary password.
|
||||
3. Run `docker compose up -d --force-recreate api`.
|
||||
4. Sign in with the new email; you're asked to change the password.
|
||||
5. On **Users & Extensions**, make your old account an admin again, reset its password (and
|
||||
two-step sign-in), sign in with it, and delete the temporary admin.
|
||||
@@ -0,0 +1,104 @@
|
||||
# voip.ms setup
|
||||
|
||||
SelfPhoneNG connects to voip.ms through a **sub-account**. voip.ms changes its portal now and
|
||||
then; if a menu name below doesn't match, look for the closest one.
|
||||
|
||||
## 1. In the voip.ms portal
|
||||
|
||||
1. **Create a sub-account** (Main Menu → Sub Accounts → Create Sub Account). Use one sub-account
|
||||
per phone system, e.g. `123456_pbx`.
|
||||
- **Authentication type:** User/Password. If your server has a fixed public IP, IP
|
||||
authentication works too.
|
||||
- **Password:** long and random. You'll paste it into SelfPhoneNG, not type it into phones.
|
||||
- **Device type:** Asterisk, IP PBX, Gateway or VoIP Switch.
|
||||
- **Allowed codecs:** G.711u (ulaw). Others are optional.
|
||||
- **DTMF mode:** RFC 2833.
|
||||
- **NAT:** Yes.
|
||||
- **CallerID number:** your main number.
|
||||
- **International calling:** leave it **off** unless you need it. It's your best protection
|
||||
against toll fraud if a password ever leaks.
|
||||
- **Encrypted SIP traffic (TLS/SRTP):** optional; see [SIP over TLS](#sip-over-tls).
|
||||
2. **Pick a POP server** near the PBX, e.g. `chicago1.voip.ms`. Write it down: the trunk and every
|
||||
number must use the same one.
|
||||
3. **Point each number at the sub-account** (DID Numbers → Manage DIDs → edit the number):
|
||||
- **Routing:** SIP/IAX → your sub-account.
|
||||
- **POP:** the **same server** as above. A different POP is the most common reason incoming
|
||||
calls don't arrive.
|
||||
4. **E911:** register a street address on every number you'll use as an emergency caller ID
|
||||
(DID → e911). Read [Emergency calls](legal.md#emergency-calls-e911) first.
|
||||
5. **Balance protection:** set a low-balance alert and the daily or per-call spending limits voip.ms
|
||||
offers, so a stolen password can't run up a big bill.
|
||||
6. **(Optional) API access**, for importing your numbers and the server list: Main Menu → SOAP
|
||||
and REST/JSON API. Enable it, set an API password and allow your server's public IP.
|
||||
|
||||
## 2. The trunk in SelfPhoneNG
|
||||
|
||||
**Trunks → New trunk** (or the wizard's Trunk step). The form has the same checklist built in.
|
||||
|
||||

|
||||
|
||||
| Field | What to enter |
|
||||
|---|---|
|
||||
| Provider | voip.ms |
|
||||
| Server | The POP you picked, e.g. `chicago1.voip.ms` |
|
||||
| Sign-in | Username and password (registration); IP address only if you set the sub-account to IP authentication |
|
||||
| Username | The sub-account, e.g. `123456_pbx` |
|
||||
| Password | The sub-account's password |
|
||||
| Transport / Port | UDP 5060 (TLS 5061 if you turned encryption on) |
|
||||
| voip.ms API (optional) | Your voip.ms email and the API password. Fills the server list and lets you import numbers. |
|
||||
|
||||
Save, then:
|
||||
|
||||
- Within a few seconds the trunk shows **Registered**.
|
||||
- **Test** checks that the server answers.
|
||||
- **Rejected** means the username or password is wrong. **Not registered** means Asterisk can't
|
||||
reach the server. See [Troubleshooting](troubleshooting.md#the-trunk-wont-register).
|
||||
|
||||
## 3. Phone numbers
|
||||
|
||||
On **Phone numbers**, either:
|
||||
|
||||
- use **Add number**: the 10-digit number, a label, and where its calls go; or
|
||||
- on the trunk form, use **Import numbers from voip.ms** (needs the API details) and route each
|
||||
number afterwards. Imported numbers hang up until you route them.
|
||||
|
||||

|
||||
|
||||
Each number can also:
|
||||
|
||||
- record every call (see [recording law](legal.md#call-recording));
|
||||
- have **office hours**;
|
||||
- have an **E911 address** note: a reminder of what you registered at voip.ms. 911 calls don't
|
||||
use it; voip.ms sends the address registered there.
|
||||
|
||||
## 4. Outbound calls
|
||||
|
||||
The wizard creates the outbound rules for:
|
||||
|
||||
- North American numbers (sent as 11 digits);
|
||||
- toll-free numbers;
|
||||
- international numbers, created **turned off**.
|
||||
|
||||
Turn international on under **Outbound rules** only if you need it, and give it only to the users
|
||||
who need it (the user's **Calling** tab).
|
||||
|
||||
911 always works from every extension, whatever its rules (see [E911](legal.md#emergency-calls-e911)).
|
||||
|
||||
## SIP over TLS
|
||||
|
||||
TLS encrypts the call setup between SelfPhoneNG and voip.ms; SRTP encrypts the audio.
|
||||
|
||||
1. **Get a certificate into `data/tls`.**
|
||||
- With the bundled Caddy: run `./scripts/tls-cert.sh`. It copies Caddy's certificate for your
|
||||
host name.
|
||||
- With your own certbot certificate:
|
||||
`./scripts/tls-cert.sh /etc/letsencrypt/live/pbx.example.com/fullchain.pem /etc/letsencrypt/live/pbx.example.com/privkey.pem`
|
||||
2. **Keep it renewed.** Run the same command every day from cron; it does nothing when the
|
||||
certificate hasn't changed, e.g.
|
||||
`17 4 * * * cd /opt/selfphoneng && ./scripts/tls-cert.sh`.
|
||||
3. **Turn it on.** **Settings → Security → Accept SIP over TLS**, then restart telephony
|
||||
(`docker compose restart asterisk`), and open TCP 5061.
|
||||
4. **Set the trunk and voip.ms.** Set the trunk's transport to TLS (port 5061) and turn on
|
||||
encryption on the voip.ms sub-account.
|
||||
|
||||
The dashboard warns when the certificate is missing or expires within 14 days.
|
||||
@@ -92,6 +92,8 @@ aren't filtered. The setting is on Settings → Security, with the provisioning
|
||||
### 3.1 Install / update
|
||||
- `scripts/install.sh` (01 §1.3).
|
||||
- `scripts/update.sh`: `git pull --ff-only`, `docker compose build`, `docker compose up -d`. Migrations run automatically on api start. Print the new version.
|
||||
As built (M11-T7): also `docker compose pull --ignore-buildable` (postgres, caddy) and `build --pull`;
|
||||
adds `--profile caddy` when the bundled Caddy container is running. No CHANGELOG.md yet.
|
||||
- Version: `package.json` version at the root, exposed at `/api/system/status`. Semver. `CHANGELOG.md` kept by hand.
|
||||
- Asterisk security releases: bump `ASTERISK_VERSION` in the Dockerfile and rebuild. The admin guide explains how.
|
||||
|
||||
|
||||
Reference in new issue
Block a user