tcp_accept_no_cl=yes listen=TCP_WS #!ifdef TLS_ROLE listen=TLS_WSS #!endif ######## NAT Traversal module - signaling functions ######## #!ifndef NATHELPER_LOADED #!trydef NATHELPER_LOADED loadmodule "nathelper.so" #!endif ######## Generic Hash Table container in shared memory ######## modparam("htable", "htable", "websockets=>size=16;initval=0") ######## Basic HTTP request handling ######## loadmodule "xhttp.so" #!trydef WS_KEEPALIVE_MECHANISM 0 #!trydef WS_KEEPALIVE_TIMEOUT 30 #!trydef WS_KEEPALIVE_PROCESSES 3 #!trydef WS_KEEPALIVE_INTERVAL 1 #!trydef WS_KEEPALIVE_DATA "Kazoo encourages you to keep alive" #!trydef WS_MAX_CONNECTIONS_PER_IP 50 #!trydef WS_MAX_CONNECTIONS_PER_PROXY 0 #!trydef WS_ALLOWED_PROXIES "0.0.0.0/0" #!trydef WS_CONNECTIONS_FROM_PROXY_ONLY 0 ######## Websocket module ######## loadmodule "websocket.so" modparam("websocket", "keepalive_mechanism", WS_KEEPALIVE_MECHANISM) modparam("websocket", "keepalive_timeout", WS_KEEPALIVE_TIMEOUT) modparam("websocket", "keepalive_processes", WS_KEEPALIVE_PROCESSES) modparam("websocket", "keepalive_interval", WS_KEEPALIVE_INTERVAL) modparam("websocket", "ping_application_data", WS_KEEPALIVE_DATA) modparam("websocket", "sub_protocols", 1) ####### Websocket Logic ######## kazoo.ws_allowed_proxies = WS_ALLOWED_PROXIES desc "comma separated list of allowed proxies in cidr notation" kazoo.ws_max_connection_per_ip = WS_MAX_CONNECTIONS_PER_IP desc "max connections per ip" kazoo.ws_max_connection_per_proxy = WS_MAX_CONNECTIONS_PER_PROXY desc "max connections per proxy" kazoo.ws_connections_via_proxy_only = WS_CONNECTIONS_FROM_PROXY_ONLY desc "only allow connections via proxy" event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if (!($rm =~ "GET")) { xlog("L_INFO", "websocket|log|rejecting HTTP request $rm from $si:$sp\n"); xhttp_reply("405", "Method Not Allowed", "", ""); exit; } if (!($hdr(Connection) =~ "Upgrade")) { xlog("L_INFO", "websocket|log|rejecting HTTP connection $hdr(Connection) request from $si:$sp\n"); xhttp_reply("400", "Bad Request", "", ""); exit; } if (!($hdr(Upgrade) =~ "websocket")) { xlog("L_INFO", "websocket|log|rejecting HTTP upgrade $hdr(Upgrade) request from $si:$sp\n"); xhttp_reply("400", "Bad Request", "", ""); exit; } if (!($hdr(Sec-WebSocket-Protocol) =~ "sip")) { xlog("L_INFO", "websocket|log|rejecting request for websocket protocol $hdr(Sec-WebSocket-Protocol) from $si:$sp\n"); xhttp_reply("400", "Bad Request", "", ""); exit; } #!ifdef MY_WEBSOCKET_DOMAIN if (!($hdr(Origin) =~ "MY_WEBSOCKET_DOMAIN")) { xlog("L_INFO", "websocket|log|rejecting HTTP request with unauthorized origin $hdr(Origin) from $si:$sp, allowed origin is MY_WEBSOCKET_DOMAIN\n"); xhttp_reply("400", "Bad Request", "", ""); exit; } #!endif if ($hdr(X-Forwarded-For) == $null) { if($sel(cfg_get.kazoo.ws_connections_via_proxy_only) == 1) { xlog("L_INFO", "websocket|log|request from $si without X-Forwarded-For Header and only allowed connections are via proxy\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } else { $var(ws_orig_ip) = $si; } } else { xlog("L_INFO", "websocket|log|request X-Forwarded-For $hdr(X-Forwarded-For) from $si\n"); $var(ws_orig_ip) = $hdr(X-Forwarded-For); } if($si != $var(ws_orig_ip)) { if(!is_in_subnet($si, $sel(cfg_get.kazoo.ws_allowed_proxies))) { xlog("L_WARNING", "websocket|log|request X-Forwarded-For $hdr(X-Forwarded-For) from invalid ip $si - allowed $sel(cfg_get.kazoo.ws_allowed_proxies)\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } if($sel(cfg_get.kazoo.ws_max_connection_per_proxy) > 0 && $sht(websockets=>$si::count) > $sel(cfg_get.kazoo.ws_max_connection_per_proxy)) { xlog("L_WARN", "websocket|log|$si is at the maximum $sel(cfg_get.kazoo.ws_max_connection_per_proxy) allowable sockets per PROXY IP, rejecting request for another websocket\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } } if($sel(cfg_get.kazoo.ws_max_connection_per_ip) > 0 && $sht(websockets=>$var(ws_orig_ip)::count) > $sel(cfg_get.kazoo.ws_max_connection_per_ip)) { xlog("L_WARN", "websocket|log|$var(ws_orig_ip) is at the maximum $sel(cfg_get.kazoo.ws_max_connection_per_ip) allowable sockets per IP, rejecting request for another websocket\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } if (ws_handle_handshake()) { $var(count) = $shtinc(websockets=>$var(ws_orig_ip)::count); $sht(websockets=>$ws_conid::ws_orig_ip) = $var(ws_orig_ip); if($si != $var(ws_orig_ip)) { $var(proxy_count) = $shtinc(websockets=>$si::count); xlog("L_INFO", "websocket|log|opened proxied websocket $ws_conid from $si for $var(ws_orig_ip):$sp\n"); } else { xlog("L_INFO", "websocket|log|opened websocket $ws_conid from $var(ws_orig_ip):$sp\n"); } exit; } xlog("L_INFO", "websocket|log|unhandled HTTP request $rm from $si:$sp\n"); xhttp_reply("404", "Not Found", "", ""); } event_route[websocket:closed] { $var(ws_orig_ip) = $sht(websockets=>$ws_conid::ws_orig_ip); $sht(websockets=>$ws_conid::ws_orig_ip) = $null; $var(count) = $shtdec(websockets=>$si::count); if($var(ws_orig_ip) != $null && $si != $var(ws_orig_ip)) { $var(countip) = $shtdec(websockets=>$var(ws_orig_ip)::count); xlog("L_INFO", "websocket|log|$si closed proxied websocket $ws_conid for $var(ws_orig_ip):$sp\n"); if ($var(countip) < 1) $sht(websockets=>$var(ws_orig_ip)::count) = $null; } else { xlog("L_INFO", "websocket|log|closed websocket $ws_conid from $var(ws_orig_ip):$sp\n"); } if ($var(count) < 1) $sht(websockets=>$si::count) = $null; }