Featured image of post kamailio auth和auth_db模块介绍

kamailio auth和auth_db模块介绍

背景

这两个模块用于账号认证的,auth_db模块必须和auth模块一起. auth_db要使用数据库存储账号信息.

官方文档地址:

  1. auth
  2. auth_db

本次测试的kamailio版本是:

version: kamailio 5.8.5 (x86_64/linux)

auth 模块

参数解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 用来对注册信息完整性检查,参数查看auth_checks_in_dlg解释, 11:URI+callid+source ip 
modparam("auth", "auth_checks_register", 11)
# 检查sip请求是否没有`ToHearder`或者`To tag`, 9:URI+source ip 
modparam("auth", "auth_checks_no_dlg", 9)
# 检查sip请求是否是dialog, 1: 检查URI是否改变;2:检查callid;3:检查From tag;4:检查源ip地址
modparam("auth", "auth_checks_in_dlg", 15)
# 设置qop 参数,默认:"", 可以为:auth, auth-int, ""
modparam("auth", "qop", "auth")
# 会记录nc值并和之前的nc值比较,如果小于之前的nc值,就会拒绝请求,依赖qop参数,默认:0
modparam("auth", "nonce_count", 1)
# 如果设置成1,nonce不能重用
modparam("auth", "one_time_nonce", 1)
# 控制nonce_count和one_time_nonce的分区,默认:1,可以为:1,2,4,8,16,32,64
modparam("auth", "nid_pool_no", 4)
# 设置最大的nonce_count,默认:1048576(1Mb memory)
modparam("auth", "nc_array_size", 4194304)   # 4Mb
# 和nc_array_size功能一致,nc_array_size=2^nc_array_order,默认:20(1Mb memory)
modparam("auth", "nc_array_order", 22)
# 设置最大的one_time_nonce,默认:1048576(1M nonces, 128Kb memory)
modparam("auth", "otn_in_flight_no", 8388608)   # 8 Mb (1Mb memory)
# 和otn_in_flight_no等价,otn_in_flight_order=2^otn_in_flight_no,默认:20(1M nonces, 128Kb memory)
modparam("auth", "otn_in_flight_order", 23)   # 8 Mb (1Mb memory)
# 用于计算nonce的密钥,默认是随机生成的字符串
modparam("auth", "secret", "johndoessecretphrase")
# 设置nonce的过期时间,单位s,默认:300s
modparam("auth", "nonce_expire", 600)   # Set nonce_expire to 600s
# 设置nonce的最大漂移时间,单位s,默认:3s
modparam("auth", "nonce_auth_max_drift", 1)   # set max drift to 1 s
# 如果设置为1, 不管事务是否存在,www_challenge()和proxy_challenge()都返回无状态的回复. 0:有状态回复.
modparam("auth", "force_stateless_reply", 1)
# 设置realm前缀,默认:""
modparam("auth", "realm_prefix", "sip.")
# 设置算法,默认:MD5,可以为:SHA-256。SHA-256使用的空间是MD5的2倍
modparam("auth", "algorithm", "SHA-256")
#是否把authentication-info添加到200OK响应中,默认:no
modparam("auth", "add_authinfo_hdr", yes)

主要函数

consume_credentials()

删除认证信息,比如:删除Authorization头部,删除Proxy-Authorization头部.

has_credentials(realm)

检查请求的Authorization或者Proxy-Authorization头部是否包含realm.

www_challenge(realm, flags)

生成WWW-Authenticate头部给响应并发送401返回.

  • realm: 不能为空,在REGISTER请求中,可以用$td(To domain),其他请求中,可以用$fd(From domain).
  • flags: 可以为:
    • 1: 构建challenge头,使用qop=auth
    • 2: 构建challenge头,使用qop=auth-int
    • 4: 在一些失败的场景中,不发送500 Internal Server Error响应
    • 16: 构建challenge头,使用stale=true

proxy_challenge(realm, flags)

生成Proxy-Authenticate头部给响应并发送407返回.参数和www_challenge一样.

auth_challenge(realm, flags)

www_challenge()proxy_challenge()的合集. 如果请求是REGISTER请求,就生成authentication返回401,其他的请求,返回407

pv_www_authenticate(realm, passwd, flags [, method])

