小编典典

带有Apache2 SNI /主机名错误的Golang ReverseProxy

go

我在Go中编写自己的ReverseProxy。ReverseProxy应该连接我的go-webserver和apache2
webserver。但是,当我在另一个IP地址上运行反向代理时,然后在我的Apache2
Web服务器上,当反向代理将请求发送到apache时,我的apache-logfile中出现以下错误。

"Hosname xxxx provided via sni and hostname xxxx2 provided via http are different"

我的反向代理和apache-webserver在https上运行。

这里有一些代码:

func (p *Proxy) directorApache(req *http.Request) {
    mainServer := fmt.Sprintf("%s:%d", Config.HostMain, Config.PortMain)
    req.URL.Scheme = "https"
    req.URL.Host = mainServer
}

func (p *Proxy) directorGo(req *http.Request) {
    goServer := fmt.Sprintf("%s:%d", Config.GoHost, Config.GoPort)
    req.URL.Scheme = "http"
    req.URL.Host = goServer
}


func (p *Proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    fmt.Println(req.URL.Path)
    if p.isGoRequest(req) {
        fmt.Println("GO")
        p.goProxy.ServeHTTP(rw, req)
        return
    }
    p.httpProxy.ServeHTTP(rw, req)
}
func main() {

    var configPath = flag.String("conf", "./configReverse.json", "Path to the Json config file.")

    flag.Parse()
    proxy := New(*configPath)
    cert, err := tls.LoadX509KeyPair(Config.PathCert, Config.PathPrivateKey)
    if err != nil {
        log.Fatalf("server: loadkeys: %s", err)
    }
    config := tls.Config{InsecureSkipVerify: true, Certificates: []tls.Certificate{cert}}

    listener, err := net.Listen("tcp",
    net.JoinHostPort(proxy.Host, strconv.Itoa(proxy.Port)))
    if err != nil {
        log.Fatalf("server: listen: %s", err)
    }
    log.Printf("server: listening on %s")
    proxy.listener = tls.NewListener(listener, &config)

    serverHTTPS := &http.Server{
        Handler:   proxy.mux,
        TLSConfig: &config,
    }

    if err := serverHTTPS.Serve(proxy.listener); err != nil {
        log.Fatal("SERVER ERROR:", err)
    }
}

也许有人对此有想法。


阅读 236

收藏
2020-07-02

共1个答案

小编典典

简短的例子

假设您正在向发起HTTP请求https://your- proxy.local。您的请求处理程序采用该http.Request结构,并将其URL字段重写为https://your-apache- backend.local

您没有考虑的是,原始HTTP请求还包含一个Host标头(Host: your- proxy.local)。将相同的请求传递给该请求时http://your-apache- backend.local,该请求中的Host标头仍会显示Host: your-proxy.local。这就是Apache所抱怨的。

说明

当您将TLS与服务器名称指示(SNI)一起使用时,请求主机名不仅将用于DNS解析,还将选择用于建立TLS连接的SSL证书。Host另一方面,HTTP
1.1 标头用于通过Apache区分多个虚拟主机。两个名称 必须匹配Apache HTTPD
Wiki中
也提到了此问题:

SNI /请求主机名不匹配,或者SNI提供了主机名而请求没有。

这是一个浏览器错误。Apache将拒绝该请求,并显示400类型错误。

还要重写Host标题。如果要保留原始Host标头,可以将其存储在X-Forwarded- Host标头中(这是非标准标头,但在反向代理中广泛使用):

func (p *Proxy) directorApache(req *http.Request) {
    mainServer := fmt.Sprintf("%s:%d", Config.HostMain, Config.PortMain)
    req.URL.Scheme = "https"
    req.URL.Host = mainServer
    req.Header.Set("X-Forwarded-Host", req.Header().Get("Host"))
    req.Host = mainServer
}
2020-07-02