这篇整理 FreeSWITCH 线上权限相关配置。

内容基于一台 Debian 11 服务器上的 /usr/local/freeswitch/etc/freeswitch 配置整理,重点看:

  1. autoload_configs/acl.conf.xml
  2. autoload_configs/event_socket.conf.xml
  3. sip_profiles/internal.xml
  4. sip_profiles/external.xml
  5. vars.xml

FreeSWITCH 权限配置最容易出问题的地方不是 XML 难写,而是边界不清楚:

  1. 哪些 IP 可以连 ESL?
  2. 哪些 IP 可以走 SIP profile?
  3. 哪些呼叫允许免鉴权?
  4. 内外网 profile 是否混用?
  5. 默认密码是否还在用?

ACL 配置

ACL 文件路径:

1
/usr/local/freeswitch/etc/freeswitch/autoload_configs/acl.conf.xml

线上配置里保留了 FreeSWITCH 默认说明,并增加了两个关键 list:domainsesl_only

1
2
3
4
5
6
7
8
9
10
11
12
<network-lists>
<list name="domains" default="deny">
<node type="allow" domain="$${domain}"/>
<node type="allow" cidr="127.0.0.1/32"/>
<node type="allow" cidr="192.168.2.184/32"/>
</list>

<list name="esl_only" default="deny">
<node type="allow" cidr="192.168.2.0/24"/>
<node type="allow" cidr="127.0.0.1/32"/>
</list>
</network-lists>

domains 用于 SIP profile 的入站访问控制。domain="$${domain}" 会根据 directory 里的
domain 信息构建 ACL,额外加入 127.0.0.1/32 和本机内网 IP,是为了允许本机和可信内网地址
访问。

esl_only 是专门给 ESL 用的 ACL。这里允许:

1
2
127.0.0.1/32
192.168.2.0/24

这个拆分很重要。SIP 能访问的范围和 ESL 能访问的范围不应该混在一起。ESL 是控制面接口,
可以 originate、hangup、uuid_transfer、监听事件,权限比普通 SIP 注册高很多。

ESL 权限

ESL inbound 配置路径:

1
/usr/local/freeswitch/etc/freeswitch/autoload_configs/event_socket.conf.xml

线上关键配置:

1
2
3
4
5
6
7
8
9
<configuration name="event_socket.conf" description="Socket Client">
<settings>
<param name="nat-map" value="false"/>
<param name="listen-ip" value="::"/>
<param name="listen-port" value="8021"/>
<param name="password" value="请改成强密码"/>
<param name="apply-inbound-acl" value="esl_only"/>
</settings>
</configuration>

listen-ip="::" 表示监听 IPv6 unspecified 地址,在很多系统上也可能同时覆盖 IPv4。
所以真正的安全边界不能只依赖监听地址,必须配合:

1
<param name="apply-inbound-acl" value="esl_only"/>

这样只有 esl_only 里允许的 IP 能连 8021。

线上建议:

  1. ESL 8021 不开放公网。
  2. ESL 只允许业务服务器、运维跳板机、本机访问。
  3. password 不使用默认值。
  4. 防火墙也要做同样的来源限制,ACL 不是唯一防线。
  5. 如果只允许本机访问,可以把 listen-ip 改成 127.0.0.1::1,并同步检查业务服务连接方式。

验证:

1
2
ss -lntup | grep 8021
fs_cli -H 127.0.0.1 -P 8021 -p 'ESL密码'

从未授权机器连接应该失败。

vars.xml 基础密码与地址

文件路径:

1
/usr/local/freeswitch/etc/freeswitch/vars.xml

关键项:

1
2
3
4
5
<X-PRE-PROCESS cmd="set" data="default_password=请改成强密码"/>
<X-PRE-PROCESS cmd="set" data="domain=$${local_ip_v4}"/>
<X-PRE-PROCESS cmd="set" data="bind_server_ip=$${domain}"/>
<X-PRE-PROCESS cmd="stun-set" data="external_rtp_ip=$${domain}"/>
<X-PRE-PROCESS cmd="stun-set" data="external_sip_ip=$${domain}"/>

default_password 会影响默认 directory 用户。测试环境常常为了方便统一密码,但线上不建议
所有分机共用一个密码。

domain=$${local_ip_v4} 表示默认 domain 使用本机 IPv4。内网测试可以这样配;如果线上需要
域名注册、多网卡、NAT 或公网线路,建议明确规划:

  1. SIP 终端注册用哪个 domain。
  2. SIP profile 监听哪个 IP。
  3. RTP 对外公布哪个 IP。
  4. 是否需要 STUN。

不要在多网卡服务器上只靠默认探测结果。

internal profile

文件路径:

1
/usr/local/freeswitch/etc/freeswitch/sip_profiles/internal.xml

