الأدلة التقنية

FreePBX: مقسم هاتف PBX كامل تديره من المتصفح

نثبت FreePBX 17 بسكربته الرسمي، ونبني من الواجهة مقسماً يستقبل المكالمات ويجريها عبر خط خارجي، ثم نختبر النسخ الاحتياطي والاستعادة فعلاً.

FreePBX: مقسم هاتف PBX كامل تديره من المتصفح

في دليل Asterisk كتبنا كل شيء بأيدينا في ملفات نصية: التحويلات في pjsip.conf، وخطة الاتصال في extensions.conf، والبريد الصوتي في voicemail.conf. وهذه الطريقة ممتازة لمن يبني منتجاً أو يحفظ إعداده في Git، ولكنها غير مناسبة لمكتب يريد أن يضيف موظف الدعم الفني تحويلة جديدة أو يغير مسار الرقم الرئيسي دون أن يفتح محرر نصوص على السيرفر. وهنا يأتي FreePBX، وهو واجهة ويب مفتوحة المصدر تديرها شركة Sangoma، تكتب ملفات Asterisk نفسها نيابة عنك من نماذج في المتصفح.

المواضيع في السلسلة:

وفي هذا المقال سوف نثبت FreePBX 17 بالطريقة الرسمية، ثم نبني من الواجهة مقسماً كاملاً ونختبر كل جزء منه بمكالمات حقيقية، وسوف نتناول ما يلي:

  • كيف يعمل FreePBX فوق Asterisk، وما الذي يكتبه وما الذي يجب ألا تلمسه.
  • الإصدارات ونظام التشغيل المدعوم رسمياً، ولماذا لا توجد طريقة Docker رسمية.
  • التثبيت بالسكربت الرسمي، وأول دخول، ومعالج جدار الحماية Firewall.
  • التحويلات Extensions، والخط الخارجي SIP Trunk، والمسار الصادر Outbound Route، ومجموعة الرنين Ring Group، والمسار الوارد Inbound Route.
  • النسخ الاحتياطي Backup والاستعادة Restore، وما حدث عندنا فعلاً عند الاستعادة.
  • ترخيص الوحدات Modules، والوحدات التجارية Commercial Modules، وثغرة 2025 التي استغلها المهاجمون في إحداها.

كيف يعمل FreePBX فوق Asterisk

FreePBX ليس مقسماً مستقلاً، وإنما تطبيق PHP يحفظ إعدادك في قاعدة بيانات MariaDB، وعندما تضغط الزر الأحمر Apply Config يقرأ القاعدة ويكتب منها ملفات Asterisk ثم يطلب منه إعادة التحميل، والصورة التالية تبين ذلك:

متصفح المدير يتصل بسيرفر Debian 12 فيه Apache وPHP 8.2 وFreePBX 17، والضغط على Submit يحفظ الإعداد في قاعدة MariaDB، ثم Apply Config أو fwconsole reload يقرأ القاعدة ويكتب ملفات Asterisk التي تنتهي ب _additional.conf ويعيد تحميل Asterisk 22، وهناك ملفات يكتبها FreePBX فلا تعدلها مثل extensions_additional.conf وpjsip.endpoint.conf، وملفات لك تبقى بعد إعادة التحميل مثل extensions_custom.conf وpjsip.endpoint_custom.conf، والهواتف والخطوط تتكلم مع Asterisk فقط
الواجهة تكتب الإعداد، وAsterisk ينفذه

ولكي ترى الفرق بين الطريقتين بالأرقام، فقد اتصلت Alice ب Bob في المقسم الذي سنبنيه في هذا المقال، فنفذ Asterisk لهذه المكالمة الداخلية الواحدة 260 خطوة في خطة الاتصال، بين التحقق من الاسم المعروض والبريد الصوتي والتحويل عند الانشغال وغيرها، أما في دليل Asterisk فكانت المكالمة نفسها أربع خطوات. والملف الذي كتبه FreePBX لخطة الاتصال في تجربتنا:

wc -l /etc/asterisk/extensions_additional.conf /etc/asterisk/extensions.conf
head -3 /etc/asterisk/extensions_additional.conf

والمخرج سوف يكون كما يلي:

  5266 /etc/asterisk/extensions_additional.conf
   378 /etc/asterisk/extensions.conf
  5644 total
;--------------------------------------------------------------------------------;
;          Do NOT edit this file as it is auto-generated by FreePBX.             ;
;--------------------------------------------------------------------------------;

وهذا هو الثمن والفائدة في الوقت نفسه: تحصل على عشرات المزايا جاهزة دون أن تكتبها، ولكن تشخيص الأعطال يحتاج أن تعرف Asterisk، لذلك اقرأ دليل Asterisk العملي قبل هذا المقال إذا لم تفعل. والقاعدة الأهم هنا: لا تقم بتعديل أي ملف ينتهي ب _additional.conf أو الملفات الأخرى التي يكتبها FreePBX، والسبب أن تعديلك يضيع عند أول ضغطة على Apply Config، وضع إعدادك اليدوي في الملفات التي تنتهي ب _custom.conf، فهي تبقى كما هي.

الإصدارات ونظام التشغيل المدعوم

بحسب جدول إصدارات FreePBX في قاعدة معرفة Sangoma، وآخر تحديث له 25 أغسطس 2026، هذه هي الحالة اليوم:

الإصدارنظام التشغيلصدرإصلاحات الأمان فقطنهاية الدعم EOLالحالة
FreePBX 16SNG72021-10-312026-09-012027-01-01مدعوم، ونظامه SNG7 انتهى دعمه
FreePBX 17Debian 122024-08-022028-02-142028-06-30الإصدار الحالي
FreePBX 18Debian 132027-01-01 (مخطط)2030-02-142030-07-01لم يصدر بعد

