fatedier / frp

A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet.
Apache License 2.0
86.08k stars 13.32k forks source link

服务端如何优雅地控制客户端的连接 #4326

Closed Developer024 closed 3 months ago

Developer024 commented 4 months ago

Describe the feature request

业务场景: 每个分发的客户端都限制了流量,如果某个客户端的流量达到了设置的阈值则应该断开服务端的连接。

当前想到几种解决方式,但是都有缺陷。请问各位还有更好的方法吗?

使用心跳包处理 (目前在用)

使用 RPC 的 Ping 接口收到客户端发送的心跳包进行流量鉴权处理。这种做法虽然可以实现业务需求,但是 客户端可以自行修改配置文件,控制ping的频率,如果客户端把心跳频率设置到1s,则后端鉴权压力过大。 不希望把这个权限下放到客户端,但是那几个rpc接口好像都不会上传心跳包的设置,也就无法做到服务端下发修改覆盖。

另外,如果使用了tcp_mux_keepalive_interval功能,则Ping接口则不会收到心跳数据。

使用连接数进行处理 (不优雅)

使用 NewUserConn接口进行流量鉴权处理。把客户端的每一条连接数都进行鉴权,但如果客户端的连接数特别高,或遭遇CC攻击,那对后端的处理也是毁灭型的破坏。

请问还有什么方式可以做到这个场景的需求吗?

Describe alternatives you've considered

No response

Affected area

fatedier commented 4 months ago

目前还没有这方面的开发计划。

adminxy commented 3 months ago

感谢大佬的回复和解决方案,我会持续关注这个issues ,有好的办法和思路我也会及时在本issues下反馈分享

adminxy commented 3 months ago

我之前准备的实现思路是,每天定时吧frps重启,强制所有客户端重新链接,进行校验,但是这样会对其他客户端/用户,的体验不是很好

adminxy commented 3 months ago

然后还有一个思路 是从这个插件【frps-auth】 看到的 仓库地址:https://github.com/chi-cat/frps-auth image 这个插件的作者实现思路,是修改了frps的源码,提供了结束代理方法

在您回复之前,我也打算尝试修改frps源码实现这一功能,但是无奈我不太会go语言,但是我也在尝试自己修改,如果可以的话,我会开源一个 会长期维护,并且包含关闭指定代理的 方法或api 的版本 ,

当然,如果有大佬愿意修改并开源,那就更好了

adminxy commented 3 months ago

目前还没有这方面的开发计划。

我在 插件《 frps-auth 仓库地址:https://github.com/chi-cat/frps-auth 》 注意到这样一句话,

frps-aut作者有提到: https://github.com/fatedier/frp/issues/4326#issuecomment-2219662622 (截屏标注位置) 【根据和frp作者大大沟通;在frp的dev分支上已经加入了类似的api,此功能可能会在dev合并到master分支后发生更改。】

想问一下您,现在frp中, 有这个api么,有的话有相关说明文档吗

Developer024 commented 3 months ago

我之前准备的实现思路是,每天定时吧frps重启,强制所有客户端重新链接,进行校验,但是这样会对其他客户端/用户,的体验不是很好

Ping接口的心跳包是可以做实时校验的。正如此Issue描述的那样。使用 服务端RPC接口的 Ping 即可。

但是此Issue引伸出了另外一个问题,如果把心跳包的权限给客户端, 由客户端决定心跳频率并不是很好。

adminxy commented 3 months ago

我之前准备的实现思路是,每天定时吧frps重启,强制所有客户端重新链接,进行校验,但是这样会对其他客户端/用户,的体验不是很好

Ping接口的心跳包是可以做实时校验的。正如此Issue描述的那样。使用 服务端RPC接口的 Ping 即可。

但是此Issue引伸出了另外一个问题,如果把心跳包的权限给客户端, 由客户端决定心跳频率并不是很好。

确实是有这个问题,如果使用Ping、NewUserConn的话,不是和优雅,

特别是ping 会使服务器变得很被动,而且会给服务器造成很大的压力,

我感觉最优雅的处理办法还是 【修改了frps的源码,提供了结束代理 方法、api(可以限制一下,只能本机调用)】

但是无奈go,我并不熟练,但是我目前也在看frp的源代码了

Developer024 commented 3 months ago

我之前准备的实现思路是,每天定时吧frps重启,强制所有客户端重新链接,进行校验,但是这样会对其他客户端/用户,的体验不是很好

Ping接口的心跳包是可以做实时校验的。正如此Issue描述的那样。使用 服务端RPC接口的 Ping 即可。 但是此Issue引伸出了另外一个问题,如果把心跳包的权限给客户端, 由客户端决定心跳频率并不是很好。

