From c61db1bffb314e0d114c5e64b47241435590dc43 Mon Sep 17 00:00:00 2001 From: lqqyt2423 <974923609@qq.com> Date: Fri, 1 Jul 2022 14:10:44 +0800 Subject: [PATCH] add some test --- Makefile | 2 +- cert/cert.go | 94 ++++++++++++++++++----------- proxy/proxy_test.go | 143 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 35 deletions(-) create mode 100644 proxy/proxy_test.go diff --git a/Makefile b/Makefile index 64d5020..58546dc 100644 --- a/Makefile +++ b/Makefile @@ -14,4 +14,4 @@ clean: .PHONY: test test: - go test ./... + go test ./... -v diff --git a/cert/cert.go b/cert/cert.go index e097fdd..4a08303 100644 --- a/cert/cert.go +++ b/cert/cert.go @@ -41,6 +41,64 @@ type CA struct { cacheMu sync.Mutex } +func createCert() (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + + template := &x509.Certificate{ + SerialNumber: big.NewInt(time.Now().UnixNano() / 100000), + Subject: pkix.Name{ + CommonName: "mitmproxy", + Organization: []string{"mitmproxy"}, + }, + NotBefore: time.Now().Add(-time.Hour * 48), + NotAfter: time.Now().Add(time.Hour * 24 * 365 * 3), + BasicConstraintsValid: true, + IsCA: true, + SignatureAlgorithm: x509.SHA256WithRSA, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{ + x509.ExtKeyUsageServerAuth, + x509.ExtKeyUsageClientAuth, + x509.ExtKeyUsageEmailProtection, + x509.ExtKeyUsageTimeStamping, + x509.ExtKeyUsageCodeSigning, + x509.ExtKeyUsageMicrosoftCommercialCodeSigning, + x509.ExtKeyUsageMicrosoftServerGatedCrypto, + x509.ExtKeyUsageNetscapeServerGatedCrypto, + }, + } + + certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + if err != nil { + return nil, nil, err + } + cert, err := x509.ParseCertificate(certBytes) + if err != nil { + return nil, nil, err + } + + return key, cert, nil +} + +// Create new ca only live in memory, will change when process restart +func NewCAMemory() (*CA, error) { + key, cert, err := createCert() + if err != nil { + return nil, err + } + return &CA{ + PrivateKey: *key, + RootCert: *cert, + StorePath: "", + cache: lru.New(100), + group: new(singleflight.Group), + }, nil +} + +// Load ca from store path or create new ca then store func NewCA(path string) (*CA, error) { storePath, err := getStorePath(path) if err != nil { @@ -178,44 +236,12 @@ func (ca *CA) load() error { } func (ca *CA) create() error { - key, err := rsa.GenerateKey(rand.Reader, 2048) + key, cert, err := createCert() if err != nil { return err } - ca.PrivateKey = *key - - template := &x509.Certificate{ - SerialNumber: big.NewInt(time.Now().UnixNano() / 100000), - Subject: pkix.Name{ - CommonName: "mitmproxy", - Organization: []string{"mitmproxy"}, - }, - NotBefore: time.Now().Add(-time.Hour * 48), - NotAfter: time.Now().Add(time.Hour * 24 * 365 * 3), - BasicConstraintsValid: true, - IsCA: true, - SignatureAlgorithm: x509.SHA256WithRSA, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - ExtKeyUsage: []x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - x509.ExtKeyUsageEmailProtection, - x509.ExtKeyUsageTimeStamping, - x509.ExtKeyUsageCodeSigning, - x509.ExtKeyUsageMicrosoftCommercialCodeSigning, - x509.ExtKeyUsageMicrosoftServerGatedCrypto, - x509.ExtKeyUsageNetscapeServerGatedCrypto, - }, - } - certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) - if err != nil { - return err - } - cert, err := x509.ParseCertificate(certBytes) - if err != nil { - return err - } + ca.PrivateKey = *key ca.RootCert = *cert if err := ca.save(); err != nil { diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go new file mode 100644 index 0000000..52a7aff --- /dev/null +++ b/proxy/proxy_test.go @@ -0,0 +1,143 @@ +package proxy + +import ( + "crypto/tls" + "io/ioutil" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "testing" + "time" + + "github.com/lqqyt2423/go-mitmproxy/cert" +) + +func handleError(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatal(err) + } +} + +func TestProxy(t *testing.T) { + // start http server + ln, err := net.Listen("tcp", "127.0.0.1:0") + handleError(t, err) + defer ln.Close() + go http.Serve(ln, nil) + + // start https server + tlsLn, err := net.Listen("tcp", "127.0.0.1:0") + handleError(t, err) + defer tlsLn.Close() + ca, err := cert.NewCAMemory() + handleError(t, err) + cert, err := ca.GetCert("localhost") + handleError(t, err) + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{*cert}, + } + go http.Serve(tls.NewListener(tlsLn, tlsConfig), nil) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("ok")) + }) + + httpEndpoint := "http://" + ln.Addr().String() + "/" + httpsPort := tlsLn.Addr().(*net.TCPAddr).Port + httpsEndpoint := "https://localhost:" + strconv.Itoa(httpsPort) + "/" + + t.Run("test http server", func(t *testing.T) { + resp, err := http.Get(httpEndpoint) + handleError(t, err) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + handleError(t, err) + if string(body) != "ok" { + t.Fatal("expected ok, bug got", string(body)) + } + }) + + t.Run("test https server", func(t *testing.T) { + t.Run("should generate not trusted error", func(t *testing.T) { + _, err := http.Get(httpsEndpoint) + if err == nil { + t.Fatal("should have error") + } + if !strings.Contains(err.Error(), "certificate is not trusted") { + t.Fatal("should get not trusted error, but got", err.Error()) + } + }) + + t.Run("should get ok when InsecureSkipVerify", func(t *testing.T) { + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + }, + } + req, err := http.NewRequest("GET", httpsEndpoint, nil) + handleError(t, err) + resp, err := client.Do(req) + handleError(t, err) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + handleError(t, err) + if string(body) != "ok" { + t.Fatal("expected ok, bug got", string(body)) + } + }) + }) + + // start proxy + testProxy, err := NewProxy(&Options{ + Addr: ":29080", // some random port + SslInsecure: true, + }) + handleError(t, err) + testProxy.AddAddon(&LogAddon{}) + go testProxy.Start() + time.Sleep(time.Millisecond * 10) // wait for test proxy startup + + t.Run("test proxy", func(t *testing.T) { + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + Proxy: func(r *http.Request) (*url.URL, error) { + return url.Parse("http://127.0.0.1:29080") + }, + }, + } + + t.Run("can proxy http", func(t *testing.T) { + req, err := http.NewRequest("GET", httpEndpoint, nil) + handleError(t, err) + resp, err := client.Do(req) + handleError(t, err) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + handleError(t, err) + if string(body) != "ok" { + t.Fatal("expected ok, bug got", string(body)) + } + }) + + t.Run("can proxy https", func(t *testing.T) { + req, err := http.NewRequest("GET", httpsEndpoint, nil) + handleError(t, err) + resp, err := client.Do(req) + handleError(t, err) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + handleError(t, err) + if string(body) != "ok" { + t.Fatal("expected ok, bug got", string(body)) + } + }) + }) +}