والجدول نفسه يبين أن FreePBX 17 يعمل مع Asterisk من 18 إلى 23، والفرع المستهدف هو 22 LTS، وهو ما ثبته السكربت عندنا. أما نظام التشغيل فالمدعوم رسمياً هو Debian 12 فقط، كما في صفحة تثبيت FreePBX 17 التي تطلب سيرفراً أو جهازاً افتراضياً VM عليه Debian 12، والسكربت نفسه يرفض أي نظام آخر برسالة صريحة، بل ويمنع ترقية النظام إلى Debian 13 لأن FreePBX 17 لا يدعمه.

وقد يتساءل البعض: أين Docker Image الرسمية؟ والإجابة أنه لا توجد، فلا الصفحة الرسمية ولا مستودع السكربت يذكران Docker، والسبب أن FreePBX يدير النظام نفسه: جدار الحماية بقواعد iptables، وfail2ban، وخدمات systemd، والتحديثات من مستودع Sangoma، وكلها تفترض أنه يملك السيرفر كاملاً. وما تجده على Docker Hub باسم FreePBX ليس جزءاً من طريقة التثبيت التي تدعمها Sangoma لFreePBX 17، لذلك لا ننصح به لمقسم حقيقي. والطريقة التي نوصي بها هي جهاز افتراضي مخصص عليه Debian 12، أما في هذا المقال فقد شغلنا السكربت الرسمي داخل Container يعمل فيه systemd كتجربة فقط، لأن البيئة نفسها هي Debian 12، ونذكر في مكانها كل ما اختلف بسبب ذلك.

المتطلبات

  • جهاز افتراضي أو سيرفر عليه Debian 12 نظيف، والصفحة الرسمية لا تحدد موارد، وما يكفي لمكتب صغير عادة معالجان وذاكرة 4 GB، مع مساحة للتسجيلات والنسخ الاحتياطية.
  • اتصال بالإنترنت أثناء التثبيت، فالسكربت ينزل الحزم من مستودع Debian ومستودع Sangoma.
  • عنوان IP ثابت داخل الشبكة، واسم نطاق إذا كانت الواجهة ستفتح بشهادة TLS.
  • منافذ SIP 5060/UDP وRTP من 10000 إلى 20000/UDP كما في إعداد FreePBX الافتراضي، ومنفذ الويب للمديرين فقط، كما سنشرح.

التثبيت بالسكربت الرسمي

التثبيت الرسمي سكربت واحد من مستودع sng_freepbx_debian_install، يضيف مستودع Sangoma ويثبت Asterisk وPHP وMariaDB وApache وFreePBX ووحداته. وهذه هي الأوامر كما في الصفحة الرسمية، تنفذها بالمستخدم root:

cd /tmp
wget https://github.com/FreePBX/sng_freepbx_debian_install/raw/master/sng_freepbx_debian_install.sh -O /tmp/sng_freepbx_debian_install.sh
sha256sum /tmp/sng_freepbx_debian_install.sh
grep -m1 SCRIPTVER= /tmp/sng_freepbx_debian_install.sh
bash /tmp/sng_freepbx_debian_install.sh

والمخرج عندنا لسطري التحقق كان كما يلي:

afef5e4b480cf545b2035f92068dc2fdd32452989d170a16a84acb6e37b7d564  /tmp/sng_freepbx_debian_install.sh
SCRIPTVER="1.15"

ولاحظ أن الرابط يشير إلى الفرع master، أي أن السكربت قد يتغير بين يوم وآخر، لذلك اقرأه قبل تشغيله واحفظ البصمة التي شغلتها في توثيقك، والإصدار الذي اختبرناه هو 1.15 من آخر commit في المستودع عند كتابة المقال. والسكربت يقبل خيارات مثل --noasterisk إذا أردت تثبيت Asterisk بنفسك، و--dahdi لكروت Sangoma الهاتفية، ويكتب سجله في /var/log/pbx/freepbx17-install-<التاريخ>.log. والتثبيت بطيء، فهو يثبت الحزم واحدة واحدة ثم ينزل الوحدات، وآخر ما طبعه عندنا:

2026-10-20 08:57:49 - Asterisk 22.11.0
2026-10-20 08:57:49 - Total script Execution Time: 4707
...
| This machine is not activated.  Activating your system ensures that |
| your machine is eligible for support and that it has the ability to |
| install Commercial Modules.                                         |

أي نحو 78 دقيقة على جهازنا، والصفحة الرسمية تقول «30 دقيقة أو أكثر» بحسب الجهاز وسرعة الإنترنت. والتفعيل Activation الذي تذكره الرسالة اختياري، ولا تحتاجه إلا إذا اشتريت وحدات تجارية أو دعماً مدفوعاً، وقد تركناه. بعد ذلك نتأكد من الإصدارات:

fwconsole -V; asterisk -V; cat /etc/debian_version; php -v | head -1
FW Console - FreePBX Utility 17.0.33.2
Asterisk 22.11.0
12.15
PHP 8.2.34 (cli) (built: Oct  4 2026 14:03:09) (NTS)
⚠️
في Container التجربة فشلت خدمة fail2ban بالخطأ Have not found any log file for ssh-iptables jail، والسبب أنه لا يوجد سيرفر SSH داخل ال Container، وعلى جهاز افتراضي حقيقي يوجد SSH وسجله فلا يظهر هذا الخطأ. وهذا مثال على ما يختلف عندما تشغل FreePBX في بيئة لم يصمم لها، لذلك تأكد بعد التثبيت أن كل الخدمات في لوحة Dashboard خضراء.

أول دخول

افتح عنوان السيرفر في المتصفح، فتظهر صفحة الإعداد الأول Initial Setup، وفيها تنشئ حساب المدير وتحدد البريد الذي تصله تنبيهات النظام، وطريقة تحديث الوحدات:

صفحة Initial Setup في FreePBX فيها اسم المستخدم admin وكلمة المرور مخفية والبريد admin@example.com واسم النظام ArabRoot Lab PBX، وخيارات التحديث التلقائي للوحدات وتحديثات الأمان مفعلة، وموعد التحقق من التحديثات
حساب المدير وسياسة التحديث في صفحة واحدة

