Files
selfphoneng/docs/admin-guide/network.md
T
iamdoubz 65a0438dde diagnostics: System → Diagnostics with a network test probe; RTP_PORT_START/END
The server checks itself (public IP, DNS, local networks, phone ACL, SIP transports, free audio
ports) and says how to fix each problem. A quick or complete network test asks the probe
(apps/probe, own image) to test this server from outside: HTTP/HTTPS, SIP over UDP/TCP/TLS, the
browser phone's /ws, sampled audio ports. The probe only tests the address that asks.
The audio port range comes from RTP_PORT_START/END (default 10000-20000).
2026-10-21 12:37:16 -05:00

221 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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; see [Call audio ports](#call-audio-ports) to use another range);
- 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, and **System** shows the command.
![Settings → Network](img/settings-network.png)
## Call audio ports
Call audio (RTP) uses UDP **10000–20000** unless you set another range in `.env` (or, in a
Portainer stack, in the api's environment):
```sh
RTP_PORT_START=40000
RTP_PORT_END=40199
```
Then `docker compose up -d` (the api writes the range into Asterisk's config), and open **the same
range** on the firewall and the router instead of 10000–20000. Each call uses two ports, so 200
ports is plenty for a small office. Pick a range nothing else on the server uses: System →
Diagnostics counts how many ports in the range are already taken.
## Diagnostics: is the server reachable?
**System → Diagnostics** checks what usually stops calls from working outside the office, and
says how to fix each problem it finds.
**This server** runs every time you open the page:
- the public IP is set, and is the address the server really reaches the internet from;
- the host name's DNS record points at that address (with Cloudflare, it must be "DNS only": its
proxy carries web pages, not SIP);
- the server's LAN address is in the local networks;
- whether phones may sign in from anywhere (the phone network limit refuses phone apps on mobile
data), and whether phone setup is limited to the local networks;
- Asterisk is listening on the SIP ports;
- how many call audio ports are free.
**From the internet** needs a **network test probe**: a small program, run on a machine outside
your network, that connects back to the server the way a phone on mobile data or your SIP provider
does. A server can't test this itself: most routers either don't route a connection to their own
public address back in, or route it without going through the port forwards.
- **Quick network test**: TCP 80 and 443, and SIP on 5060 over UDP (and TCP if it's on).
- **Complete network test**: also SIP over TLS (if on), the browser phone's `/ws`, and a sample
of the call audio ports (the server listens on them while the probe sends to them).
To run a probe, on any machine outside your network (a small VPS is plenty):
```sh
docker compose -f deploy/probe/compose.yml up -d
```
Then put its address, for example `http://203.0.113.50:8080`, in **Settings → Network → Network
test probe**. The probe only ever tests the address a request comes from, so nobody can use it
against someone else, and each address gets a few tests per 10 minutes. Its SIP test is one
OPTIONS request; if you run fail2ban or CrowdSec, add the probe's address to their allow list so a
test never counts toward a ban.
## 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 # or your RTP_PORT_START:RTP_PORT_END
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;
- the **outside calls at once** limits (Settings → Security) as low as your office needs: per
extension (3 by default) and for the whole system (10). A stolen password then runs up at most
that many calls at a time. Forwarded calls count for the person who set the forward; 911 is
never limited. Callers over a limit hear "all circuits are busy".
Never reuse a SIP password. SelfPhoneNG generates long random ones; phones get them from the
setup files.