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}