واترك تحديثات الأمان التلقائية Automatic Module Security Updates مفعلة، والسبب أن ثغرات FreePBX تستغل بسرعة بعد نشرها كما سنرى في قسم الوحدات التجارية. وبعد الدخول عرض FreePBX علينا شاشة التفعيل ثم خمس شاشات إعلانية لمنتجات Sangoma، وكلها تتخطاها بزر Skip، ثم شاشة اختيار اللغة. ولاحظ أن قائمة لغات رسائل الصوت Sound Prompts Language في هذا الإصدار لا تحتوي العربية، فرسائلك العربية تسجلها بنفسك وترفعها من صفحة System Recordings.

بعد ذلك يبدأ معالج جدار الحماية Sangoma Smart Firewall، وهو مفتوح المصدر بترخيص AGPLv3+، ويسألك أربعة أسئلة:

معالج Sangoma Smart Firewall يسأل هل يوثق بالجهاز الذي تستخدمه الآن بعنوان 172.17.0.1/32، مع زر Yes وNo وAbort
الجواب الأول يمنع أن تغلق الباب على نفسك
  • هل يوثق بالجهاز الذي تستخدمه الآن؟ أجب نعم، وإلا قد تغلق الواجهة على نفسك.
  • هل يوثق بالشبكة التي أنت عليها؟ نعم فقط إذا كانت شبكة المكتب الخاصة، ولا تقم بذلك إذا كنت تدير السيرفر من شبكة عامة.
  • هل يفعل الجدار المتجاوب Responsive Firewall؟ هذا الخيار يسمح للهواتف البعيدة بالتسجيل دون أن تضيف عناوينها، ويحظر من يكرر محاولات التسجيل الفاشلة.
  • هل يكتشف عنوانك العام تلقائياً؟ وهذا السؤال يحتاج انتباهاً، كما سنرى في قسم الخط الخارجي.

بعد المعالج تظهر لوحة Dashboard، وفيها حالة الخدمات والتنبيهات:

لوحة FreePBX Dashboard فيها FreePBX 17.0.33.2 باسم ArabRoot Lab PBX، وقائمة الخدمات Asterisk وMySQL وWeb Server بعلامة خضراء وFail2Ban بعلامة حمراء، وتنبيهات منها تحويل عنوان سيرفر HTTP المصغر في Asterisk إلى localhost، ورسوم الإحصاءات واستخدام الشبكة
Fail2Ban باللون الأحمر بسبب بيئة ال Container كما ذكرنا

ومن التنبيهات المفيدة هنا «Updated Asterisk Mini HTTP Server Bind Address to Localhost»، أي أن FreePBX جعل سيرفر HTTP في Asterisk، وهو الذي يقدم ARI، يستمع على 127.0.0.1 فقط، وهذا عكس ما فعلناه في تجربة دليل Asterisk، وهو الإعداد الصحيح للإنتاج.

خريطة التجربة

لكي نختبر المكالمات الخارجية دون خط حقيقي، شغلنا بجانب FreePBX مقسم Asterisk 22.11.0 آخر من دليل Asterisk يلعب دور مزود الخدمة Carrier، فيقبل تسجيل الخط ويرد على الأرقام التي تبدأ ب 05 برسالة صوتية، ويمكنه أن يرسل مكالمة إلى الرقم المباشر DID الخاص بنا. والكل على شبكة Docker داخلية 10.89.0.0/24: FreePBX على 10.89.0.10، والمزود على 10.89.0.50، وهاتفا baresip للتحويلتين 1001 و1002. وقد أضفنا هذه الشبكة إلى المنطقة الموثوقة في جدار الحماية بالأمر:

fwconsole firewall trust 10.89.0.0/24
Attempting to add '10.89.0.0/24' to Zone 'trusted' ... Success!

والأرقام في التجربة: الرقم الرئيسي للشركة 966112345678، ومجموعة الرنين 600، وأرقام الجوال التي نتصل بها بنمط 05XXXXXXXX. والصورة التالية تبين الطريق الذي سنبنيه:

المزود يرسل المكالمة عبر SIP Trunk إلى المسار الوارد الذي يطابق الرقم 966112345678، فيرسلها إلى مجموعة الرنين 600 التي ترن على 1001 و1002 معاً عشرين ثانية ثم تذهب إلى البريد الصوتي 1001، وفي الاتجاه الآخر 1001 تتصل بالرقم 0501234567 فيطابق المسار الصادر النمط 05XXXXXXXX ويضع رقم المتصل 966112345678 ويخرج عبر الخط إلى المزود، وتحتها أن الرقم غير المعروف يسمع رسالة ss-noservice والرقم الذي لا يطابق مساراً صادراً يسمع cannot complete as dialed
الرقم المباشر يختار المسار الوارد، ونمط الاتصال يختار المسار الصادر

التحويلات Extensions

من القائمة Connectivity ثم Extensions، اختر Add Extension ثم Add New SIP [chan_pjsip] Extension. وفي التبويب General تكتب رقم التحويلة والاسم الذي يظهر:

نموذج Add PJSIP Extension في التبويب General فيه User Extension 1001 وDisplay Name Alice، وحقل Secret مملوء بكلمة مرور مولدة ومخفية، وإعدادات User Manager لإنشاء مستخدم جديد بكلمة مرور مولدة
كلمة مرور التحويلة Secret يولدها FreePBX عشوائية ويخفيها

ولاحظ حقل Secret، فقد ولد فيه FreePBX كلمة مرور عشوائية من 32 حرفاً، فلا تقم باستبدالها بكلمة سهلة لأن الموظف «لا يستطيع كتابتها في الهاتف»، والسبب أن كلمة المرور هذه تكتب مرة واحدة في الهاتف أو تصل إليه بالتهيئة الآلية Provisioning، والتحويلة ذات كلمة المرور الضعيفة هي أول ما يجربه من يفحص المنفذ 5060. وفي التبويب Voicemail نفعل البريد الصوتي ونضع الرقم السري:

