Commit f7bd3fa
2021-11-12 17:57:49
go.mod
@@ -0,0 +1,5 @@
+module github.com/xlg-pkg/proxy-server
+
+go 1.17
+
+require github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4
go.sum
@@ -0,0 +1,5 @@
+github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY=
+github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
+github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
main.go
@@ -0,0 +1,172 @@
+package main
+
+import (
+ "bytes"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/tls"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/pem"
+ "flag"
+ "fmt"
+ "log"
+ "math/big"
+ "net"
+ "net/http"
+ "sync"
+ "syscall"
+ "time"
+
+ "github.com/elazarl/goproxy"
+)
+
+var (
+ username = *flag.String("username", "", "username")
+ password = *flag.String("password", "", "password")
+ certificate = *flag.String("certificate", "", "certificate")
+ key = *flag.String("key", "", "key")
+ host = *flag.String("host", "localhost", "host")
+ port = *flag.String("port", "8080", "port")
+ verbose = *flag.Bool("verbose", false, "verbosity")
+)
+
+type CertificateStore struct {
+ certs map[string]*tls.Certificate
+ locks map[string]*sync.Mutex
+ sync.Mutex
+}
+
+func (s *CertificateStore) Fetch(host string, generate func() (*tls.Certificate, error)) (*tls.Certificate, error) {
+ hostLock := s.LockFor(host)
+ hostLock.Lock()
+ defer hostLock.Unlock()
+
+ cert, ok := s.certs[host]
+ var err error
+ if !ok {
+ cert, err = generate()
+ if err != nil {
+ return nil, err
+ }
+ s.certs[host] = cert
+ }
+ return cert, nil
+}
+
+func (s *CertificateStore) LockFor(host string) *sync.Mutex {
+ s.Lock()
+ defer s.Unlock()
+
+ lock, ok := s.locks[host]
+ if !ok {
+ lock = &sync.Mutex{}
+ s.locks[host] = lock
+ }
+ return lock
+}
+
+func listenAddress() string {
+ return fmt.Sprintf("%s:%s", host, port)
+}
+
+func generateSelfSignedCert() (tls.Certificate, error) {
+ priv, err := rsa.GenerateKey(rand.Reader, 4096)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ template := x509.Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{Organization: []string{"xlg"}},
+ NotBefore: time.Now(),
+ NotAfter: time.Now().Add(time.Hour * 24 * 365),
+ KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
+ ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+ BasicConstraintsValid: true,
+ IsCA: true,
+ }
+ if ip := net.ParseIP(host); ip != nil {
+ template.IPAddresses = append(template.IPAddresses, ip)
+ } else {
+ template.DNSNames = append(template.DNSNames, host)
+ }
+
+ derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ crtPem := &bytes.Buffer{}
+ pem.Encode(crtPem, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+
+ keyPem := &bytes.Buffer{}
+ pem.Encode(keyPem, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
+
+ return tls.X509KeyPair(crtPem.Bytes(), keyPem.Bytes())
+}
+
+func certFrom(certificate, key string) (tls.Certificate, error) {
+ if certificate != "" && key != "" {
+ return tls.LoadX509KeyPair(certificate, key)
+ }
+ return generateSelfSignedCert()
+}
+
+func main() {
+ flag.Parse()
+
+ ca, err := certFrom(certificate, key)
+ if err != nil {
+ log.Fatal(err)
+ }
+ goproxy.GoproxyCa = ca
+ goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
+ goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
+ goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
+ goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: goproxy.TLSConfigFromCA(&ca)}
+
+ proxy := goproxy.NewProxyHttpServer()
+ proxy.Verbose = verbose
+ dialer := &net.Dialer{Control: func(network, address string, conn syscall.RawConn) error { return nil }}
+ proxy.Tr = &http.Transport{
+ Dial: dialer.Dial,
+ DialContext: dialer.DialContext,
+ TLSClientConfig: &tls.Config{},
+ Proxy: http.ProxyFromEnvironment,
+ }
+ proxy.CertStore = &CertificateStore{
+ certs: map[string]*tls.Certificate{},
+ locks: map[string]*sync.Mutex{},
+ }
+ proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
+ proxy.OnRequest().DoFunc(func(r *http.Request, p *goproxy.ProxyCtx) (*http.Request, *http.Response) {
+ log.Printf("%s %s\n", r.Method, r.URL)
+ if proxy.Verbose {
+ for k, v := range r.Header {
+ log.Printf("%s: %v\n", k, v)
+ }
+ }
+
+ return r, nil
+ })
+ proxy.OnResponse().DoFunc(func(r *http.Response, p *goproxy.ProxyCtx) *http.Response {
+ if r == nil {
+ log.Printf("No response from server\n")
+ return r
+ }
+
+ log.Printf("%d %s\n", r.StatusCode, r.Request.URL)
+ if proxy.Verbose {
+ for k, v := range r.Header {
+ log.Printf("%s: %v\n", k, v)
+ }
+ }
+
+ return r
+ })
+
+ address := listenAddress()
+ log.Printf("Listening and serving HTTP on http://%s\n", address)
+ log.Fatal(http.ListenAndServe(address, proxy))
+}
README.md
@@ -0,0 +1,8 @@
+# proxy-server
+
+A tiny proxy server.
+
+
+```bash
+$ curl -k --proxy http://localhost:8080 https://www.eff.org/
+```