- infrastructure/freeswitch/Dockerfile: debian:trixie-slim + SignalWire packaged freeswitch-meta-vanilla, avoiding a C/C++ build on a 1.9GB RAM VM - FREESWITCH_PAT used only via Docker BuildKit secret, apt credentials file created and deleted within the same RUN — verified absent from the final image with docker history - minimal module set (agente.md secao 15): sofia, event_socket, commands, dptools, callcenter, avmd, curl, local_stream, etc. mod_xml_curl installed but disabled — it refuses to load without a configured gateway-url, which will exist once b2bcall-fs-config is built - entrypoint.sh rotates the Event Socket password away from the 'ClueCon' default at container runtime (never baked into the image); fails loudly if ESL_PASSWORD is unset - port 8021 not published to the host; only reachable from other containers on the compose network - found and fixed: freeswitch-conf-vanilla is a Recommends (not a Depends) of freeswitch-meta-vanilla, so --no-install-recommends silently produced an empty /etc/freeswitch and a crash loop - verified end-to-end: fs_cli status via ESL with the custom password, default password rejected, expected modules loaded, healthcheck green, ~44MB RAM usage - docs/FREESWITCH.md, docs/NETWORK_ARCHITECTURE.md (network_mode decision deferred until a real SIP trunk exists)
4.5 KiB
FreeSWITCH
Imagem própria em infrastructure/freeswitch/ (agente.md secao 19), rodando como
serviço Docker freeswitch (container b2bcall-freeswitch).
Por que pacotes prontos, e não compilar da fonte
O servidor de desenvolvimento tem só ~1.9GB de RAM. Compilar FreeSWITCH (C/C++,
dezenas de módulos) arriscaria OOM e levaria muito tempo. Em vez disso, a imagem
usa os pacotes .deb pré-compilados do repositório oficial do SignalWire
(freeswitch.signalwire.com), autenticado com FREESWITCH_PAT — exatamente o
uso previsto para essa credencial (agente.md secao 11). O repositório do
SignalWire tem pacotes para o codename trixie, então a imagem usa
debian:trixie-slim como base (mesma versão do host, mas isso é coincidência —
o container é isolado e poderia usar qualquer Debian suportado pelo repo).
Manuseio do PAT (agente.md secao 11)
FREESWITCH_PAT entra no build via Docker BuildKit secret
(--mount=type=secret,id=freeswitch_pat), nunca como ARG/ENV. O arquivo de
credenciais do apt (/etc/apt/auth.conf.d/freeswitch.conf, que contém o PAT em
texto puro) é criado e apagado dentro do mesmo RUN, então nunca aparece em
nenhuma camada da imagem final — verificado com docker history --no-trunc.
docker-compose.yml declara o secret assim:
secrets:
freeswitch_pat:
environment: FREESWITCH_PAT
e o serviço freeswitch referencia secrets: [freeswitch_pat] em build:.
Pacotes instalados
freeswitch-meta-vanilla # core + config de referência (a mesma usada em
# praticamente todo tutorial/livro de FreeSWITCH)
freeswitch-conf-vanilla # ⚠ Recommends de meta-vanilla, não Depends —
# precisa ser listado explicitamente com
# --no-install-recommends (foi um bug real
# durante o setup: sem isso /etc/freeswitch
# fica vazio e o container entra em crash loop)
freeswitch-mod-callcenter # ACD (agente.md secao 37)
freeswitch-mod-avmd # detecção de caixa postal/beep (secao 87)
freeswitch-mod-curl # chamadas HTTP a partir do dialplan
mod_xml_curl está instalado mas desativado em
overrides/autoload_configs/modules.conf.xml — o módulo se recusa a carregar
sem pelo menos um binding com gateway-url configurada ("Binding has no
url!"), e essa URL só existirá quando o b2bcall-fs-config for criado (fase
"XML Curl", logo em seguida). Reativar lá.
mod_signalwire foi removido da lista de módulos: é específico da nuvem do
SignalWire, que não usamos (só o repositório de pacotes).
Event Socket (agente.md secao 22)
Senha alterada da padrão (ClueCon) para ${ESL_PASSWORD} (gerado com
openssl rand, vive só em .env) via entrypoint.sh, que faz um sed no
event_socket.conf.xml em runtime — a senha real nunca é copiada para a
imagem, só injetada via variável de ambiente do container. O entrypoint falha
alto (set -eu + ${ESL_PASSWORD:?...}) se a variável não estiver definida —
nunca sobe com a senha padrão por engano.
Porta 8021 não é publicada no host (ports: ausente no compose) — só
alcançável por outros containers na rede interna do Docker Compose
(b2bcall_default), pelo nome de serviço freeswitch. Isso satisfaz
"NÃO pública... acesso somente pela aplicação autorizada" sem precisar de ACL
adicional por enquanto (ACL fica como hardening futuro, ver TODO).
O que NÃO foi feito nesta fase (fica para as próximas, por design)
Seguindo a ordem do próprio agente.md (secao 232): esta fase só cobre "ter o
FreeSWITCH rodando e alcançável". As próximas fases constroem em cima:
- Event Socket:
b2bcall-fs-events, conexão ESL permanente (secao 21). - XML Curl:
b2bcall-fs-config, reativamod_xml_curlapontando pra esse serviço (secao 26). - Extensions/Trunks/Dialplan: hoje o directory/dialplan estático da config
vanilla continua com os 20 ramais de teste (1000-1019, senhas fracas — README
do próprio pacote avisa isso). Como as portas SIP não estão publicadas no
host, isso fica contido, mas precisa ser substituído por
mod_xml_curldinâmico antes de qualquer tronco/ramal real existir. - mod_odbc_cdr: não instalado ainda — só faz sentido junto da fase de CDR.
Verificação manual
source <(grep '^ESL_PASSWORD=' .env)
docker exec b2bcall-freeswitch fs_cli -p "$ESL_PASSWORD" -x "status"
docker exec b2bcall-freeswitch fs_cli -p "$ESL_PASSWORD" -x "module_exists mod_sofia"