FRP多Token

原文地址: https://lzxz1234.cn/archives/201

简介

之前一直用 Ngrok,但 Ngrok 有比较严重的内存泄露问题,经常无故退出。所以最近更改到了 frp。

frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议。
由于 frp 默认只支持一个 token,这样当 token 泄露更改时会导致全部客户端不可用,所以就有了本次修改。

过程

先 fork 代码库 https://github.com/fatedier/frp,简单阅读后可知,登录时 token 校验逻辑位于 server/service.go 文件中第 320 行:

if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg) != loginMsg.PrivilegeKey {

所以我们可以新建 server/auth.go 主要处理授权逻辑:

package server

import (
    "database/sql"
    "github.com/fatedier/frp/models/msg"
    "github.com/fatedier/frp/utils/util"
    _ "github.com/mattn/go-sqlite3"
)

var db *sql.DB

type User struct {
    UserName string
    Token string
}

func GetAuthKey(loginMsg *msg.Login) (key string) {

    user := GetUser(loginMsg.User)
    key = util.GetAuthKey(user.Token, loginMsg.Timestamp)
    return
}

func GetToken(loginMsg *msg.Login) (key string) {

    user := GetUser(loginMsg.User)
    key = user.Token
    return
}

func GetUser(userName string) (user User) {

    if db == nil {
        db, _ = sql.Open("sqlite3", "/sqlite3.db")
        db.Exec("create table if not exists user(user_name varchar(128), token varchar(128))")
    }

    stmt, _ := db.Prepare("SELECT user_name, token FROM user where user_name = ?")
    defer stmt.Close()
    rows, _ := stmt.Query(userName)
    defer rows.Close()

    if rows.Next() {
        rows.Scan(&user.UserName, &user.Token)
    }
    return
}

此处将用户信息存储在了 sqlite3 中,同时将用户做成实体,后续可以根据用户限制代理类型个数等。同时由于引用了 github.com/mattn/go-sqlite3 包,所以也需要在 vendor 目录里添加对应包。

其它修改

除了登录时的 token 校验,还有其它地方也需要同步修改,主要是 server/control.go 和 server/proxy.go,这两个文件的修改主要用于替换 token 的获取逻辑,由全局获取改为数据库获取:

control.go
212 行改为:

encWriter, err := crypto.NewWriter(ctl.conn, []byte(GetToken(ctl.loginMsg)))

242 行改为:

encReader := crypto.NewReader(ctl.conn, []byte(GetToken(ctl.loginMsg)))

proxy.go
311 行改为:

rwc, err = frpIo.WithEncryption(rwc, []byte(GetToken(pxy.ctl.loginMsg)))

669 行改为:

local, err = frpIo.WithEncryption(local, []byte(GetToken(pxy.GetControl().loginMsg)))

All Done!
到此重新编辑部署就可以了,更多还可以删除 cmdfrps 目录里启动命令里的 Token 字段设置,不改也无所谓,因为设置也不会生效了