Skip to content

Transparent http proxy with Golang and tproxy

Transparent http proxy with Golang and tproxy published on 3 Comments on Transparent http proxy with Golang and tproxy

Recently I started interesting in Go language. First impressions of programming in Go are very good. In short brief, I like simplicity of this language, that you can not complicate the code too much. Goroutines and channels looks promising as solution for concurrency, and it seems simple to use. Static compiled binaries are easy to deploy. Performance is good.

I would like to share description and simple implementation in Go of fully transparent reverse or forward http proxy.

Go standard libraries – net/http and net/http/httputil provides everything needed to implement it.
Below the simplest implementation of http proxy:

Now, we can just set http proxy in browser and it will be working.

Transparent http proxy

But what if we want to to setup fully transparent proxy? For example: when we don’t want to configure manually browser on clients, but all outgoing http traffic should be pass by proxy for some reasons – logging, caching, make security scan for viruses etc. In this scenario transparent proxy is located between the client and the internet. Another use case of transparent http proxy is to set up it inline in communication between services in data center and performing some operations like traffic filtering, checking authorization etc.

System Configuration (routing table, tproxy)

I will describing scenario where http transparent proxy is acting on router which is the default gateway for my local network.
My network looks as follows:

Tproxy will be use to redirect traffic.
Tproxy allows as to redirect traffic designated to remote location to the local process.

From Linux 4.18 tproxy is included in nf_tables.

How tproxy works in details is described here:
https://www.kernel.org/doc/Documentation/networking/tproxy.txt
https://powerdns.org/tproxydoc/tproxy.md.html
https://people.netfilter.org/hidden/nfws/nfws-2008-tproxy_slides.pdf

Configuration of routing table and tproxy:

 

Http proxy in Go

I had to wait for Go 1.11 to be able to create custom socket with IP_TRANSPARENT param. From Go 1.11 there is possible to pass socket option before start listening or dialing. ListenConfig provide this.
https://go-review.googlesource.com/c/go/+/72810
https://golang.org/pkg/net/#ListenConfig

The key in implementation is to create custom listener for http.Serve and use LocalAddrContextKey to get destinetion address to which client want to connect. In fact address:port values from http.LocalAddrContextKey, are the values from local socket dynamicly created by tproxy.

Starting proxy:

Client from local network (192.168.1.6) is connecting to remote site on port 217.73.181.197:80. This connection is handled through the proxy.
Nestat is showing one very interesting thing:

Tproxy created tcp socket with remote site address (217.73.181.197:80) on my local machine. My router has only 192.168.1.1 and 37.247.61.7 addresses, routing table 100 does the job.
Go http proxy after receive request from client (192.168.1.6), made a connection to exactly the same address:port as it received. MAGIC! 🙂

3 Comments

hi, I just wonder, what “-j TPROXY –on-port 8888” does in the following rule:

# mark packets with dst port = 80 (and use route table 100) nad redirect to Go http proxy listening on 127.0.0.1:8888
iptables -t mangle -A PREROUTING -s 192.166.1.0/24 -p tcp –dport 80 -j TPROXY –tproxy-mark 0x1/0x1 –on-port 8888 –on-ip 127.0.0.1

Does it tell linux kernel to deriver match package to local process(:8888)? If so, since local process get the package already, why need to change route tables by:
ip route add local 0.0.0.0/0 dev lo table 100
ip rule add fwmark 1 lookup 100

hi, I just wonder, what “-j TPROXY –on-port 8888” does in the following rule:

# mark packets with dst port = 80 (and use route table 100) nad redirect to Go http proxy listening on 127.0.0.1:8888
iptables -t mangle -A PREROUTING -s 192.166.1.0/24 -p tcp –dport 80 -j TPROXY –tproxy-mark 0x1/0x1 –on-port 8888 –on-ip 127.0.0.1

Does it tell linux kernel to deriver match package to local process(:8888)? If so, since local process get the package already, why need to change route tables by:
ip route add local 0.0.0.0/0 dev lo table 100
ip rule add fwmark 1 lookup 100

Leave a Reply

Your email address will not be published. Required fields are marked *