Home | 简体中文 | 繁体中文 | 杂文 | Github | 知乎专栏 | Facebook | Linkedin | Youtube | 打赏(Donations) | About
知乎专栏

181.21. freeswitch Docker 配置案例

181.21.1. 备份 freeswitch 配置文件

				
cp /etc/freeswitch/vars.xml{,.backup}
cp /etc/freeswitch/sip_profiles/internal.xml{,.backup}
cp /etc/freeswitch/sip_profiles/external.xml{,.backup}
cp /etc/freeswitch/dialplan/default.xml{,.backup}
cp /etc/freeswitch/dialplan/public.xml{,.backup}
cp /etc/freeswitch/tls/wss.pem{,.backup}
				
			

181.21.2. 基本配置

修改默认密码

				
  <X-PRE-PROCESS cmd="set" data="default_password=13113668890" />				
				
			

配置SIP域

				
  <X-PRE-PROCESS cmd="set" data="domain=pbx.netkiller.cn"/>				
				
			

监听端口修改未 5061

				
 <X-PRE-PROCESS cmd="set" data="internal_sip_port=5061"/>
				
			

181.21.3. 配置拨出规则号段

				
    <extension name="Local_Extension">
        <condition field="destination_number" expression="^(1\d{2,3}|46\d{5})$">
        <action application="export" data="dialed_extension=$1"/>
        <!-- bind_meta_app can have these args <key> [a|b|ab] [a|b|o|s] <app> -->
        <action application="bind_meta_app" data="1 b s execute_extension::dx XML features"/>
        <action application="bind_meta_app" data="2 b s record_session::$${recordings_dir}/${caller_id_number}.${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
        <action application="bind_meta_app" data="3 b s execute_extension::cf XML features"/>
        <action application="bind_meta_app" data="4 b s execute_extension::att_xfer XML features"/>
        <action application="set" data="ringback=${us-ring}"/>
        <action application="set" data="transfer_ringback=$${hold_music}"/>
        <action application="set" data="call_timeout=30"/>
        <!-- <action application="set" data="sip_exclude_contact=${network_addr}"/> -->
        <action application="set" data="hangup_after_bridge=true"/>
        <!--<action application="set" data="continue_on_fail=NORMAL_TEMPORARY_FAILURE,USER_BUSY,NO_ANSWER,TIMEOUT,NO_ROUTE_DESTINATION"/> -->
        <action application="set" data="continue_on_fail=true"/>
        <action application="hash" data="insert/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/>
        <action application="hash" data="insert/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/>
        <action application="set" data="called_party_callgroup=${user_data(${dialed_extension}@${domain_name} var callgroup)}"/>
        <action application="hash" data="insert/${domain_name}-last_dial_ext/${called_party_callgroup}/${uuid}"/>
        <action application="hash" data="insert/${domain_name}-last_dial_ext/global/${uuid}"/>
        <!--<action application="export" data="nolocal:rtp_secure_media=${user_data(${dialed_extension}@${domain_name} var rtp_secure_media)}"/>-->
        <action application="hash" data="insert/${domain_name}-last_dial/${called_party_callgroup}/${uuid}"/>
        <action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
        <action application="answer"/>
        <action application="sleep" data="1000"/>
        <action application="bridge" data="loopback/app=voicemail:default ${domain_name} ${dialed_extension}"/>
      </condition>
    </extension>				
				
			

181.21.4. 禁用外部配置

				
[root@netkiller ~]# cat autoload_configs/sofia.conf.xml 
<configuration name="sofia.conf" description="sofia Endpoint">

  <global_settings>
    <param name="log-level" value="0"/>
    <!-- <param name="abort-on-empty-external-ip" value="true"/> -->
    <!-- <param name="auto-restart" value="false"/> -->
    <param name="debug-presence" value="0"/>
    <!-- <param name="capture-server" value="udp:homer.domain.com:5060"/> -->

    <!-- 
        the new format for HEPv2/v3 and capture ID    
        
        protocol:host:port;hep=2;capture_id=200;

    -->

    <!-- <param name="capture-server" value="udp:homer.domain.com:5060;hep=3;capture_id=100"/> -->
  </global_settings>

  <!--
      The rabbit hole goes deep.  This includes all the
      profiles in the sip_profiles directory that is up
      one level from this directory.
  -->
  <profiles>
    <X-PRE-PROCESS cmd="include" data="../sip_profiles/*.xml"/>
  </profiles>

</configuration>
				
			
				
[root@netkiller ~]# mv /etc/freeswitch/sip_profiles/external.xml /etc/freeswitch/sip_profiles/external.xml.backup 
[root@netkiller ~]# mv /etc/freeswitch/sip_profiles/external-ipv6.xml /etc/freeswitch/sip_profiles/external-ipv6.xml.backup
				
			
				