التبويب Voicemail في نموذج التحويلة 1001، وفيه Enabled على Yes وVoicemail Password مخفية وEmail Address alice@example.com وخيارات المرفق والقراءة والحذف
هذا التبويب يكتب السطر نفسه الذي كتبناه بأيدينا في voicemail.conf

بعد Submit كررنا الخطوات للتحويلة 1002 باسم Bob، ثم ضغطنا Apply Config، فظهرت التحويلتان في القائمة:

قائمة All Extensions فيها 1001 Alice و1002 Bob من نوع pjsip، مع أعمدة انتظار المكالمات CW وعدم الإزعاج DND والتحويل
التحويلتان بعد Apply Config

ولكي ترى ماذا كتب FreePBX، انظر إلى قسم التحويلة 1001 في /etc/asterisk/pjsip.endpoint.conf، وهذا جزء منه:

[1001]
type=endpoint
aors=1001
auth=1001-auth
allow=ulaw,alaw,gsm,g726,g722
context=from-internal
callerid=Alice <1001>
dtmf_mode=rfc4733
direct_media=yes
mailboxes=1001@default

هذه هي الأقسام نفسها التي كتبناها في دليل Asterisk، مع فرقين يستحقان الانتباه: السياق from-internal هو الذي يحدد ما يحق للتحويلة الاتصال به، وdirect_media=yes يجعل الصوت يمر مباشرة بين الهاتفين إذا استطاعا، وهذا يقلل الحمل على المقسم، ولكنه يسبب صوتاً في اتجاه واحد إذا كان الهاتفان في شبكتين لا تصل إحداهما للأخرى، كما شرحنا في مقال تتبع المكالمة، وتغييره من التبويب Advanced في التحويلة.

الخط الخارجي SIP Trunk

من Connectivity ثم Trunks، اختر Add SIP (chan_pjsip) Trunk. في التبويب General نكتب اسم الخط carrier، والرقم الذي يظهر للطرف الآخر Outbound CallerID وهو 966112345678، وعدد القنوات الأقصى Maximum Channels، وهو عدد المكالمات المتزامنة التي يسمح بها اشتراكك. ثم في التبويب pjsip Settings نكتب بيانات الحساب كما أعطاها المزود:

التبويب pjsip Settings في نموذج الخط فيه Username arabroot-trunk وSecret وAuthentication على Outbound وRegistration على Send وSIP Server 10.89.0.50 والمنفذ 5060 وContext from-pstn
التسجيل Registration يرسله FreePBX إلى المزود بالاسم وكلمة المرور

وأغلب المزودين يعملون بإحدى طريقتين: التسجيل باسم مستخدم وكلمة مرور، كما في تجربتنا، أو التعرف على المقسم بعنوان IP ثابت دون تسجيل، وفي الثانية تختار Registration على None وتعطي المزود عنوانك العام. وقد شرحنا الفرق في مقال ربط المقسم بشبكة الهاتف ضمن هذه السلسلة.

بعد الحفظ وApply Config سألنا Asterisk عن التسجيل:

asterisk -rx 'pjsip show registrations'
 carrier/sip:10.89.0.50:5060                             carrier                     Rejected          (exp. 41s)

رفض المزود التسجيل، وفي سجل المزود كان السبب:

NOTICE[30]: res_pjsip/pjsip_distributor.c:722 log_failed_request: Request 'OPTIONS' from '<sip:[email protected]>' failed for '10.89.0.10:5060' (callid: 84f52de4-5bfa-437a-85c2-ef06564cf5e1) - No matching endpoint found

ولاحظ sip:carrier@...، فقد أرسل FreePBX اسم الخط carrier في الترويسة وليس اسم المستخدم الذي أعطاه المزود، والمزود يبحث عن الحساب بهذا الاسم فلا يجده. والحل في التبويب Advanced داخل pjsip Settings، بحقلين:

التبويب Advanced في إعدادات الخط فيه Contact User arabroot-trunk وFrom User فارغ وClient URI sip:arabroot-trunk@10.89.0.50:5060، مع خيارات Send RPID/PAI وTrust RPID/PAI
Client URI وContact User لتسجيل الخط، واترك From User فارغاً
  • Client URI: العنوان الذي يسجل به الخط، ووضعنا فيه sip:[email protected]:5060.
  • Contact User: اسم المستخدم في العنوان الذي يرسل إليه المزود المكالمات الواردة.

وفي أول محاولة وضعنا اسم المستخدم أيضاً في From User، فنجح التسجيل، ولكن عندما اتصلت Alice برقم جوال وصل إلى المزود رقم المتصل بهذا الشكل:

    -- Executing [0501234567@from-customer:1] NoOp("PJSIP/arabroot-trunk-00000000", "Carrier: call to 0501234567 from "" <arabroot-trunk>") in new stack

أي أن رقم الشركة ضاع واستبدل باسم الحساب، لأن From User يفرض ما يكتب في الترويسة التي يأخذ منها المزود رقم المتصل. وعندما أفرغنا الحقل ظهر الرقم الصحيح:

    -- Executing [0501234567@from-customer:1] NoOp("PJSIP/arabroot-trunk-00000001", "Carrier: call to 0501234567 from "" <966112345678>") in new stack

لذلك لا تقم بتعبئة From User إلا إذا طلبه المزود صراحة، والسبب أن بعض المزودين يرفض المكالمة أو يظهر رقماً خاطئاً للعميل إذا لم يكن رقم المتصل من أرقامك.

العنوان العام وشبكاتك المحلية

بعد أن نجح التسجيل كان عنوان الاتصال الذي حفظه المزود هو العنوان العام لجهازنا وليس 10.89.0.10، والسبب جواب «نعم» على السؤال الأخير في معالج الجدار، فقد اكتشف FreePBX العنوان العام وكتبه في إعدادات NAT، وجعل الشبكة المحلية Local Networks شبكة واحدة فقط هي شبكة Docker الافتراضية. وهذا يعني أن Asterisk سيكتب العنوان العام في SDP لكل جهاز خارج تلك الشبكة، بما فيها هواتف المكتب، وهي الوصفة الكاملة للصوت في اتجاه واحد. والإصلاح في Settings ثم Asterisk SIP Settings:

