SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor

自架 VoIP 伺服器:Asterisk 與 FreePBX 怎麼選?

在自己的 VPS 執行 Asterisk 或 FreePBX,了解 SIP trunk、RTP 連接埠範圍與防火牆規則,並掌握掃描器如何發現服務及通話詐騙如何開始。

自架 VoIP 伺服器是什麼

自架 VoIP 伺服器是執行於你所控制之 VPS 上的 SIP 伺服器。通話會由你自己的機器路由,而不是由代管電話服務處理。VoIP(voice over IP)會將語音封裝為 UDP 封包。SIP(session initiation protocol)是用來建立與結束通話的訊號協定。音訊不會經由 SIP 傳送,而這項事實正是下文多數問題的原因。

要讓系統正常運作,需要以下 4 個部分。

  • PBX(private branch exchange)軟體。Asterisk 是最常見的選擇。它管理分機與撥號計畫。
  • 端點。桌上型電話或 softphone 會使用使用者名稱與 secret 向 PBX 註冊。
  • SIP trunk。這是向供應商付費取得的帳戶,可將你的系統連接至公共電話網路,並租用實體電話號碼。
  • 媒體路徑。RTP(real-time transport protocol)流程會使用各自的 UDP 埠傳送音訊。

自架 PBX 不代表可以自架電話號碼。號碼由電信業者提供,你仍須按號碼數量與通話分鐘數付費。你所擁有的是通話路由、語音信箱、錄音與分機清單。你也必須自行負責服務安全,因為攻擊者會為了金錢攻擊這類服務。

自架 VoIP 伺服器需要哪些連接埠?

SIP signalling 使用 UDP 和 TCP 的 5060 埠,SIP over TLS(transport layer security)則使用 5061 埠。這些連接埠只負責建立通話。每通電話的音訊會建立獨立的 UDP flow,並傳送到 RTP range 中指定的連接埠。Asterisk 隨附範例 rtp.conf,其中設定了 rtpstart=10000rtpend=20000;編譯時內建的預設值為 5000 和 31000。每通電話會從該 range 取用 2 個連接埠,分別用於 RTP 和 RTCP(RTP control protocol)。

多數初次設定失敗的原因就在這個分離設計。通話可以連線,兩支電話也都顯示已接通,但雙方都聽不到聲音,因為防火牆允許 5060,卻丟棄所有 RTP 封包。Signalling 和 media 是不同的 flow,因此需要分開設定防火牆規則。如果你不熟悉這項區別,建議先閱讀Linux 上的連接埠與 listening socket 如何運作,再開放任何連接埠。

先縮小 range,再開放連接埠。20,000 個連接埠遠超過小型系統的需求。每通電話需要 2 個連接埠,因此大小為 200 的 range 可同時支援 100 通電話。

[general]
rtpstart=10000
rtpend=10200

使用 sudo asterisk -rx "core reload" 套用設定。

應該安裝 Asterisk 還是 FreePBX?

Asterisk 是核心引擎。您可以在 /etc/asterisk 中使用文字檔設定它,也可以自行撰寫撥號計畫。FreePBX 是以 PHP 和 JavaScript 撰寫的網頁介面,建置在 Asterisk 之上,會代您產生這些檔案,並加入語音信箱與通話佇列等模組。

對 VPS 而言,真正重要的差異在於伺服器的管理權。以 2026 年 8 月為準,官方 FreePBX 17 安裝程式預期使用原始的 Debian 12 系統,並會安裝 Asterisk、網頁伺服器、資料庫伺服器與 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 完全沒有網頁介面,每項設定都是可在檔案中閱讀並存放於 git 的已記錄指令。如果安裝 FreePBX,請將其網頁埠限制為您自己的 IP 位址,或透過 VPN 存取,因為 PBX 的管理 GUI 可能成為攻擊目標,而它能直接連到金錢相關資源。

版本說明,截至 2026 年 8 月的正確資訊如下:Asterisk 22 是目前的長期支援版本,於 2024 年 10 月發布,並支援安全性修正至 2028 年 10 月。Asterisk 23 是標準版本。Ubuntu 24.04 在 universe repository 中提供 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 ldconfig

install_prereq install 會為您的發行版安裝建置相依套件,install_prereq test 則只列出將執行的命令,不會變更任何內容。make menuselect 會開啟模組選擇器,您可在 Codec Translators 下啟用 codec_opusmake samples 僅應在全新安裝時執行,因為它會將範例設定寫入 /etc/asteriskmake config 會將 init script 安裝至 /etc/init.d/asterisk;systemd 會透過 SysV 相容層執行該 script,因此之後即可使用 sudo systemctl enable --now asterisk

無論採用哪種方式,sudo asterisk -rvvv 都會連接至執行中的 daemon,並提供 CLI。在其中執行 core show version,即可顯示實際安裝的版本。

設定 SIP trunk 與一個分機

