AdguardTeam / gomitmproxy

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

first record does not look like a TLS handshake #1

Closed domyway closed 4 years ago

domyway commented 4 years ago

go version go1.14 darwin/amd64 mac

examples/mitm/main.go output error

2020/08/18 16:31:10 84778#1 [debug] mitm: cache miss for 127.0.0.1
2020/08/18 16:31:10 84778#35 [info] start listening to [::]:8080
2020/08/18 16:31:20 84778#35 [debug] id=100001: accepted connection from 127.0.0.1:60014
2020/08/18 16:31:20 84778#5 [debug] id=100001: waiting for request
2020/08/18 16:31:20 84778#6 [debug] id=100001: failed to read request: tls: first record does not look like a TLS handshake
2020/08/18 16:31:20 84778#5 [debug] id=100001: closing connection due to: tls: first record does not look like a TLS handshake
ameshkov commented 4 years ago

What's your setup? How exactly have you configured proxy?

domyway commented 4 years ago

go run $GOROOT/src/crypto/tls/generate_cert.go --host 127.0.0.1 cert.pem key.pem

export http_proxy=http://127.0.0.1:8080 export https_proxy=http://127.0.0.1:8080

curl -v https://www.google.com

AND the IDE console output

API server listening at: 127.0.0.1:35150
2020/08/18 16:49:26 87002#1 [debug] mitm: cache miss for 127.0.0.1
2020/08/18 16:49:26 87002#10 [info] start listening to [::]:8080
2020/08/18 16:49:30 87002#10 [debug] id=100001: accepted connection from 127.0.0.1:61119
2020/08/18 16:49:30 87002#26 [debug] id=100001: waiting for request
2020/08/18 16:49:30 87002#27 [debug] id=100001: failed to read request: tls: first record does not look like a TLS handshake
2020/08/18 16:49:30 87002#26 [debug] id=100001: closing connection due to: tls: first record does not look like a TLS handshake
package main

import (
    "bytes"
    "crypto/rsa"
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"
    "net"
    "net/http"
    "os"
    "os/signal"
    "strings"
    "syscall"
    "time"

    "github.com/AdguardTeam/gomitmproxy/proxyutil"

    "github.com/AdguardTeam/golibs/log"
    "github.com/AdguardTeam/gomitmproxy"
    "github.com/AdguardTeam/gomitmproxy/mitm"

    _ "net/http/pprof"
)

func main() {
    log.SetLevel(log.DEBUG)

    // go func() {
    //  log.Println(http.ListenAndServe("localhost:8080", nil))
    // }()

    // READ CERT AND KEY
    tlsCert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    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, &CustomCertsStorage{
        certsCache: map[string]*tls.Certificate{}},
    )
    if err != nil {
        log.Fatal(err)
    }

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

    // GENERATE A CERT FOR HTTP OVER TLS PROXY
    proxyCert, err := mitmConfig.GetOrCreateCert("127.0.0.1")
    if err != nil {
        panic(err)
    }
    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{*proxyCert},
        MinVersion:   tls.VersionTLS12,
    }

    // PREPARE PROXY
    addr := &net.TCPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 8080,
    }

    proxy := gomitmproxy.NewProxy(gomitmproxy.Config{
        ListenAddr: addr,
        TLSConfig:  tlsConfig,

        // Username: "user",
        // Password: "pass",
        // APIHost: "gomitmproxy",

        MITMConfig: mitmConfig,
        // MITMExceptions: []string{"example.com"},

        OnRequest:  onRequest,
        OnResponse: onResponse,
        OnConnect:  onConnect,
    })

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

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

    // CLOSE THE PROXY
    proxy.Close()
}

func onRequest(session *gomitmproxy.Session) (*http.Request, *http.Response) {
    req := session.Request()

    log.Printf("onRequest: %s %s", req.Method, req.URL.String())

    if req.URL.Host == "example.net" {
        body := strings.NewReader("<html><body><h1>Replaced response</h1></body></html>")
        res := proxyutil.NewResponse(http.StatusOK, body, req)
        res.Header.Set("Content-Type", "text/html")
        session.SetProp("blocked", true)
        return nil, res
    }

    if req.URL.Host == "testgomitmproxy" {
        body := strings.NewReader("<html><body><h1>Served by gomitmproxy</h1></body></html>")
        res := proxyutil.NewResponse(http.StatusOK, body, req)
        res.Header.Set("Content-Type", "text/html")
        return nil, res
    }

    return nil, nil
}

func onResponse(session *gomitmproxy.Session) *http.Response {
    log.Printf("onResponse: %s", session.Request().URL.String())

    if _, ok := session.GetProp("blocked"); ok {
        log.Printf("onResponse: was blocked")
        return nil
    }

    res := session.Response()
    req := session.Request()

    if strings.Index(res.Header.Get("Content-Type"), "text/html") != 0 {
        // Do nothing with non-HTML responses
        return nil
    }

    b, err := proxyutil.ReadDecompressedBody(res)
    // Close the original body
    _ = res.Body.Close()
    if err != nil {
        return proxyutil.NewErrorResponse(req, err)
    }

    // Use latin1 before modifying the body
    // Using this 1-byte encoding will let us preserve all original characters
    // regardless of what exactly is the encoding
    body, err := proxyutil.DecodeLatin1(bytes.NewReader(b))
    if err != nil {
        return proxyutil.NewErrorResponse(session.Request(), err)
    }

    // Modifying the original body
    modifiedBody, err := proxyutil.EncodeLatin1(body + "<!-- EDITED -->")
    if err != nil {
        return proxyutil.NewErrorResponse(session.Request(), err)
    }

    res.Body = ioutil.NopCloser(bytes.NewReader(modifiedBody))
    res.Header.Del("Content-Encoding")
    res.ContentLength = int64(len(modifiedBody))
    return res
}

func onConnect(session *gomitmproxy.Session, proto string, addr string) net.Conn {
    host, _, err := net.SplitHostPort(addr)

    if err == nil && host == "testgomitmproxy" {
        // Don't let it connecting there -- we'll serve it by ourselves
        return &proxyutil.NoopConn{}
    }

    return nil
}

// CustomCertsStorage - an example of a custom cert storage
type CustomCertsStorage struct {
    certsCache map[string]*tls.Certificate // cache with the generated certificates
}

// Get gets the certificate from the storage
func (c *CustomCertsStorage) Get(key string) (*tls.Certificate, bool) {
    v, ok := c.certsCache[key]
    return v, ok
}

// Set saves the certificate to the storage
func (c *CustomCertsStorage) Set(key string, cert *tls.Certificate) {
    c.certsCache[key] = cert
}
ameshkov commented 4 years ago

I suppose you're running it as a HTTPS (HTTP over TLS) proxy, while curl expects a plain HTTP proxy.

Please read here how to use it with curl: https://daniel.haxx.se/blog/2016/11/26/https-proxy-with-curl/

Alternatively, you can modify the code and remove TLSConfig from here to make it run a plain HTTP proxy: https://github.com/AdguardTeam/gomitmproxy/blob/master/examples/mitm/main.go#L72

domyway commented 4 years ago

I want to use HTTPS . I set the wifi proxy 127.0.0.1:8080. and use chrome : https://www.google.com

Same error .

ameshkov commented 4 years ago

I want to use HTTPS .

You're confusing things.

If your proxy is on localhost, the TLS tunnel does not provide any value to you.