Skip to content

Instantly share code, notes, and snippets.

@riza
Created September 14, 2022 19:41
Show Gist options
  • Save riza/bb85ddd44a6ea6605f080755b8ea0940 to your computer and use it in GitHub Desktop.
Save riza/bb85ddd44a6ea6605f080755b8ea0940 to your computer and use it in GitHub Desktop.
simple ssl proxy with goproxy
package main
/*
#!/usr/bin/env bash
case `uname -s` in
Linux*) sslConfig=/etc/ssl/openssl.cnf;;
Darwin*) sslConfig=/System/Library/OpenSSL/openssl.cnf;;
esac
openssl req \
-newkey rsa:2048 \
-x509 \
-nodes \
-keyout server.key \
-new \
-out server.pem \
-subj /CN=localhost \
-reqexts SAN \
-extensions SAN \
-config <(cat $sslConfig \
<(printf '[SAN]\nsubjectAltName=DNS:localhost')) \
-sha256 \
-days 3650
*/
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"github.com/elazarl/goproxy"
"io/ioutil"
"log"
"math/big"
"net"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
var (
certCache = make(map[string]*tls.Certificate)
certLock = &sync.Mutex{}
)
const (
caCert = `certHere`
privKey = `keyHere`
)
func main() {
proxyCert, err := tls.X509KeyPair([]byte(caCert), []byte(privKey))
if err != nil {
log.Fatalf("X509 Key Pair fail %v", err)
}
if proxyCert.Leaf, err = x509.ParseCertificate(proxyCert.Certificate[0]); err != nil {
log.Fatalf("Parse certificate fail %v", err)
}
goproxy.GoproxyCa = proxyCert
goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: tlsConfigFromCert(&proxyCert)}
goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: tlsConfigFromCert(&proxyCert)}
goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: tlsConfigFromCert(&proxyCert)}
goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: tlsConfigFromCert(&proxyCert)}
proxyHandler := goproxy.NewProxyHttpServer()
server := &http.Server{
Addr: ":8080",
Handler: proxyHandler,
}
proxyHandler.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
var resp *http.Response
var bodyBytes []byte
if r != nil {
bodyBytes, _ = ioutil.ReadAll(r.Body)
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
}
fmt.Println("Sending request")
return r, resp
})
proxyHandler.OnResponse().DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
var bodyBytes []byte
if r != nil {
bodyBytes, _ = ioutil.ReadAll(r.Body)
// Restore the io.ReadCloser to its original state
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
}
fmt.Println("Received response")
return r
})
server.ListenAndServe()
}
func tlsConfigFromCert(ca *tls.Certificate) func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
return func(host string, ctx *goproxy.ProxyCtx) (c *tls.Config, err error) {
parts := strings.SplitN(host, ":", 2)
hostname := parts[0]
port := 443
if len(parts) > 1 {
port, err = strconv.Atoi(parts[1])
if err != nil {
port = 443
}
}
cert := getCachedCert(hostname, port)
if cert == nil {
cert, err = signHost(ca, hostname, port)
if err != nil {
return nil, err
}
setCachedCert(hostname, port, cert)
}
config := tls.Config{
InsecureSkipVerify: true,
Certificates: []tls.Certificate{*cert},
}
return &config, nil
}
}
func signHost(ca *tls.Certificate, host string, port int) (cert *tls.Certificate, err error) {
var x509ca *x509.Certificate
var template x509.Certificate
if x509ca, err = x509.ParseCertificate(ca.Certificate[0]); err != nil {
return
}
notBefore := time.Now()
aYear := time.Duration(365) * time.Hour
notAfter := notBefore.Add(aYear)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, err
}
template = x509.Certificate{
SerialNumber: serialNumber,
Issuer: x509ca.Subject,
Subject: pkix.Name{
Country: []string{"US"},
Locality: []string{""},
Organization: []string{"broxy"},
OrganizationalUnit: []string{"https://github.com/rhaidiz/broxy/"},
CommonName: "broxy mitm certificate",
},
NotBefore: notBefore,
NotAfter: notAfter,
//KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
//ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
//BasicConstraintsValid: true,
}
if ip := net.ParseIP(host); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, host)
}
var certpriv *rsa.PrivateKey
if certpriv, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
return
}
var derBytes []byte
if derBytes, err = x509.CreateCertificate(rand.Reader, &template, x509ca, &certpriv.PublicKey, ca.PrivateKey); err != nil {
return
}
return &tls.Certificate{
Certificate: [][]byte{derBytes, ca.Certificate[0]},
PrivateKey: certpriv,
}, nil
}
func getCachedCert(domain string, port int) *tls.Certificate {
certLock.Lock()
defer certLock.Unlock()
if cert, found := certCache[fmt.Sprintf("%s:%d", domain, port)]; found {
return cert
}
return nil
}
func setCachedCert(domain string, port int, cert *tls.Certificate) {
certLock.Lock()
defer certLock.Unlock()
certCache[fmt.Sprintf("%s:%d", domain, port)] = cert
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment