Go 如何实现 支持SNI代理?

36 min read

SNI代理是一种基于TLS的代理,它可以根据客户端发送的TLS握手过程中的Server Name Indication字段(SNI字段)进行智能转发,从而实现对多个域名的代理支持。

在Go中要实现支持SNI代理,可以使用第三方库来简化实现过程。其中比较常用的是goproxy库,它提供了代理、中间人攻击和SNI代理等功能。

以下是使用goproxy库实现SNI代理的示例代码:

package main

import (
    "github.com/elazarl/goproxy"
    "log"
    "net/http"
    "os"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()

    // 设置SNI代理功能
    proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
    proxy.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
        // 获取SNI字段
        serverName := ctx.Req.RemoteAddr // 默认SNI
        if len(ctx.Req.TLS.ServerName) > 0 {
            serverName = ctx.Req.TLS.ServerName
        }

        // 返回ConnectAction,使用SNI字段连接目标地址
        return goproxy.MitmConnect, host + ":" + serverName
    })

    // 设置日志输出
    logFile, err := os.Create("proxy.log")
    if err != nil {
        panic(err)
    }
    defer logFile.Close()
    proxy.Verbose = true
    proxy.Logger = log.New(logFile, "", log.LstdFlags)

    // 启动代理服务器
    log.Println("Proxy server listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", proxy))
}

在上面的示例代码中,我们首先创建了一个goproxy代理,并使用HandleConnect方法设置了SNI代理功能。通过AlwaysMitm将所有HTTPS请求都强制使用中间人攻击,进而捕获并解析TLS握手过程中的SNI字段。

当有新的HTTPS连接到达时,我们在HandleConnectFunc中获取SNI字段,并使用该字段连接到目标地址。最后通过设置Verbose打开日志输出,通过设置Logger将日志输出到文件中。

最后,我们通过调用http.ListenAndServe方法启动代理服务器,并将监听端口设置为8080。

需要注意的是,这里的示例代码只是简化版的SNI代理实现,还需要根据实际需求进行改进和完善。