Skip to content

Instantly share code, notes, and snippets.

@tam7t
Created April 26, 2015 22:02
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save tam7t/dc21e384e66963dae78f to your computer and use it in GitHub Desktop.
Save tam7t/dc21e384e66963dae78f to your computer and use it in GitHub Desktop.
Minimal TLS MITM transparent proxy
/* tlsprox - minimal tls MITM transparent proxy... in go!
* by @tam7t
*
* Usage:
* If we want to MITM https://example.com first get example.com's ip address
* then add localhost to /etc/hosts:
*
* 127.0.0.1 example.com
*
* > go build tlsprox.go
* > sudo ./tlsprox.go -raddr=<example.com ip addr>:443
*/
package main
import (
"crypto/tls"
"flag"
"io"
"log"
"net"
"os"
)
func main() {
var certFile, keyFile, listenAddr, remoteAddr string
flag.StringVar(&certFile, "cert", "cert.pem", "certificate")
flag.StringVar(&keyFile, "key", "key.pem", "key")
flag.StringVar(&listenAddr, "laddr", ":443", "listener address")
flag.StringVar(&remoteAddr, "raddr", "8.8.8.8:443", "remote address")
flag.Parse()
var cert, _ = tls.LoadX509KeyPair(certFile, keyFile)
var tlsConfig = tls.Config{
Certificates: []tls.Certificate{cert},
}
log.Println("starting server")
listener, err := tls.Listen("tcp", listenAddr, &tlsConfig)
if err != nil {
log.Printf("could not listen for connections: ", err.Error())
os.Exit(1)
}
defer listener.Close()
for {
conn, err := listener.Accept()
conn.(*tls.Conn).Handshake()
if err != nil {
log.Printf("error accepting connection: %s", err)
} else {
log.Printf("accepted from %s", conn.RemoteAddr())
go handleConnection(conn, remoteAddr)
}
}
}
func handleConnection(conn net.Conn, remote string) {
defer conn.Close()
// make connection to remote
// When using /etc/hosts to set host=localhost for MITM, you cannot make the
// remote connection by hostname. Using ip for the tls connection will fail on
// cert validation so InsecureSkipVerify is set to true
remoteConn, err := tls.Dial("tcp", remote, &tls.Config{InsecureSkipVerify: true})
if err != nil {
log.Print("failed to connect to remote: " + err.Error())
return
}
defer remoteConn.Close()
remoteConn.Handshake()
serverClosed := make(chan struct{}, 1)
clientClosed := make(chan struct{}, 1)
go broker(remoteConn, conn, clientClosed) // forward client to remote
go broker(conn, remoteConn, serverClosed) // forward remote to client
// wait for one side of the connection to close
// and then signal the other side of the connection to close
var waitFor chan struct{}
select {
case <-clientClosed:
remoteConn.Close()
waitFor = serverClosed
case <-serverClosed:
conn.Close()
waitFor = clientClosed
}
// wait for the other side to finish shutting down
<-waitFor
log.Println("server: conn: closed")
}
// modified from
// https://gist.github.com/jbardin/821d08cb64c01c84b81a
func broker(dst, src net.Conn, srcClosed chan struct{}) {
io.Copy(dst, src) // blocks until either EOF on src or an error occurs
src.Close()
srcClosed <- struct{}{}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment