gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
77.95k stars 7.97k forks source link

Can i use gin as reverse proxy for sing-box ? #3625

Open dickymuliafiqri opened 1 year ago

dickymuliafiqri commented 1 year ago

Description

I have nginx as Reverse Proxy for sing-box (VPN server) with following configuration

stream {
    map $ssl_preread_server_name $singbox {
            some.example.com trojan;
    }
    upstream trojan {
            server 127.0.0.1:8080;
    }
    server {
            listen 443      reuseport;
            proxy_pass      $singbox;
            ssl_preread     on;
            proxy_protocol  on;
    }
}

thus, i'm just wondering 'can i use gin instead of nginx' ? Tried to configure gin with following code: [How to reproduce]

But gin response with: http: proxy error: context canceled And so on the sing-box side: http: Accept error: proxyproto: proxy protocol signature not present; retrying in 1s

How to reproduce

package main

import (
    "fmt"
    "net/http"
    "net/http/httputil"

    "github.com/gin-gonic/gin"
)

func main() {
    // Create a new gin-gonic router
    r := gin.Default()

    // Add a new route to the router that will handle requests to the endpoint you want to proxy
    r.GET("/wss", func(c *gin.Context) {
        // Use the ReverseProxy function from the net/http/httputil package to forward the request to the backend server
        director := func(req *http.Request) {
            req.URL.Scheme = "http"
            req.URL.Host = "localhost:8080"

            fmt.Println(req.Header)
        }
        proxy := &httputil.ReverseProxy{Director: director}
        proxy.ServeHTTP(c.Writer, c.Request)
    })

    r.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })

    // Start the gin-gonic server
    r.RunTLS(":443", "/etc/certs/cert.pem", "/etc/certs/key.pem")
}

Expectations

Gin to work as nginx as reverse proxy

Actual result

Gin: http: proxy error: context canceled Sing-box: http: Accept error: proxyproto: proxy protocol signature not present; retrying in 1s

Environment

yousifnimah commented 1 year ago

The issue you're encountering with using Gin as a reverse proxy is related to the handling of the Proxy Protocol. In your NGINX configuration, you have enabled the proxy_protocol directive, which allows NGINX to communicate the client's original IP address to the backend server. However, in your Gin code, you haven't implemented the Proxy Protocol support.

To resolve the issue and make Gin work as a reverse proxy with Proxy Protocol support, you can use the github.com/pires/go-proxyproto package. This package provides a middleware for Gin that handles the Proxy Protocol headers. Here's an updated version of your code that includes the necessary modifications:

package main

import (
    "fmt"
    "net/http"
    "net/http/httputil"

    "github.com/gin-gonic/gin"
    "github.com/pires/go-proxyproto"
)

func main() {
    r := gin.Default()

    r.Use(proxyproto.NewProxyProtocolMiddleware())

    r.GET("/wss", func(c *gin.Context) {
        director := func(req *http.Request) {
            req.URL.Scheme = "http"
            req.URL.Host = "localhost:8080"

            fmt.Println(req.Header)
        }
        proxy := &httputil.ReverseProxy{Director: director}
        proxy.ServeHTTP(c.Writer, c.Request)
    })

    r.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })

    r.RunTLS(":443", "/etc/certs/cert.pem", "/etc/certs/key.pem")
}