[root@netkiller ~]# docker exec -it freeswitch fs_cli -x "sofia status profile external" 
Invalid Profile!

[root@netkiller ~]# ss -lnt| grep 5080				
				
			

181.21.5. Websocket 配置

			
[root@netkiller ~]# vim /etc/freeswitch/sip_profiles/internal.xml
<param name="aggressive-nat-detection" value="true"/>			
			
			
				
[root@netkiller freeswitch]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"
=================================================================================================
Name                    internal
Domain Name             N/A
Auto-NAT                false
DBName                  sofia_reg_internal
Pres Hosts              sip.netkiller.cn,172.22.11.170
Dialplan                XML
Context                 public
Challenge Realm         auto_from
RTP-IP                  172.22.11.170
Ext-RTP-IP              47.112.9.199
SIP-IP                  172.22.11.170
Ext-SIP-IP              47.112.9.199
URL                     sip:mod_sofia@47.112.9.199:5061
BIND-URL                sip:mod_sofia@47.112.9.199:5061;maddr=172.22.11.170;transport=udp,tcp
WS-BIND-URL             sip:mod_sofia@172.22.11.170:5066;transport=ws
WSS-BIND-URL            sips:mod_sofia@172.22.11.170:7443;transport=wss
HOLD-MUSIC              local_stream://moh
OUTBOUND-PROXY          N/A
CODECS IN               OPUS,G722,PCMU,PCMA,H264,VP8
CODECS OUT              OPUS,G722,PCMU,PCMA,H264,VP8
TEL-EVENT               101
DTMF-MODE               rfc2833
CNG                     13
SESSION-TO              0
MAX-DIALOG              0
MAX-RECV-RPS            1000
NOMEDIA                 false
LATE-NEG                true
PROXY-MEDIA             false
AGGRESSIVENAT           true
CALLS-IN                0
FAILED-CALLS-IN         0
CALLS-OUT               0
FAILED-CALLS-OUT        0
REGISTRATIONS           0

[root@netkiller ~]# ss -lnt| grep 5080				
				
			
			
[root@netkiller freeswitch]# docker exec -it freeswitch fs_cli -x "sofia status profile internal reg"

Registrations:
=================================================================================================
Call-ID:        IYijpAe8k7x7HOsdS36W28VNHrYa30yB
User:           1000@sip.netkiller.cn
Contact:        "Neo" <sip:1000@113.80.25.220:18621;ob>
Agent:          Telephone 1.6
Status:         Registered(UDP)(unknown) EXP(2026-09-20 02:53:09) EXPSECS(274)
Ping-Status:    Reachable
Ping-Time:      0.00
Host:           iZwz91c64odbewa32r2or7Z
IP:             113.80.25.220
Port:           18621
Auth-User:      1000
Auth-Realm:     sip.netkiller.cn
MWI-Account:    1000@sip.netkiller.cn

Total items returned: 1
=================================================================================================			
			
			
			
openssl s_client -connect webrtc.netkiller.cn:443		


	
			
			

检查 HTTP/1.1 101 Switching Protocols,正确响应应该是 HTTP/1.1 101 Switching Protocols。

			
root@spiritcloudp ~# curl --http1.1 -i -N \
                           -H "Connection: Upgrade" \
                           -H "Upgrade: websocket" \
                           -H "Sec-WebSocket-Version: 13" \
                           -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
                           https://sip.netkiller.cn
HTTP/1.1 101 Switching Protocols
Alt-Svc: h3=":443"; ma=2592000
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: Caddy
Upgrade: websocket
Date: Sun, 20 Sep 2026 10:47:15 GMT			
			
			

181.21.6. Verto