قسم NAT Settings في Asterisk SIP Settings فيه External Address 203.0.113.10 وزر Detect Network Settings، وLocal Networks فيها 172.17.0.0/16 و10.89.0.0/24 وزر Add Local Network Field
كل شبكة فيها هواتفك يجب أن تكون في Local Networks

استبدلنا العنوان العام بعنوان توثيق للمقال، وأضفنا شبكة الهواتف، وبعد Apply Config أصبح الملف الذي يكتبه FreePBX كما يلي:

grep -E "external|local_net" /etc/asterisk/pjsip.transports.conf
external_media_address=203.0.113.10
external_signaling_address=203.0.113.10
local_net=172.17.0.0/16
local_net=10.89.0.0/24

وهي الخيارات نفسها التي ذكرناها في آخر مقال تتبع المكالمة للمقسم الذي يقف خلف NAT، والقاعدة: كل شبكة خاصة فيها هواتف أو مقسمات أخرى يجب أن تكون في Local Networks، وكل ما عداها يرى العنوان الخارجي.

المسار الصادر Outbound Route

الخط وحده لا يكفي، وهذا ما قاله FreePBX نفسه في صفحة الخط: «WARNING: This trunk is not used by any routes!». فالمسار الصادر هو الذي يقرر أي الأرقام تخرج من أي خط. من Connectivity ثم Outbound Routes ثم Add Outbound Route، نكتب الاسم ورقم المتصل للمسار، ونختار الخط في Trunk Sequence:

نموذج Outbound Routes في التبويب Route Settings فيه Route Name mobile وRoute CID 966112345678 وTrunk Sequence for Matched Routes فيه الخط carrier
ترتيب الخطوط Trunk Sequence يجعل الخط الثاني احتياطياً للأول

ثم في التبويب Dial Patterns نكتب النمط، وطريقة كتابته هي نفسها أنماط Asterisk، ف X أي رقم:

التبويب Dial Patterns فيه حقول prepend وprefix والنمط 05XXXXXXXX وCallerID
النمط 05XXXXXXXX يطابق أرقام الجوال من عشرة أرقام

والحقلان prepend وprefix لتعديل الرقم قبل خروجه، مثل حذف صفر أو إضافة رمز الدولة إذا طلب المزود الرقم بصيغة دولية. اتصلت Alice بالرقم 0501234567، وهذا ما وصل إلى المزود:

    -- Executing [0501234567@from-customer:1] NoOp("PJSIP/arabroot-trunk-00000001", "Carrier: call to 0501234567 from "" <966112345678>") in new stack
    -- Executing [0501234567@from-customer:2] Answer("PJSIP/arabroot-trunk-00000001", "") in new stack
    -- Executing [0501234567@from-customer:3] Playback("PJSIP/arabroot-trunk-00000001", "tt-monkeys") in new stack

ثم اتصلت برقم دولي لا يطابق أي مسار، وهذا ما ظهر في سجل FreePBX:

Executing [00441234567@from-internal:5] Playback("PJSIP/1001-00000006", "silence/1&cannot-complete-as-dialed&check-number-dial-again,noanswer") in new stack
Executing [00441234567@from-internal:7] Congestion("PJSIP/1001-00000006", "20") in new stack

المتصل يسمع «لا يمكن إتمام المكالمة كما طلبت»، وهذا هو السلوك الآمن: ما لم تسمح به صراحة لا يخرج. لذلك لا تقم بإنشاء مسار بنمط عام مثل X. يخرج أي رقم إلى أي مكان، والسبب أن التحويلة المخترقة عندها تتصل بأرقام دولية مكلفة على حسابك، وهو ما يسمى الاحتيال على المكالمات Toll Fraud، وسوف نخصص له مقال تأمين المقسم. واجعل المكالمات الدولية في مسار منفصل بكلمة مرور Route Password أو لتحويلات محددة.

مجموعة الرنين Ring Group

الرقم الرئيسي للشركة لا يرن عادة على موظف واحد، وإنما على مجموعة. من Applications ثم Ring Groups ثم Add Ring Group:

نموذج Ring Groups Add فيه Ring-Group Number 600 وGroup Description Reception وExtension List فيها 1001 و1002 وRing Strategy ringall وRing Time 20
ringall يرن على الجميع معاً، وأول من يرد يأخذ المكالمة

واستراتيجية الرنين Ring Strategy لها خيارات أخرى، مثل hunt التي ترن على التحويلات واحدة بعد الأخرى، وmemoryhunt التي تضيف تحويلة في كل دورة. وفي أسفل النموذج، في Destination if no answer، اخترنا البريد الصوتي للتحويلة 1001 برسالة «غير متاح»، فالمتصل الذي لا يرد عليه أحد خلال 20 ثانية يترك رسالة بدل أن يسمع الرنين إلى الأبد.

المسار الوارد Inbound Route

المسار الوارد يقرر أين تذهب المكالمة القادمة من الخط بحسب الرقم المطلوب DID. من Connectivity ثم Inbound Routes ثم Add Inbound Route:

نموذج Add Incoming Route فيه Description Main number وDID Number 966112345678 وCallerID Number ANY وSet Destination على Ring Groups ثم 600 Reception
الرقم المباشر 966112345678 يذهب إلى مجموعة الرنين 600

ولاحظ أن الرقم يجب أن يكتب بالصيغة نفسها التي يرسلها المزود بالضبط، فإذا أرسل +966112345678 أو 0112345678 فلن يطابق المسار. بعد ذلك أرسل المزود مكالمة إلى رقمنا من سطر أوامره:

asterisk -rx 'channel originate PJSIP/966112345678@arabroot-trunk application Playback tt-monkeys'

وهذا ما ظهر في سجل FreePBX، بعد الاختصار:

