mitm proxy examples

This commit is contained in:
telanflow
2020-08-13 16:17:33 +08:00
parent 085821dbf9
commit a8616bbe98
11 changed files with 193 additions and 84 deletions

View File

@@ -113,6 +113,8 @@ func (ctx *Context) Next(req *http.Request) (*http.Response, error) {
ctx.mi++ ctx.mi++
if ctx.mi >= total { if ctx.mi >= total {
ctx.mi = -1 ctx.mi = -1
// Final request coverage
ctx.Request = req
// To make the middleware available to the tunnel proxy, // To make the middleware available to the tunnel proxy,
// no response is obtained when the request method is equal to Connect // no response is obtained when the request method is equal to Connect
if req.Method == http.MethodConnect { if req.Method == http.MethodConnect {

View File

@@ -0,0 +1,69 @@
package main
import (
"errors"
"github.com/telanflow/mps"
"log"
"net/http"
"os"
"os/signal"
"regexp"
"syscall"
)
// A simple mitm proxy server
func main() {
quitSignChan := make(chan os.Signal)
// create proxy server
proxy := mps.NewHttpProxy()
// The Connect request is processed using MitmHandler
proxy.HandleConnect = mps.NewMitmHandlerWithContext(proxy.Ctx)
// Middleware
proxy.UseFunc(func(req *http.Request, ctx *mps.Context) (*http.Response, error) {
log.Printf("[INFO] middleware -- %s %s", req.Method, req.URL)
return ctx.Next(req)
})
// Filter
reqGroup := proxy.OnRequest(mps.FilterHostMatches(regexp.MustCompile("^.*$")))
reqGroup.DoFunc(func(req *http.Request, ctx *mps.Context) (*http.Request, *http.Response) {
log.Printf("[INFO] req -- %s %s", req.Method, req.URL)
return req, nil
})
respGroup := proxy.OnResponse()
respGroup.DoFunc(func(resp *http.Response, err error, ctx *mps.Context) (*http.Response, error) {
if err != nil {
log.Printf("[ERRO] resp -- %s %v", ctx.Request.Method, err)
return resp, err
}
log.Printf("[INFO] resp -- %d", resp.StatusCode)
return resp, err
})
// Started proxy server
srv := http.Server{
Addr: "localhost:8080",
Handler: proxy,
}
go func() {
log.Printf("MitmProxy started listen: http://%s", srv.Addr)
err := srv.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) {
return
}
if err != nil {
quitSignChan <- syscall.SIGKILL
log.Fatalf("MitmProxy start fail: %v", err)
}
}()
// quit signal
signal.Notify(quitSignChan, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGQUIT)
<-quitSignChan
_ = srv.Close()
log.Fatal("MitmProxy server stop!")
}

View File

@@ -14,23 +14,23 @@ import (
// A simple reverse proxy server // A simple reverse proxy server
func main() { func main() {
targetHost, _ := url.Parse("https://www.google.com") targetURL, _ := url.Parse("https://www.google.com")
quitSignChan := make(chan os.Signal) quitSignChan := make(chan os.Signal)
// reverse proxy server // reverse proxy server
proxy := mps.NewReverseHandler() proxy := mps.NewReverseHandler()
proxy.UseFunc(middleware.SingleHostReverseProxy(targetHost)) proxy.UseFunc(middleware.SingleHostReverseProxy(targetURL))
reqGroup := proxy.OnRequest() reqGroup := proxy.OnRequest()
reqGroup.DoFunc(func(req *http.Request, ctx *mps.Context) (*http.Request, *http.Response) { reqGroup.DoFunc(func(req *http.Request, ctx *mps.Context) (*http.Request, *http.Response) {
log.Printf("[INFO] req -- %s", req.Host) log.Printf("[INFO] req -- %s %s", req.Method, req.Host)
return req, nil return req, nil
}) })
respGroup := proxy.OnResponse() respGroup := proxy.OnResponse()
respGroup.DoFunc(func(resp *http.Response, err error, ctx *mps.Context) (*http.Response, error) { respGroup.DoFunc(func(resp *http.Response, err error, ctx *mps.Context) (*http.Response, error) {
if err != nil { if err != nil {
log.Printf("[ERRO] resp -- %v", err) log.Printf("[ERRO] resp -- %s %v", ctx.Request.Method, err)
return nil, err return nil, err
} }
log.Printf("[INFO] resp -- %d", resp.StatusCode) log.Printf("[INFO] resp -- %d", resp.StatusCode)

View File

@@ -18,30 +18,30 @@ func main() {
// create a http proxy server // create a http proxy server
proxy := mps.NewHttpProxy() proxy := mps.NewHttpProxy()
proxy.UseFunc(func(req *http.Request, ctx *mps.Context) (*http.Response, error) { proxy.UseFunc(func(req *http.Request, ctx *mps.Context) (*http.Response, error) {
log.Printf("[INFO] middleware -- %s\n", req.URL) log.Printf("[INFO] middleware -- %s %s", req.Method, req.URL)
return ctx.Next(req) return ctx.Next(req)
}) })
reqGroup := proxy.OnRequest(mps.FilterHostMatches(regexp.MustCompile("^.*$"))) reqGroup := proxy.OnRequest(mps.FilterHostMatches(regexp.MustCompile("^.*$")))
reqGroup.DoFunc(func(req *http.Request, ctx *mps.Context) (*http.Request, *http.Response) { reqGroup.DoFunc(func(req *http.Request, ctx *mps.Context) (*http.Request, *http.Response) {
log.Printf("[INFO] req -- %s\n", req.URL) log.Printf("[INFO] req -- %s %s", req.Method, req.URL)
return req, nil return req, nil
}) })
respGroup := proxy.OnResponse() respGroup := proxy.OnResponse()
respGroup.DoFunc(func(resp *http.Response, err error, ctx *mps.Context) (*http.Response, error) { respGroup.DoFunc(func(resp *http.Response, err error, ctx *mps.Context) (*http.Response, error) {
if err != nil { if err != nil {
log.Printf("[ERRO] resp -- %v\n", err) log.Printf("[ERRO] resp -- %s %v", ctx.Request.Method, err)
return resp, err return resp, err
} }
log.Printf("[INFO] resp -- %d\n", resp.StatusCode) log.Printf("[INFO] resp -- %d", resp.StatusCode)
return resp, err return resp, err
}) })
// Start server // Start server
srv := &http.Server{ srv := &http.Server{
Addr: "127.0.0.1:8081", Addr: "localhost:8080",
Handler: proxy, Handler: proxy,
} }
go func() { go func() {

58
filter_group.go Normal file
View File

@@ -0,0 +1,58 @@
package mps
import "net/http"
type FilterGroup interface {
Handle()
}
// ReqCondition is a request filter group
type ReqFilterGroup struct {
ctx *Context
filters []Filter
}
func (cond *ReqFilterGroup) DoFunc(fn func(req *http.Request, ctx *Context) (*http.Request, *http.Response)) {
cond.Do(RequestHandleFunc(fn))
}
func (cond *ReqFilterGroup) Do(h RequestHandle) {
cond.ctx.UseFunc(func(req *http.Request, ctx *Context) (*http.Response, error) {
total := len(cond.filters)
for i := 0; i < total; i++ {
if !cond.filters[i].Match(req) {
return ctx.Next(req)
}
}
req, resp := h.HandleRequest(req, ctx)
if resp != nil {
return resp, nil
}
return ctx.Next(req)
})
}
// ReqCondition is a response filter group
type RespFilterGroup struct {
ctx *Context
filters []Filter
}
func (cond *RespFilterGroup) DoFunc(fn func(resp *http.Response, err error, ctx *Context) (*http.Response, error)) {
cond.Do(ResponseHandleFunc(fn))
}
func (cond *RespFilterGroup) Do(h ResponseHandle) {
cond.ctx.UseFunc(func(req *http.Request, ctx *Context) (*http.Response, error) {
total := len(cond.filters)
for i := 0; i < total; i++ {
if !cond.filters[i].Match(req) {
return ctx.Next(req)
}
}
resp, err := ctx.Next(req)
return h.HandleResponse(resp, err, ctx)
})
}

View File

@@ -2,26 +2,31 @@ package mps
import "net/http" import "net/http"
type Handle interface {
RequestHandle
ResponseHandle
}
type RequestHandle interface { type RequestHandle interface {
Handle(req *http.Request, ctx *Context) (*http.Request, *http.Response) HandleRequest(req *http.Request, ctx *Context) (*http.Request, *http.Response)
} }
// A wrapper that would convert a function to a RequestHandle interface type // A wrapper that would convert a function to a RequestHandle interface type
type RequestHandleFunc func(req *http.Request, ctx *Context) (*http.Request, *http.Response) type RequestHandleFunc func(req *http.Request, ctx *Context) (*http.Request, *http.Response)
// RequestHandle.Handle(req, ctx) <=> RequestHandleFunc(req, ctx) // RequestHandle.Handle(req, ctx) <=> RequestHandleFunc(req, ctx)
func (f RequestHandleFunc) Handle(req *http.Request, ctx *Context) (*http.Request, *http.Response) { func (f RequestHandleFunc) HandleRequest(req *http.Request, ctx *Context) (*http.Request, *http.Response) {
return f(req, ctx) return f(req, ctx)
} }
type ResponseHandle interface { type ResponseHandle interface {
Handle(resp *http.Response, err error, ctx *Context) (*http.Response, error) HandleResponse(resp *http.Response, err error, ctx *Context) (*http.Response, error)
} }
// A wrapper that would convert a function to a ResponseHandle interface type // A wrapper that would convert a function to a ResponseHandle interface type
type ResponseHandleFunc func(resp *http.Response, err error, ctx *Context) (*http.Response, error) type ResponseHandleFunc func(resp *http.Response, err error, ctx *Context) (*http.Response, error)
// ResponseHandle.Handle(resp, ctx) <=> ResponseHandleFunc(resp, ctx) // ResponseHandle.Handle(resp, ctx) <=> ResponseHandleFunc(resp, ctx)
func (f ResponseHandleFunc) Handle(resp *http.Response, err error, ctx *Context) (*http.Response, error) { func (f ResponseHandleFunc) HandleResponse(resp *http.Response, err error, ctx *Context) (*http.Response, error) {
return f(resp, err, ctx) return f(resp, err, ctx)
} }

View File

@@ -9,13 +9,13 @@ import (
// The basic proxy type. Implements http.Handler. // The basic proxy type. Implements http.Handler.
type HttpProxy struct { type HttpProxy struct {
// HTTPS requests use the TunnelHandler proxy by default // Handles Connect requests use the TunnelHandler by default
HttpsHandler http.Handler HandleConnect http.Handler
// HTTP requests use the ForwardHandler proxy by default // HTTP requests use the ForwardHandler by default
HttpHandler http.Handler HttpHandler http.Handler
// HTTP requests use the ReverseHandler proxy by default // HTTP requests use the ReverseHandler by default
ReverseHandler http.Handler ReverseHandler http.Handler
// Client request Context // Client request Context
@@ -27,10 +27,10 @@ func NewHttpProxy() *HttpProxy {
ctx := NewContext() ctx := NewContext()
return &HttpProxy{ return &HttpProxy{
Ctx: ctx, Ctx: ctx,
// default HTTP proxy // default handles Connect method
HandleConnect: &TunnelHandler{Ctx: ctx},
// default handles HTTP request
HttpHandler: &ForwardHandler{Ctx: ctx}, HttpHandler: &ForwardHandler{Ctx: ctx},
// default HTTPS proxy
HttpsHandler: &TunnelHandler{Ctx: ctx},
// default Reverse proxy // default Reverse proxy
ReverseHandler: &ReverseHandler{Ctx: ctx}, ReverseHandler: &ReverseHandler{Ctx: ctx},
} }
@@ -39,10 +39,21 @@ func NewHttpProxy() *HttpProxy {
// Standard net/http function. // Standard net/http function.
func (proxy *HttpProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (proxy *HttpProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodConnect { if req.Method == http.MethodConnect {
proxy.HttpsHandler.ServeHTTP(rw, req) proxy.HandleConnect.ServeHTTP(rw, req)
return return
} }
// reverse proxy http request for example:
// GET / HTTP/1.1
// Host: www.example.com
// Connection: keep-alive
//
// forward proxy http request for example :
// GET http://www.example.com/ HTTP/1.1
// Host: www.example.com
// Proxy-Connection: keep-alive
//
// Determines whether the path is absolute
if !req.URL.IsAbs() { if !req.URL.IsAbs() {
proxy.ReverseHandler.ServeHTTP(rw, req) proxy.ReverseHandler.ServeHTTP(rw, req)
} else { } else {

View File

@@ -51,8 +51,32 @@ func NewMitmHandler() *MitmHandler {
} }
} }
// Create a MitmHandler, use default cert.
func NewMitmHandlerWithContext(ctx *Context) *MitmHandler {
return &MitmHandler{
Ctx: ctx,
BufferPool: pool.DefaultBuffer,
Certificate: cert.DefaultCertificate,
CertContainer: cert.NewMemProvider(),
}
}
// Create a MitmHandler with cert pem block
func NewMitmHandlerWithCert(certPEMBlock, keyPEMBlock []byte) (*MitmHandler, error) {
certificate, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
if err != nil {
return nil, err
}
return &MitmHandler{
Ctx: NewContext(),
BufferPool: pool.DefaultBuffer,
Certificate: certificate,
CertContainer: cert.NewMemProvider(),
}, nil
}
// Create a MitmHandler with cert file // Create a MitmHandler with cert file
func NewMitmHandlerWithCert(certFile, keyFile string) (*MitmHandler, error) { func NewMitmHandlerWithCertFile(certFile, keyFile string) (*MitmHandler, error) {
certificate, err := tls.LoadX509KeyPair(certFile, keyFile) certificate, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -67,7 +91,7 @@ func NewMitmHandlerWithCert(certFile, keyFile string) (*MitmHandler, error) {
// Standard net/http function. You can use it alone // Standard net/http function. You can use it alone
func (mitm *MitmHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (mitm *MitmHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
// Execution middleware // execution middleware
ctx := mitm.Ctx.WithRequest(r) ctx := mitm.Ctx.WithRequest(r)
resp, err := ctx.Next(r) resp, err := ctx.Next(r)
if err != nil && err != MethodNotSupportErr { if err != nil && err != MethodNotSupportErr {

View File

@@ -1,33 +0,0 @@
package mps
import (
"net/http"
)
// ReqCondition is a request condition group
type ReqFilterGroup struct {
ctx *Context
filters []Filter
}
func (cond *ReqFilterGroup) DoFunc(fn func(req *http.Request, ctx *Context) (*http.Request, *http.Response)) {
cond.Do(RequestHandleFunc(fn))
}
func (cond *ReqFilterGroup) Do(h RequestHandle) {
cond.ctx.UseFunc(func(req *http.Request, ctx *Context) (*http.Response, error) {
total := len(cond.filters)
for i := 0; i < total; i++ {
if !cond.filters[i].Match(req) {
return ctx.Next(req)
}
}
req, resp := h.Handle(req, ctx)
if resp != nil {
return resp, nil
}
return ctx.Next(req)
})
}

View File

@@ -1,27 +0,0 @@
package mps
import (
"net/http"
)
type RespFilterGroup struct {
ctx *Context
filters []Filter
}
func (cond *RespFilterGroup) DoFunc(fn func(resp *http.Response, err error, ctx *Context) (*http.Response, error)) {
cond.Do(ResponseHandleFunc(fn))
}
func (cond *RespFilterGroup) Do(h ResponseHandle) {
cond.ctx.UseFunc(func(req *http.Request, ctx *Context) (*http.Response, error) {
total := len(cond.filters)
for i := 0; i < total; i++ {
if !cond.filters[i].Match(req) {
return ctx.Next(req)
}
}
resp, err := ctx.Next(req)
return h.Handle(resp, err, ctx)
})
}

View File

@@ -43,7 +43,7 @@ func NewTunnelHandlerWithContext(ctx *Context) *TunnelHandler {
// Standard net/http function. You can use it alone // Standard net/http function. You can use it alone
func (tunnel *TunnelHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (tunnel *TunnelHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// Execution middleware // execution middleware
ctx := tunnel.Ctx.WithRequest(req) ctx := tunnel.Ctx.WithRequest(req)
resp, err := ctx.Next(req) resp, err := ctx.Next(req)
if err != nil && err != MethodNotSupportErr { if err != nil && err != MethodNotSupportErr {