Verto 配置文件

			
[root@netkiller freeswitch]# cat autoload_configs/verto.conf.xml
<configuration name="verto.conf" description="HTML5 Verto Endpoint">

  <settings>
    <param name="debug" value="0"/>
    <!-- <param name="kslog" value="true"/> -->
    <!-- seconds to wait before hanging up a disconnected channel -->
    <!-- <param name="detach-timeout-sec" value="120"/> -->
    <!-- enable broadcasting all FreeSWITCH events in Verto -->
    <!-- <param name="enable-fs-events" value="false"/> -->
    <!-- enable broadcasting FreeSWITCH presence events in Verto -->
    <!-- <param name="enable-presence" value="true"/> -->
  </settings>

  <profiles>
    <profile name="default-v4">
      <param name="bind-local" value="$${local_ip_v4}:8081"/>
      <param name="bind-local" value="$${local_ip_v4}:8082" secure="true"/>
      <param name="force-register-domain" value="$${domain}"/>
      <param name="secure-combined" value="$${certs_dir}/wss.pem"/>
      <param name="secure-chain" value="$${certs_dir}/wss.pem"/>
      <param name="userauth" value="true"/>
      <!-- setting this to true will allow anyone to register even with no account so use with care -->
      <param name="blind-reg" value="false"/>
      <param name="mcast-ip" value="224.1.1.1"/>
      <param name="mcast-port" value="1337"/>
      <param name="rtp-ip" value="$${local_ip_v4}"/>
      <param name="ext-rtp-ip" value="$${external_rtp_ip}"/>
      <param name="local-network" value="localnet.auto"/>
      <param name="outbound-codec-string" value="opus,h264,vp8"/>
      <param name="inbound-codec-string" value="opus,h264,vp8"/>

      <param name="apply-candidate-acl" value="localnet.auto"/>
      <param name="apply-candidate-acl" value="wan_v4.auto"/>
      <param name="apply-candidate-acl" value="rfc1918.auto"/>
      <param name="apply-candidate-acl" value="any_v4.auto"/>
      <param name="timer-name" value="soft"/>
      
    </profile>

    <profile name="default-v6">
      <param name="bind-local" value="[$${local_ip_v6}]:8081"/>
      <param name="bind-local" value="[$${local_ip_v6}]:8082" secure="true"/>
      <param name="force-register-domain" value="$${domain}"/>
      <param name="secure-combined" value="$${certs_dir}/wss.pem"/>
      <param name="secure-chain" value="$${certs_dir}/wss.pem"/>
      <param name="userauth" value="true"/>
      <!-- setting this to true will allow anyone to register even with no account so use with care -->
      <param name="blind-reg" value="false"/>
      <param name="rtp-ip" value="$${local_ip_v6}"/>
      <!--  <param name="ext-rtp-ip" value=""/> -->
      <param name="outbound-codec-string" value="opus,h264,vp8"/>
      <param name="inbound-codec-string" value="opus,h264,vp8"/>

      <param name="apply-candidate-acl" value="wan_v6.auto"/>
      <param name="apply-candidate-acl" value="rfc1918.auto"/>
      <param name="apply-candidate-acl" value="any_v6.auto"/>
      <param name="apply-candidate-acl" value="wan_v4.auto"/>
      <param name="apply-candidate-acl" value="any_v4.auto"/>
      <param name="timer-name" value="soft"/>
      
    </profile>
  </profiles>
</configuration>			
			
			
			
[root@netkiller freeswitch]# grep -r mod_verto *
autoload_configs/modules.conf.xml:    <load module="mod_verto"/>	

[root@netkiller freeswitch]# grep -r mod_rtc *
autoload_configs/modules.conf.xml:    <load module="mod_rtc"/>		
			
			

禁用 SSL

			
<param name="bind-local" value="$${local_ip_v4}:8082" secure="false"/>			
			
			
			
			
			
			
			
			
			
			

181.21.7. docker 环境 freeswitch 管理

			
# 解除模块
docker exec -it freeswitch fs_cli -x "unload mod_signalwire"
			
# 查看所有SIP profile的状态
docker exec -it freeswitch fs_cli -x "sofia status"

# 查看某个profile的详细信息(如internal或external)
docker exec -it freeswitch fs_cli -x "sofia status profile internal"

# 查看 internal profile 下的所有注册用户信息(已注册的终端列表)
docker exec -it freeswitch fs_cli -x "sofia status profile internal reg"

# 重新加载XML配置(包括用户、拨号计划等)
docker exec -it freeswitch fs_cli -x "reloadxml"

# 列出当前所有正在运行的会议室及相关信息
docker exec -it freeswitch fs_cli -x "conference list"

# 挂断指定UUID的通话
docker exec -it freeswitch fs_cli -x "uuid_kill <uuid>"

# 显示当前所有活动的通道(channels),包括呼叫详情
docker exec -it freeswitch fs_cli -x "show channels"

# 查看指定 UUID 的通道详细信息,UUID 是通话的唯一标识符
docker exec -it freeswitch fs_cli -x "uuid_dump 44e97f0b-bc3c-41d8-9929-936a890c3cb4"

# 使用 Lua 脚本 dissolve_conference.lua 解散会议室,参数是会议室ID和发起者IP(这里示例是会议室12345,IP 154.11.80.119)
docker exec -it freeswitch fs_cli -x "luarun dissolve_conference.lua 12345-154.11.80.119"			
			
			
			
[root@testing default]# docker exec -it freeswitch fs_cli -x "sofia status"
                     Name          Type                                       Data      State
=================================================================================================
            external-ipv6       profile   sip:mod_sofia@[2408:4003:1150:2600:5ec0:2ed4:4199:547e]:5080  RUNNING (0)
            172.22.11.164         alias                                   internal      ALIASED
                 external       profile          sip:mod_sofia@120.79.201.161:5080      RUNNING (0)
    external::example.com       gateway                    sip:joeuser@example.com      NOREG
            internal-ipv6       profile   sip:mod_sofia@[2408:4003:1150:2600:5ec0:2ed4:4199:547e]:5060  RUNNING (0)
                 internal       profile          sip:mod_sofia@120.79.201.161:5060      RUNNING (0)
