kamailio关于via那点事

如果kamailio作为代理服务器,在转到目的路由时 不删除原始的via信息 会造成信息泄露

如果 Kamailio 作为代理服务器(SIP Proxy)在转发 SIP 请求时不删除原始的 Via 信息,这确实可能会造成信息泄露


为什么不删除 Via 可能会泄露信息?

  1. 暴露内部网络信息

    • Via 头包含了 前一个发送者的 IP 地址、端口和传输方式(UDP/TCP/TLS)。
    • 如果 Via 头未被清理,可能会泄露 内部服务器 IP(如私有地址 192.168.x.x)或运营商信息
    • 例如,假设 Kamailio 运行在 10.10.10.1,但 SIP 服务器在 192.168.1.100,那么 Via 头可能会泄露:
      Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK12345
      
      这个信息 不应该暴露给外网
  2. 攻击者可利用 Via 进行网络拓扑分析

    • 恶意用户可能利用 Via 头中的 IP 地址 扫描 SIP 代理的拓扑结构,寻找潜在的攻击点(如未受保护的 SIP 服务器)。
  3. SIP 设备可能错误处理额外的 Via

    • 某些 SIP 设备(特别是运营商的 SBC 或 PBX)可能不期望看到 额外的 Via,从而导致 SIP 处理异常(如 400 Bad Request483 Too Many Hops)。

✅ 解决方案:让 Kamailio 在转发请求时删除原始 Via

方法 1:使用 remove_hf("Via")

Kamailio 提供了 remove_hf("Via") 指令,可以在 SIP 代理模式下 移除所有 Via 头并重新生成,防止信息泄露。
kamailio.cfg 配置文件中:

request_route {
    if (!is_method("ACK")) {
        remove_hf("Via");  # 删除所有 Via 头
    }
    append_hf("Via: SIP/2.0/UDP $si:$sp;branch=z9hG4bK$var(branch)\r\n");
    t_relay();
}

解释

  • 移除 Via,防止泄露内部 IP。
  • 手动添加新的 Via,只显示 Kamailio 作为代理的 IP($si:$sp)。

方法 2:使用 topos 模块(推荐)

topos 是 Kamailio 提供的 “拓扑隐藏”(Topology Hiding)模块,可以自动删除 Via 头,并在响应时恢复,防止信息泄露。

安装 topos 模块
sudo apt install kamailio-topos-modules
kamailio.cfg 启用 topos
loadmodule "topos.so"

request_route {
    if (!is_method("ACK")) {
        topos_request();
    }
    t_relay();
}

onreply_route {
    topos_reply();
}

解释

  • topos_request() 删除 ViaRecord-Route,隐藏内部拓扑
  • topos_reply() 在响应时恢复正确的路由,确保 SIP 消息能正确返回。

结论

不删除 Via 确实会导致信息泄露,特别是在 NAT 或运营商环境中!
推荐使用 remove_hf("Via")topos 模块来移除 Via,保护 SIP 代理的内部拓扑信息!

使用topos模块自动更正via

使用 topos 模块后,目的地路由的 Via 头信息

当 Kamailio 作为 SIP 代理(Proxy) 并启用了 topos 模块时,它会自动删除原始的 Via,并在转发 SIP 请求时仅留下 Kamailio 自己的 Via。那么,在**目的地路由(最终 SIP 服务器)**收到的请求中,Via 头的情况如下:


1. 启用了 topos,最终目的地收到的 Via

示例:

客户端 (EP1) 发送初始请求

INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK111111
Via: SIP/2.0/UDP 10.10.10.1:5060;branch=z9hG4bK222222
Via: SIP/2.0/UDP 222.211.75.75:5060;branch=z9hG4bK333333

这里 Via 头中包含了所有中间代理的 IP(内部网络信息)


Kamailio 代理(启用了 topos

topos_request() 处理后,Kamailio 会:

  • 删除原始 Via192.168.1.10010.10.10.1222.211.75.75)。
  • 只留下 Kamailio 自己的 Via

Kamailio 转发后的 SIP 请求

INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP 47.100.62.225:5060;branch=z9hG4bKabcdef

这里 Via 只保留了 Kamailio 自己的 IP(47.100.62.225),不会暴露任何内部 IP!


2. 目的地收到的 Via

最终,目的地路由(222.223.190.118) 只会看到 Kamailio 的 Via

INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP 47.100.62.225:5060;branch=z9hG4bKabcdef

原始 Via(192.168.1.100、10.10.10.1、222.211.75.75)已被隐藏,保护了 SIP 代理的内部网络结构!