线上关键配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<param name="context" value="public"/>
<param name="rtp-ip" value="$${local_ip_v4}"/>
<param name="sip-ip" value="$${local_ip_v4}"/>
<param name="apply-inbound-acl" value="domains"/>
<param name="local-network-acl" value="localnet.auto"/>
<param name="manage-presence" value="true"/>
<param name="accept-blind-auth" value="true"/>
<param name="nonce-ttl" value="60"/>
<param name="auth-calls" value="$${internal_auth_calls}"/>
<param name="inbound-reg-force-matching-username" value="true"/>
<param name="ext-rtp-ip" value="$${external_rtp_ip}"/>
<param name="ext-sip-ip" value="$${external_sip_ip}"/>
<param name="force-register-domain" value="$${domain}"/>
<param name="force-subscription-domain" value="$${domain}"/>

这里最需要关注的是:

1
<param name="apply-inbound-acl" value="domains"/>

它表示 internal profile 的入站请求要经过 domains 这个 ACL。也就是说,不是任意来源都可以
直接打到 internal profile。

另一个高风险项是:

1
<param name="accept-blind-auth" value="true"/>

这个参数用于允许某些网关或上游设备以较宽松的方式通过认证。它不是所有环境都应该打开。
如果上游线路是固定 IP 的 SIP trunk,可以配合 ACL 限制来源;如果是公网软电话注册场景,
就要谨慎评估。

auth-calls 决定是否要求呼叫鉴权。internal profile 如果对公网可达,必须非常谨慎。
常见建议是:

  1. 终端注册走 internal,开启鉴权。
  2. SIP trunk 呼入走 external 或单独 profile,通过来源 IP 白名单控制。
  3. 不要把 accept-blind-auth=true 和大范围 ACL 放在一起。

数据库连接也在 profile 中配置:

1
2
<param name="odbc-dsn"
value="pgsql://hostaddr=数据库IP dbname=freeswitch user=用户名 password=密码 options='-c statement_timeout=3000' connect_timeout=5 keepalives=1 keepalives_idle=30 keepalives_interval=5 keepalives_count=3" />

这个配置会影响 SIP profile 相关数据,例如注册、恢复、状态存储等。线上建议加超时和 keepalive,
避免数据库短暂异常时把 SIP 处理拖死。

external profile

文件路径:

1
/usr/local/freeswitch/etc/freeswitch/sip_profiles/external.xml

线上关键配置:

1
2
3
4
5
6
7
8
9
<param name="context" value="public"/>
<param name="local-network-acl" value="localnet.auto"/>
<param name="manage-presence" value="false"/>
<param name="nonce-ttl" value="60"/>
<param name="auth-calls" value="false"/>
<param name="rtp-ip" value="$${local_ip_v4}"/>
<param name="sip-ip" value="$${local_ip_v4}"/>
<param name="ext-rtp-ip" value="$${external_rtp_ip}"/>
<param name="ext-sip-ip" value="$${external_sip_ip}"/>

external profile 通常用于对接外部 SIP trunk。这里 auth-calls=false 比较常见,因为运营商
或者上游网关可能不走分机密码认证,而是通过固定 IP 和网关配置鉴权。

但这也意味着必须用其他方式限制来源:

  1. 防火墙只允许运营商或上游网关 IP 访问 SIP 端口。
  2. profile 层增加 ACL。
  3. dialplan 层限制可呼叫的号码范围。
  4. 出局路由不要让任意呼入直接转外呼。

external profile 不建议直接给普通软电话注册使用。如果确实要支持公网注册,建议单独做一个
profile,独立端口、独立 ACL、独立鉴权策略。

core db 配置

路径:

1
/usr/local/freeswitch/etc/freeswitch/autoload_configs/switch.conf.xml

线上使用 PostgreSQL core db:

1
2
<param name="core-db-dsn"
value="pgsql://hostaddr=数据库IP dbname=freeswitch user=用户名 password=密码 options='-c statement_timeout=3000' connect_timeout=5 keepalives=1 keepalives_idle=30 keepalives_interval=5 keepalives_count=3" />

这里同样建议:

  1. 独立数据库账号。
  2. 最小权限。
  3. 明确连接超时。
  4. 明确查询超时。
  5. 开启 keepalive。

数据库权限不是 SIP 权限,但数据库卡住会影响 FreeSWITCH 的稳定性,所以线上配置要把超时写清楚。

线上日志配置

FreeSWITCH 线上日志要先收敛默认输出,再给排查留临时开关。日志量过大不仅占磁盘,
也会在高并发时影响 I/O 和问题定位效率。

日志级别

FreeSWITCH 常用日志级别可以按 0-7 理解:

1
2
3
4
5
6
7
8
0 CONSOLE
1 ALERT
2 CRIT
3 ERR
4 WARNING
5 NOTICE
6 INFO
7 DEBUG

