[Core, mod_commands] Interface allowlist (#3086)

* [core] Add interface allowlist to gate module app/api registration

Adds an optional, presence-activated allowlist in switch.conf.xml that
controls which modules may register application / api / json_api /
chat-application interfaces. With no <interface-allowlist> configured
nothing is enforced; when at least one <allow> entry is present, only
listed interfaces register at load time and all others are refused (the
module still loads and switch_loadable_module_process still returns
SUCCESS -- the blocked interface is simply never exposed).

Entries match at three levels of precision:
  mod_commands            - whole module
  mod_commands.system     - any interface named "system"
  mod_commands.system.api - a specific type (app|api|json_api|chat_app)

Enforcement lives in switch_loadable_module_process() so every module, at
boot and at runtime `load`, is subject to the same policy. This gives
operators a way to disable the "system"/"spawn" shell-exec API commands
(and equivalents) system-wide.

Also adds the `interface_allowlist_dump [modules] [plain]` API, which
walks the loaded modules and prints their interfaces in the allowlist key
format so the current state can be captured and pruned offline into config.

* [mod_commands] Add tests for the interface allowlist

New test_interface_allowlist boots the core with an active
<interface-allowlist> (conf_interface_allowlist/) that permits only a
couple of mod_commands interfaces, then loads mod_commands and verifies:

- listed commands register and run (status, version) while unlisted and
  shell-exec commands are refused (system, spawn, uptime) -- refusal
  surfaces as switch_api_execute returning FALSE / command-not-found,
  with the command function never invoked;
- a "module.name.type" entry gates by type: the API "status" loads while
  the JSON API of the same name stays blocked;
- interface_allowlist_dump prints the config format in its xml, modules
  and plain variants, and reflects module capabilities (system appears in
  the dump even though it was blocked from registering).

* [config] Fix interior -- in interface-allowlist comment breaking XML parse

The explanatory comment used -- as em-dash pairs. The XML parser treats
-- inside a comment as the comment close, causing an "unclosed <!--"
error that prevents the whole freeswitch.xml from parsing (boot and
reloadxml both fail). Replace the -- pairs with ordinary punctuation.

* update .gitignore

* [core] Warn when interface-allowlist section is present but parses no entries

Co-authored-by: Chris Rienzo <chris@signalwire.com>
This commit is contained in:
Andrey VolkandChris Rienzo authored and GitHub committed 2026-08-05 18:44:33 +03:00
1 parent a75491a318
commit c1bb5c6ab3
8 files changed
+454 -1

No files matched your search

@@ -208,4 +208,39 @@
</settings>
<!--
Interface allowlist (optional security hardening).
When this section contains at least one <allow> entry, ONLY the interfaces listed here
are permitted to register; every other application / api / json_api / chat-application
from ANY module is refused at load time (the module still loads, it just does not expose
the blocked interface). With this section absent or empty, nothing is enforced and all
interfaces load normally.
An interface is permitted if it matches ANY entry. Entries may be written at three
levels of precision:
<allow name="mod_commands"/> - permit every interface in mod_commands
<allow name="mod_commands.status"/> - permit any interface named "status" (all types)
<allow name="mod_commands.status.api"/> - permit ONLY the api named "status"
The optional trailing type token is one of: app, api, json_api, chat_app. This is how
you permit, e.g., the APP named "system" while still blocking an API/JSON-API of the
same name.
WARNING: this is an aggressive, system-wide allowlist. Once enabled you must list every
module (or interface) whose apps/APIs you rely on, including management commands from
mod_commands (load, unload, reload, status, ...) and your dialplan apps; otherwise the
system will not function. It only affects app/api/json_api/chat-app registration; codecs,
endpoints, timers, formats, etc. are never gated.
-->
<!--
<interface-allowlist>
<allow name="mod_commands"/>
<allow name="mod_dptools"/>
<allow name="mod_dialplan_xml"/>
<allow name="mod_sofia"/>
</interface-allowlist>
-->
</configuration>