Executing [966112345678@from-pstn:1] Set("PJSIP/carrier-00000007", "__DIRECTION=INBOUND") in new stack
Executing [966112345678@from-pstn:4] Set("PJSIP/carrier-00000007", "__FROM_DID=966112345678") in new stack
Executing [966112345678@from-pstn:25] Goto("PJSIP/carrier-00000007", "ext-group,600,1") in new stack
Executing [600@ext-group:2] Set("PJSIP/carrier-00000007", "__MCGROUP=600") in new stack
PJSIP/1002-00000008 is ringing
PJSIP/1002-00000008 answered PJSIP/carrier-00000007

المكالمة دخلت من السياق from-pstn الذي حددناه في الخط، ثم إلى مجموعة الرنين، ورد Bob. ثم أوقفنا هاتف Bob وأعدنا المكالمة، فلم يرد أحد خلال 20 ثانية:

Executing [s-NOANSWER@macro-vm:2] VoiceMail("PJSIP/carrier-0000000a", "1001@default,u") in new stack
-rw-r--r-- 1 asterisk asterisk 826284 Oct 20 12:24 msg0000.wav

وأخيراً أرسل المزود مكالمة إلى رقم لا يوجد له مسار وارد، 966119999999:

Executing [966119999999@from-pstn:2] NoOp("PJSIP/carrier-00000009", "Received an unknown call with DID set to 966119999999") in new stack
Executing [s@from-pstn:5] Playback("PJSIP/carrier-00000009", "ss-noservice") in new stack

المتصل يسمع أن الرقم غير موجود في الخدمة، ثم يقرأ له المقسم الرقم. وهذه الرسالة في السجل هي أول ما تبحث عنه عندما يقول لك أحد «اتصلت بالرقم الجديد ولم يرن أي هاتف»، فغالباً المزود يرسل الرقم بصيغة غير التي كتبتها في المسار.

التحقق من النجاح

  • لوحة Dashboard كل خدماتها خضراء، والأمر fwconsole ma list كل وحداته Enabled.
  • asterisk -rx 'pjsip show registrations' يظهر الخط Registered، وpjsip show contacts يظهر الهواتف.
  • مكالمة داخلية، ومكالمة خارجية يصل فيها رقم الشركة إلى الطرف الآخر، ومكالمة واردة ترن على مجموعة الرنين.
  • مكالمة واردة دون رد تنتهي برسالة في البريد الصوتي.
  • رقم لا يطابق أي مسار صادر لا يخرج.

النسخ الاحتياطي والاستعادة

وحدة Backup & Restore مفتوحة المصدر بترخيص GPLv3+، ومن Admin ثم Backup & Restore ثم Add Backup تنشئ مهمة نسخ:

نموذج Add Backup فيه Backup Name nightly وBackup Description All modules kept 7 runs وBackup Items بعدد 96 وحدة وCustom Files فيها مجلد __ASTETCDIR__
النسخة تشمل بيانات كل الوحدات ومجلد إعداد Asterisk

اختر في Storage Location مكان الحفظ، وأقله المكان المحلي Local، ثم أضف مكاناً خارج السيرفر مثل SFTP أو S3، وفعل الجدولة Schedule، وحدد عدد النسخ التي تبقى. ويمكنك تشغيل النسخة من سطر الأوامر، وهو أنسب للمراقبة:

fwconsole backup --list
fwconsole backup --backup=6d096676-b9da-4399-88a8-0a17efc17415
ls -lh /var/spool/asterisk/backup/

والمخرج سوف يكون كما يلي، بعد الاختصار:

| nightly     | All modules, kept 7 runs | 6d096676-b9da-4399-88a8-0a17efc17415 |
Finished created backup file: 20261020-122904-1792499344-17.0.33.2-784229356.tar.gz
	Saving to: Local:'Local backup storage' instance ,File location: /var/spool/asterisk/backup/20261020-122904-1792499344-17.0.33.2-784229356.tar.gz
There were warnings during the backup process
-rw-r--r-- 1 asterisk root  69M Oct 20 12:29 20261020-122904-1792499344-17.0.33.2-784229356.tar.gz

النسخة استغرقت نحو 12 ثانية، وحجمها 69 MB لمقسم فيه تحويلتان فقط، وعند فتحها وجدنا أن أكبر ما فيها ملفات موسيقى الانتظار، وملف بيانات وحدة Endpoint Manager التجارية بحجم 30 MB، وهي مثبتة رغم أننا لم نشترها. أما التحذيرات فكانت من وحدات لا بيانات لها، ومن وحدة تجارية تخطاها النظام لأنه غير مفعل.

والآن الجزء الذي يتجاهله أغلب الناس: هل تعمل الاستعادة فعلاً؟ حذفنا التحويلة 1002 من الواجهة، ثم استعدنا النسخة:

fwconsole backup --restore=/var/spool/asterisk/backup/20261020-122904-1792499344-17.0.33.2-784229356.tar.gz

وهذا ما حدث بالترتيب:

  1. استغرقت الاستعادة نحو 20 دقيقة على جهازنا، من 12:31 إلى 12:50 بحسب /var/log/asterisk/backup.log، وأغلب الوقت في بيانات وحدة Endpoint Manager، لذلك لا تتوقع أن تعود الخدمة بعد دقيقة من بدء الاستعادة.
  2. عادت التحويلة 1002 بكلمة مرورها نفسها، وسجل هاتف Bob دون أن نغير فيه شيئاً.
  3. بعد الاستعادة لم تعد الواجهة تفتح من جهازنا، فقد أعيد بناء قواعد جدار الحماية دون الشبكات الموثوقة، وعادت بالأمر fwconsole firewall restart من سطر الأوامر.
  4. صفحة الخط أظهرت الخطأ Unable to locate the FreePBX BMO Class 'Sipsettings'، وfwconsole ma list أظهر الوحدة sipsettings بحالة Not Installed (Locally available).

وفي سجل الاستعادة كان السبب:

