Repository navigation
FreeSBC v0.3.0
FreeSBC v0.3.0 hardens the public edge and finishes topology hiding. Client
legs now hide the switch the way carrier legs already did, a distributed REGISTER
brute force, slow scanners and malformed datagrams are now banned rather than
only dropped, a lost 2xx ACK is reported as such, and the edge can mirror its
SIP signaling to HOMER over HEP. It adds edge.advertise for NATs that
translate a port, a WebUI login page, a deployment guide for two FreeSBC nodes,
and fixes for several call-setup races. There are no configuration breaking
changes: a v0.2.0 file loads unchanged. Still one static binary, one YAML file,
no database, no Redis, no kernel modules, no external media process.
Breaking changes
None. Every new configuration key is optional, no key was renamed or removed,
and the Go and sipgo requirements are unchanged (Go 1.27.2 to build from
source, sipgo v1.4.3). The behavior changes below are what an operator may
notice after upgrading; read them before restarting a production node.
Behavior changes
- Per-AoR REGISTER lockout is on by default (#207, #211). Refused REGISTER
credentials are now also counted per AoR across all sources. Over
shield.register_ban_aor(default10/10m) the AoR is locked for
shield.ban, and a REGISTER for it from a source with no live binding of
it is dropped silently. A deliberate lockout of a real AoR is an accepted
trade-off. Setshield.register_ban_aor: offto disable. - Failed REGISTER credentials ban the source (#186). A REGISTER with
Authorizationanswered 401/403/404/407 is a strike; over
shield.register_ban(default10/10m) the source IP is banned for
shield.ban. Refreshes of a live binding and carrier sources never count.
Setshield.register_ban: offto disable. - Admission drops now lead to a ban (#195). A public source that is
neither a carrier nor a registered IP and keeps getting dropped
(out-of-dialog INVITE, SUBSCRIBE, MESSAGE, OPTIONS, dialog-less BYE/INFO)
is banned forshield.banovershield.admission_ban(default50/5m). A
UDP-sourced ban blocks only UDP from that IP. Set
shield.admission_ban: offto disable. - Malformed public datagrams count as strikes (#205). A malformed UDP
datagram or WS/WSS frame feeds the sameshield.admission_banbudget; the
read still goes to sipgo unchanged. There is no separate key, so
shield.admission_ban: offdisables this too. - Public OPTIONS from unregistered sources is dropped silently (#218). A
public OPTIONS is answered only for a live registration source, a carrier
source, or a To tag naming a live dialog. A phone whose binding has lapsed
gets no reply to its OPTIONS keep-alive until it registers again, and a
client that probes with OPTIONS before its first REGISTER gets none. Counted
asoptions_not_admitted. No opt-out. - Dialog-less public BYE and INFO are refused (#194). A public BYE or INFO
naming no dialog is forwarded only from a live registration or a carrier
source; otherwise it gets 481 and nothing goes to the switch. Counted as
dialogless_not_admitted. No opt-out. - Drain refuses only carrier calls (#204). Before, drain refused every new
INVITE with 503. Now carrier to switch and switch to carrier get 503 +
Retry-After: 30, while client to switch and switch to client are admitted.
While draining, an OPTIONS from a carrier source, from the switch to
FreeSBC itself, or from the switch to a carrier host also gets 503. A
switch OPTIONS to anfsbc=token with no live binding is 404. To stop all
new calls, drain the switch as well. - Client legs hide the switch (#192, #212). Toward a public client,
FreeSBC now sends its own public Via and a single public Record-Route (the
private.ippair exists on the switch side only); the switch's Via, Server,
User-Agent,X-and otherP-headers, Warning, Path, Service-Route and
P-Associated-URI are removed;private.ipor a switch IP in From, To and
identity headers is masked topublic.ip. A switch 3xx redirect toward a
client is no longer usable, and hosts in free text such as Subject are not
rewritten. A client that relied on a switch header will no longer see it.
Carrier legs keep the switch's other headers (P-Charge-Infoand similar).
No opt-out. - An ACK that reuses the INVITE branch is forwarded (#179). An ACK after a
2xx that reused the INVITE branch used to be swallowed by sipgo and never
reached the switch; it is now forwarded. TheACK missedwarnings are gone. - Lost 2xx ACKs end calls as
ack_timeout(#177, #189). A call whose 2xx
ACK never arrives now ends with reasonack_timeout(a 32 s backstop BYEs
both ends) instead of looking like a normalbye_callee. Everycall ended
log line gainsterminated_by=caller|callee|proxy. A BYE that crosses the
ACK gets a 500 ms grace first. - UDP SIP socket buffers (#215). Every UDP SIP socket requests a 4 MiB
receive and 1 MiB send buffer. If the kernel caps it, one Warn per socket
appears at startup (on a defaultnet.core.wmem_maxof 212992 the send
warning shows until the sysctl is raised). Startup never fails over it. - Admission summary count (#216). The once-a-minute
admission dropsWARN
now also counts struck malformed reads and prints both kinds, so its first
number is admission plus malformed. - WebUI login (#201). The WebUI signs in with a login form and an
in-memory cookie session (idle 30 min, absolute 12 h, at most 8) instead of
the browser's Basic Auth dialog. Existing sessions do not survive a
restart. Basic Auth is unchanged for/metrics,/api/*and pprof, so
Prometheus and curl scripts keep working. - New or extended read-only admin output.
/api/shield/bansgains kind
udp_ipand reasonsregister,admissionandmalformed;
/api/statusgainslistener_info;/api/switch-nodesshows
cooling_downwhile a REGISTER breaker is open.freesbc_edge_draining
now means "refusing new carrier calls". See New metrics below.
What is new
Edge hardening
- REGISTER brute-force defence (#186, #207, #211). Strikes per source and
per AoR, with anti-forgery: a strike counts only if its digest nonce was in
a challenge FreeSBC relayed to that source. A locked AoR survives a flood of
distinct struck AoRs. New offender viewGET /api/shield/offenders. - Slow scanner bans (#195, #205, #216). Admission drops and malformed
public reads are struck againstshield.admission_ban; the bans and the
summary WARN are described above. - REGISTER circuit breaker (#193). A switch node that leaves REGISTER
unanswered 3 times in a row is not dialed for 30 s, 1 m, then 5 m, with one
probe when half-open; when every node is open the edge answers 503 +
Retry-After: 30itself. Ends the 32 s wait and the WARN storm during a
registrar outage. INVITE is unchanged. - Late 2xx after CANCEL or an abandoned re-INVITE (#222, #224). A 2xx
that crosses a CANCEL (or a timed-out re-INVITE) is now ACKed, and BYEd for
an initial INVITE, instead of leaving the switch dialog to its own timers. - Provisionals no longer lost (#219). A 180/183 read just before a final
is relayed in wire order, so the caller still hears ringback or early media.
Works around emiago/sipgo#306. - Fewer parked goroutines (#226). A finished UDP server transaction no
longer parks a goroutine for 32 s, which lowers memory under a high call
rate. The unit file gains commentedMemoryMaxandGOMEMLIMITlines.
NAT
edge.advertise(#164, #225). For a NAT that translates a port:
edge.advertise.<udp|tcp|tls|ws|wss>is the public port named in Via,
Contact, Record-Route and the carrier REGISTER Contact, while the socket
bindsedge.listen. Restart-only, signaling only; thertprange is never
offset. Validated againstedge.listenand duplicate TCP ports.- NAT checklist (#161, #178). Behind NAT,
freesbc check,initandrun
print the advertised endpoints and the port forwards the router needs
(udp/16060->5060), and/api/statusgainslistener_info.
Observability
- HOMER over HEP (#127, #128, #214). Optional
hep:section (address,
transportudp|tcp|tls,tls.ca|cert|key,capture_id,node_name,
redact_auth, default true) mirrors every SIP message on every socket to
HOMER over HEPv3. Off when absent, restart-only. RTCP reporting from the
switch side is documented indocs/edge.md(#129, #217). - New metrics.
freesbc_edge_ack_missed_total{plane},
freesbc_edge_register_auth_failures_total,
freesbc_edge_register_unanswered_total,
freesbc_edge_register_breaker_rejects_total,
freesbc_tls_cert_disk_differs,freesbc_hep_packets_sent_total,
freesbc_hep_packets_dropped_total,freesbc_hep_errors_total{kind}, and on
Linuxfreesbc_udp_receive_buffer_errors_totaland
freesbc_udp_in_errors_total(#203). New
freesbc_edge_admission_drops_totalreasons:dialogless_not_admitted,
register_aor_locked,options_not_admitted. - Health conditions.
ack_missed:<plane>(3 misses in 10 minutes),
tls_cert_renewedandtls_cert_disk_error(the certificate file is
re-read at start and every 10 minutes; #209).
Admin
- Login page with a cookie session and a Log out button;
logoutaudit event
(#201).
Docs and examples
docs/two-node.md: two independent FreeSBC nodes with FreeSWITCH and
Asterisk examples, per-node gateways and failover, and a rolling restart
with/api/drain(#150, #220).examples/switch/is updated to match.docs/troubleshooting.md: calls that connect and then drop (#165, #191).- Switch-side settings for the Call-ID host and NOTIFY/MESSAGE bodies (#168,
#199, thanks @javayou13131).
Fixes
Known limitations
Full list: docs/edge.md, "Known limitations". The limitations of v0.2.0
still apply, except that client legs are now hidden (#166, #167) and
ACK missed warnings are gone (#147). Still open:
- Single node. All state (calls, dialogs, bindings, bans) is in memory; a
restart drops everything. Drain the node before a restart. - A public call must come from a registered transport address or a carrier
source. A phone whose NAT mapping changed since its last REGISTER is refused
until it registers again; so is a silent OPTIONS keep-alive (#218). - Call-ID passes through unchanged, and NOTIFY/MESSAGE bodies are proxied byte
for byte; hide the switch's name there with the switch-side settings in
docs/edge.md. - A late forked 2xx on a re-INVITE and a re-INVITE relay are not covered by the
provisional replay (#219). - Memory still grows with the sustained call rate: since #226 no goroutine is
parked, but sipgo keeps each UDP transaction's state for 32 s after its final
response, and an ended dialog stays reachable for about as long (#228); cap
it with theMemoryMaxandGOMEMLIMITlines infreesbc.service. - No transcoding; switches are UDP at literal
IP:port; no TURN or full ICE;
IPv6 is untested; REFER is in-dialog only; no PUBLISH. - Non-goals: CDR, clustering, being a registrar. See "Not in scope" in the README.
Getting started
Download, verify and unpack (set os to linux or darwin, arch to amd64
or arm64):
v=v0.3.0 os=linux arch=amd64
curl -LO https://github.com/rasonyang/freesbc/releases/download/$v/freesbc_${v}_${os}_${arch}.tar.gz
curl -LO https://github.com/rasonyang/freesbc/releases/download/$v/SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS # macOS: shasum -a 256 -c --ignore-missing SHA256SUMS
tar -xzf freesbc_${v}_${os}_${arch}.tar.gz && cd freesbc_${v}_${os}_${arch}
./freesbc versionUpgrading from v0.2.0: replace the binary (and freesbc.service on linux),
run ./freesbc check -c freesbc.yaml, drain the node with POST /api/drain,
then restart. Only shield.* is hot; edge.advertise and hep need a
restart.
New install: init asks for the switch address, detects the local addresses,
and refuses to overwrite an existing file:
./freesbc init -c freesbc.yaml
./freesbc check -c freesbc.yaml
./freesbc run -c freesbc.yamlNon-interactive: ./freesbc init -c freesbc.yaml --switch 10.77.0.10:5060 --private-ip 10.77.0.2 --public-ip 203.0.113.7 --public-bind 172.31.5.10.
The topology needs a second IP or NIC for private.ip. To run under systemd,
follow "Run as a service" in the README (linux archives include
freesbc.service). Before exposing FreeSBC to the internet, read
docs/design.md (security model) and docs/edge.md (switch-side requirements).
Apache License 2.0.