AdguardTeam / gomitmproxy

Simple golang mitm proxy implementation
https://adguard.com/
GNU General Public License v3.0
293 stars 59 forks source link

Connection not closed in TLS interception mode? #27

Closed trco closed 4 months ago

trco commented 4 months ago

Thank you for sharing your great project.

I've reviewed the setup you provided in your README with gomitmproxy in TLS interception mode. It seems that after receiving a response from the Github API, the connection isn't being closed properly, leading to it hanging endlessly.

I'm proxying to Github API using curl -x http://host.docker.internal:6666 https://api.github.com -v.

In the simple HTTP setup, the connection is correctly closed after receiving the response, as indicated by the message " Connection #0 to host host.docker.internal left intact" in verbose output of the curl command. However, in the TLS interception setup, the message " no chunk, no close, no size. Assume close to signal end" is printed before response and connection is than hanging endlessly.

Is it possible that there may be a bug or misconfiguration causing the connections to hang indefinitely in TLS interception mode?

Is this potentially similar to https://github.com/AdguardTeam/gomitmproxy/issues/14?

Thanks for considering my post.

Simple TLS interception setup causing issues:

func main() {
    tlsCert, err := tls.LoadX509KeyPair("demo.crt", "demo.key")
    if err != nil {
        log.Fatal(err)
    }
    privateKey := tlsCert.PrivateKey.(*rsa.PrivateKey)

    x509c, err := x509.ParseCertificate(tlsCert.Certificate[0])
    if err != nil {
        log.Fatal(err)
    }

    mitmConfig, err := mitm.NewConfig(x509c, privateKey, nil)
    if err != nil {
        log.Fatal(err)
    }

    mitmConfig.SetValidity(time.Hour * 24 * 7) // generate certs valid for 7 days
    mitmConfig.SetOrganization("gomitmproxy")  // cert organization

    proxy := gomitmproxy.NewProxy(gomitmproxy.Config{
        ListenAddr: &net.TCPAddr{
            IP:   net.IPv4(0, 0, 0, 0),
            Port: 6666,
        },
        MITMConfig: mitmConfig,
    })

    err = proxy.Start()
    if err != nil {
        log.Fatal(err)
    }

    signalChannel := make(chan os.Signal, 1)
    signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)
    <-signalChannel

    // Clean up
    proxy.Close()
}
trco commented 4 months ago

When running OnResponse with a debugger processing stops in proxy.go at...

// handleLoop processes requests in a loop
func (p *Proxy) handleLoop(ctx *Context) {
    for {
        deadline := time.Now().Add(p.timeout)
        _ = ctx.SetDeadline(deadline)

        if err := p.handleRequest(ctx); err != nil {
            log.Debug("id=%s: closing connection due to: %v", ctx.ID(), err)
            return
        }
    }
}
trco commented 4 months ago

Ok, I believe I finally understand what's happening. The connection between the client and proxy is left open for 5 minutes, which is the default timeout set on a newly created proxy via the NewProxy function.

I guess this is a performance optimization feature and not an issue as I thought initially.

Closing the issue.