coTurn配置(主要是客户端登录验证)及验证原理分析

想支持WebRTC服务器coTurn必须开启long-term credentials mechanism,也就是加上-a选项。

一、最简单不使用数据库直接设置密码


./turnserver -a --no-tls --no-dtls --no-stun -u testuser:testpwd -r myrealm -v
./turnutils_peer -v
./turnutils_uclient -u testuser -w testpwd -e 192.168.0.100 -r 3480 192.168.0.100

这儿的realm设置好像没鸟用,客户端并不需要设置(也没得设置)就可以登录。不过使用下面的方法就要用到了。

二、使用TURN REST API


需要开启--use-auth-secret。这个所谓的TURN REST API主要意思就是给临时密码设置个过期时间,就像oauth里的accesstoken。用户每次先来你自己开发的REST API取出临时密码和密码过期时间,然后再去coTurn验证登录。
REST API和coTurn都访问你指定的数据库来获取密码。
当然这个TURN REST API你可以随便用自己的喜欢的方式来做,比如RESTFUL API,自定义的tcp或者udp消息,websocket......只要能跟客户端通讯就可以。

--use-auth-secret TURN REST API flag.
Flag that sets a special WebRTC authorization option
that is based upon authentication secret. The feature purpose
is to support "TURN Server REST API" as described in
the TURN REST API section below.
This option uses timestamp as part of combined username:
usercombo -> "timestamp:username",
turn user -> usercombo,
turn password -> base64(hmac(input_buffer = usercombo, key = shared-secret)).
This allows TURN credentials to be accounted for a specific user id.
If you don't have a suitable id, the timestamp alone can be used.
This option is just turns on secret-based authentication.
The actual value of the secret is defined either by option static-auth-secret,
or can be found in the turn_secret table in the database.

时间戳和user的值合并成usercombo:"timestamp:username",这个timestamp就是这个临时密码的有效时间,如果当前时间比这个时间大的话就表示过期无效了。
password的值为:
base64(hmac(input_buffer = usercombo, key = shared-secret))
这里面的shared-secret的值由--static-auth-secret选项定义,如果没有开启此选项则在数据库的turn_secret表里查找。

turn_secret表里可以设置多个不同的secret key,coTurn在验证密码时会遍历获取此表中所有的secret计算签名,直到成功或者遍历完成。

补充:

客户端访问http restful api接口取到要登录turn的用户名和密码。
这个用户名是包含了时间戳和用户名,称为usercombo,格式为:timestamp:username。
密码也是restful api计算返回的,使用上面的算法传入usercombo和shared-secret计算出密码返回给客户端。
shared-secret可以在turnserver启动时--static-auth-secret选项指定,也可以在数据库的turn_secret表中指定,而且可以多个,restful api可以随机取一个进行计算,turnserver在校验"密码"时会遍历所有的shared-secret一个一个计算,跟用户上传的"密码"进行对比验证。
这儿使用多个shared-secret的好处是:每次用户到restful api获取密码时,因为shared-secret从多个里面随机选一个,密码随着shared-secret的不同而变化,这样可以增加黑客的破解难度。
上面所说的turnserver验证的"密码"其实不是从restful api取回的密码,而是经过md5算法算出的key,算法在下面有讲到。
时间戳的作用:表示密码的有效时间。
当超过预定时间时服务器重新计算更新数据库存储的username。
密码过期后客户端必须重新从restfulapi获取新的usercombo和password才能登录turn。

三、coTurn验证登录原理


coTurn验证客户端登录的过程其实是验证签名的过程。
WebRTC通过TURN REST API拿到上面计算出的password以后, 也使用MD5(user:realm:password)计算出key,然后在turn协议中使用HMAC-SHA1和key签名自己的内容,并且把签名也附加到内容后面。
coTurn收到WebRTC用户登录的username、realm和自己表中的shared-secret实时计算出key,然后也使用key对WebRTC传输过来的内容进行HMAC-SHA1签名,然后跟WebRTC的签名进行对比,如果一样则通过验证。

WebRTC本身没有realm,这个realm是跟coTurn登录前握手得到的。WebRTC登录消息提供username、收到的realm和HMAC签名。

coTurn代码在:

userdb.c中的
get_user_key函数里

在验证WebRTC过程中,coTurn只去数据库中取了secret列表,并没有取用户的password,验证只是验证WebRTC的消息包的签名而已,其实一切安全的保障在于secret,如果secret泄露则系统就被破解了,黑客可以使用secret和任何用户名登录。虽然可以有多个secret但是只要一个secret泄露了都完蛋,多个secret也许是为了增加黑客碰撞猜测secret的难度?当password过期以后,TURN REST API要重新生成新的password,此时去secret列表中随机取一个secret进行加密得到password。
因为secretr的重要性所以secret最好长一点比如32字节随机值,使用一段时间后需要动态更新,以防止被黑客碰撞猜测出来。
当然secret列表太长的话肯定影响登录验证速度,因为coTurn要去遍历secret列表一个个验证。

四、使用Redis数据库


正常情况下一般要把每个客户端的用户名和密码一起存在数据库并且加密。coTurn支持大多数数据库,我选择Redis。

secret表使用集合存储:
smembers turn/realm/<realm>/secret

password(key)表,使用key存储:
get turn/realm/<realm>/user/<user>/key

其他设置参考schema.userdb.redis文件。

五、oauth支持


coTurn支持oauth,但是目前版本的WebRTC还不支持oauth,一开始我看了WebRTC1.0草案,里面写了支持oauth,其实只是草案计划而目前还没有实现,这个误导了我好久,MD。
所以现在我也不测试coTurn的oauth了。

六、admin user做什么用的


原来coTurn还提供了一个HTTPS页面:

turn_admin_server.c:
static void write_https_home_page(ioa_socket_handle s)

这个函数输出一个页面,可以进行一些系统设置,比如修改realm,查看users等。
admin用户就是用来访问这个页面。

coTurn不仅支持https管理页面还支持Telnet。
在启动turnserver时可以使用--no-http关闭https管理页面,使用--no-cli关闭Telnet支持。