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
84.88k stars 13.19k forks source link

通过https访问穿透的页面偶尔会出现http 400错误 #2141

Closed saixin closed 3 years ago

saixin commented 3 years ago

客户端配置 [apihttps] type = https custom_domains = www.test.com plugin = https2http plugin_local_addr = 127.0.0.1:1231 plugin_crt_path = /home/ssl/.crt plugin_key_path = /home/ssl/.key plugin_host_header_rewrite = 127.0.0.1 plugin_header_X-From-Where = nginx

[apihttp] type = http local_ip = 127.0.0.1 local_port = 1231 custom_domains=www.test.com

服务端配置 [common] bind_port = 7000

http和https

vhost_http_port = 80 vhost_https_port = 443

异常信息 客户端配置了https穿透和http穿透,指向同一个端口, 通过https访问穿透的页面偶尔会出现http 400错误,暂时没找到错误的规律,而通过http访问穿透的页面则不会出现http 400错误 ![Uploading 屏幕截图 2020-12-09 092625.jpg…]()

saixin commented 3 years ago

屏幕截图 2020-12-09 092625

yuyulei commented 3 years ago

frpc & frps 两边有异常日志吗?然后你的站点服务有对应 400 这条请求的(access/error或其他)日志吗?

ihappywall commented 3 years ago
frps.ini
[common]
bind_port = 7000
token=xxxxxxxxxxxx
vhost_http_port = 8081
vhost_https_port = 8082
log_file = /home/frp/frps.log
log_level = trace
log_max_days = 7

dashboard_port=7500
dashboard_user=xxx
dashboard_pwd=xxx
frpc.ini
[common]
server_addr = xx.xx.xxx.xxx
server_port = 7000
token=xxxxxxxxxx

log_file = ./frps.log
log_level = info
log_max_days = 7

[test_htts2http]
type = https
custom_domains = xxxx.xxxxxxxxxx.com
plugin = https2http
plugin_local_addr = xxx.xxx.x.x:8080
plugin_crt_path = D:/xxx/xxx/xxxx/xxxxxx.com.pem
plugin_key_path = D:/xxx/xxx/xxxx/xxxxxx.com.key
plugin_host_header_rewrite = xxx.xxx.x.x
jquery脚本 发送post请求
      var $inputFile = $(e.currentTarget);
      var formData = new FormData();
      formData.append("picture", $inputFile[0].files[0]);

      $.ajax({
        url: url,
        type: 'post',
        dataType: 'json',
        data: formData,
        contentType: false,
        processData: false,
        success: function(json) {},
        error: function(xhr, ajaxOptions, thrownError) {
          console.log(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
          alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
        }
      });

我也是出现了400 bad request问题。 就是在火狐浏览器上用 post multiple/form-data方式上传10几k的图片,有时候可以,有时候就返回400错误。 在谷歌浏览器就没有这个问题。 首先可以确定不是web后端程序问题,在出现400错误时,后端的访问日志看不到访问记录。应该也不是前端的问题。 然后看frps.log 和 frpc.log 都看不到什么相关的错误日志。 看了一下go程序: 在 /pkg/plugin/client/http_proxy.go文件

func (hp *HTTPProxy) handleConnectReq(req *http.Request, rwc io.ReadWriteCloser) {
    defer rwc.Close()
    if ok := hp.Auth(req); !ok {
        res := getBadResponse()
        res.Write(rwc)
        return
    }

    remote, err := net.Dial("tcp", req.URL.Host)
    if err != nil {
        res := &http.Response{
            StatusCode: 400,
            Proto:      "HTTP/1.1",
            ProtoMajor: 1,
            ProtoMinor: 1,
        }
        res.Write(rwc)
        return
    }
    rwc.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))

    frpIo.Join(remote, rwc)
}

这里有返回了400错误,却不打印任何日志。会不会是这个地方的错误,net.Dial的错误。

github-actions[bot] commented 3 years ago

Issues go stale after 45d of inactivity. Stale issues rot after an additional 10d of inactivity and eventually close.