3. 响应中的 Via 处理

当 目的地(222.223.190.118) 发送 200 OK 响应时,它的 Via 头如下:

SIP/2.0 200 OK
Via: SIP/2.0/UDP 47.100.62.225:5060;branch=z9hG4bKabcdef

然后 Kamailio 会使用 topos_reply() 恢复正确的返回路径但不会暴露目的地的 Via,所以客户端最终收到的响应仍然不会看到 222.223.190.118 的 Via


结论

目的地路由(最终 SIP 服务器)收到的 Via 头只包含 Kamailio 的 IP,不包含原始的 Via 头,确保了内部网络信息不被泄露!
响应时 Kamailio 也会正确恢复路径,确保 SIP 消息能够正确返回!

下面是一个完整的 Kamailio 作为 SIP 代理服务器(Proxy)kamailio.cfg 配置,结合 Lua 脚本 进行 呼叫计费,并使用 topos 模块隐藏 Via 信息,确保 SIP 请求的安全性。


功能概述

  1. 基本 SIP 代理功能
  2. 启用 topos 模块,隐藏 Via
  3. 使用 Lua 进行通话计费
  4. 转发 INVITE、ACK、BYE 等必要的 SIP 请求
  5. 防止环路、错误处理
  6. 超时释放通话

Lua 计费脚本 (/usr/local/kamailio/lua/billing.lua)

-- billing.lua - 计费逻辑(计算通话费用)
local billing = {}

function billing.calculate_fee(duration)
    local rate_per_second = 0.05  -- 每秒 0.05 元
    local cost = duration * rate_per_second
    return cost
end

function billing.log_call(call_id, caller, callee, duration)
    local cost = billing.calculate_fee(duration)
    local log_str = string.format(
        "CALL BILLING - Call-ID: %s, From: %s, To: %s, Duration: %d sec, Cost: %.2f 元",
        call_id, caller, callee, duration, cost
    )
    -- 记录日志
    KSR.info(log_str)
end

return billing

Kamailio kamailio.cfg 配置

####### 全局配置 ########

#!KAMAILIO
#!define WITH_LUA
#!define WITH_TOPO

loadmodule "sl.so"                # SIP 事务管理
loadmodule "tm.so"                # SIP 事务管理
loadmodule "rr.so"                # 记录路由
loadmodule "maxfwd.so"            # Max-Forwards 处理
loadmodule "siputils.so"          # 处理 SIP 头
loadmodule "topos.so"             # 隐藏 Via 头
loadmodule "topos_redis.so"       # Redis 版本的 topos(推荐)
loadmodule "lua.so"               # Lua 脚本支持

modparam("topos", "storage", "redis://127.0.0.1:6379/0")  # 存储 SIP 会话信息到 Redis
modparam("lua", "load", "/usr/local/kamailio/lua/billing.lua")

####### SIP 请求路由 ########

request_route {
    if (!mf_process_maxfwd(10)) {
        sl_send_reply("483", "Too Many Hops");
        exit;
    }

    if (is_method("OPTIONS")) {
        sl_send_reply("200", "OK");
        exit;
    }

    # 处理 INVITE
    if (is_method("INVITE")) {
        record_route();
        topos_request();  # 删除原始 Via 信息
        route(1);
        exit;
    }

    # 处理 BYE(通话结束)
    if (is_method("BYE")) {
        handle_bye();
        topos_request();  # 确保 Via 头处理
        route(1);
        exit;
    }

    route(1);
}

route[1] {
    if (!t_relay()) {
        sl_reply_error();
    }
    exit;
}

####### 响应处理 ########

onreply_route {
    topos_reply();  # 确保 Via 头处理
}

####### 处理 BYE 进行计费 ########

event_route[tm:local-request] {
    if (is_method("BYE")) {
        handle_bye();
    }
}

####### 计费逻辑(Lua 计费) ########

route[handle_bye] {
    xlog("L_INFO", "Handling BYE request for call billing");

    # 获取通话时长
    if ($Ts - $T_req_time > 0) {
        $var(duration) = $Ts - $T_req_time;
    } else {
        $var(duration) = 0;
    }

    # 获取通话 ID、主叫和被叫
    $var(call_id) = $ci;
    $var(caller) = $fU;
    $var(callee) = $tU;

    # 调用 Lua 计费
    $var(lua_result) = lua_runstring(
        string.format("require 'billing'.log_call('%s', '%s', '%s', %d)",
            $var(call_id), $var(caller), $var(callee), $var(duration))
    );

    xlog("L_INFO", "Billing logged: " + $var(lua_result));
}