确实是有这个问题,如果使用Ping、NewUserConn的话,不是和优雅,

特别是ping 会使服务器变得很被动,而且会给服务器造成很大的压力,

我感觉最优雅的处理办法还是 【修改了frps的源码,提供了结束代理 方法、api(可以限制一下,只能本机调用)】

但是无奈go,我并不熟练,但是我目前也在看frp的源代码了

是的,较优解当然是可以提供一个由服务端主动关闭连接的api。

不过如果想工作量小一点的话,我想是增强 NewProxy 这个RPC接口,可以上报客户端的心跳配置,再由鉴权服务器进行修改下发,从服务端就可以控制客户端的上报频次。

adminxy commented 3 months ago

我之前准备的实现思路是,每天定时吧frps重启,强制所有客户端重新链接,进行校验,但是这样会对其他客户端/用户,的体验不是很好

Ping接口的心跳包是可以做实时校验的。正如此Issue描述的那样。使用 服务端RPC接口的 Ping 即可。 但是此Issue引伸出了另外一个问题,如果把心跳包的权限给客户端, 由客户端决定心跳频率并不是很好。

确实是有这个问题,如果使用Ping、NewUserConn的话,不是和优雅, 特别是ping 会使服务器变得很被动,而且会给服务器造成很大的压力, 我感觉最优雅的处理办法还是 【修改了frps的源码,提供了结束代理 方法、api(可以限制一下,只能本机调用)】 但是无奈go,我并不熟练,但是我目前也在看frp的源代码了

是的,较优解当然是可以提供一个由服务端主动关闭连接的api。

不过如果想工作量小一点的话,我想是增强 NewProxy 这个RPC接口,可以上报客户端的心跳配置,再由鉴权服务器进行修改下发,从服务端就可以控制客户端的上报频次。

这样确实也可以

强制客户端设置心跳在合理范围内,就允许创建这个代理,

但是这个也要涉及到改动frp代码。原版的frp是不支持这个功能的

当然这个要比做一个关闭代理api简单,

但是你这个 【 增强 NewProxy 】的方案 这个我突然想到了几个问题:

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。

2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

Developer024 commented 3 months ago

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。

2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

这个得看源码怎么实现的了,我这一部分功能我还没去看过。

RPC接口是有替换能力的,直接从服务端拦截下发覆盖即可 文档引用

等有时间我看看提个pr

adminxy commented 3 months ago

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。 2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

这个得看源码怎么实现的了,我这一部分功能我还没去看过。

RPC接口是有替换能力的,直接从服务端拦截下发覆盖即可 文档引用

等有时间我看看提个pr

我等测试一下这个的效果,如果服务器可以直接拦截覆盖,这样看起来就没多大的问题了,我有时间测试一下,效果如何

adminxy commented 3 months ago

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。 2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

这个得看源码怎么实现的了,我这一部分功能我还没去看过。

RPC接口是有替换能力的,直接从服务端拦截下发覆盖即可 文档引用

等有时间我看看提个pr

我在【 https://gofrp.org/zh-cn/docs/features/common/server-plugin/#%E6%8E%A5%E5%8F%A3 】里面并没有找到,那个RPC 请求请求,会携带客户端的心跳时间配置,以及如何修改返回客户端配置

如果可以的话,方便提供一个示例么

Developer024 commented 3 months ago

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。 2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

这个得看源码怎么实现的了,我这一部分功能我还没去看过。 RPC接口是有替换能力的,直接从服务端拦截下发覆盖即可 文档引用 等有时间我看看提个pr

我在【 https://gofrp.org/zh-cn/docs/features/common/server-plugin/#%E6%8E%A5%E5%8F%A3 】里面并没有找到,那个RPC 请求请求,会携带客户端的心跳时间配置,以及如何修改返回客户端配置

如果可以的话,方便提供一个示例么

你的问题,正如此issue最开始的描述。但是那几个rpc接口好像都不会上传心跳包的设置,也就无法做到服务端下发修改覆盖。 如果需要携带此参数需要修改源码提交pr,这是我认为完成需求最简便的方法。

关于您说的 如何修改返回客户端配置, 请查看文档中关于RPC返回替换部分描述。

允许且需要替换操作内容

{
    "unchange": false,
    "content": {
        ... // 替换后的操作信息,格式必须和请求时的一致
    }
}
adminxy commented 3 months ago

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。 2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

这个得看源码怎么实现的了,我这一部分功能我还没去看过。 RPC接口是有替换能力的,直接从服务端拦截下发覆盖即可 文档引用 等有时间我看看提个pr

我在【 https://gofrp.org/zh-cn/docs/features/common/server-plugin/#%E6%8E%A5%E5%8F%A3 】里面并没有找到,那个RPC 请求请求,会携带客户端的心跳时间配置,以及如何修改返回客户端配置 如果可以的话,方便提供一个示例么