ax2009live commented 3 years ago

我也出现过类似的问题,在用nginx反向代理的时候出现过此类问题,我以为是设置的问题;后面在正常的网站也出现此问题;

为了验证这个问题,我用全新的多个vps和多个全新的网站去测试,确定frps服务器在处理https存在bug, 我测试过多个frps版本,包括最后的版本;

问题示意描述: frps服务器: ip 11.11.11.11
frpc客户端A: ip 22.22.22.22 终端域名 A.client.com ip-192.168.0.20:443 支持http/2 HSTS (跟http/2有关系?) frpc客户端B: ip 33.33.33.33 终端域名 B.client.com ip-192.168.0.30:443 支持http/1.1

测试:用装在电视的chrome浏览器和手机浏览器测试, 当访问 https://B.client.com,服务器11.11.11.11会引导浏览器指向33.33.33.33指定主机,浏览很正常; 在同一浏览器下,打开https://A.client.com,服务器11.11.11.11会引导浏览器指向22.22.22.22指定主机,浏览也正常; 但是反过来再去访问https://B.client.com,瞬间报错404,静态、动态的页面都测试过; 过一段时间在去访问https://B.client.com,可以正常显示, 但访问https://A.client.com后再去访问https://B.client.com,问题又会出现;

先访问A.client.com,再去访问B.client.com,也会报错;

反过来再去访问https://B.client.com报错,估计:frps把ip指向了A.client.com的ip,这样浏览器便会报错;

为了验证此问题,我特意在主机 ip-192.168.0.20 用nginx反向代理做了两个站点, 一个域名是A.client.com, 一个B.client.com (这个站点的内容跟frpc客户端B- ip-192.168.0.30站点内容是不一样的)

frpc客户端A的配置只设置了一个https://A.client.com,

测试: (外网访问,访问的ip跟 frpc客户端A 和 frpc客户端B 都不在同一网段) 访问https://A.client.com, 再访问https://B.client.com,不会报错, 会访问到frpc客户端A ip-192.168.0.20 下定制B.client.com,而不是我们想要的 “ frpc客户端B ip-192.168.0.30的B.client.com” 这很明显访问B.client.com,ip指向了frpc客户端A的ip

这种https的ip错乱,让frpc配置的https主机被访问很慢;

我是把浏览器装在安卓盒子下,问题很容易显现,手机浏览器也是; 电脑上的浏览器缓存大,要多试几次才会出现;

不经过frps去访问网站都是正常的;

楼上说的http正常,我没有仔细测试过;http的站点不安全,我一般不用;

此问题未解决之前,通过frp部署https网站,,很麻烦; 如有使用者的网站是http/2, 其他人的httpss网站都受到影响;

猜测:http/2 多路复用底层采用"增加二进制分帧层"的方法,frps 在处理http/2 报文的时候有问题, 实际测试:处理http/1.1 的网站不存在此问题 ;

希望大神 @fatedier 能早日解决这个问题;

fatedier commented 3 years ago

@ax2009live 能够提供一些本地简单的复现手段吗?

ax2009live commented 3 years ago

@ax2009live 能够提供一些本地简单的复现手段吗? 不是很明白: 复现手段?你们没有搭建测试吗? 单独发了邮件到您的gmail邮箱!

sillyguy2021 commented 3 years ago

我也遇到同樣的現象, 開始以為自己服務器設置問題, @fatedier 大神有在著手修補這個問題嗎? 現在部署的https網站時不時出現訪問異常。 望盡快解決,感謝!

fatedier commented 3 years ago

@sillyguy2021 最近比较忙,不太有时间排查,需要可以的话,希望能有人能帮忙排查一下这个问题。

sillyguy2021 commented 3 years ago

這個功能很關鍵,影響面不小,希望有熱心的能人幫忙排查一下;

fakeboboliu commented 3 years ago

热心人路过,特地按照上面 https://github.com/fatedier/frp/issues/2141#issuecomment-774131794 语焉不详言语混乱高亮奇妙的描述复现了一下,复现失败

ihappywall commented 3 years ago

