找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 245|回复: 0

FRP从数据库获取Token和User

[复制链接]

28

主题

1

回帖

148

积分

管理员

积分
148
发表于 2024-8-20 09:27:44 | 显示全部楼层 |阅读模式


2019-05-13

FRP从数据库获取Token和User (minirplus.com)

FRP从数据库获取Token和User
环境
目标
  • 客户端连接时查询Mysql数据库中是否存在对应用户ID
  • 若存在用户ID则从数据库中查询对应的token进行客户端验证
  • 若不存在则返回连接失败
调试
获取FRP源码
  1. git clone https://github.com/fatedier/frp.git $GOPATH/src/github.com/fatedier/frp
复制代码

编译源码

  1. cd $GOPATH/src/github.com/fatedier/frp
  2. make
复制代码
测试运行

  1. cd $GOPATH/src/github.com/fatedier/frp/bin
  2. ./frps -c ./frps.ini
复制代码
分析
FRP的主要入口是github.com/fatedier/frp/server/service.go
主要验证代码在第349-353行
  1. // Check auth.
  2. if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
  3.         err = fmt.Errorf("authorization failed")
  4.         return
  5. }
复制代码
其中调用了util.GetAuthKey函数,这个函数在github.com/fatedier/frp/utils/util/util.go中的第43-49行
  1. func GetAuthKey(token string, timestamp int64) (key string) {
  2.         token = token + fmt.Sprintf("%d", timestamp)
  3.         md5Ctx := md5.New()
  4.         md5Ctx.Write([]byte(token))
  5.         data := md5Ctx.Sum(nil)
  6.         return hex.EncodeToString(data)
  7. }
复制代码

综合这两段代码可以发现frp中对客户端的验证方法主要是通过获取服务器frps.ini配置文件中的token字段和timestamp组合求MD5 hash之后再进行16进制转换后再和客户端传递的PrivilegeKey值进行对比来验证。当然,客户端提交的PrivilegeKey也是这样计算的。
那么如果要接入数据库,就需要改造这个验证
通过新建验证函数,将token的获取方式从配置文件改为数据库,后续的hash和hex都继续保留即可。
但这仅仅是客户端的第一次验证环节,frp在客户端成功注册后,会马上进行控制层的数据交换,这时就会遇到第二个使用token的环节,既控制层的数据是用token加密的。
这个环节在github.com/fatedier/frp/server/control.go的第226-311行,关键行如下
  1. encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbServerCfg.Token))
  2. encReader := crypto.NewReader(ctl.conn, []byte(g.GlbServerCfg.Token))
复制代码
可以看到,其中的加密都用到了token,也需要用新的获取token的方法更换
在这步完成之后,客户端的注册和控制数据的交互已经没有问题了,但是紧接着会出现第三个使用token的环节,既http代理有一个数据加密的选项,如果开启加密,那么将使用token对http数据流进行加密。
这个环节在github.com/fatedier/frp/server/proxy/http.go的第116行
  1. rwc, err = frpIo.WithEncryption(rwc, []byte(g.GlbServerCfg.Token))
复制代码
其中的token也需要使用数据库方法替换,但是这里有一个坑,就是http.go太底层了,导致执行的时候并没有客户端的认证信息被传递到这里,所以没有办法获取用户ID。
解决方法就是在github.com/fatedier/frp/server/control.go的第427行在创建Proxy的时候手动传递一个用户ID给底层
  1. pxy, err := proxy.NewProxy(ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.loginMsg.User)
复制代码
然后在github.com/fatedier/frp/server/proxy/proxy.go的第145行接住这个用户ID
  1. func NewProxy(runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int,
  2.         getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, loginUser string) (pxy Proxy, err error) {
复制代码
然后在后面的basePxy中增加这个用户ID字段,使得在http.go里可以调用
  1. basePxy := BaseProxy{
  2.                 name:           pxyConf.GetBaseInfo().ProxyName,
  3.                 rc:             rc,
  4.                 statsCollector: statsCollector,
  5.                 listeners:      make([]frpNet.Listener, 0),
  6.                 poolCount:      poolCount,
  7.                 getWorkConnFn:  getWorkConnFn,
  8.                 Logger:         log.NewPrefixLogger(runId),
  9.                 loginUser:      loginUser,
  10.         }
复制代码
当然除了http外,tcp也有一个use_encryption的选项,也是用的token进行的加密,在github.com/fatedier/frp/server/proxy/proxy.go的第212行,也需要替换
  1. local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token))
复制代码
可以在proxy.go增加一个函数用于调用basePxy里传递过来的用户ID
  1. func (pxy *BaseProxy) GetLoginUser() string {
  2.         return pxy.loginUser
  3. }
复制代码

Know More
https://lzxz1234.cn/archives/201



2019-05-13

FRP从数据库获取Token和User (minirplus.com)

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|整天BBB

GMT+8, 2025-1-10 14:17 , Processed in 0.105093 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表