验证credentials是否正确,如果正确,标记此credentials已经验证了,如果失败,后面可以主动调用www_challenge()再次请求验证.

  • 返回值:

    • -1: 通用错误,不发送回复
    • -2: 密码错误
    • -4: nonce过期
    • -5: 请求没有包含realmAuthorization头部
    • -6: nonce已经被用过了
  • flags:

    • 1: password必须是HA1格式
    • 2: 构建不带qopWWW-Authenticate头部,并添加到avp变量
    • 4: 构建带qop=authWWW-Authenticate头部,并添加到avp变量
    • 8: 构建带qop=auth-intWWW-Authenticate头部,并添加到avp变量
    • 16: 构建带stale=trueWWW-Authenticate头部
    • 32: 身份验证失败时,nc不失效

pv_proxy_authenticate(realm, passwd, flags)

功能和pv_www_authenticate()一样,只是验证失败之后,可以调用proxy_challenge重新验证.

pv_auth_check(realm, passwd, flags, checks)

pv_www_authenticatepv_proxy_authenticate的合集

auth_get_www_authenticate(realm, flags, pvdest)

构建WWW-Authenticate头部,设置结果到pvdest变量.参数和pv_www_authenticate一致

auth_db 模块

参数解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 数据库连接
modparam("auth_db", "db_url", "DBURL")
# 是否使用HA1格式的密码来认证
modparam("auth_db", "calculate_ha1", 1)
# 是否使用domain来认证,默认:0
modparam("auth_db", "use_domain", 1)
# 设置从数据库获取的凭证
modparam("auth_db", "load_credentials", "$avp(i:123)=rpid;email_address")
# 是否检查subscriber表版本,默认:1
modparam("auth_db", "version_table", 0)

主要函数

www_authenticate(realm, table [, method])

验证credentials是否正确,如果正确,标记此credentials已经验证了,如果失败,后面可以主动调用www_challenge()再次请求验证.

  • 返回值:
    • -1: 通用错误,不发送回复
    • -2: 密码错误
    • -3: 用户不存在
    • -4: nonce过期
    • -5: 请求没有包含realmAuthorization头部
    • -6: nonce已经被用过了
    • -8: From/To/RURI中的用户和认证的用户不一致

www_authorize(realm, table)

www_authenticate功能一样

proxy_authenticate(realm, table)

验证credentials是否正确,如果正确,标记此credentials已经验证了,如果失败,后面可以主动调用proxy_challenge()再次请求验证.

proxy_authorize(realm, table)

proxy_authenticate功能一样

auth_check(realm, table, flags)

www_authenticateproxy_authenticate的合集

is_subscriber(uri, dbtable, flags)

检查uri是否是subscriber

实战

官方配置

  1. 官方配置文件:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
!define WITH_AUTH

listen=udp:172.16.4.111:5460

#!ifdef WITH_AUTH
loadmodule "auth.so"
loadmodule "auth_db.so"
#!ifdef WITH_IPAUTH
loadmodule "permissions.so"
#!endif
#!endif

#!ifdef WITH_AUTH
modparam("auth_db", "db_url", DBURL)
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "")
modparam("auth_db", "use_domain", MULTIDOMAIN)

# ----- permissions params -----
#!ifdef WITH_IPAUTH
modparam("permissions", "db_url", DBURL)
modparam("permissions", "load_backends", 1)
#!endif

#!endif
... 

request_route {
    ...
    route(AUTH);
    ...
}

route[AUTH] {
#!ifdef WITH_AUTH

#!ifdef WITH_IPAUTH
        if((!is_method("REGISTER")) && allow_source_address()) {
                # source IP allowed
                return;
        }
#!endif

        if (is_method("REGISTER") || from_uri==myself) {
                # authenticate requests
                if (!auth_check("$fd", "subscriber", "1")) {
                        auth_challenge("$fd", "0");
                        exit;
                }
                # user authenticated - remove auth header
                if(!is_method("REGISTER|PUBLISH"))
                        consume_credentials();
        }
        # if caller is not local subscriber, then check if it calls
        # a local destination, otherwise deny, not an open relay here
        if (from_uri!=myself && uri!=myself) {
                sl_send_reply("403", "Not relaying");
                exit;
        }

#!else

        # authentication not enabled - do not relay at all to foreign networks
        if(uri!=myself) {
                sl_send_reply("403", "Not relaying");
                exit;
        }

#!endif
        return;
}