你的问题,正如此issue最开始的描述。但是那几个rpc接口好像都不会上传心跳包的设置,也就无法做到服务端下发修改覆盖。 如果需要携带此参数需要修改源码提交pr,这是我认为完成需求最简便的方法。

关于您说的 如何修改返回客户端配置, 请查看文档中关于RPC返回替换部分描述。

允许且需要替换操作内容

{
    "unchange": false,
    "content": {
        ... // 替换后的操作信息,格式必须和请求时的一致
    }
}

比如我要把心跳包设置为10 您看这样对吗 { "unchange": False, "content": {'user': {'user': 'fake',"transport.heartbeatInterval ":"10", 'metas': {'token': 'fake', 'version': '1.0.0'}, 'run_id': '705bc8dfd7d73481'}} }

我这样设置客户端似乎并没有生效,您看下是不是哪里不对

Developer024 commented 3 months ago

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。 2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

这个得看源码怎么实现的了,我这一部分功能我还没去看过。 RPC接口是有替换能力的,直接从服务端拦截下发覆盖即可 文档引用 等有时间我看看提个pr

我在【 https://gofrp.org/zh-cn/docs/features/common/server-plugin/#%E6%8E%A5%E5%8F%A3 】里面并没有找到,那个RPC 请求请求,会携带客户端的心跳时间配置,以及如何修改返回客户端配置 如果可以的话,方便提供一个示例么

你的问题,正如此issue最开始的描述。但是那几个rpc接口好像都不会上传心跳包的设置,也就无法做到服务端下发修改覆盖。 如果需要携带此参数需要修改源码提交pr,这是我认为完成需求最简便的方法。 关于您说的 如何修改返回客户端配置, 请查看文档中关于RPC返回替换部分描述。

允许且需要替换操作内容

{
    "unchange": false,
    "content": {
        ... // 替换后的操作信息,格式必须和请求时的一致
    }
}

比如我要把心跳包设置为10 您看这样对吗 { "unchange": False, "content": {'user': {'user': 'fake',"transport.heartbeatInterval ":"10", 'metas': {'token': 'fake', 'version': '1.0.0'}, 'run_id': '705bc8dfd7d73481'}} }

我这样设置客户端似乎并没有生效,您看下是不是哪里不对

请仔细看此issue提供的问题以及刚才给你回复的comment,其中有提到

        ... // 替换后的操作信息,格式必须和请求时的一致

现在的问题是 客户端都没有上报这个参数,你直接进行下发自然不会生效。

adminxy commented 3 months ago

1.客户端 必须使用修改过配套的frpc,这对于用户来说不是很友好,而且有的设备比如软路由,他的frpc一般是系统内置的,更换起来并不方便。 2.假如我的客户端是经过我修改的,我的frpc,创建代理的时候的时候,请求参数里面的心跳时间直接设置成任意的,但是其实我的frpc客户端是不发送心跳的

这个得看源码怎么实现的了,我这一部分功能我还没去看过。 RPC接口是有替换能力的,直接从服务端拦截下发覆盖即可 文档引用 等有时间我看看提个pr

我在【 https://gofrp.org/zh-cn/docs/features/common/server-plugin/#%E6%8E%A5%E5%8F%A3 】里面并没有找到,那个RPC 请求请求,会携带客户端的心跳时间配置,以及如何修改返回客户端配置 如果可以的话,方便提供一个示例么

你的问题,正如此issue最开始的描述。但是那几个rpc接口好像都不会上传心跳包的设置,也就无法做到服务端下发修改覆盖。 如果需要携带此参数需要修改源码提交pr,这是我认为完成需求最简便的方法。 关于您说的 如何修改返回客户端配置, 请查看文档中关于RPC返回替换部分描述。

允许且需要替换操作内容

{
    "unchange": false,
    "content": {
        ... // 替换后的操作信息,格式必须和请求时的一致
    }
}

比如我要把心跳包设置为10 您看这样对吗 { "unchange": False, "content": {'user': {'user': 'fake',"transport.heartbeatInterval ":"10", 'metas': {'token': 'fake', 'version': '1.0.0'}, 'run_id': '705bc8dfd7d73481'}} } 我这样设置客户端似乎并没有生效,您看下是不是哪里不对

请仔细看此issue提供的问题以及刚才给你回复的comment,其中有提到

        ... // 替换后的操作信息,格式必须和请求时的一致

现在的问题是 客户端都没有上报这个参数,你直接进行下发自然不会生效。

好的,了解了,我以为是客户端有上报呢,感谢大佬

lmxslpc commented 3 months ago

NPS不就是服务端控制的么