自托管VoIP服务器:Asterisk还是FreePBX?
在自己的VPS上运行Asterisk或FreePBX,了解SIP中继、RTP端口范围与防火墙规则,避免5060已开放但通话无声音,并识别话费欺诈的起点。
什么是自托管 VoIP 服务器
自托管 VoIP 服务器是在您控制的 VPS 上运行的 SIP 服务器。因此,通话由您自己的服务器路由,而不是由托管电话服务路由。VoIP(IP 语音)将语音作为 UDP 数据包传输。SIP(会话初始协议)负责建立和结束通话的信令。音频不会通过 SIP 传输,而这一点正是下面大多数问题的根源。
要构建可用的系统,需要以下四个部分。
- PBX(专用交换机)软件。Asterisk 是通常的选择。它保存分机和拨号计划。
- 终端。桌面电话或软电话使用用户名和密钥向 PBX 注册。
- SIP 中继。向服务提供商购买的账户,用于连接公共电话网络,并租用真实电话号码。
- 媒体路径。RTP(实时传输协议)流使用独立的 UDP 端口传输音频。
自托管 PBX 不代表可以自托管电话号码。电话号码由运营商提供,您仍需按号码和通话分钟付费。您拥有的是通话路由、语音信箱、录音和分机列表。您还需要负责服务的安全,因为攻击者会以牟利为目的攻击这类服务。
自托管 VoIP 服务器需要开放哪些端口?
SIP 信令使用 UDP 和 TCP 的 5060 端口,使用 SIP over TLS(传输层安全)时使用 5061 端口。这些端口只负责呼叫建立。每个呼叫的音频都是独立的 UDP 流,发送到从 RTP 范围中分配的端口。Asterisk 提供了一个示例 rtp.conf,其中设置了 rtpstart=10000 和 rtpend=20000;编译时的默认值为 5000 和 31000。每个呼叫会从该范围占用两个端口,分别用于 RTP 和 RTCP(RTP 控制协议)。
大多数首次配置失败都源于这种分离。呼叫可以接通,两部电话都显示已接听,但双方都听不到声音,因为防火墙允许 5060,却丢弃了所有 RTP 数据包。信令和媒体是独立的数据流,因此需要分别配置防火墙规则。如果您刚接触这一概念,建议先阅读Linux 中端口和监听套接字的工作方式,再开放端口。
在开放端口前先缩小范围。开放两万个端口远超小型系统的需求。每个呼叫需要两个端口,因此包含 200 个端口的范围可同时支持 100 个呼叫。
[general]
rtpstart=10000
rtpend=10200使用 sudo asterisk -rx "core reload" 应用此配置。
应安装 Asterisk 还是 FreePBX?
Asterisk 是核心引擎。您通过 /etc/asterisk 中的文本文件配置它,并自行编写拨号方案。FreePBX 是使用 PHP 和 JavaScript 编写的 Web 界面,运行在 Asterisk 之上,可代您生成这些文件,并添加语音邮件和呼叫队列模块。
对于 VPS,关键区别在于谁拥有这台机器。截至 2026 年 8 月,官方 FreePBX 17 安装程序要求使用原始的 Debian 12 系统,并会安装 Asterisk、Web 服务器、数据库服务器和 PHP。如果目标主机已经运行其他服务,安装结果通常不会理想。请为 FreePBX 单独准备一台 VPS。
wget https://github.com/FreePBX/sng_freepbx_debian_install/raw/master/sng_freepbx_debian_install.sh -O /tmp/sng_freepbx_debian_install.sh
sudo bash /tmp/sng_freepbx_debian_install.sh安装日志位于 /var/log/pbx/freepbx17-install.log。脚本提前停止时,应首先查看此日志。
FreePBX 负责管理它生成的配置文件。在 FreePBX 主机上手动编辑 pjsip.conf 后,只要 GUI 再次写入该文件,您的修改就会消失。FreePBX 会读取文件名中包含 _custom 的独立文件,用于手动编写的配置,并且不会修改这些文件。
实际取舍如下。FreePBX 提供 GUI,同时会将电话系统的登录页面暴露到公网。原生 Asterisk 完全没有 Web 界面,每项设置都是文档化指令,您可以在文件中查看并纳入 git 管理。如果安装 FreePBX,请将其 Web 端口限制为您自己的地址,或通过 VPN 访问,因为 PBX 的管理 GUI 是攻击目标,攻击者可通过它直接接触资金相关功能。
版本说明,截至 2026 年 8 月有效:Asterisk 22 是当前的长期支持版本,于 2024 年 10 月发布,并会持续接收安全修复到 2028 年 10 月。Asterisk 23 是标准版本。Ubuntu 24.04 的 universe 仓库提供 Asterisk 20.6.0。
在 Ubuntu 24.04 上安装 Asterisk
使用发行版软件包是最快的方法。Ubuntu 会为其应用补丁,并由 systemd 自动启动。
sudo apt update
sudo apt install -y asterisk
sudo asterisk -rx "core show version"从源代码构建则可以获得当前的长期支持版本。
sudo apt update
sudo apt install -y build-essential wget
cd /usr/local/src
sudo wget https://downloads.asterisk.org/pub/telephony/asterisk/asterisk-22-current.tar.gz
sudo tar -xzf asterisk-22-current.tar.gz
cd asterisk-22.*
sudo contrib/scripts/install_prereq install
sudo ./configure
sudo make menuselect
sudo make -j"$(nproc)"
sudo make install
sudo make samples
sudo make config
sudo ldconfiginstall_prereq install 会为您的发行版安装构建依赖项,install_prereq test 会打印将要执行的命令,但不会进行任何更改。make menuselect 会打开模块选择器,您可以在其中的 Codec Translators 下启用 codec_opus。仅在全新安装时运行 make samples,因为它会将示例配置写入 /etc/asterisk。make config 会将 init 脚本安装到 /etc/init.d/asterisk,systemd 会通过其 SysV 兼容层驱动该脚本,因此之后 sudo systemctl enable --now asterisk 即可正常工作。
无论采用哪种方式,sudo asterisk -rvvv 都会连接到正在运行的 daemon,并提供 CLI。在其中运行 core show version,即可显示实际安装的版本。
配置 SIP 中继和一个分机
PJSIP 是当前 Asterisk 使用的 SIP 通道驱动程序。其配置位于 /etc/asterisk/pjsip.conf,由相互按名称引用的小型类型化区段组成。不同类型的区段可以使用相同名称,因此下面的每个区块都称为 mytrunk。
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
[mytrunk]
type=registration
outbound_auth=mytrunk
server_uri=sip:sip.example.com
client_uri=sip:1234567890@sip.example.com
retry_interval=60
[mytrunk]
type=auth
auth_type=userpass
username=1234567890
password=REPLACE_WITH_A_LONG_RANDOM_SECRET
[mytrunk]
type=aor
contact=sip:sip.example.com:5060
[mytrunk]
type=endpoint
context=from-trunk
disallow=all
allow=ulaw
outbound_auth=mytrunk
aors=mytrunk
[mytrunk]
type=identify
endpoint=mytrunk
match=sip.example.comregistration 对象发送 REGISTER,告知服务提供商应将呼叫送到哪里。呼叫从服务提供商到达后,identify 对象根据源地址将其匹配到此端点;服务提供商通常会公布多个地址,均应列在那里。出站注册和端点有意分为两个独立对象:前者告知服务提供商您的位置,后者决定如何处理到达的呼叫。
座机还需要另外三个对象。
[6001]
type=endpoint
context=internal
disallow=all
allow=ulaw
auth=auth6001
aors=6001
direct_media=no
[auth6001]
type=auth
auth_type=userpass
username=6001
password=REPLACE_WITH_A_LONG_RANDOM_SECRET
[6001]
type=aor
max_contacts=1direct_media=no 使 Asterisk 保持在媒体路径中。如果省略它,Asterisk 会尝试让两个端点直接互相发送 RTP;当电话位于家庭路由器后的 NAT(网络地址转换)环境中时,这通常会失败。密钥绝不能与分机号相同。上游示例为分机 6001 使用 password=6001,以便示例易于阅读;扫描器会首先尝试这种模式。
/etc/asterisk/extensions.conf 中的拨号计划决定每个上下文可以执行的操作。
[internal]
exten => 6001,1,Dial(PJSIP/6001,20)
exten => _9X.,1,Dial(PJSIP/${EXTEN:1}@mytrunk,60)
[from-trunk]
exten => 1234567890,1,Dial(PJSIP/6001,20)
same => n,Hangup()这两个上下文构成安全边界。from-trunk 处理从服务提供商到达的呼叫,并且只能呼叫分机 6001。它无法匹配 _9X. 模式,因此外部呼叫无法通过您的中继再次发起外呼。合并这两个上下文,就会形成典型的资费欺诈路径:陌生人拨打您的号码,拨号计划使用您的账户发起外呼,费用由您承担。
应用配置并检查结果。
sudo asterisk -rx "pjsip reload"
sudo asterisk -rx "pjsip show registrations"
sudo asterisk -rx "pjsip show endpoints"pjsip show registrations 应列出 mytrunk,状态为 Registered。Rejected 表示服务提供商拒绝了您的凭据。Unregistered 表示您的 REGISTER 没有收到响应,下一步应检查防火墙。
SIP 和 RTP 的防火墙规则
信令和媒体承载的风险不同,因此应采用不同的处理方式。将 5060 限制为服务提供商实际使用的地址,以及电话所在的网络。
sudo ufw allow proto udp from 203.0.113.10 to any port 5060
sudo ufw allow proto tcp from 203.0.113.10 to any port 5060
sudo ufw allow 10000:10200/udp
sudo ufw status verboseRTP 端口范围无法进一步大幅缩小,因为媒体流的来源地址通常与信令不同。向服务提供商索取其媒体子网;如果对方公布了这些子网,请将访问范围限制为这些子网。端口范围只需覆盖业务最繁忙时段的需求即可。默认策略、规则顺序以及 ufw 的其他用法,请参阅 VPS 的 ufw 防火墙基础指南。
同时检查 IPv6。如果 IPV6=no 在 /etc/default/ufw 中已设置,ufw 将完全不过滤 IPv6。因此,绑定到 :: 的守护进程可通过 IPv6 访问,且不受上述任何规则限制。使用 ufw 为 IPv6 开放端口介绍了这两个协议族之间的差异。大多数服务提供商还会在控制面板中提供独立的网络防火墙。该防火墙会在数据包到达 VPS 之前执行,因此端口必须在这两个位置都开放。
SIP 暴力破解和话费欺诈不是可选问题
将 5060 暴露在公网地址上后,扫描就会开始。模式通常很固定:来自多个源地址的 REGISTER 和 INVITE 请求,尝试使用常见的分机号码和常见密码。Asterisk 会记录每次失败,日志行的格式如下。
Request 'REGISTER' from '<sip:1000@198.51.100.20>' failed for '198.51.100.20:5060' (callid: 5f1a5c0d) - No matching endpoint found必须认真处理这个问题,因为它会直接造成经济损失。攻击者会利用被盗用的分机拨打昂贵的国际电话,通常是会向攻击者分成的高费率号码。由于通话使用了您的凭据,费用由您承担。攻击以机器速度运行,而且通常会在夜间持续进行。
以下 6 项控制措施是必需的,不是可选的加固措施。
- 绝不要创建号码与自身编号相同或密码为短单词的分机。使用
openssl rand -base64 24生成密码,然后粘贴进去。 - 关闭匿名入站呼叫。PJSIP 默认拒绝未识别的呼叫,只有创建名为
anonymous的 endpoint 后才会接受此类呼叫。不要创建它。 - 将 trunk 的 context 与任何能够外拨的 context 分开,如上所示。
- 在 ufw 和服务提供商的网络防火墙中,都按源地址限制信令流量。
- 在 SIP trunk 服务商处设置消费上限,并禁用您从不拨打的国际目的地。这是其他控制措施失效时唯一能够限制损失的措施。
- 对 Asterisk 日志运行 fail2ban。
Asterisk 还可以在来自同一地址的未匹配请求反复出现后生成安全事件。pjsip.conf 的 [global] 部分接受 unidentified_request_count,默认值为 5;并接受 unidentified_request_period,默认值为 5 秒。两者结合后表示:同一地址在 5 秒内产生 5 次未匹配请求时,就会生成安全事件,fail2ban 可以据此采取措施。
使用 fail2ban 屏蔽扫描器
fail2ban 已附带一个预先编写好的 asterisk jail。它覆盖 5060 和 5061 端口,读取 /var/log/asterisk/messages,默认使用 maxretry = 10。在 /etc/fail2ban/jail.local 中启用该 jail 前,它不会运行。
[asterisk]
enabled = true
maxretry = 5
findtime = 600
bantime = 86400sudo systemctl restart fail2ban
sudo fail2ban-client status asterisk正常结果会列出 jail 的日志文件,以及当前被屏蔽地址的数量。在公网开放 5060 端口时,该数量通常会在一天内不再为零。附带的过滤器会匹配上文的 No matching endpoint found 通知,也会匹配 Asterisk 的结构化 SecurityEvent 行。这些事件会写入单独的日志通道,而该通道在 /etc/asterisk/logger.conf 中被注释掉了;如果需要匹配这些事件,请在那里启用该通道,并将该文件添加到 jail 的 logpath 中。
[logfiles]
console => notice,warning,error
messages.log => notice,warning,error
security.log => security使用 sudo asterisk -rx "logger reload" 重新加载日志记录器。该过滤器还包含适用于 asterisk.service 的 journalmatch,因此如果不想保留日志文件,也可以使用 journal 后端。安装、jail.local 结构,以及解除对误锁地址的屏蔽,参见适用于 Ubuntu 24.04 的 fail2ban 指南。
远程 VPS 上的延迟与编解码器选择
延迟由地理位置决定,无法通过配置消除。ITU-T G.114 建议将单向延迟保持在 150 ms 以下,并认为最高约 400 ms 仍可使用。手机发出的音频先到达 VPS,再从 VPS 发往中继服务商,因此 VPS 所在区域不合适时,这段往返路径会产生两次延迟。应将 VPS 部署在靠近手机或服务商的位置。如果两者无法同时满足,优先靠近手机,因为这一段通常经过消费者互联网,抖动最严重。
编解码器决定每通话所需的带宽。这里的每种编解码器都每 20 ms 发送一个数据包,即每秒发送 50 个数据包。每个数据包除音频负载外,还包含 40 字节的 IP、UDP 和 RTP 头部。
The data behind this chart
[
{
"label": "G.711 ulaw",
"payload_kbps": 64,
"ip_kbps": 80
},
{
"label": "G.722",
"payload_kbps": 64,
"ip_kbps": 80
},
{
"label": "Opus at 24 kbps",
"payload_kbps": 24,
"ip_kbps": 40
},
{
"label": "G.729",
"payload_kbps": 8,
"ip_kbps": 24
}
]G.711 ulaw 是大多数中继上的默认编解码器。其负载为 64 kbps;加上头部后,进行中的通话在每个方向上占用 80 kbps。Opus 在 24 kbps 下占用 40 kbps,而 G.729 可将带宽降至 24 kbps,但会牺牲音频质量并增加 CPU 开销。这些数值是根据头部计算得出的,而不是实测值:负载速率加上每秒 50 个数据包、每个数据包 40 字节的头部开销。以太网或 VLAN 帧封装还会在实际链路上增加少量开销。
转码会消耗 CPU。如果手机和中继都使用 ulaw,则只允许使用 ulaw,Asterisk 会直接传递音频,不进行处理。Opus 在有丢包的链路上表现良好,但 Opus 与 G.711 之间的转码需要外部 codec_opus 模块。应在 make menuselect 中选择该模块;它默认不会构建。
出现的问题及日志中的字符串
呼叫已接通,但双方都听不到声音。 RTP 未到达。确认 rtp.conf 中的范围就是您在防火墙中放行的范围,然后在发起呼叫时使用 sudo tcpdump -ni any udp portrange 10000-10200 监视数据包。完全没有数据包,说明防火墙或提供商网络中的防火墙正在丢弃这些数据包。
只有一个方向有音频。 一方正在向无法接收 RTP 的地址发送数据。这是地址问题,不是端口问题。如果 VPS 的网络接口直接配置了公网地址,则不需要进行 NAT 处理。如果提供商为 VPS 分配私有地址,并通过一对一映射提供公网地址,请在传输配置中设置公网地址,并将私有地址范围列入 local_net。
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
local_net=10.0.0.0/8
external_media_address=198.51.100.5
external_signaling_address=198.51.100.5日志中出现 No matching endpoint found。 请求未按 IP 地址或用户名匹配到任何端点。对于扫描器发出的请求,这是正常现象,fail2ban 会处理。对于您自己的提供商,这表示 identify 部分未列出对方呼叫您时使用的地址。
注册状态为 Rejected。 提供商拒绝了您在 auth 部分配置的凭据。在 CLI 中运行 pjsip set logger on,监视一次 REGISTER 请求及其响应,然后将 client_uri 和 username 与提供商发给您的值进行比较。
日志中完全没有有用信息。 Asterisk 默认将 notice 级别及更高级别的日志写入 messages.log。重现问题时,使用 core set verbose 4 和 pjsip set logger on 提高日志级别;完成后关闭两者,因为 SIP logger 会记录每个数据包。
对外开放前
PBX 与其他自行托管的服务不同。Web 应用出故障,可能只是页面无法访问。PBX 出故障后,可能在您熟睡的几小时内产生高额电话费。请在不运行其他服务的 VPS 上部署 PBX,将 5060 端口限制为已知地址,为每个分机设置随机密钥,并为中继账户设置消费上限。服务器的其他部分也需要达到与任何对外暴露服务器相同的基线要求,这篇文章介绍了 VPS 托管的实际安全性。
FAQ
自托管 VoIP 服务器需要开放哪些端口?
SIP 信令使用 UDP 和 TCP 时需要开放端口 5060;使用基于 TLS 的 SIP 时需要开放端口 5061;RTP 媒体还需要开放一段 UDP 端口范围。Asterisk 的示例 rtp.conf 使用 10000 到 20000,编译时内置的默认范围是 5000 到 31000。每个通话会占用该范围内的两个端口,因此包含 two hundred 个端口的范围可支持 one hundred 个并发通话。将 RTP 范围按 UDP 开放,并将 5060 限制为服务提供商的地址和您自己的网络,不要对所有来源开放。
应该单独安装 Asterisk,还是使用 FreePBX?
如果您希望攻击面较小,能够将配置文件保存在 git 中,并且愿意编写拨号计划,请直接安装 Asterisk。如果您希望通过 GUI 管理分机、语音信箱和呼叫队列,请安装 FreePBX。截至 August 2026,FreePBX 17 安装程序要求使用原版 Debian 12 计算机,并会安装 Asterisk、Web 服务器、数据库服务器和 PHP,因此应为它单独分配一个 VPS。FreePBX 会重新生成它管理的配置文件,因此手动修改应放在其 _custom 文件中。
为什么通话接通后没有音频?
信令建立成功,但媒体流没有建立。SIP 通过端口 5060 建立通话,音频则是发往 RTP 范围内某个端口的独立 UDP 流;该流被某个环节丢弃了。检查 rtp.conf 中的范围是否与防火墙开放的范围一致,同时检查服务提供商的网络防火墙和服务器上的防火墙。通话期间运行 sudo tcpdump -ni any udp portrange 10000-10200:如果没有数据包,说明数据包在到达前就已被阻止。
如何阻止 SIP 暴力破解攻击和话费欺诈?
为每个分机设置较长的随机密钥,绝不能使用与分机号相同的密钥。将中继使用的 context 与任何可以拨出的 context 分开,避免入站通话通过您的账户再次拨出。将端口 5060 限制为服务提供商的地址。在 fail2ban 中启用 asterisk jail;它会读取 /var/log/asterisk/messages,并封禁产生 No matching endpoint found 次失败的地址。然后通过服务提供商设置消费上限,并阻止未使用的国际目的地,因为如果其他措施失效,只有这些控制措施能够限制损失。
位于远距离区域的 VPS 会影响通话质量吗?
会,因为音频需要经过两段路径:电话到 VPS,然后 VPS 到中继服务提供商。ITU-T G.114 建议单向延迟低于 150 ms;VPS 位置不合适时,距离本身就可能消耗大部分延迟预算。选择靠近电话所在位置的区域,因为这一段通常经过抖动最严重的消费者互联网。编解码器选择无法解决延迟,只会改变带宽;因此 G.729 可以节省字节,但无法挽救 200 ms 的路径。