官方使用的是auth_check, 由于数据表subscriber没有数据, 直接注册坐席会报错

  1. 输出结果:

sip信令图: sip

可以看到,当REGISTER第一次请求时,会返回401错误,并且WWW-Authenticate头部包含realmnonce. 软电话收到401之后,再次请求REGISTER时,由于subscriber没有用户,所以还是会返回401.

subscriber的结构为: subcriber

新增用户:

1
insert into `subscriber`('username','password')values('1004','1234');

再次注册电话成功: sip

自定义配置

配置文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
modparam("auth", "auth_checks_in_dlg", 15)
modparam("auth", "auth_checks_no_dlg", 9)
modparam("auth", "auth_checks_register", 11)
modparam("auth", "qop", "auth")   # set qop=auth
modparam("auth", "nonce_count", 1) # enable nonce_count support
modparam("auth", "nonce_expire", 600)   # Set nonce_expire to 600s
modparam("auth", "use_domain", 1)
modparam("auth", "algorithm", "SHA-256")
modparam("auth", "add_authinfo_hdr", yes)
modparam("auth", "realm_prefix", "sip.")

使用pv_www_authenticate

route(AUTH)路由中,修改:

1
2
3
4
 if (!auth_check("$fd", "subscriber", "1")) {
    auth_challenge("$fd", "0");
    exit;
 }

为:

1
2
3
4
5
6
7
$var(passwd)="1234";
xlog("L_INFO", "------$pr|$rm|$td------\n");
if(!pv_www_authenticate("$td","$var(passwd)", 4)) {
        www_challenge("$td", "1");
        xlog("L_INFO", "------authenticate failed.\n");
        exit;
}

输出的syslog:

1
2
3
4
5(3398) INFO: |25-05-22 15:16:31|b68c22b49064436190e4062acac56d5f| <script>: ------udp|REGISTER|172.16.4.111------
5(3398) INFO: |25-05-22 15:16:31|b68c22b49064436190e4062acac56d5f| <script>: ------authenticate failed.
6(3399) INFO: |25-05-22 15:16:31|b68c22b49064436190e4062acac56d5f| <script>: ------172.16.80.13:56001|172.16.4.111:5460|udp|REGISTER
6(3399) INFO: |25-05-22 15:16:31|b68c22b49064436190e4062acac56d5f| <script>: ------udp|REGISTER|172.16.4.111------

返回的401 sip图: sip

可以看到,由于配置参数设置了qopalgorithm, 所以返回的WWW-Authenticate头部包含qopalgorithm

软电话再次REGISTER的sip信令图: sip

可以看到,Authorization头部包含qopalgorithm

这种方式好处就是不用请求数据库,但是密码是存变量里,需要单独维护.

使用pv_proxy_authenticate

1
2
3
4
5
6
7
$var(passwd)="1234";
xlog("L_INFO", "------$pr|$rm|$td------\n");
if(!pv_www_authenticate("$td","$var(passwd)", 4)) {
        www_challenge("$td", "1");
        xlog("L_INFO", "------authenticate failed.\n");
        exit;
}

修改为:

1
2
3
4
5
6
7
$var(passwd)="1234";
xlog("L_INFO", "------$pr|$rm|$td------\n");
if(!pv_proxy_authenticate("$td","$var(passwd)", 4)) {
        proxy_challenge("$td", "1");
        xlog("L_INFO", "------authenticate failed.\n");
        exit;
}

返回407 sip图: sip

可以看到此时返回的是407, Proxy-Authenticate头部包含qopalgorithm

软电话再次REGISTER的sip信令图: sip

Proxy-Authorization头部包含qopalgorithm

总结

  1. 如果不想用数据库,单独使用auth,不过密码需要维护
  2. 如果想使用数据库,就使用auth_dbauth
  3. 如果想返回401,就用pv_www_authenticate,www_authenticatepv_auth_check
  4. 如果想返回407,就用pv_proxy_authenticate,proxy_authenticateauth_check
本博客已稳定运行
发表了37篇文章 · 总计67.06k字
本站总访问量 次 · 您是本站第 位访问者
粤ICP备2025368587号-1| 使用 Hugo 构建
主题 StackJimmy 设计