A small SIP proxy / SBC example. Kamailio in front, Asterisk behind, Homer for tracing. Everything runs with docker compose up.
- Internal SIP clients register to Kamailio with simple demo credentials.
- Kamailio routes calls. User-to-user calls are resolved via the usrloc registrar (no media anchoring). Numeric (PSTN-shaped) numbers and the
9196echo get handed to Asterisk. - Asterisk holds the upstream ITSP credentials and bridges outbound calls through its
trunkpjsip endpoint. - DID-to-user mapping for inbound calls lives in a small Lua table (
kamailio/did_map.lua). - Every SIP message from Kamailio and Asterisk is mirrored to Homer over HEP. Open
http://localhost:9080to see ladder diagrams of every call.
Local dev — softphones on the same machine or LAN:
cp .env.example .env
# edit .env if you want a real ITSP trunk
make up
make waitPoint a softphone (Zoiper, Linphone, MicroSIP) at localhost:5060. Register as alice or bob, password 1234. Dial 9196 for the echo test. Open Homer at http://localhost:9080.
Production on a VPS — softphones over the public internet:
See docs/deploy.md for the full walkthrough (DNS, firewall, Caddy with auto-TLS for the panel, fail2ban, pike rate limiting, NAT-aware Asterisk).
Short version:
cp .env.prod.example .env
# edit .env with your VPS public IP, panel hostname, ITSP creds
make panel-setup # interactive admin password (12+ chars)
make prod-up # COMPOSE_PROFILES=prod, brings up caddy + fail2ban tooFor a web UI (users, DID map, trunk creds, live calls, health), see panel/:
make panel-setup # one-time: set admin password
make up # brings up the panel along with the rest of the stackOpen http://localhost:8080 (or set PANEL_HTTP_PORT in .env).
make smokeHealth-checks every container, verifies the schema is seeded, and confirms Asterisk pjsip transports are up + Homer is responding.
sip-proxy/
├── docker-compose.yml orchestration
├── Makefile up / down / wait / smoke / logs / clean / nuke
├── .env.example ITSP_* placeholders
├── kamailio/
│ ├── Dockerfile
│ ├── kamailio.cfg thin bootstrap, loads modules, hands off to Lua
│ ├── kamailio.lua routing logic (KEMI)
│ ├── users.lua auth helper
│ ├── did_map.lua DID -> internal user
│ └── entrypoint.sh
├── asterisk/
│ ├── Dockerfile andrius/asterisk:18-current + our config
│ ├── entrypoint.sh renders pjsip.conf.tmpl from .env at boot
│ ├── conf/pjsip.conf.tmpl transports + kamailio endpoint + trunk endpoint
│ └── conf/extensions.conf 9196 echo + outbound-to-trunk dialplan
├── mysql/init/ Kamailio schema + demo users (alice/bob, both pwd 1234)
├── homer/ postgres init + heplify config
├── panel/ React + FastAPI admin panel
└── test/
├── wait.sh polls each service for readiness
└── smoke.sh end-to-end health smoke
For dev (make up), demo passwords are 1234 and the panel binds to localhost:8080. Don't expose this to the internet.
For prod (make prod-up), the repo ships with:
- Kamailio
pikemodule — drops bursts above 16 req/2s per source IP fail2bancontainer — bans IPs after 5 auth failures in 10 min for 1 hour- Caddy with auto-TLS for the panel — Let's Encrypt cert, HTTPS-only
panel-setupenforces ≥12-char admin password, stored as bcrypt in a docker secret file- NAT-aware Asterisk via
EXTERNAL_IP - Open firewall ports limited to 80, 443, 5060, 10000–10100
Still not in v1, see "future work" in docs/design.md:
- TLS for SIP itself (port 5061)
- SRTP for media
- Audit log of admin actions
- Allowlist proxy in front of the docker socket
Internal SIP clients authenticate against Kamailio's subscriber table (their creds live only in MariaDB). When they dial a PSTN number, Kamailio routes the INVITE to Asterisk; Asterisk's trunk pjsip endpoint authenticates upstream using the credentials that live only in .env. The upstream provider never sees the internal user's identity, and internal users never see the trunk credentials.
For inbound, the upstream sends a call addressed to the trunk identity. Kamailio whitelists the upstream's IP, looks up the dialed DID in did_map.lua, resolves the matching internal user via the registrar, and forwards the INVITE to their registered contact.
Both Kamailio (via siptrace module) and Asterisk (via res_hep) ship every SIP message to heplify-server over HEP v3. heplify writes to PostgreSQL and Homer renders them in the web UI at http://localhost:9080. Default Homer login: admin / sipcapture.
| Piece | Purpose |
|---|---|
| Kamailio 5.8 (KEMI Lua) | SIP proxy / SBC + registrar |
| Asterisk 18 (pjsip) | Media + echo + outbound trunk |
| MariaDB 11 | Kamailio subscriber + location tables |
| heplify-server | HEP capture collector |
| PostgreSQL 15 | Homer storage |
| Homer 7 web | Capture UI |
MIT. See LICENSE.
