toppyr-stack/kamailio/kamailio.cfg
Dotty Dotter 39e0052eac feat: Kamailio SIP Proxy hinzugefügt (Dotty Phone)
Kamailio als TCP-only SIP Proxy für Voice-Interface mit Openclaw.
TLS-Terminierung via Traefik auf Port 5061 (Let's Encrypt).
Zwei SIP-Accounts: tobias (Linphone iOS) und dotty (Mac Mini Asterisk).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 19:48:27 +02:00

238 lines
5.5 KiB
INI

#!KAMAILIO
#
# Dotty Phone — Kamailio SIP Proxy
# Reiner Signaling-Proxy, kein Media-Handling.
# Zwei Accounts: tobias (Linphone iOS) und dotty (Mac Mini Asterisk)
#
####### Global Parameters #######
debug=2
log_stderror=yes
fork=yes
children=2
auto_aliases=no
# SIP Listen: TCP only (TLS via Traefik Reverse Proxy)
listen=tcp:0.0.0.0:5060
####### Modules Section #######
loadmodule "kex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "nathelper.so"
loadmodule "auth.so"
loadmodule "htable.so"
loadmodule "pike.so"
####### Module Parameters #######
# --- User Location (in-memory, kein DB) ---
modparam("usrloc", "db_mode", 0)
# --- Registrar ---
modparam("registrar", "default_expires", 120)
modparam("registrar", "min_expires", 60)
modparam("registrar", "max_expires", 300)
# --- NAT Helper ---
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "sipping_bflag", 7)
modparam("nathelper", "sipping_from", "sip:keepalive@sip.toppyr.de")
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
# --- htable für Credentials ---
# Format: username => ha1_hash
# HA1 = MD5(username:sip.toppyr.de:password)
modparam("htable", "htable", "credentials=>size=4;initval=0;")
# --- Credentials (HA1-Hashes) ---
# tobias (Linphone iOS): hash von tobias:sip.toppyr.de:8pjd6eskjKmCihsu
# dotty (Mac Mini): hash von dotty:sip.toppyr.de:XlF11A9eeBDXbWb3
# Diese werden im request_route geladen (siehe unten)
# --- Pike (Rate Limiting) ---
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 30)
modparam("pike", "remove_latency", 4)
# --- Credentials laden ---
include_file "kamailio-local.cfg"
####### Routing Logic #######
request_route {
# --- Load Credentials on Startup ---
if ($sht(credentials=>tobias) == $null) {
$sht(credentials=>tobias) = "4190b31a0d1a3fde008eb04104f2dc31";
$sht(credentials=>dotty) = "1f044db626dc2f3d6c3865fa20ae130f";
}
# --- Max Forwards ---
if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483", "Too Many Hops");
exit;
}
# --- Sanity Check ---
if (!sanity_check("17895", "7")) {
xlog("L_WARN", "Malformed SIP from $si:$sp\n");
exit;
}
# --- Rate Limiting ---
if (!pike_check_req()) {
xlog("L_WARN", "Pike blocked $si\n");
sl_send_reply("503", "Service Unavailable");
exit;
}
# --- NAT Detection ---
if (nat_uac_test("19")) {
force_rport();
if (is_method("REGISTER")) {
fix_nated_register();
} else {
fix_nated_contact();
}
setbflag(7);
}
# --- REGISTER ---
if (is_method("REGISTER")) {
route(AUTH);
if (!save("location")) {
sl_send_reply("500", "Server Error");
}
exit;
}
# --- Record-Route für Dialoge ---
if (is_method("INVITE|SUBSCRIBE")) {
record_route();
}
# --- In-Dialog Requests ---
if (has_totag()) {
if (loose_route()) {
if (isbflagset(7)) {
add_rr_param(";nat=yes");
}
route(RELAY);
exit;
}
# ACK ohne Route-Header
if (is_method("ACK")) {
if (t_check_trans()) {
route(RELAY);
exit;
}
exit;
}
sl_send_reply("404", "Not Found");
exit;
}
# --- Authentifizierung für alles außer ACK/CANCEL ---
if (is_method("INVITE|MESSAGE|SUBSCRIBE|NOTIFY|OPTIONS")) {
route(AUTH);
}
# --- MESSAGE (SIP-Textnachricht) ---
if (is_method("MESSAGE")) {
if (!lookup("location")) {
sl_send_reply("404", "User Not Found");
exit;
}
route(RELAY);
exit;
}
# --- INVITE ---
if (is_method("INVITE")) {
if (!lookup("location")) {
# Mac Mini offline
sl_send_reply("480", "Temporarily Unavailable");
exit;
}
route(RELAY);
exit;
}
# --- CANCEL ---
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
# --- OPTIONS (Keepalive) ---
if (is_method("OPTIONS")) {
sl_send_reply("200", "OK");
exit;
}
# --- Alles andere ---
route(RELAY);
}
# --- AUTH Route ---
route[AUTH] {
if ($sht(credentials=>$au) == $null) {
if (is_method("REGISTER")) {
www_challenge("sip.toppyr.de", "0");
} else {
proxy_challenge("sip.toppyr.de", "0");
}
exit;
}
if (is_method("REGISTER")) {
if (!pv_www_authenticate("sip.toppyr.de", "$sht(credentials=>$au)", "0")) {
www_challenge("sip.toppyr.de", "0");
exit;
}
} else {
if (!pv_proxy_authenticate("sip.toppyr.de", "$sht(credentials=>$au)", "0")) {
proxy_challenge("sip.toppyr.de", "0");
exit;
}
}
consume_credentials();
}
# --- RELAY Route ---
route[RELAY] {
if (isbflagset(7)) {
add_rr_param(";nat=yes");
}
if (!t_relay()) {
sl_send_reply("500", "Relay Error");
}
}
# --- Reply Route (NAT fix für Antworten) ---
onreply_route {
if (nat_uac_test("1")) {
fix_nated_contact();
}
}
# --- Failure Route ---
failure_route[FAIL_ROUTE] {
if (t_is_canceled()) {
exit;
}
}