Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2Ray Transport HTTP does not work behind a reverse proxy #1728

Closed
FPPweb3 opened this issue May 1, 2024 · 9 comments
Closed

V2Ray Transport HTTP does not work behind a reverse proxy #1728

FPPweb3 opened this issue May 1, 2024 · 9 comments

Comments

@FPPweb3
Copy link

FPPweb3 commented May 1, 2024

Operating system

Linux

System version

Debian 12

Installation type

Original sing-box Command Line

If you are using a graphical client, please provide the version of the client.

No response

Version

sing-box version 1.8.12

Environment: go1.22.2 linux/amd64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_ech,with_utls,with_reality_server,with_acme,with_clash_api
Revision: 0ae1afef449778e57e2d597d49ec7052c89b4964
CGO: disabled

Description

The configuration works without a reverse proxy, but with it requests stop reaching the Sing-Box server, the logs are empty

Reproduction

Server

{
  "log": {
    "level": "trace",
    "output": "box.log"
  },
  "dns": {
    "servers": [
      {
        "address": "tls://8.8.8.8"
      }
    ]
  },
  "inbounds": [
    {
      "type": "trojan",
      "tag": "trojan-in",
      "listen": "127.0.0.1",
      "listen_port": 4443,
      "sniff": true,
      "sniff_override_destination": false,
      "users": [
        {
          "name": "user",
          "password": "password"
        }
      ],
      "transport": {
        "type": "http",
        "host": "185.237.97.248.sslip.io"
      }
    }
  ],
  "outbounds": [
    {
      "type": "direct"
    },
    {
      "type": "dns",
      "tag": "dns-out"
    }
  ],
  "route": {
    "rules": [
      {
        "protocol": "dns",
        "outbound": "dns-out"
      }
    ]
  }
}

Reverse Proxy