数值越小表示问题越严重,默认输出越少;数值越大表示日志越详细,输出也越多。
线上默认建议控制在 NOTICE,需要排查注册、呼叫、媒体或模块细节时,再临时打开
DEBUG

核心配置文件

日志相关配置主要看三个文件:

1
2
3
/usr/local/freeswitch/etc/freeswitch/autoload_configs/switch.conf.xml
/usr/local/freeswitch/etc/freeswitch/autoload_configs/console.conf.xml
/usr/local/freeswitch/etc/freeswitch/autoload_configs/logfile.conf.xml

switch.conf.xml 里可以设置全局日志上限,线上建议不要长期放到 debug

1
<param name="loglevel" value="notice"/>

console.conf.xml 控制控制台输出。线上如果主要看文件日志,控制台可以保持 info
notice,避免终端输出过多:

1
<param name="loglevel" value="notice"/>

logfile.conf.xml 控制文件日志、轮转、是否写入 UUID、实际写入级别等。线上建议文件日志
保留足够的异常和呼叫状态信息,但不要默认写入 debug

一个偏保守的生产示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<configuration name="logfile.conf" description="File Logging">
<settings>
<param name="rotate-on-hup" value="true"/>
</settings>

<profiles>
<profile name="default">
<settings>
<param name="logfile" value="/usr/local/freeswitch/var/log/freeswitch/freeswitch.log"/>
<param name="rollover" value="104857600"/>
<param name="maximum-rotate" value="30"/>
<param name="uuid" value="true"/>
</settings>
<mappings>
<map name="all" value="notice,warning,err,crit,alert"/>
</mappings>
</profile>
</profiles>
</configuration>

这里的含义是:

  1. rotate-on-hup=true:收到 HUP 后重新打开日志文件,方便配合 logrotate
  2. rollover=104857600:单个日志文件约 100 MB 后轮转。
  3. maximum-rotate=30:FreeSWITCH 自身最多保留 30 个轮转文件。
  4. uuid=true:日志里带呼叫 UUID,方便过滤单次呼叫链路。
  5. mappings 不包含 debug:避免生产环境默认写入大量调试日志。

动态调试命令

线上排查可以临时调高日志级别,不一定每次都改 XML:

1
2
fs_cli -x "fsctl loglevel debug"
fs_cli -x "fsctl loglevel notice"

如果已经进入 fs_cli,可以直接执行:

1
2
/log debug
/log notice

排查 SIP 信令时,优先只打开目标 profile 的 siptrace:

1
2
3
4
fs_cli -x "sofia profile internal siptrace on"
fs_cli -x "sofia profile internal siptrace off"
fs_cli -x "sofia profile external siptrace on"
fs_cli -x "sofia profile external siptrace off"

排查结束后必须恢复日志级别,并关闭 siptrace:

1
2
3
fs_cli -x "fsctl loglevel notice"
fs_cli -x "sofia profile internal siptrace off"
fs_cli -x "sofia profile external siptrace off"

不要在高并发线上长期打开:

1
fs_cli -x "sofia global siptrace on"

全局 siptrace 会把大量 SIP 报文写入日志,既影响性能,也容易把真正的错误淹没。
需要临时抓 SIP 包时,更推荐在服务器上用 sngrep 观察,减少 FreeSWITCH 自身日志压力。

logrotate 配置建议

如果使用系统 logrotate 管理 FreeSWITCH 日志,可以按每日轮转、保留 14 天、gzip 压缩来配置。
示例文件:/etc/logrotate.d/freeswitch

1
2
3
4
5
6
7
8
9
10
11
12
13
/usr/local/freeswitch/var/log/freeswitch/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
copytruncate
sharedscripts
postrotate
/bin/kill -HUP $(cat /usr/local/freeswitch/var/run/freeswitch/freeswitch.pid 2>/dev/null) 2>/dev/null || true
endscript
}

如果 logfile.conf.xml 已开启 rotate-on-hup=truepostrotate 里发送 HUP 可以让
FreeSWITCH 重新打开日志文件。不同安装方式下 pid 文件路径可能不同,需要按实际路径确认。

排查建议

常用日志排查命令:

1
2
3
4
5
tail -f -n 500 /usr/local/freeswitch/var/log/freeswitch/freeswitch.log
grep -n "ERR\|WARNING" /usr/local/freeswitch/var/log/freeswitch/freeswitch.log
grep -n "sofia_reg\|REGISTER" /usr/local/freeswitch/var/log/freeswitch/freeswitch.log
grep -n "mod_event_socket\|8021" /usr/local/freeswitch/var/log/freeswitch/freeswitch.log
grep -n "core-db-dsn\|odbc\|pgsql" /usr/local/freeswitch/var/log/freeswitch/freeswitch.log

定位单次呼叫时,先拿到 UUID,再过滤整条链路:

