func main() { flag.Parse() p := martian.NewProxy() var x509c *x509.Certificate var priv interface{} if *generateCA { var err error x509c, priv, err = mitm.NewAuthority("martian.proxy", "Martian Authority", 30*24*time.Hour) if err != nil { log.Fatal(err) } } else if *cert != "" && *key != "" { tlsc, err := tls.LoadX509KeyPair(*cert, *key) if err != nil { log.Fatal(err) } priv = tlsc.PrivateKey x509c, err = x509.ParseCertificate(tlsc.Certificate[0]) if err != nil { log.Fatal(err) } } if x509c != nil && priv != nil { mc, err := mitm.NewConfig(x509c, priv) if err != nil { log.Fatal(err) } mc.SetValidity(*validity) mc.SetOrganization(*organization) p.SetMITM(mc) // Expose certificate authority. ah := martianhttp.NewAuthorityHandler(x509c) configure("/authority.cer", ah) } fg := fifo.NewGroup() hbhm := header.NewHopByHopModifier() fg.AddRequestModifier(hbhm) fg.AddRequestModifier(header.NewForwardedModifier()) fg.AddRequestModifier(header.NewBadFramingModifier()) vm := header.NewViaModifier("martian") fg.AddRequestModifier(vm) m := martianhttp.NewModifier() fg.AddRequestModifier(m) fg.AddResponseModifier(m) fg.AddResponseModifier(hbhm) fg.AddResponseModifier(vm) p.SetRequestModifier(fg) p.SetResponseModifier(fg) // Proxy specific handlers. // These handlers take precendence over proxy traffic and will not be // intercepted. // Configure modifiers. configure("/configure", m) // Verify assertions. vh := verify.NewHandler() vh.SetRequestVerifier(m) vh.SetResponseVerifier(m) configure("/verify", vh) // Reset verifications. rh := verify.NewResetHandler() rh.SetRequestVerifier(m) rh.SetResponseVerifier(m) configure("/verify/reset", rh) l, err := net.Listen("tcp", *addr) if err != nil { log.Fatal(err) } log.Println("martian: proxy started on:", l.Addr()) go p.Serve(l) sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, os.Kill) <-sigc log.Println("martian: shutting down") }
func TestIntegrationMITM(t *testing.T) { t.Parallel() l, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } p := NewProxy() defer p.Close() tr := martiantest.NewTransport() tr.Func(func(req *http.Request) (*http.Response, error) { res := proxyutil.NewResponse(200, nil, req) res.Header.Set("Request-Scheme", req.URL.Scheme) return res, nil }) p.SetRoundTripper(tr) p.SetTimeout(600 * time.Millisecond) ca, priv, err := mitm.NewAuthority("martian.proxy", "Martian Authority", 2*time.Hour) if err != nil { t.Fatalf("mitm.NewAuthority(): got %v, want no error", err) } mc, err := mitm.NewConfig(ca, priv) if err != nil { t.Fatalf("mitm.NewConfig(): got %v, want no error", err) } p.SetMITM(mc) tm := martiantest.NewModifier() reqerr := errors.New("request error") reserr := errors.New("response error") tm.RequestError(reqerr) tm.ResponseError(reserr) p.SetRequestModifier(tm) p.SetResponseModifier(tm) go p.Serve(l) conn, err := net.Dial("tcp", l.Addr().String()) if err != nil { t.Fatalf("net.Dial(): got %v, want no error", err) } defer conn.Close() req, err := http.NewRequest("CONNECT", "//example.com:443", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // CONNECT example.com:443 HTTP/1.1 // Host: example.com if err := req.Write(conn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } // Response MITM'd from proxy. res, err := http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } if got, want := res.StatusCode, 200; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } if got, want := res.Header.Get("Warning"), reserr.Error(); !strings.Contains(got, want) { t.Errorf("res.Header.Get(%q): got %q, want to contain %q", "Warning", got, want) } roots := x509.NewCertPool() roots.AddCert(ca) tlsconn := tls.Client(conn, &tls.Config{ ServerName: "example.com", RootCAs: roots, }) defer tlsconn.Close() req, err = http.NewRequest("GET", "https://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // GET / HTTP/1.1 // Host: example.com if err := req.Write(tlsconn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } // Response from MITM proxy. res, err = http.ReadResponse(bufio.NewReader(tlsconn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } defer res.Body.Close() if got, want := res.StatusCode, 200; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } if got, want := res.Header.Get("Request-Scheme"), "https"; got != want { t.Errorf("res.Header.Get(%q): got %q, want %q", "Request-Scheme", got, want) } if got, want := res.Header.Get("Warning"), reserr.Error(); !strings.Contains(got, want) { t.Errorf("res.Header.Get(%q): got %q, want to contain %q", "Warning", got, want) } }
func TestIntegrationTransparentMITM(t *testing.T) { t.Parallel() ca, priv, err := mitm.NewAuthority("martian.proxy", "Martian Authority", 2*time.Hour) if err != nil { t.Fatalf("mitm.NewAuthority(): got %v, want no error", err) } mc, err := mitm.NewConfig(ca, priv) if err != nil { t.Fatalf("mitm.NewConfig(): got %v, want no error", err) } // Start TLS listener with config that will generate certificates based on // SNI from connection. // // BUG: tls.Listen will not accept a tls.Config where Certificates is empty, // even though it is supported by tls.Server when GetCertificate is not nil. l, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } l = tls.NewListener(l, mc.TLS()) p := NewProxy() defer p.Close() tr := martiantest.NewTransport() tr.Func(func(req *http.Request) (*http.Response, error) { res := proxyutil.NewResponse(200, nil, req) res.Header.Set("Request-Scheme", req.URL.Scheme) return res, nil }) p.SetRoundTripper(tr) tm := martiantest.NewModifier() p.SetRequestModifier(tm) p.SetResponseModifier(tm) go p.Serve(l) roots := x509.NewCertPool() roots.AddCert(ca) tlsconn, err := tls.Dial("tcp", l.Addr().String(), &tls.Config{ // Verify the hostname is example.com. ServerName: "example.com", // The certificate will have been generated during MITM, so we need to // verify it with the generated CA certificate. RootCAs: roots, }) if err != nil { t.Fatalf("tls.Dial(): got %v, want no error", err) } defer tlsconn.Close() req, err := http.NewRequest("GET", "https://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // Write Encrypted request directly, no CONNECT. // GET / HTTP/1.1 // Host: example.com if err := req.Write(tlsconn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } res, err := http.ReadResponse(bufio.NewReader(tlsconn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } defer res.Body.Close() if got, want := res.StatusCode, 200; got != want { t.Fatalf("res.StatusCode: got %d, want %d", got, want) } if got, want := res.Header.Get("Request-Scheme"), "https"; got != want { t.Errorf("res.Header.Get(%q): got %q, want %q", "Request-Scheme", got, want) } if !tm.RequestModified() { t.Errorf("tm.RequestModified(): got false, want true") } if !tm.ResponseModified() { t.Errorf("tm.ResponseModified(): got false, want true") } }
func TestIntegrationConnect(t *testing.T) { t.Parallel() l, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } p := NewProxy() defer p.Close() // Test TLS server. ca, priv, err := mitm.NewAuthority("martian.proxy", "Martian Authority", time.Hour) if err != nil { t.Fatalf("mitm.NewAuthority(): got %v, want no error", err) } mc, err := mitm.NewConfig(ca, priv) if err != nil { t.Fatalf("mitm.NewConfig(): got %v, want no error", err) } tl, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("tls.Listen(): got %v, want no error", err) } tl = tls.NewListener(tl, mc.TLS()) go http.Serve(tl, http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(299) })) tm := martiantest.NewModifier() reqerr := errors.New("request error") reserr := errors.New("response error") // Force the CONNECT request to dial the local TLS server. tm.RequestFunc(func(req *http.Request) { req.URL.Host = tl.Addr().String() }) tm.RequestError(reqerr) tm.ResponseError(reserr) p.SetRequestModifier(tm) p.SetResponseModifier(tm) go p.Serve(l) conn, err := net.Dial("tcp", l.Addr().String()) if err != nil { t.Fatalf("net.Dial(): got %v, want no error", err) } defer conn.Close() req, err := http.NewRequest("CONNECT", "//example.com:443", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // CONNECT example.com:443 HTTP/1.1 // Host: example.com // // Rewritten to CONNECT to host:port in CONNECT request modifier. if err := req.Write(conn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } // CONNECT response after establishing tunnel. res, err := http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } if got, want := res.StatusCode, 200; got != want { t.Fatalf("res.StatusCode: got %d, want %d", got, want) } if !tm.RequestModified() { t.Error("tm.RequestModified(): got false, want true") } if !tm.ResponseModified() { t.Error("tm.ResponseModified(): got false, want true") } if got, want := res.Header.Get("Warning"), reserr.Error(); !strings.Contains(got, want) { t.Errorf("res.Header.Get(%q): got %q, want to contain %q", "Warning", got, want) } roots := x509.NewCertPool() roots.AddCert(ca) tlsconn := tls.Client(conn, &tls.Config{ ServerName: "example.com", RootCAs: roots, }) defer tlsconn.Close() req, err = http.NewRequest("GET", "https://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Connection", "close") // GET / HTTP/1.1 // Host: example.com // Connection: close if err := req.Write(tlsconn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } res, err = http.ReadResponse(bufio.NewReader(tlsconn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } defer res.Body.Close() if got, want := res.StatusCode, 299; got != want { t.Fatalf("res.StatusCode: got %d, want %d", got, want) } if got, want := res.Header.Get("Warning"), reserr.Error(); strings.Contains(got, want) { t.Errorf("res.Header.Get(%q): got %s, want to not contain %s", "Warning", got, want) } }
func TestIntegrationConnectDownstreamProxy(t *testing.T) { t.Parallel() // Start first proxy to use as downstream. dl, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } downstream := NewProxy() defer downstream.Close() dtr := martiantest.NewTransport() dtr.Respond(299) downstream.SetRoundTripper(dtr) ca, priv, err := mitm.NewAuthority("martian.proxy", "Martian Authority", 2*time.Hour) if err != nil { t.Fatalf("mitm.NewAuthority(): got %v, want no error", err) } mc, err := mitm.NewConfig(ca, priv) if err != nil { t.Fatalf("mitm.NewConfig(): got %v, want no error", err) } downstream.SetMITM(mc) go downstream.Serve(dl) // Start second proxy as upstream proxy, will CONNECT to downstream proxy. ul, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } upstream := NewProxy() defer upstream.Close() // Set upstream proxy's downstream proxy to the host:port of the first proxy. upstream.SetDownstreamProxy(&url.URL{ Host: dl.Addr().String(), }) go upstream.Serve(ul) // Open connection to upstream proxy. conn, err := net.Dial("tcp", ul.Addr().String()) if err != nil { t.Fatalf("net.Dial(): got %v, want no error", err) } defer conn.Close() req, err := http.NewRequest("CONNECT", "//example.com:443", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // CONNECT example.com:443 HTTP/1.1 // Host: example.com if err := req.Write(conn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } // Response from downstream proxy starting MITM. res, err := http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } if got, want := res.StatusCode, 200; got != want { t.Fatalf("res.StatusCode: got %d, want %d", got, want) } roots := x509.NewCertPool() roots.AddCert(ca) tlsconn := tls.Client(conn, &tls.Config{ // Validate the hostname. ServerName: "example.com", // The certificate will have been MITM'd, verify using the MITM CA // certificate. RootCAs: roots, }) defer tlsconn.Close() req, err = http.NewRequest("GET", "https://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // GET / HTTP/1.1 // Host: example.com if err := req.Write(tlsconn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } // Response from MITM in downstream proxy. res, err = http.ReadResponse(bufio.NewReader(tlsconn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } defer res.Body.Close() if got, want := res.StatusCode, 299; got != want { t.Fatalf("res.StatusCode: got %d, want %d", got, want) } }
func TestIntegrationTLSHandshakeErrorCallback(t *testing.T) { t.Parallel() l, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } p := NewProxy() defer p.Close() // Test TLS server. ca, priv, err := mitm.NewAuthority("martian.proxy", "Martian Authority", time.Hour) if err != nil { t.Fatalf("mitm.NewAuthority(): got %v, want no error", err) } mc, err := mitm.NewConfig(ca, priv) if err != nil { t.Fatalf("mitm.NewConfig(): got %v, want no error", err) } cb := make(chan error) mc.SetHandshakeErrorCallback(func(_ *http.Request, err error) { cb <- err }) p.SetMITM(mc) tl, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("tls.Listen(): got %v, want no error", err) } tl = tls.NewListener(tl, mc.TLS()) go http.Serve(tl, http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(200) })) tm := martiantest.NewModifier() // Force the CONNECT request to dial the local TLS server. tm.RequestFunc(func(req *http.Request) { req.URL.Host = tl.Addr().String() }) go p.Serve(l) conn, err := net.Dial("tcp", l.Addr().String()) if err != nil { t.Fatalf("net.Dial(): got %v, want no error", err) } defer conn.Close() req, err := http.NewRequest("CONNECT", "//example.com:443", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // CONNECT example.com:443 HTTP/1.1 // Host: example.com // // Rewritten to CONNECT to host:port in CONNECT request modifier. if err := req.Write(conn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } // CONNECT response after establishing tunnel. if _, err := http.ReadResponse(bufio.NewReader(conn), req); err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } tlsconn := tls.Client(conn, &tls.Config{ ServerName: "example.com", // Client has no cert so it will get "x509: certificate signed by unknown authority" from the // handshake and send "remote error: bad certificate" to the server. RootCAs: x509.NewCertPool(), }) defer tlsconn.Close() req, err = http.NewRequest("GET", "https://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } req.Header.Set("Connection", "close") if got, want := req.Write(tlsconn), "x509: certificate signed by unknown authority"; !strings.Contains(got.Error(), want) { t.Fatalf("Got incorrect error from Client Handshake(), got: %v, want: %v", got, want) } if got, want := <-cb, "remote error: bad certificate"; !strings.Contains(got.Error(), want) { t.Fatalf("Got incorrect error from Server Handshake(), got: %v, want: %v", got, want) } }
func TestServerClosesConnection(t *testing.T) { t.Parallel() dstl, err := net.Listen("tcp4", ":0") if err != nil { t.Fatalf("Failed to create http listener: %v", err) } defer dstl.Close() go func() { t.Logf("Waiting for server side connection") conn, err := dstl.Accept() if err != nil { t.Fatalf("Got error while accepting connection on destination listener: %v", err) } t.Logf("Accepted server side connection") buf := make([]byte, 16384) if _, err := conn.Read(buf); err != nil { t.Fatalf("Error reading: %v", err) } _, err = conn.Write([]byte("HTTP/1.1 301 MOVED PERMANENTLY\r\n" + "Server: \r\n" + "Date: \r\n" + "Referer: \r\n" + "Location: http://www.foo.com/\r\n" + "Content-type: text/html\r\n" + "Connection: close\r\n\r\n")) if err != nil { t.Fatalf("Got error while writting to connection on destination listener: %v", err) } conn.Close() }() l, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } ca, priv, err := mitm.NewAuthority("martian.proxy", "Martian Authority", 2*time.Hour) if err != nil { t.Fatalf("mitm.NewAuthority(): got %v, want no error", err) } mc, err := mitm.NewConfig(ca, priv) if err != nil { t.Fatalf("mitm.NewConfig(): got %v, want no error", err) } p := NewProxy() p.SetMITM(mc) defer p.Close() // Start the proxy with a listener that will return a temporary error on // Accept() three times. go p.Serve(newTimeoutListener(l, 3)) conn, err := net.Dial("tcp", l.Addr().String()) if err != nil { t.Fatalf("net.Dial(): got %v, want no error", err) } defer conn.Close() req, err := http.NewRequest("CONNECT", fmt.Sprintf("//%s", dstl.Addr().String()), nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // CONNECT example.com:443 HTTP/1.1 // Host: example.com if err := req.Write(conn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } res, err := http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } res.Body.Close() _, err = conn.Write([]byte("GET / HTTP/1.1\r\n" + "User-Agent: curl/7.35.0\r\n" + fmt.Sprintf("Host: %s\r\n", dstl.Addr()) + "Accept: */*\r\n\r\n")) if err != nil { t.Fatalf("Error while writing GET request: %v", err) } res, err = http.ReadResponse(bufio.NewReader(io.TeeReader(conn, os.Stderr)), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } _, err = ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("error while ReadAll: %v", err) } defer res.Body.Close() }
func TestHTTPThroughConnectWithMITM(t *testing.T) { t.Parallel() l, err := net.Listen("tcp", "[::1]:0") if err != nil { t.Fatalf("net.Listen(): got %v, want no error", err) } p := NewProxy() defer p.Close() tm := martiantest.NewModifier() tm.RequestFunc(func(req *http.Request) { ctx := NewContext(req) ctx.SkipRoundTrip() if req.Method != "GET" && req.Method != "CONNECT" { t.Errorf("unexpected method on request handler: %v", req.Method) } }) p.SetRequestModifier(tm) ca, priv, err := mitm.NewAuthority("martian.proxy", "Martian Authority", 2*time.Hour) if err != nil { t.Fatalf("mitm.NewAuthority(): got %v, want no error", err) } mc, err := mitm.NewConfig(ca, priv) if err != nil { t.Fatalf("mitm.NewConfig(): got %v, want no error", err) } p.SetMITM(mc) go p.Serve(l) conn, err := net.Dial("tcp", l.Addr().String()) if err != nil { t.Fatalf("net.Dial(): got %v, want no error", err) } defer conn.Close() req, err := http.NewRequest("CONNECT", "//example.com:80", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // CONNECT example.com:80 HTTP/1.1 // Host: example.com if err := req.Write(conn); err != nil { t.Fatalf("req.Write(): got %v, want no error", err) } // Response skipped round trip. res, err := http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } res.Body.Close() if got, want := res.StatusCode, 200; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } req, err = http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // GET http://example.com/ HTTP/1.1 // Host: example.com if err := req.WriteProxy(conn); err != nil { t.Fatalf("req.WriteProxy(): got %v, want no error", err) } // Response from skipped round trip. res, err = http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } res.Body.Close() if got, want := res.StatusCode, 200; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } req, err = http.NewRequest("GET", "http://example.com", nil) if err != nil { t.Fatalf("http.NewRequest(): got %v, want no error", err) } // GET http://example.com/ HTTP/1.1 // Host: example.com if err := req.WriteProxy(conn); err != nil { t.Fatalf("req.WriteProxy(): got %v, want no error", err) } // Response from skipped round trip. res, err = http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("http.ReadResponse(): got %v, want no error", err) } res.Body.Close() if got, want := res.StatusCode, 200; got != want { t.Errorf("res.StatusCode: got %d, want %d", got, want) } }
func main() { flag.Parse() mlog.SetLevel(*level) p := martian.NewProxy() // Respond with 404 to any unknown proxy path. http.HandleFunc(*api+"/", http.NotFound) var x509c *x509.Certificate var priv interface{} if *generateCA { var err error x509c, priv, err = mitm.NewAuthority("martian.proxy", "Martian Authority", 30*24*time.Hour) if err != nil { log.Fatal(err) } } else if *cert != "" && *key != "" { tlsc, err := tls.LoadX509KeyPair(*cert, *key) if err != nil { log.Fatal(err) } priv = tlsc.PrivateKey x509c, err = x509.ParseCertificate(tlsc.Certificate[0]) if err != nil { log.Fatal(err) } } if x509c != nil && priv != nil { mc, err := mitm.NewConfig(x509c, priv) if err != nil { log.Fatal(err) } mc.SetValidity(*validity) mc.SetOrganization(*organization) mc.SkipTLSVerify(*skipTLSVerify) p.SetMITM(mc) // Expose certificate authority. ah := martianhttp.NewAuthorityHandler(x509c) configure("/authority.cer", ah) // Start TLS listener for transparent MITM. tl, err := net.Listen("tcp", *tlsAddr) if err != nil { log.Fatal(err) } go p.Serve(tls.NewListener(tl, mc.TLS())) } stack, fg := httpspec.NewStack("martian") p.SetRequestModifier(stack) p.SetResponseModifier(stack) m := martianhttp.NewModifier() fg.AddRequestModifier(m) fg.AddResponseModifier(m) if *harLogging { hl := har.NewLogger("martian", "2.0.0") stack.AddRequestModifier(hl) stack.AddResponseModifier(hl) configure("/logs", har.NewExportHandler(hl)) configure("/logs/reset", har.NewResetHandler(hl)) } // Proxy specific handlers. // These handlers take precendence over proxy traffic and will not be // intercepted. // Configure modifiers. configure("/configure", m) // Verify assertions. vh := verify.NewHandler() vh.SetRequestVerifier(m) vh.SetResponseVerifier(m) configure("/verify", vh) // Reset verifications. rh := verify.NewResetHandler() rh.SetRequestVerifier(m) rh.SetResponseVerifier(m) configure("/verify/reset", rh) l, err := net.Listen("tcp", *addr) if err != nil { log.Fatal(err) } if *trafficShaping { tsl := trafficshape.NewListener(l) tsh := trafficshape.NewHandler(tsl) configure("/shape-traffic", tsh) l = tsl } log.Println("martian: proxy started on:", l.Addr()) go p.Serve(l) sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, os.Kill) <-sigc log.Println("martian: shutting down") }
// StartWithCertificate runs a proxy on addr and configures a cert for MITM func StartWithCertificate(proxyAddr string, cert string, key string) (*Martian, error) { flag.Set("logtostderr", "true") signal.Ignore(syscall.SIGPIPE) l, err := net.Listen("tcp", proxyAddr) if err != nil { return nil, err } mlog.Debugf("mobileproxy: started listener: %v", l.Addr()) p := martian.NewProxy() mux := http.NewServeMux() p.SetMux(mux) if cert != "" && key != "" { tlsc, err := tls.X509KeyPair([]byte(cert), []byte(key)) if err != nil { log.Fatal(err) } mlog.Debugf("mobileproxy: loaded cert and key") x509c, err := x509.ParseCertificate(tlsc.Certificate[0]) if err != nil { log.Fatal(err) } mlog.Debugf("mobileproxy: parsed cert") mc, err := mitm.NewConfig(x509c, tlsc.PrivateKey) if err != nil { log.Fatal(err) } mc.SetValidity(12 * time.Hour) mc.SetOrganization("Martian Proxy") p.SetMITM(mc) mux.Handle("martian.proxy/authority.cer", martianhttp.NewAuthorityHandler(x509c)) mlog.Debugf("mobileproxy: install cert from http://martian.proxy/authority.cer") } stack, fg := httpspec.NewStack("martian.mobileproxy") p.SetRequestModifier(stack) p.SetResponseModifier(stack) // add HAR logger hl := har.NewLogger() stack.AddRequestModifier(hl) stack.AddResponseModifier(hl) m := martianhttp.NewModifier() fg.AddRequestModifier(m) fg.AddResponseModifier(m) mlog.Debugf("mobileproxy: set martianhttp modifier") // Proxy specific handlers. // These handlers take precendence over proxy traffic and will not be intercepted. // Retrieve HAR logs mux.Handle("martian.proxy/logs", har.NewExportHandler(hl)) mux.Handle("martian.proxy/logs/reset", har.NewResetHandler(hl)) // Update modifiers. mux.Handle("martian.proxy/configure", m) mlog.Debugf("mobileproxy: configure with requests to http://martian.proxy/configure") // Verify assertions. vh := verify.NewHandler() vh.SetRequestVerifier(m) vh.SetResponseVerifier(m) mux.Handle("martian.proxy/verify", vh) mlog.Debugf("mobileproxy: check verifications with requests to http://martian.proxy/verify") // Reset verifications. rh := verify.NewResetHandler() rh.SetRequestVerifier(m) rh.SetResponseVerifier(m) mux.Handle("martian.proxy/verify/reset", rh) mlog.Debugf("mobileproxy: reset verifications with requests to http://martian.proxy/verify/reset") go p.Serve(l) mlog.Infof("mobileproxy: started proxy on listener") return &Martian{ proxy: p, listener: l, mux: mux, }, nil }