PJSIP 是目前 Asterisk 使用的 SIP channel driver。其設定檔位於 /etc/asterisk/pjsip.conf,由彼此以名稱參照的小型具型別 section 組成。不同型別的 section 可以共用相同名稱,因此以下每個區塊都命名為 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.com

registration object 會傳送 REGISTER,告知 provider 應將來話送到哪裡。identify object 會依來源位址,將來自 provider 的通話比對至此 endpoint;provider 通常會公布多個位址,應一併列在其中。Outbound registration 與 endpoint 刻意分成不同 object:前者告知 provider 你的所在位置,後者決定如何處理來話。

桌上型電話還需要另外 3 個 object。

[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=1

direct_media=no 會讓 Asterisk 保留在 media path 中。省略此設定時,Asterisk 會嘗試讓兩個 endpoint 直接互傳 RTP;如果電話位於家用 router 的 NAT (network address translation) 後方,這會失敗。secret 絕對不可與分機號碼相同。上游範例為分機 6001 使用 password=6001,以保持範例易讀;掃描器也會優先嘗試這種模式。

/etc/asterisk/extensions.conf 中的 dialplan 會決定每個 context 可執行的操作。

[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()

這 2 個 context 是安全邊界。from-trunk 處理來自 provider 的通話,只能響鈴分機 6001。它無法符合 _9X. pattern,因此外部來話無法透過你的 trunk 再撥出。合併這些 context 就會建立典型的 toll fraud 路徑:陌生人撥打你的號碼,dialplan 以你的帳戶撥出,而費用由你負擔。

套用設定並進行檢查。

sudo asterisk -rx "pjsip reload"
sudo asterisk -rx "pjsip show registrations"
sudo asterisk -rx "pjsip show endpoints"

pjsip show registrations 應列出 mytrunk,狀態為 RegisteredRejected 表示 provider 拒絕你的認證資訊。Unregistered 表示你的 REGISTER 沒有收到回應,接著檢查 firewall。

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 verbose

RTP 範圍較難進一步縮小,因為媒體通常會從不同於訊號的位址送達。請向供應商索取其媒體子網路;若供應商有公布,請限制為這些子網路。範圍只需涵蓋流量最繁忙時段的需求。預設政策、規則順序及 ufw 的其他運作方式,請參閱 VPS 的 ufw 防火牆基礎指南

也請檢查 IPv6。若 IPV6=no 已在 /etc/default/ufw 中設定,ufw 完全不會篩選 IPv6,因此繫結至 :: 的 daemon 可透過 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 後才會接受。不要建立這個 endpoint。
  • 如上所示,將 trunk 的 context 與任何能撥出電話的 context 分開。
  • 在 ufw 和電信商的網路防火牆中,依來源位址限制 signalling。
  • 向 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 = 86400
sudo systemctl restart fail2ban
sudo fail2ban-client status asterisk

正常結果會列出 jail 的日誌檔案,以及目前遭封鎖位址的數量。對外開放 5060 埠時,該數量通常會在一天內不再是 0。內建 filter 會比對上述 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" 重新載入 logger。該 filter 也包含適用於 asterisk.servicejournalmatch,因此若不想保留日誌檔案,也可以使用 journal backend。安裝、jail.local 結構,以及解除封鎖誤遭封鎖的位址,請參閱Ubuntu 24.04 的 fail2ban 指南

遠端 VPS 的延遲與編解碼器選擇

延遲取決於地理位置,無法透過設定消除。ITU-T G.114 建議將單向延遲維持在 150 ms 以下,並認為最高約 400 ms 仍可使用。手機的音訊會先傳到 VPS,再傳送到 trunk provider,因此 VPS 若位於不適當的區域,就會讓這段往返路徑承受兩次延遲。請將 VPS 放在靠近手機或 provider 的位置;若兩者位置相距甚遠,優先靠近手機,因為這一段通常經過消費者網際網路,jitter 最嚴重。

編解碼器會決定每通電話所需的頻寬。此處每種編解碼器每 20 ms 傳送一個封包,也就是每秒 50 個封包;每個封包除了音訊 payload 外,還包含 40 bytes 的 IP、UDP 與 RTP 標頭。

ChartBandwidth per concurrent call, one direction, 20 ms packets
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 是大多數 trunk 的預設編解碼器。其 payload 為 64 kbps,加上標頭後,進行中的通話在每個方向會使用 80 kbps。Opus 在 24 kbps 時為 40 kbps,而 G.729 可降至 24 kbps,但會犧牲音訊品質並增加 CPU 使用量。這些數值是標頭計算結果,不是實測值:payload 速率加上每秒 50 個封包、每個封包 40 bytes 的標頭。Ethernet 或 VLAN framing 會讓實際線路上的流量再增加一些。

Transcoding 會消耗 CPU。如果手機與 trunk 都使用 ulaw,請只允許 ulaw,Asterisk 就會直接轉送音訊而不進行處理。Opus 在有封包遺失的連線上表現良好,但 Opus 與 G.711 之間的 transcoding 需要外部 codec_opus module。請在 make menuselect 中選取此 module;它預設不會建置。

發生故障時的現象與訊息

通話已連線,但雙方都聽不到聲音。 RTP 未抵達。確認 rtp.conf 中的範圍就是防火牆開放的範圍,然後在撥打電話時使用 sudo tcpdump -ni any udp portrange 10000-10200 監看封包。如果完全沒有封包,表示防火牆或供應商的網路防火牆正在丟棄封包。

只有單向音訊。 其中一方將 RTP 傳送到無法接收的位址。這是位址問題,不是連接埠問題。如果 VPS 的介面直接使用公開位址,則不需要處理 NAT。如果供應商提供給 VPS 私有位址,並透過一對一公開位址映射,請在 transport 上設定公開位址,並將私有範圍列在 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 位址或使用者名稱對應到任何 endpoint。若請求來自掃描器,這是正常情況,fail2ban 會處理。若請求來自您自己的供應商,表示 identify 區段未列出對方呼叫您時使用的位址。

註冊狀態為 Rejected 供應商拒絕了 auth 區段中的認證資訊。在 CLI 執行 pjsip set logger on,監看一個 REGISTER 及其回應,然後將 client_uriusername 與供應商提供的資訊比對。

日誌中完全沒有有用資訊。 Asterisk 預設會將 notice 等級以上的訊息寫入 messages.log。重現問題時,使用 core set verbose 4pjsip set logger on 提高日誌等級;完成後請關閉兩者,因為 SIP logger 會寫入每個封包。

公開服務之前

PBX 與其他自架服務不同。Web 應用程式故障,可能只是無法開啟頁面。PBX 故障則可能在你熟睡時,於數小時內產生高額電話費。請在只執行 PBX 的 VPS 上部署,將 5060 限制為已知來源位址,為每個分機設定隨機 secret,並為 trunk 帳戶設定費用上限。伺服器其餘部分也需要具備與任何公開伺服器相同的基準安全設定,詳見這篇檢視 VPS 託管實際安全性的文章

FAQ

自架 VoIP 伺服器需要開放哪些連接埠?

SIP signalling over UDP 和 TCP 使用 5060;若使用 SIP over TLS,則使用 5061;RTP media 則需要一段 UDP 連接埠範圍。Asterisk 的範例 rtp.conf 使用 10000 至 20000,而編譯時內建的預設值為 5000 至 31000。每通電話會從該範圍使用 2 個連接埠,因此 200 個連接埠可處理 100 通同時通話。將 RTP 範圍開放為 UDP,並將 5060 限制為供應商的位址與自有網路,不要對所有來源開放。

應該單獨安裝 Asterisk,還是使用 FreePBX?

如果需要較小的攻擊面、可存放在 git 中的設定檔,且願意自行撰寫 dialplan,請直接安裝 Asterisk。如果需要管理 extensions、voicemail 與 call queues 的 GUI,請安裝 FreePBX。截至 August 2026,FreePBX 17 installer 預期使用原始的 Debian 12 machine,並會安裝 Asterisk、web server、database server 與 PHP,因此應為它配置專用 VPS。FreePBX 會重新產生它所管理的設定檔,因此手動修改應放在其 _custom 檔案中。

為什麼通話接通後沒有音訊?

signalling 成功,但 media 沒有成功。SIP 在 5060 建立通話,而音訊是傳送至 RTP 範圍內某個連接埠的獨立 UDP flow,該 flow 可能遭到某處丟棄。確認 rtp.conf 中的範圍與防火牆開放的範圍一致,也檢查供應商的 network firewall 以及伺服器上的防火牆。通話期間執行 sudo tcpdump -ni any udp portrange 10000-10200:如果沒有封包,表示封包在抵達前已遭到封鎖。

如何阻擋 SIP brute force attacks 與 toll fraud?

為每個 extension 設定長且隨機的 secret,絕不要使用與 extension number 相同的值。將 trunk 使用的 context 與可撥出電話的任何 context 分開,避免 inbound call 使用你的帳戶再次撥出。將 5060 限制為供應商的位址。在 fail2ban 中啟用 asterisk jail;它會讀取 /var/log/asterisk/messages,並封鎖產生 No matching endpoint found 次失敗的位址。接著請供應商設定 spending cap,並封鎖未使用的 international destinations,因為只有這項控制能在其他防護失效時限制損失上限。

VPS 位於遙遠的 region 會影響通話品質嗎?

會,因為音訊需要經過 2 段路徑:phone 到 VPS,再由 VPS 到 trunk provider。ITU-T G.114 建議單向延遲低於 150 ms,而位置不佳的 VPS 可能僅因距離就耗用大部分的延遲額度。請選擇靠近 phones 的 region,因為該段通常經過 jitter 最嚴重的 consumer internet。Codec choice 無法解決延遲,只會改變頻寬;因此 G.729 可節省 bytes,但無法挽救 200 ms 的 path。

#voip#asterisk#freepbx#sip#self-hosting