main
  1package main
  2
  3import (
  4	"bytes"
  5	"crypto/rand"
  6	"crypto/rsa"
  7	"crypto/tls"
  8	"crypto/x509"
  9	"crypto/x509/pkix"
 10	"encoding/pem"
 11	"flag"
 12	"fmt"
 13	"log"
 14	"math/big"
 15	"net"
 16	"net/http"
 17	"sync"
 18	"syscall"
 19	"time"
 20
 21	"github.com/elazarl/goproxy"
 22)
 23
 24var (
 25	certificate = *flag.String("certificate", "", "Path to x509 Certificate fille")
 26	key         = *flag.String("key", "", "Path to the private key file")
 27	host        = *flag.String("host", "127.0.0.1", "Interface to bind to")
 28	port        = *flag.String("port", "8080", "Port to bind to")
 29	verbose     = *flag.Bool("verbose", false, "Enable verbose output")
 30)
 31
 32type CertificateStore struct {
 33	certs map[string]*tls.Certificate
 34	locks map[string]*sync.Mutex
 35	sync.Mutex
 36}
 37
 38func (s *CertificateStore) Fetch(host string, generate func() (*tls.Certificate, error)) (*tls.Certificate, error) {
 39	hostLock := s.LockFor(host)
 40	hostLock.Lock()
 41	defer hostLock.Unlock()
 42
 43	cert, ok := s.certs[host]
 44	var err error
 45	if !ok {
 46		cert, err = generate()
 47		if err != nil {
 48			return nil, err
 49		}
 50		s.certs[host] = cert
 51	}
 52	return cert, nil
 53}
 54
 55func (s *CertificateStore) LockFor(host string) *sync.Mutex {
 56	s.Lock()
 57	defer s.Unlock()
 58
 59	lock, ok := s.locks[host]
 60	if !ok {
 61		lock = &sync.Mutex{}
 62		s.locks[host] = lock
 63	}
 64	return lock
 65}
 66
 67func listenAddress() string {
 68	return fmt.Sprintf("%s:%s", host, port)
 69}
 70
 71func generateSelfSignedCert() (tls.Certificate, error) {
 72	priv, err := rsa.GenerateKey(rand.Reader, 4096)
 73	if err != nil {
 74		log.Fatal(err)
 75	}
 76
 77	template := x509.Certificate{
 78		SerialNumber:          big.NewInt(1),
 79		Subject:               pkix.Name{Organization: []string{"xlg"}},
 80		NotBefore:             time.Now(),
 81		NotAfter:              time.Now().Add(time.Hour * 24 * 365),
 82		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
 83		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
 84		BasicConstraintsValid: true,
 85		IsCA:                  true,
 86	}
 87	if ip := net.ParseIP(host); ip != nil {
 88		template.IPAddresses = append(template.IPAddresses, ip)
 89	} else {
 90		template.DNSNames = append(template.DNSNames, host)
 91	}
 92
 93	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
 94	if err != nil {
 95		log.Fatal(err)
 96	}
 97
 98	crtPem := &bytes.Buffer{}
 99	pem.Encode(crtPem, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
100
101	keyPem := &bytes.Buffer{}
102	pem.Encode(keyPem, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
103
104	return tls.X509KeyPair(crtPem.Bytes(), keyPem.Bytes())
105}
106
107func certFrom(certificate, key string) (tls.Certificate, error) {
108	if certificate != "" && key != "" {
109		return tls.LoadX509KeyPair(certificate, key)
110	}
111	return generateSelfSignedCert()
112}
113
114func main() {
115	flag.Parse()
116
117	ca, err := certFrom(certificate, key)
118	if err != nil {
119		log.Fatal(err)
120	}
121	goproxy.GoproxyCa = ca
122	goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
123	goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
124	goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
125	goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
126
127	proxy := goproxy.NewProxyHttpServer()
128	proxy.Verbose = verbose
129	dialer := &net.Dialer{Control: func(network, address string, conn syscall.RawConn) error { return nil }}
130	proxy.Tr = &http.Transport{
131		Dial:            dialer.Dial,
132		DialContext:     dialer.DialContext,
133		TLSClientConfig: &tls.Config{},
134		Proxy:           http.ProxyFromEnvironment,
135	}
136	proxy.CertStore = &CertificateStore{
137		certs: map[string]*tls.Certificate{},
138		locks: map[string]*sync.Mutex{},
139	}
140	proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
141	proxy.OnRequest().DoFunc(func(r *http.Request, p *goproxy.ProxyCtx) (*http.Request, *http.Response) {
142		log.Printf("%s %s\n", r.Method, r.URL)
143		if proxy.Verbose {
144			for k, v := range r.Header {
145				log.Printf("%s: %v\n", k, v)
146			}
147		}
148
149		return r, nil
150	})
151	proxy.OnResponse().DoFunc(func(r *http.Response, p *goproxy.ProxyCtx) *http.Response {
152		if r == nil {
153			log.Printf("No response from server\n")
154			return r
155		}
156
157		log.Printf("%d %s\n", r.StatusCode, r.Request.URL)
158		if proxy.Verbose {
159			for k, v := range r.Header {
160				log.Printf("%s: %v\n", k, v)
161			}
162		}
163
164		return r
165	})
166
167	address := listenAddress()
168	log.Printf("Listening and serving HTTP on http://%s\n", address)
169	log.Fatal(http.ListenAndServe(address, proxy))
170}