=================================================================================================
4 profiles 1 alias
			
			

			
[root@netkiller ~]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"
=================================================================================================
Name                    internal
Domain Name             N/A
Auto-NAT                false
DBName                  sofia_reg_internal
Pres Hosts              pbx.netkiller.cn,172.22.11.164
Dialplan                XML
Context                 public
Challenge Realm         auto_from
RTP-IP                  172.22.11.164
Ext-RTP-IP              120.79.201.161
SIP-IP                  172.22.11.164
Ext-SIP-IP              120.79.201.161
URL                     sip:mod_sofia@120.79.201.161:5060
BIND-URL                sip:mod_sofia@120.79.201.161:5060;maddr=172.22.11.164;transport=udp,tcp
TLS-URL                 sip:mod_sofia@120.79.201.161:5061
TLS-BIND-URL            sips:mod_sofia@120.79.201.161:5061;maddr=172.22.11.164;transport=tls
WS-BIND-URL             sip:mod_sofia@172.22.11.164:5066;transport=ws
WSS-BIND-URL            sips:mod_sofia@172.22.11.164:7443;transport=wss
HOLD-MUSIC              local_stream://moh
OUTBOUND-PROXY          N/A
CODECS IN               OPUS,G722,PCMU,PCMA,H264,VP8
CODECS OUT              OPUS,G722,PCMU,PCMA,H264,VP8
TEL-EVENT               101
DTMF-MODE               rfc2833
CNG                     13
SESSION-TO              0
MAX-DIALOG              0
MAX-RECV-RPS            1000
NOMEDIA                 false
LATE-NEG                true
PROXY-MEDIA             false
AGGRESSIVENAT           false
CALLS-IN                43
FAILED-CALLS-IN         43
CALLS-OUT               0
FAILED-CALLS-OUT        0
REGISTRATIONS           4			
			
			

查看注册情况

			
[root@netkiller ~]# docker exec -it freeswitch fs_cli -x "sofia status profile internal reg"

Registrations:
=================================================================================================
Call-ID:        4640d1c4-68b2ec4e@192.168.23.21
User:           1720@pbx.netkiller.cn
Contact:        "1720" <sip:1720@100.64.56.237:65477;fs_nat=yes;fs_path=sip%3A1720%40183.14.132.54%3A17945>
Agent:          Linksys/PAP2T-5.1.6(LS)
Status:         Registered(UDP-NAT)(unknown) EXP(2026-02-10 03:17:18) EXPSECS(860)
Ping-Status:    Reachable
Ping-Time:      0.00
Host:           testing
IP:             183.14.132.54
Port:           17945
Auth-User:      1720
Auth-Realm:     pbx.netkiller.cn
MWI-Account:    1720@pbx.netkiller.cn

Call-ID:        shfboftsbn9tnfnmk1ivib
User:           4600442@pbx.netkiller.cn
Contact:        "" <sip:0ia9s501@qkr364fkldvi.invalid;transport=ws;fs_nat=yes;fs_path=sip%3A0ia9s501%40116.24.64.62%3A60193%3Btransport%3Dwss>
Agent:          JsSIP 3.13.4
Status:         Registered(WSS-NAT)(unknown) EXP(2026-02-10 03:10:22) EXPSECS(444)
Ping-Status:    Reachable
Ping-Time:      0.00
Host:           testing
IP:             116.24.64.62
Port:           60193
Auth-User:      4600442
Auth-Realm:     pbx.netkiller.cn
MWI-Account:    4600442@pbx.netkiller.cn

Call-ID:        349CB636C2D53CDC0516A816AA744C0A8FFD08B0
User:           4600441@pbx.netkiller.cn
Contact:        "" <sip:4600441@172.16.0.24:43769;rinstance=402A5B8C;transport=tcp;fs_nat=yes;fs_path=sip%3A4600441%40183.14.29.46%3A34738%3Brinstance%3D402A5B8C%3Btransport%3Dtcp>
Agent:          Groundwire/5.3.6 (build 1431795; Android 8.1.0; arm64-v8a)
Status:         Registered(TCP-NAT)(unknown) EXP(2026-02-10 03:13:57) EXPSECS(659)
Ping-Status:    Reachable
Ping-Time:      0.00
Host:           testing
IP:             183.14.29.46
Port:           34738
Auth-User:      4600441
Auth-Realm:     pbx.netkiller.cn
MWI-Account:    4600441@pbx.netkiller.cn

Total items returned: 3
=================================================================================================