server {
	listen 443 ssl http2 default_server;
	listen [::]:443 ssl http2 default_server;
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

	root /var/www/html;
	index index.html index.htm index.nginx-debian.html;
	server_name _;

	location / {
		try_files $uri $uri/ =404;
	}

        location /abc {
                proxy_pass http://127.0.0.1:4443;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

Client

{
        "log": {
                "level": "trace"
        },
        "dns": {
                "servers": [{
                        "tag": "dns-local",
                        "address": "local",
                        "detour": "direct"
                }],
                "final": "dns-local"
        },
        "inbounds": [{
                "type": "mixed",
                "tag": "mixed-in",
                "listen": "127.0.0.1",
                "listen_port": 2080,
                "sniff": true
        }],
        "outbounds": [{
                        "type": "direct",
                        "tag": "direct"
                },
                {
                        "type": "block",
                        "tag": "block"
                },
                {
                        "type": "dns",
                        "tag": "dns-out"
                },
                {
                        "type": "trojan",
                        "tag": "proxy",
                        "server": "example.com",
                        "server_port": 443,
                        "password": "password",
                        "tls": {
                                "enabled": true,
                                "server_name": "example.com",
                                "alpn": "h2",
                                "min_version": "1.3",
                                "utls": {
                                        "enabled": true,
                                        "fingerprint": "randomized"
                                }
                        },
                        "transport": {
                                "type": "http",
                                "host": "example.com",
                                "path": "/abc"
                        }
                }
        ],
        "route": {
                "rules": [{
                        "protocol": "dns",
                        "outbound": "dns-out"
                }],
                "final": "proxy"
        }
}

Logs

Server Sing-Box

INFO router: updated default interface eth0, index 2
INFO inbound/trojan[trojan-in]: tcp server started at [::]:4443
INFO sing-box started (0.00s)

Client test request

curl -x socks5://127.0.0.1:2080 -v 2ip.io
*   Trying 127.0.0.1:2080...
* Connected to 127.0.0.1 (127.0.0.1) port 2080
* Host 2ip.io:80 was resolved.
* IPv6: (none)
* IPv4: 195.201.201.33
* SOCKS5 connect to 195.201.201.33:80 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 2080
> GET / HTTP/1.1
> Host: 2ip.io
> User-Agent: curl/8.5.0
> Accept: */*
> 
* Empty reply from server
* Closing connection
curl: (52) Empty reply from server

Client Sing-Box

INFO[0000] router: updated default interface eth0, index 2
INFO[0000] inbound/mixed[mixed-in]: tcp server started at 127.0.0.1:2080
INFO[0000] sing-box started (0.00s)
INFO[0009] [752958368 0ms] inbound/mixed[mixed-in]: inbound connection from 127.0.0.1:39808
INFO[0009] [752958368 35ms] inbound/mixed[mixed-in]: inbound connection to 195.201.201.33:80
DEBUG[0009] [752958368 37ms] router: sniffed protocol: http, domain: 2ip.io
INFO[0009] [752958368 37ms] outbound/trojan[proxy]: outbound connection to 195.201.201.33:80
DEBUG[0009] [752958368 39ms] dns: lookup domain example.com
DEBUG[0009] [752958368 40ms] outbound/direct[direct]: outbound packet connection to 1.1.1.1:53
DEBUG[0009] [752958368 40ms] outbound/direct[direct]: outbound packet connection to 1.1.1.1:53
DEBUG[0009] [752958368 50ms] dns: lookup succeed for example.com: SERVER_IP
DEBUG[0071] [752958368 1m1s] inbound/mixed[mixed-in]: connection closed: process connection from 127.0.0.1:39808: upload: raw-read tcp 127.0.0.1:2080->127.0.0.1:39808: use of closed network connection | download: http2: Transport: cannot retry err [stream error: stream ID 1; PROTOCOL_ERROR; received from peer] after Request.Body was written; define Request.GetBody to avoid this error

Nginx access log:

CLIENT_IP - - [30/Apr/2024:21:24:30 -0400] "PUT /abc HTTP/2.0" 408 0 "-" "Go-http-client/2.0"


### Supporter

- [ ] I am a [sponsor](https://github.com/sponsors/nekohasekai/)

### Integrity requirements

- [X] I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values.
- [X] I confirm that I have provided the server and client configuration files and process that can be reproduced locally, instead of a complicated client configuration file that has been stripped of sensitive data.
- [X] I confirm that I have provided the simplest configuration that can be used to reproduce the error I reported, instead of depending on remote servers, TUN, graphical interface clients, or other closed-source software.
- [X] I confirm that I have provided the complete configuration files and logs, rather than just providing parts I think are useful out of confidence in my own intelligence.
@k3-cat
Copy link

k3-cat commented May 1, 2024

I think you made a mistake when configuring NGINX.

According to the Doc, the path that NGINX passes to sing-box is /abc instead of /.

So, I think you should either add a "path": "/abc" entry into your sing-box(server)'s config, or change the proxy config of NGINX to proxy_pass http://127.0.0.1:4443/ (note the trailing slash).

@FPPweb3
Copy link
Author

FPPweb3 commented May 1, 2024

Thanks, but the error hasn't changed:

Nginx access:

CLIENT_IP - - [01/May/2024:06:04:54 -0400] "PUT /abc HTTP/2.0" 408 0 "-" "Go-http-client/2.0"

Client Sing-Box:

May 01 13:03:49 kali sing-box[3201]: INFO[0000] sing-box started (0.00s)
May 01 13:03:53 kali sing-box[3201]: INFO[0004] [1149158058 0ms] inbound/mixed[mixed-in]: inbound connection from 127.0.0.1:44470
May 01 13:03:53 kali sing-box[3201]: INFO[0004] [1149158058 35ms] inbound/mixed[mixed-in]: inbound connection to 195.201.201.33:80
May 01 13:03:53 kali sing-box[3201]: DEBUG[0004] [1149158058 37ms] router: sniffed protocol: http, domain: 2ip.io
May 01 13:03:53 kali sing-box[3201]: INFO[0004] [1149158058 37ms] outbound/trojan[proxy]: outbound connection to 195.201.201.33:80
May 01 13:03:53 kali sing-box[3201]: DEBUG[0004] [1149158058 38ms] dns: lookup domain example.com
May 01 13:03:53 kali sing-box[3201]: DEBUG[0004] [1149158058 39ms] outbound/direct[direct]: outbound packet connection to 1.1.1.1:53
May 01 13:03:53 kali sing-box[3201]: DEBUG[0004] [1149158058 39ms] outbound/direct[direct]: outbound packet connection to 1.1.1.1:53
May 01 13:03:54 kali sing-box[3201]: DEBUG[0004] [1149158058 752ms] dns: lookup succeed for example.com: SERVER_IP
May 01 13:04:55 kali sing-box[3201]: DEBUG[0065] [1149158058 1m1s] inbound/mixed[mixed-in]: connection closed: process connection from 127.0.0.1:44470: upload: raw-read tcp 127.0.0.1:2080->127.0.0.1:44470: use of closed network connection | download: http2: Transport: cannot retry err [stream error: stream ID 1; PROTOCOL_ERROR; received from peer] after Request.Body was written; define Request.GetBody to avoid this error

Curl Client:

curl -x socks5://127.0.0.1:2080 -v 2ip.io
*   Trying 127.0.0.1:2080...
* Connected to 127.0.0.1 (127.0.0.1) port 2080
* Host 2ip.io:80 was resolved.
* IPv6: (none)
* IPv4: 195.201.201.33
* SOCKS5 connect to 195.201.201.33:80 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 2080
> GET / HTTP/1.1
> Host: 2ip.io
> User-Agent: curl/8.5.0
> Accept: */*
> 
* Empty reply from server
* Closing connection
curl: (52) Empty reply from server

Server Sing-Box:

INFO router: updated default interface eth0, index 2
INFO inbound/trojan[trojan-in]: tcp server started at [::]:4443
INFO sing-box started (0.00s

@mmmray
Copy link

mmmray commented May 1, 2024

I am fairly sure that no matter the transport/stream settings, the inner trojan connection needs a bidirectional pipe to work. The traditional request/response cycle in HTTP is not that, it's why v2fly has meek now. You can use websocket transport instead of http transport, it should pass nginx without a problem.

@FPPweb3
Copy link
Author

FPPweb3 commented May 1, 2024

I am fairly sure that no matter the transport/stream settings, the inner trojan connection needs a bidirectional pipe to work. The traditional request/response cycle in HTTP is not that, it's why v2fly has meek now. You can use websocket transport instead of http transport, it should pass nginx without a problem.

But why was this transport added if it doesn’t work?

@k3-cat
Copy link

k3-cat commented May 1, 2024

It works, but not behind NGINX. According to this, I think nginx is not (and never will in the future) using HTTP/2 to communicate with wing-box.

This is the first time I realized nginx works like that, and sorry for providing wrong information before.

@FPPweb3
Copy link
Author

FPPweb3 commented May 1, 2024

I am fairly sure that no matter the transport/stream settings, the inner trojan connection needs a bidirectional pipe to work. The traditional request/response cycle in HTTP is not that, it's why v2fly has meek now. You can use websocket transport instead of http transport, it should pass nginx without a problem.

Requests reach the server only after the connection is broken, I checked this using TCPDUMP.

@FPPweb3
Copy link
Author

FPPweb3 commented May 1, 2024

It works, but not behind NGINX. According to this, I think nginx is not (and never will in the future) using HTTP/2 to communicate with wing-box.

This is the first time I realized nginx works like that, and sorry for providing wrong information before.

But the Sing-Box documentation states that if the Trojan on the server does not use TLS, Plain HTTP/1.1 will be used as transport:

https://sing-box.sagernet.org/configuration/shared/v2ray-transport/#structure:~:text=TLS%20is%20not%20enforced.%20If%20TLS%20is%20not%20configured%2C%20plain%20HTTP%201.1%20is%20used.

@radioactiveAHM
Copy link

radioactiveAHM commented May 9, 2024

The http is often misleading because it appears to be a simple request-response mechanism. In V2ray "type": "http" Transport is just a deceptive cover for the new streams. However, for proxy protocols, especially when dealing with TLS handshakes and other complexities, you actually need a bidirectional transport layer like WebSockets, gRPC, or WebTransport. NGINX does support both WebSocketsand and gRPC for reverse proxy.

@dyhkwong
Copy link
Contributor

Close this as it is not a sing-box bug. Try caddy instead.

@dyhkwong dyhkwong closed this as not planned Won't fix, can't repro, duplicate, stale May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants