Skip to content

Commit

Permalink
feat: 🎸 Added API url for downloading CA cert
Browse files Browse the repository at this point in the history
  • Loading branch information
ameshkov committed Nov 8, 2019
1 parent ccede89 commit 3f21124
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 12 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ proxy := gomitmproxy.NewProxy(gomitmproxy.Config{
})
```

If you configure the `APIHost`, you'll be able to download the CA certificate
from `http://[APIHost]/cert.crt` when the proxy is configured.

```go
// Navigate to http://gomitmproxy/cert.crt to download the CA certificate
proxy.APIHost = "gomitmproxy"
```

### Custom certs storage

By default, `gomitmproxy` uses an in-memory map-based storage for the certificates,
Expand Down Expand Up @@ -218,12 +226,12 @@ mitmConfig, err := mitm.NewConfig(x509c, privateKey, &CustomCertsStorage{
* [X] How-to doc
* [X] Travis configuration
* [X] Proxy-Authorization
* [ ] Unit tests
* [X] WebSockets support (see [this](https://github.com/google/martian/issues/31))
* [X] `certsCache` -- allow custom implementations
* [X] Support HTTP CONNECT over TLS
* [ ] Test plain HTTP requests inside HTTP CONNECT
* [ ] Test memory leaks
* [X] Test plain HTTP requests inside HTTP CONNECT
* [X] Test memory leaks
* [ ] Unit tests
* [ ] Check & fix TODOs
* [ ] MITM
* [X] Basic MITM
Expand Down
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ type Config struct {
MITMConfig *mitm.Config // If not nil, MITM is enabled for the proxy
MITMExceptions []string // A list of hostnames for which MITM will be disabled

// APIHost is a name of the gomitmproxy API
// If it is set to "", there will be no API
// Here are the methods exposed:
// 1. apihost/cert.crt -- serves the authority cert (if MITMConfig is configured)
APIHost string

// OnRequest is called when the request has been just received,
// but has not been sent to the remote server.
//
Expand Down
23 changes: 15 additions & 8 deletions examples/mitm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ import (
"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:6060", nil))
}()

// READ CERT AND KEY
tlsCert, err := tls.LoadX509KeyPair("demo.crt", "demo.key")
if err != nil {
Expand All @@ -43,13 +49,13 @@ func main() {
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},
}
//proxyCert, err := mitmConfig.GetOrCreateCert("127.0.0.1")
//if err != nil {
// panic(err)
//}
//tlsConfig := &tls.Config{
// Certificates: []tls.Certificate{*proxyCert},
//}

// PREPARE PROXY
addr := &net.TCPAddr{
Expand All @@ -59,10 +65,11 @@ func main() {

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

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

MITMConfig: mitmConfig,
MITMExceptions: []string{"example.com"},
Expand Down
5 changes: 5 additions & 0 deletions mitm/mitm.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ func NewConfig(ca *x509.Certificate, privateKey *rsa.PrivateKey, storage CertsSt
}, nil
}

// GetCA returns the authority cert
func (c *Config) GetCA() *x509.Certificate {
return c.ca
}

// SetOrganization sets the organization name that
// will be used in generated certs
func (c *Config) SetOrganization(organization string) {
Expand Down
39 changes: 38 additions & 1 deletion proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bufio"
"bytes"
"crypto/tls"
"encoding/pem"
"io"
"net"
"net/http"
Expand Down Expand Up @@ -224,6 +225,10 @@ func (p *Proxy) handleRequest(ctx *Context) error {
}
}

if session.req.Host == p.APIHost {
return p.handleAPIRequest(session)
}

if !customRes {
// check proxy authorization
if p.Username != "" {
Expand Down Expand Up @@ -292,6 +297,33 @@ func (p *Proxy) handleRequest(ctx *Context) error {
return nil
}

// handleAPIRequest handles a request to gomitmproxy's API
func (p *Proxy) handleAPIRequest(session *Session) error {
if session.req.URL.Path == "/cert.crt" && p.MITMConfig != nil {
// serve ca
b := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: p.MITMConfig.GetCA().Raw,
})

// nolint:bodyclose
// body is actually closed
session.res = NewResponse(http.StatusOK, bytes.NewReader(b), session.req)
defer session.res.Body.Close()
session.res.Close = true
session.res.Header.Set("Content-Type", "application/x-x509-ca-cert")
session.res.ContentLength = int64(len(b))
return p.writeResponse(session)
}

// nolint:bodyclose
// body is actually closed
session.res = newErrorResponse(session.req, errors.Errorf("wrong API method"))
defer session.res.Body.Close()
session.res.Close = true
return p.writeResponse(session)
}

// returns true if this session's response or request signals that
// the connection must be closed
func (p *Proxy) isClosing(session *Session) bool {
Expand Down Expand Up @@ -580,11 +612,16 @@ func (p *Proxy) canMITM(hostname string) bool {
}

// Remove the port if it exists.
host, _, err := net.SplitHostPort(hostname)
host, port, err := net.SplitHostPort(hostname)
if err == nil {
hostname = host
}

if port != "443" {
log.Debug("do not attempt to MITM connections to a port different from 443")
return false
}

p.invalidTLSHostsMu.RLock()
_, found := p.invalidTLSHosts[hostname]
p.invalidTLSHostsMu.RUnlock()
Expand Down

0 comments on commit 3f21124

Please sign in to comment.