[2026-10-20 12:31:14] [8db2e0eb-1edd-46e0-893f-0a6106dc837c.INFO]: Resetting sipsettings module data [] []
[2026-10-20 12:32:15] [8db2e0eb-1edd-46e0-893f-0a6106dc837c.ERROR]: The process "'fwconsole' 'extip'" exceeded the timeout of 60 seconds.

أي أن الاستعادة حاولت اكتشاف العنوان العام عبر الإنترنت فانتهت المهلة، فتوقفت استعادة هذه الوحدة. وأعدنا تثبيتها بالأمر fwconsole ma install sipsettings، ولكن إعدادات NAT التي تحملها، أي العنوان الخارجي والشبكات المحلية، كانت قد ضاعت، فأعدناها يدوياً من الواجهة. وقد يكون جزء من هذا بسبب بيئة ال Container، ولكن الدرس واحد في كل بيئة: النسخة الاحتياطية التي لم تجرب استعادتها مجرد ملف، لذلك جرب الاستعادة على جهاز افتراضي منفصل مرة كل فترة، وبعدها تحقق من fwconsole ma list ومن إعدادات Asterisk SIP Settings ومن تسجيل الخط ومن مكالمة في كل اتجاه. وقد شرحنا طريقة تنظيم ذلك في النسخ الاحتياطي بقاعدة 3-2-1 واختبار الاستعادة.

ترخيص الوحدات والوحدات التجارية

FreePBX نفسه مرخص بترخيص GPL كما يكتب في أسفل كل صفحة، ولكنه في الحقيقة إطار Framework ووحدات Modules، ولكل وحدة ترخيصها. والأمر fwconsole ma list يعرض العمود License، وعندما عددنا الوحدات التي ثبتها السكربت كانت النتيجة كما يلي:

الترخيصعدد الوحدات
GPLv3+53
AGPLv3+ وAGPLv315
GPLv2+ وGPLv29
Commercial وCommercial+35
بلا ترخيص مكتوب1
المجموع113

أي أن ما يقارب ثلث الوحدات تجارية، وكلها مثبتة ومفعلة Enabled بعد التثبيت دون شراء، وكل منها بجانبه زر Buy في صفحة Module Admin:

صفحة Module Updates في FreePBX فيها قائمة الوحدات مع الإصدار والناشر Sangoma Technologies والترخيص والحالة، ووحدات مثل Advanced Recovery وCall Accounting وClass of Service وPBX MFA وPhone Apps وSangomaConnect وSystem Admin ترخيصها Commercial وبجانبها زر Buy، ووحدات مثل Backup وAsterisk CLI وCertificate Manager ترخيصها GPLv3+ أو AGPLv3+
الوحدات التجارية مثبتة مسبقاً، وبجانب كل منها زر Buy

والوحدات التجارية مشفرة بأداة ionCube، ولهذا ثبت السكربت الحزمة ioncube-loader-82، ولا تعمل مزاياها المدفوعة إلا بعد التفعيل والشراء من متجر FreePBX. ومن أشهرها Endpoint Manager لتهيئة الهواتف آلياً، وClass of Service لتحديد صلاحيات الاتصال لكل مجموعة، وSystem Admin، وPBX MFA للتحقق بخطوتين، وتقارير الطوابير والتسجيلات. أما الأسعار فلم نستطع التحقق منها عند كتابة المقال لأن متجر freepbx.org رفض طلبات أدوات التحقق الآلي، فراجعها في المتجر نفسه عند الشراء، ولاحظ أن الوحدات تباع بتراخيص لمدة محددة، والتحديثات بعد انتهائها تحتاج تجديداً.

وقد يتساءل البعض: إذا كانت الوحدة التجارية لا تعمل دون شراء، فما الضرر من بقائها مثبتة؟ والإجابة في هذه القصة:

💡
في أغسطس 2025 نشرت Sangoma النشرة GHSA-m42g-xg4c-5f3h عن ثغرة CVE-2025-57819 في وحدة Endpoint التجارية، تسمح لأي شخص يصل إلى صفحة الإدارة بتجاوز تسجيل الدخول وحقن أوامر SQL ثم تنفيذ أوامر على السيرفر، بتقييم 10 من 10. وقالت النشرة إن الاستغلال بدأ في 21 أغسطس 2025 أو قبله ضد أنظمة واجهتها مكشوفة على الإنترنت دون تقييد بالعناوين، وأضافتها CISA إلى قائمة الثغرات المستغلة في 29 أغسطس. والإصلاح كان في الإصدار 17.0.3 من الوحدة لFreePBX 17، والإصدار المثبت عندنا 17.0.16.6. والدرس أن الوحدة التي لم تشترها كانت كافية لاختراق السيرفر، وأن صفحة الإدارة المكشوفة على الإنترنت هي الباب.

لذلك احذف الوحدات التجارية التي لن تشتريها، أو عطلها على الأقل، بالأمر fwconsole ma remove أو fwconsole ma disable متبوعاً باسم الوحدة، وتأكد أنها لا تعتمد عليها وحدة تستخدمها قبل الحذف، ثم fwconsole reload.

صفحة الإدارة ليست للإنترنت

وكما بينت القصة السابقة، فأهم قرار أمني في FreePBX هو ألا تفتح صفحة الإدارة /admin للإنترنت أبداً، وهو ما طلبته Sangoma نفسها في منشور Please Lock Down Your Administrator Access بعد تلك الثغرة. والطريقة العملية:

  • اجعل منفذ الويب في جدار الحماية مسموحاً للمنطقة Trusted فقط، وهي شبكة المكتب أو شبكة VPN مثل WireGuard، ويدخل المدير من البيت عبر ال VPN.
  • إذا احتاج الموظفون صفحة المستخدم UCP من خارج المكتب، فافتحها وحدها بشهادة TLS، ولا تفتح معها صفحة الإدارة.
  • اترك تحديثات الأمان التلقائية مفعلة، وراجع لوحة Dashboard وتنبيهاتها.
  • افتح SIP وRTP للهواتف والمزود فقط، واترك الجدار المتجاوب Responsive Firewall للهواتف البعيدة، وسوف نختبر الحماية من فحص SIP ومحاولات التخمين في مقال تأمين المقسم.