1
grep "呼叫UUID" /usr/local/freeswitch/var/log/freeswitch/freeswitch.log

线上排查的原则是:默认日志收敛,发现问题后临时放开,定位结束立即恢复。

修改后的加载方式

修改 XML 后先检查格式。至少执行:

1
fs_cli -x reloadxml

如果改了 SIP profile 的监听 IP、ACL、NAT、数据库连接,建议重启对应 profile:

1
2
fs_cli -x "sofia profile internal restart"
fs_cli -x "sofia profile external restart"

如果只改了 ESL 的 ACL 或密码,最稳妥是重启 mod_event_socket 或安排 FreeSWITCH 进程重启。
线上已有通话时不要随便重启主进程。

检查:

1
2
3
fs_cli -x "sofia status"
fs_cli -x "sofia status profile internal"
fs_cli -x "sofia status profile external"

日志:

1
tail -f -n 500 /usr/local/freeswitch/var/log/freeswitch/freeswitch.log

线上推荐配置原则

1. ESL 是控制面,只走内网

ESL 8021 能控制通话,不要暴露公网。

推荐:

1
<param name="apply-inbound-acl" value="esl_only"/>

esl_only 里只放:

1
2
3
127.0.0.1/32
业务服务器IP/32
跳板机IP/32

不要为了方便写成 0.0.0.0/0

2. SIP 分 internal 和 external

internal 负责分机注册和内网业务,external 负责外部线路。不要把所有入口都塞进一个 profile。

internal 建议:

  1. 开启鉴权。
  2. 限制来源 ACL。
  3. 使用强密码。
  4. 只开放给可信终端或内网。

external 建议:

  1. 只允许 SIP trunk 来源 IP。
  2. 不允许任意号码转外呼。
  3. dialplan 做号码规则限制。
  4. 运营商 IP 变化时同步更新防火墙和 ACL。

3. 默认密码必须改

vars.xml 里的 default_password 不要保留默认值。更进一步,线上应该避免所有分机共用一个
默认密码。

4. ACL 和防火墙同时做

ACL 是 FreeSWITCH 应用层限制,防火墙是系统层限制。线上两层都要做。

常见组合:

1
2
3
4
5
ESL 8021     只允许业务服务器和跳板机
SIP 5060 只允许 SIP trunk、可信终端网段
SIP 5080 只允许外部线路来源
RTP 端口段 按运营商或业务实际情况开放
PostgreSQL 只允许 FreeSWITCH 服务器访问

5. 日志默认收敛,排查临时放开

线上不要长期打开 debug 或全局 siptrace。默认日志级别保持 notice,遇到问题时临时
调高到 debug,排查结束立即恢复。

6. 配置变更先备份

变更前先备份:

1
2
cp -r /usr/local/freeswitch/etc/freeswitch \
/usr/local/freeswitch/etc/freeswitch.bak_$(date +%F_%H%M%S)

配置出错时,FreeSWITCH 可能还能运行,但新加载的 profile、dialplan、ACL 不生效。
备份比事后回忆改了哪里可靠。

排查清单

ESL 连不上:

1
2
3
ss -lntup | grep 8021
grep -n "apply-inbound-acl" /usr/local/freeswitch/etc/freeswitch/autoload_configs/event_socket.conf.xml
grep -n "esl_only" /usr/local/freeswitch/etc/freeswitch/autoload_configs/acl.conf.xml

SIP 注册失败:

1
2
3
fs_cli -x "sofia status profile internal"
fs_cli -x "sofia status"
tail -f /usr/local/freeswitch/var/log/freeswitch/freeswitch.log

SIP trunk 呼入失败:

1
2
3
4
fs_cli -x "sofia status profile external"
fs_cli -x "sofia profile external siptrace on"
tail -f -n 500 /usr/local/freeswitch/var/log/freeswitch/freeswitch.log
fs_cli -x "sofia profile external siptrace off"

数据库异常:

1
2
3
grep -n "core-db-dsn" /usr/local/freeswitch/etc/freeswitch/autoload_configs/switch.conf.xml
grep -n "odbc-dsn" /usr/local/freeswitch/etc/freeswitch/sip_profiles/internal.xml
grep -n "odbc-dsn" /usr/local/freeswitch/etc/freeswitch/sip_profiles/external.xml

总结

FreeSWITCH 线上权限配置可以按一句话理解:

1
ESL 只给业务控制面,SIP 按入口拆 profile,ACL 和防火墙都要收紧,默认密码全部换掉。

安装成功只是第一步。真正上线前,至少要确认:

  1. ESL 8021 不在公网暴露。
  2. internal/external profile 边界明确。
  3. SIP trunk 来源 IP 有白名单。
  4. 分机密码不是默认值。
  5. 数据库连接有超时。
  6. 每次改配置都有备份和回滚路径。