diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1bf06bdbfa2e416d92276362832915dde2883add --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# go-mmproxy + +This is a Go reimplementation of [mmproxy](https://github.com/cloudflare/mmproxy), created to improve on mmproxy's runtime stability while providing potentially greater performance in terms of connection and packet throughput. + +`go-mmproxy` is a standalone application that unwraps HAProxy's [PROXY protocol](http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) (also adopted by other projects such as NGINX) so that the TCP connection to the end server comes from client's - instead of proxy server's - IP address and port number. +Because they share basic mechanisms, [Cloudflare's blogpost on mmproxy](https://blog.cloudflare.com/mmproxy-creative-way-of-preserving-client-ips-in-spectrum/) serves as a great write-up on how `go-mmproxy` works under the hood. + +## Building + +```shell +go get github.com/path-network/go-mmproxy +``` + +You'll need at least `go 1.11` to build the `go-mmproxy` binary. +See [Go's Getting Started](https://golang.org/doc/install) if your package manager does not carry new enough version of golang. + +## Requirements + +`go-mmproxy` has to be ran: + +- on the same server as the proxy target, as the communication happens over the loopback interface; +- as root or with `CAP_NET_ADMIN` capability to be able to set `IP_TRANSPARENT` socket opt. + +## Running + +### Routing setup + +Route all traffic originating from loopback back to loopback: + +```shell +ip rule add from 127.0.0.1/8 iif lo table 123 +ip route add local 0.0.0.0/0 dev lo table 123 + +ip -6 rule add from ::1/128 iif lo table 123 +ip -6 route add local ::/0 dev lo table 123 +``` + +If `--mark` option is given to `go-mmproxy`, all packets routed to the loopback interface will have the mark set. +This can be used for setting up more advanced routing rules with iptables, for example when you need traffic from loopback to be routed outside of the machine. + +### Starting go-mmproxy + +``` +Usage of ./go-mmproxy: + -4 string + Address to which IPv4 TCP traffic will be forwarded to (default "127.0.0.1:443") + -6 string + Address to which IPv6 TCP traffic will be forwarded to (default "[::1]:443") + -allowed-subnets string + Path to a file that contains allowed subnets of the proxy servers + -l string + Adress the proxy listens on (default "0.0.0.0:8443") + -mark int + The mark that will be set on outbound packets +``` + +Example invocation: + +```shell +sudo ./go-mmproxy -l 0.0.0.0:25577 -4 127.0.0.1:25578 -6 [::1]:25578 --allow-subnets ./path-prefixes.txt +``` diff --git a/main.go b/main.go index a5fdc720aff0b89bcba1a3f0d70a37e80a5be2b0..de40383b5d39d23e12e919dcf0c858ebf7c081e5 100644 --- a/main.go +++ b/main.go @@ -28,10 +28,10 @@ var allowedSubnets []*net.IPNet func init() { flag.StringVar(&listenAddr, "l", "0.0.0.0:8443", "Adress the proxy listens on") - flag.StringVar(&targetAddr4, "4", "0.0.0.0:443", "Address to which IPv4 TCP traffic will be forwarded to") - flag.StringVar(&targetAddr6, "6", "[::]:443", "Address to which IPv6 TCP traffic will be forwarded to") - flag.IntVar(&mark, "mark", 123, "The mark that will be set on outbound packets") - flag.StringVar(&allowedSubnetsPath, "allowed-subnets", "", "Path to a file that contains subnets of the proxy servers") + flag.StringVar(&targetAddr4, "4", "127.0.0.1:443", "Address to which IPv4 TCP traffic will be forwarded to") + flag.StringVar(&targetAddr6, "6", "[::1]:443", "Address to which IPv6 TCP traffic will be forwarded to") + flag.IntVar(&mark, "mark", 0, "The mark that will be set on outbound packets") + flag.StringVar(&allowedSubnetsPath, "allowed-subnets", "", "Path to a file that contains allowed subnets of the proxy servers") } func readRemoteAddrPROXYv2(conn net.Conn, ctrlBuf []byte) (net.Addr, net.Addr, []byte, error) { @@ -172,10 +172,12 @@ func dialUpstreamControl(sport int) func(string, string, syscall.RawConn) error syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, ipBindAddressNoPort, 1) } - syscallErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) - if syscallErr != nil { - syscallErr = fmt.Errorf("setsockopt(SOL_SOCK, SO_MARK, %d): %s", mark, syscallErr.Error()) - return + if mark != 0 { + syscallErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) + if syscallErr != nil { + syscallErr = fmt.Errorf("setsockopt(SOL_SOCK, SO_MARK, %d): %s", mark, syscallErr.Error()) + return + } } if network == "tcp6" {