التحديث إلى إصدار أحدث

  • داخل FreePBX 17 تحدث الوحدات من Admin ثم Module Admin، أو بالأمر fwconsole ma upgradeall ثم fwconsole reload، وتحدث النظام وAsterisk بحزم Debian عبر apt لأنها تأتي من مستودع Sangoma.
  • لتغيير فرع Asterisk يوفر FreePBX السكربت asterisk-version-switch كما في جدول الإصدارات، والجدول يقول إن Asterisk 23 أضيف لFreePBX 17 في يناير 2026 للتجربة فقط وليس للإنتاج، فابق على 22.
  • الانتقال من FreePBX 16 إلى 17 ليس ترقية في المكان، فقد انتقل الأساس من SNG7 إلى Debian، وإعلان الإصدار 17 نص على أنك تحتاج تثبيتاً جديداً ثم نقل البيانات، أي نسخة احتياطية من 16 واستعادتها على 17، ومع ما رأيناه في قسم الاستعادة فخطط لهذا الانتقال على جهاز منفصل.
  • FreePBX 18 على Debian 13 مخطط له بحسب الجدول في 2027، ولن تحتاج الانتقال إليه قبل أن يقترب موعد نهاية دعم 17 في يونيو 2028.

مشكلات شائعة وحلولها

العرضالسببالحل
تعديلك في ملف Asterisk يختفيالملف يكتبه FreePBXضع التعديل في ملف _custom.conf
تسجيل الخط Rejected والمزود يقول No matching endpoint foundFreePBX يرسل اسم الخط بدل اسم الحسابClient URI وContact User في Advanced
الطرف الآخر يرى اسم الحساب بدل رقم الشركةFrom User معبأأفرغ From User واستخدم Outbound CallerID
صوت في اتجاه واحد مع هواتف المكتبشبكة الهواتف ليست في Local Networksأضفها في Asterisk SIP Settings
المكالمة الواردة لا ترن، والسجل فيه Received an unknown call with DIDصيغة الرقم لا تطابق المسار الوارداكتب الرقم كما يرسله المزود بالضبط
المتصل يسمع cannot-complete-as-dialedلا يوجد مسار صادر يطابق الرقمأضف نمطاً محدداً، وليس X.
الواجهة لا تفتح بعد استعادة نسخةقواعد الجدار أعيدت دون الشبكات الموثوقةfwconsole firewall restart من سطر الأوامر
خطأ Unable to locate the FreePBX BMO Classوحدة غير مثبتة بعد الاستعادةfwconsole ma list ثم fwconsole ma install

إزالة التجربة

نزيل ما أنشأناه بالاسم فقط:

docker rm -f arb-fpbx arb-fpbx-carrier arb-fpbx-bob
docker network rm arb-fpbx-net
docker rmi arb-fpbx-base:12.15

تدريب عملي

  • أنشئ من Applications ثم Time Conditions شرطاً لوقت الدوام من الأحد إلى الخميس، واجعل المسار الوارد يذهب إليه بدل مجموعة الرنين، ثم قارن ما كتبه FreePBX في extensions_additional.conf بما كتبناه بأيدينا في دليل Asterisk.
  • جرب استعادة نسختك الاحتياطية على جهاز افتراضي جديد عليه FreePBX 17، ثم اكتب قائمة بكل ما احتجت إصلاحه بعدها.

الخلاصة

  • FreePBX واجهة تكتب ملفات Asterisk، ولا تعدل ما يكتبه، بل ضع إعدادك في ملفات _custom.conf.
  • الإصدار الحالي FreePBX 17 على Debian 12 فقط، بالسكربت الرسمي، ولا توجد طريقة Docker رسمية، فاستخدم جهازاً افتراضياً مخصصاً.
  • الخط وحده لا يكفي: المسار الصادر يختار الخط بنمط الرقم، والمسار الوارد يختار الوجهة بالرقم المباشر DID.
  • راجع إعدادات NAT بعد معالج الجدار، فكل شبكة فيها هواتف يجب أن تكون في Local Networks.
  • جرب الاستعادة فعلاً، ففي تجربتنا استغرقت 20 دقيقة وأسقطت إعدادات SIP وأغلقت الواجهة حتى أعدنا تشغيل الجدار.
  • ثلث الوحدات تجارية ومثبتة مسبقاً، فاحذف ما لن تشتريه، ولا تفتح صفحة الإدارة على الإنترنت أبداً.

وفي المقال القادم بإذن الله سوف نتناول FreeSWITCH، وهو الخيار عندما يصبح عدد المكالمات أكبر من مكتب واحد.

دليلدليل Asterisk العملي: من التحويلة Extension إلى الرد الآلي IVRنبني مقسم Asterisk من الكود المصدري في Docker، ونكتب خطة اتصال فيها بريد صوتي ورد آلي وتسجيل مكالمات، ثم نتحكم به عبر AMI وARI.دليلتتبع مكالمة SIP عبر الشبكة: لماذا يصل الصوت في اتجاه واحد؟نبني مقسماً وهاتفين خلف NAT في تجربة عملية، ونتتبع المكالمة حزمة بحزمة حتى نعرف لماذا يضيع الصوت وكيف نصلحه.دليلالنسخ الاحتياطي بقاعدة 3-2-1 واختبار الاستعادةكيف تحدد كم بيانات تقبل أن تخسر، وتبني نسخاً لا يستطيع المخترق حذفها، وتتأكد أنك تستطيع استعادتها فعلاً.

سجل التحديثات

  • أكتوبر 2026: كتابة الدليل واختباره على FreePBX 17.0.33.2 وAsterisk 22.11.0 بالسكربت الرسمي على Debian 12.15، مع مزود SIP تجريبي من Asterisk وهواتف baresip.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.