我解决了,就是 frpc 不要用 https2http

frps.ini
[common]
bind_port = 7000
token=xxxxxxxxxxxx
vhost_http_port = 80xx
vhost_https_port = 80xx
log_file = /home/frp/frps.log
log_level = trace
log_max_days = 7

dashboard_port=7500
dashboard_user=xxx
dashboard_pwd=xxx
frpc.ini
[common]
server_addr = xx.xx.xxx.xxx
server_port = 7000
token=xxxxxxxxxx

log_file = ./frps.log
log_level = info
log_max_days = 7

[https]
type = https
local_ip = 127.0.0.1
local_port = 80xx
custom_domains = xxxx.xxxxxxxxxx.com

我的问题是 frpc 使用 https2http,在火狐浏览器上传文件时经常出现 400 bad request,在谷歌浏览器没有问题。apache日志是看不到任何访问日志的,搜到了几篇文章才知道是 https2http 的问题。 总之最佳的是 frps -> frpc(不处理https) -> apache(处理https)

我还遇到的一个问题是,一开始我用的是 nginx -> frps -> frpc -> apache,最好也不要在 frps 前加 nginx 什么的,经过 nginx 加解密之后传给 frps 跟 直接传给 frps 可能不一样,有时候可能会出现问题。

我参考的几个链接: https://cloud.tencent.com/developer/article/1541075 https://my.oschina.net/timebear/blog/4547704 https://github.com/fatedier/frp/issues/520

saixin commented 3 years ago

CSRF(Cross-site request forgery)是跨站请求伪造,也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。 看看是不是你的项目启用了拦截验证,我的项目遇到了这个情况,我把请求错误的方法屏蔽CSRF验证,400错误就没了。

ax2009live commented 3 years ago

CSRF(Cross-site request forgery)是跨站请求伪造,也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。 看看是不是你的项目启用了拦截验证,我的项目遇到了这个情况,我把请求错误的方法屏蔽CSRF验证,400错误就没了。

可能像你说的那样,但是没时间折腾 CSRF验证 的问题,开启 CSRF验证 应该有它的道理;

我现在测试,两个网站就在同一个 froc 两个网站打开也有类似的问题,

[a.ax2009live.com.com]
type = https
local_ip = 172.18.0.199
local_port = 443
use_encryption = true
use_compression = true
custom_domains = a.ax2009live.com
tls_enable = true
proxy_protocol_version = v2

[b.ax2009live.com-443]
type = https
local_ip =172.18.0.200
local_port = 443
use_encryption = true
use_compression = true
custom_domains = b.ax2009live.com
proxy_protocol_version = v2

访问 https://b.ax2009live.com 再去访问 https://a.ax2009live.com 内容会串 , 其实是把 a.ax2009live.com 重定向 b.ax2009live.com ( 或反过来,以最新打开域名内容为优先 ) windows chrome 和 android chrome 都遇到同一的问题,一试一个准; 过几分钟恢复, ( https://a.ax2009live.comhttps://b.ax2009live.com 可以直接访问, 问题已经解决,关闭开放 ) 跟 chrome 内核一样的浏览器,测出有这个问题;

IE、firefox,safari 没有测出问题; (firefox 45 测出 断断续续 有问题)

不知道是 frp 有没有关系,还是跟 chrome 内核有没有关系?

https://a.ax2009live.com/hello-world 应该是如下这个样子 image https://b.ax2009live.com/hello-world 应该是如下这个样子 image

ax2009live commented 3 years ago

根据 https://github.com/fatedier/frp/issues/628 @Jonkimi 所说的,问题解决了;

内容复制如下:

查阅到的连接复用相关资料 Connection ResuseHow Connection Resuse works on multiple browsers

我的网站使用的是同一个通配域名证书,都配置了 HTTP2,符合 rfc7540 里所说的 reuse 情况。

找到的解决方法: if you do not want connection sharing to happen, have a different IP address and/or ensure no overlaps in certificates, 我把通配域名证书改成单域名证书问题解决,非 frp 的问题。