代码解析

  1. topos 模块

    • topos_request(); 删除 Via,防止信息泄露
    • topos_reply(); 确保返回路径正确
    • 使用 Redis 存储会话信息,防止 Kamailio 重启导致 Via 丢失
  2. Lua 计费

    • calculate_fee(duration) 计算通话费用
    • log_call(call_id, caller, callee, duration) 记录计费日志
  3. 计费触发时机

    • onreply_route {} 处理 SIP 响应
    • event_route[tm:local-request] {} 监听 BYE 事件并触发 Lua 计费
  4. SIP 代理功能

    • 转发 INVITE、ACK、BYE
    • 防止环路(Max-Forwards 机制)
    • 记录路由(Record-Route 机制)

运行流程

呼叫过程:

  1. EP1 -> Kamailio 发送 INVITE
  2. Kamailio 使用 topos_request() 删除原始 Via
  3. Kamailio -> 目的地 转发 INVITE
  4. 目的地 -> Kamailio 发送 200 OK
  5. Kamailio 使用 topos_reply() 恢复路径 并转发给 EP1
  6. EP1 发送 BYE
  7. Kamailio 调用 Lua 计算通话时长和费用
  8. Kamailio 记录计费日志,并转发 BYE目的地

示例日志

CALL BILLING - Call-ID: c12345abcd, From: 01089672389, To: 13545769997, Duration: 120 sec, Cost: 6.00 元

结论

隐藏 Via 头,保护代理服务器信息
使用 Lua 实现通话计费
支持 topos,防止 Via 泄露
支持基本 SIP 代理功能(INVITE、BYE、ACK)

已经使用了lua脚本,为什么还要把路由相关代码写入到kamailio.cfg中呢?

这是因为 Kamailio 的路由逻辑必须在 kamailio.cfg 中定义,Lua 主要是用于 业务逻辑扩展(如计费),而不是完全替代 kamailio.cfg 的 SIP 处理能力。下面详细解释 为什么 kamailio.cfg 仍然需要路由逻辑


为什么 kamailio.cfg 仍然需要路由逻辑?

Kamailio 主要用于 SIP 代理、注册和路由控制,其核心逻辑(如 INVITE 处理、路由转发、事务管理等)都是由 kamailio.cfg 配置完成的。Lua 只是一个辅助工具,不能完全代替 Kamailio 处理 SIP 消息。

Kamailio kamailio.cfg 主要职责

  1. 解析 SIP 请求

    • 识别 SIP 方法(如 INVITEBYE
    • 处理 Max-Forwards 以防止循环
    • 记录路由 (Record-Route) 以保持通话链路
  2. 管理 SIP 事务

    • tm(事务管理)模块用于 SIP 事务控制
    • sl(状态回复)模块用于无事务性消息回复
  3. 控制 SIP 代理逻辑

    • 进行 t_relay()(SIP 请求转发)
    • 处理 onreply_route{}(SIP 响应处理)
    • 通过 topos 隐藏 Via

Lua 主要职责

  1. 提供业务逻辑(如计费、日志记录)
  2. 与外部系统交互(如数据库、Redis、REST API)
  3. 增强 Kamailio,但不能完全取代 kamailio.cfg

Lua 不能直接控制 Kamailio 的 SIP 代理行为,它只是一个扩展工具。例如,Lua 不能执行 t_relay() 这样的 SIP 代理操作,必须在 kamailio.cfg 中完成。


为什么不能只用 Lua 处理 SIP?

  • Lua 不能直接处理 SIP 事务(如 t_relay(),必须在 kamailio.cfg 内调用。
  • Lua 不能替代 kamailio.cfg 的核心 SIP 处理逻辑(如 record_route())。
  • Kamailio 是一个高性能 SIP 代理,C 语言模块比 Lua 解析更快,直接在 kamailio.cfg 处理 SIP 逻辑可以提升性能。

最佳实践:结合 kamailio.cfg 和 Lua

  • kamailio.cfg 负责 SIP 代理、转发、事务管理
  • Lua 负责计费、日志、数据库交互

例如:

  • Kamailio 处理 BYE 请求并调用 Lua 计费
  • Lua 计算通话时长,并将费用存入数据库
  • Kamailio 继续处理 SIP 路由

结论

不能完全用 Lua 替代 kamailio.cfg,因为 Lua 不能直接处理 SIP 代理逻辑。
最佳方式是:kamailio.cfg 处理 SIP 代理,Lua 负责计费、日志等业务逻辑。

你可能感兴趣的:(VOIP那些事,kamailio)